Babeltrace 2 C API  2.1.0
Open-source trace manipulation framework
Simple filter component class

This example shows a basic filter component class packaged as a shared object plugin.

The name of the plugin is distill and the name of the filter component class is theone. Therefore the component class is identified in the babeltrace2 command-line tool as filter.distill.theone.

A filter.distill.theone component removes specific event messages from a stream based on the name of their event class.

A filter.distill.theone component accepts a single initialization parameter, names, which is an array value of string values. The array value contains the names of the classes of the events to discard.

A filter.distill.theone component creates a single input port named in and a single output port named out.

To simplify this example, a filter.distill.theone component isn't resilient and needs a valid input and valid initialization parameters. The code also doesn't check the return status codes of API functions for simplicity, but you must check them in production code.

The filter component class implementation and the shared object plugin macros are in the same file, distill.c:

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <string.h>
#include <stdbool.h>
#include <babeltrace2/babeltrace.h>
/* Filter component's private data */
struct distill {
/* Names of the classes of the events to discard (owned by this) */
const bt_value *names_value;
/* Component's input port (weak) */
};
/*
* Initializes the filter component.
*/
static
bt_self_component_filter *self_component_filter,
const bt_value *params, void *initialize_method_data)
{
/* Allocate a private data structure */
struct distill *distill = malloc(sizeof(*distill));
/*
* Keep a reference of the `names` array value parameter so that the
* "next" method of a message iterator can access it to decide
* whether or not to discard an event message.
*/
distill->names_value =
bt_value_get_ref(distill->names_value);
/* Set the component's user data to our private data structure */
distill);
/*
* Add an input port named `in` to the filter component.
*
* This is needed so that this filter component can be connected to
* a filter or a source component. With a connected upstream
* component, this filter component's message iterator can create a
* message iterator to consume messages.
*
* Add an output port named `out` to the filter component.
*
* This is needed so that this filter component can be connected to
* a filter or a sink component. Once a downstream component is
* connected, it can create our message iterator.
*/
"in", NULL, &distill->in_port);
"out", NULL, NULL);
}
/*
* Finalizes the filter component.
*/
static
void distill_finalize(bt_self_component_filter *self_component_filter)
{
/* Retrieve our private data from the component's user data */
struct distill *distill = bt_self_component_get_data(
/* Put all references */
bt_value_put_ref(distill->names_value);
/* Free the allocated structure */
free(distill);
}
/* Message iterator's private data */
struct distill_message_iterator {
/* (Weak) link to the component's private data */
struct distill *distill;
/* Upstream message iterator (owned by this) */
bt_message_iterator *message_iterator;
};
/*
* Initializes the message iterator.
*/
static
distill_message_iterator_initialize(
bt_self_message_iterator *self_message_iterator,
{
/* Allocate a private data structure */
struct distill_message_iterator *distill_iter =
malloc(sizeof(*distill_iter));
/* Retrieve the component's private data from its user data */
struct distill *distill = bt_self_component_get_data(
/* Keep a link to the component's private data */
distill_iter->distill = distill;
/* Create the upstream message iterator */
distill->in_port, &distill_iter->message_iterator);
/* Set the message iterator's user data to our private data structure */
bt_self_message_iterator_set_data(self_message_iterator, distill_iter);
}
/*
* Finalizes the message iterator.
*/
static
void distill_message_iterator_finalize(
bt_self_message_iterator *self_message_iterator)
{
/* Retrieve our private data from the message iterator's user data */
struct distill_message_iterator *distill_iter =
bt_self_message_iterator_get_data(self_message_iterator);
/* Free the allocated structure */
free(distill_iter);
}
/*
* Returns `true` if `message` passes, that is, one of:
*
* * It's not an event message.
* * The event message doesn't need to be discarded based on its event
* class's name.
*/
static
bool message_passes(struct distill_message_iterator *distill_iter,
const bt_message *message)
{
bool passes = true;
/* Move as is if it's not an event message */
goto end;
}
/* Borrow the event message's event and its class */
const bt_event *event =
const bt_event_class *event_class = bt_event_borrow_class_const(event);
/* Get the event class's name */
const char *name = bt_event_class_get_name(event_class);
for (uint64_t i = 0; i < bt_value_array_get_length(
distill_iter->distill->names_value); i++) {
const char *discard_name = bt_value_string_get(
distill_iter->distill->names_value, i));
if (strcmp(name, discard_name) == 0) {
passes = false;
goto end;
}
}
end:
return passes;
}
/*
* Returns the next message to the message iterator's user.
*
* This method can fill the `messages` array with up to `capacity`
* messages.
*
* To keep this example simple, we put a single message into `messages`
* and set `*count` to 1 (if the message iterator isn't ended).
*/
static
bt_message_iterator_class_next_method_status distill_message_iterator_next(
bt_self_message_iterator *self_message_iterator,
bt_message_array_const messages, uint64_t capacity,
uint64_t *count)
{
/* Retrieve our private data from the message iterator's user data */
struct distill_message_iterator *distill_iter =
bt_self_message_iterator_get_data(self_message_iterator);
/* Consume a batch of messages from the upstream message iterator */
bt_message_array_const upstream_messages;
uint64_t upstream_message_count;
consume_upstream_messages:
next_status = bt_message_iterator_next(distill_iter->message_iterator,
&upstream_messages, &upstream_message_count);
/* Initialize the return status to a success */
switch (next_status) {
/* End of iteration: put the message iterator's reference */
bt_message_iterator_put_ref(distill_iter->message_iterator);
goto end;
goto end;
goto end;
goto end;
default:
break;
}
/* Output message array index */
uint64_t i = 0;
/* For each consumed message */
for (uint64_t upstream_i = 0; upstream_i < upstream_message_count;
upstream_i++) {
/* Current message */
const bt_message *upstream_message = upstream_messages[upstream_i];
/* Check if the upstream message passes */
if (message_passes(distill_iter, upstream_message)) {
/* Move upstream message to output message array */
messages[i] = upstream_message;
i++;
continue;
}
/* Discard upstream message: put its reference */
bt_message_put_ref(upstream_message);
}
if (i == 0) {
/*
* We discarded all the upstream messages: get a new batch of
* messages, because this method _cannot_ return
* `BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK` and put no
* messages into its output message array.
*/
goto consume_upstream_messages;
}
*count = i;
end:
return status;
}
/* Mandatory */
/* Define the `distill` plugin */
BT_PLUGIN(distill);
/* Define the `theone` filter component class */
BT_PLUGIN_FILTER_COMPONENT_CLASS(theone, distill_message_iterator_next);
/* Set some of the `theone` filter component class's optional methods */
theone, distill_message_iterator_initialize);
distill_message_iterator_finalize);

As per the Compile and link a Babeltrace 2 shared object plugin guide, you can build the shared object plugin as such:

1 $ cc distill.c -fPIC -c $(pkg-config --cflags babeltrace2)
2 $ ld distill.o -o distill.so -shared $(pkg-config --libs babeltrace2)

With the babeltrace2 tool, you can use a filter.distill.theone component, reading a CTF trace (see babeltrace2-source.ctf.fs(7)) for example:

1 $ babeltrace2 --plugin-path=. /path/to/ctf/trace \
2  --component=filter.distill.theone \
3  --params='names=["sched_switch", "rcu_utilization", "kmem_kfree"]'