[lttng-dev] Usage example of libbabeltrace (babeltrace2) to read CTF traces from a GUI
Philippe Proulx
pproulx at efficios.com
Fri Jun 18 23:42:01 EDT 2021
----- Original Message -----
> From: "Steven Rostedt" <rostedt at goodmis.org>
> To: "Philippe Proulx" <pproulx at efficios.com>
> Cc: "Mathieu Desnoyers" <mathieu.desnoyers at efficios.com>, "Jeremie Galarneau" <jgalar at efficios.com>, "lttng-dev"
> <lttng-dev at lists.lttng.org>, linux-trace-devel at vger.kernel.org
> Sent: Tuesday, 15 June, 2021 11:57:38
> Subject: Re: [lttng-dev] Usage example of libbabeltrace (babeltrace2) to read CTF traces from a GUI
> [ Adding linux-trace-devel too ]
>
> On Tue, 15 Jun 2021 11:13:41 -0400
> Philippe Proulx <pproulx at efficios.com> wrote:
>
>> I can cook a minimal working example in the following days.
>
> That would be great, thanks!
Here you go (Pastebin: <https://pastebin.com/0MFQkiAw>):
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <assert.h>
#include <babeltrace2/babeltrace.h>
/*
* Shared data between our relay sink component and the
* bt_graph_run_once() call site.
*/
struct relay_data {
bt_message_array_const msgs;
uint64_t msg_count;
};
/*
* Consumer method of our relay sink component class.
*
* See <https://babeltrace.org/docs/v2.0/libbabeltrace2/group__api-graph.html#ga301a677396bd8f5bd8b920fd5fa60418>.
*/
static bt_graph_simple_sink_component_consume_func_status relay_consume(
bt_message_iterator * const msg_iter,
void * const user_data)
{
struct relay_data * const relay_data =
(struct relay_data *) user_data;
bt_message_iterator_next_status msg_iter_next_status;
bt_graph_simple_sink_component_consume_func_status status =
BT_GRAPH_SIMPLE_SINK_COMPONENT_CONSUME_FUNC_STATUS_OK;
/* Consume the next messages, storing them in `*relay_data` */
msg_iter_next_status = bt_message_iterator_next(msg_iter,
&relay_data->msgs, &relay_data->msg_count);
switch (msg_iter_next_status) {
case BT_MESSAGE_ITERATOR_NEXT_STATUS_END:
status = BT_GRAPH_SIMPLE_SINK_COMPONENT_CONSUME_FUNC_STATUS_END;
break;
case BT_GRAPH_SIMPLE_SINK_COMPONENT_CONSUME_FUNC_STATUS_MEMORY_ERROR:
status = BT_GRAPH_SIMPLE_SINK_COMPONENT_CONSUME_FUNC_STATUS_MEMORY_ERROR;
break;
case BT_GRAPH_SIMPLE_SINK_COMPONENT_CONSUME_FUNC_STATUS_ERROR:
status = BT_GRAPH_SIMPLE_SINK_COMPONENT_CONSUME_FUNC_STATUS_ERROR;
break;
default:
assert(status ==
BT_GRAPH_SIMPLE_SINK_COMPONENT_CONSUME_FUNC_STATUS_OK);
break;
}
return status;
}
/*
* Adds our own sink component named `relay` to the trace processing
* graph `graph`.
*
* On success, `*comp` is the added sink component.
*
* See <https://babeltrace.org/docs/v2.0/libbabeltrace2/group__api-graph.html#api-graph-lc-add-ss>.
*/
static bt_graph_add_component_status add_relay_comp(bt_graph * const graph,
struct relay_data * const relay_data,
const bt_component_sink ** const comp)
{
return bt_graph_add_simple_sink_component(graph, "relay", NULL,
relay_consume, NULL, relay_data, comp);
}
/*
* Creates and returns the parameters to initialize the `src.ctf.fs`
* component with the trace directory `trace_dir`.
*
* See <https://babeltrace.org/docs/v2.0/libbabeltrace2/group__api-val.html>.
*/
static bt_value *create_ctf_fs_comp_params(const char * const trace_dir)
{
bt_value *params;
bt_value *inputs = NULL;
bt_value_map_insert_entry_status insert_entry_status;
bt_value_array_append_element_status append_elem_status;
/* Create an empty map value */
params = bt_value_map_create();
if (!params) {
goto error;
}
/*
* Insert an empty array value having the key `inputs`.
*
* `inputs` is a borrowed value object here, _not_ our
* reference.
*/
insert_entry_status = bt_value_map_insert_empty_array_entry(params,
"inputs", &inputs);
if (insert_entry_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
goto error;
}
/* Append the trace directory to the `inputs` array value */
append_elem_status = bt_value_array_append_string_element(inputs,
trace_dir);
if (append_elem_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
goto error;
}
goto end;
error:
BT_VALUE_PUT_REF_AND_RESET(params);
end:
return params;
}
/*
* Adds a `src.ctf.fs` component named `ctf` to read the trace directory
* `trace_dir` to the trace processing graph `graph`.
*
* See <https://babeltrace.org/docs/v2.0/man7/babeltrace2-source.ctf.fs.7/>.
*
* On success, `*comp` is the added source component.
*/
static bt_graph_add_component_status add_ctf_fs_comp(bt_graph * const graph,
const char * const trace_dir,
const bt_component_source ** const comp)
{
const bt_plugin *plugin;
bt_plugin_find_status plugin_find_status;
const bt_component_class_source *comp_cls;
bt_value *params = NULL;
bt_graph_add_component_status add_comp_status;
/* Find the `ctf` plugin */
plugin_find_status = bt_plugin_find("ctf", BT_TRUE, BT_TRUE,
BT_TRUE, BT_TRUE, BT_TRUE, &plugin);
if (plugin_find_status != BT_PLUGIN_FIND_STATUS_OK) {
goto error;
}
/* Borrow the `fs` source component class within the `ctf` plugin */
comp_cls = bt_plugin_borrow_source_component_class_by_name_const(
plugin, "fs");
if (!comp_cls) {
goto error;
}
/* Create the parameters to initialize the source component */
params = create_ctf_fs_comp_params(trace_dir);
if (!params) {
goto error;
}
/* Add the source component to the graph */
add_comp_status = bt_graph_add_source_component(graph, comp_cls,
"ctf", params, BT_LOGGING_LEVEL_NONE, comp);
goto end;
error:
add_comp_status = BT_GRAPH_ADD_COMPONENT_STATUS_ERROR;
end:
bt_plugin_put_ref(plugin);
bt_value_put_ref(params);
return add_comp_status;
}
/*
* Adds a `flt.utils.muxer` component named `muxer` to the trace
* processing graph `graph`.
*
* See <https://babeltrace.org/docs/v2.0/man7/babeltrace2-filter.utils.muxer.7/>.
*
* On success, `*comp` is the added filter component.
*/
static bt_graph_add_component_status add_muxer_comp(bt_graph * const graph,
const bt_component_filter ** const comp)
{
const bt_plugin *plugin;
bt_plugin_find_status plugin_find_status;
const bt_component_class_filter *comp_cls;
bt_graph_add_component_status add_comp_status;
/* Find the `utils` plugin */
plugin_find_status = bt_plugin_find("utils", BT_TRUE, BT_TRUE,
BT_TRUE, BT_TRUE, BT_TRUE, &plugin);
if (plugin_find_status != BT_PLUGIN_FIND_STATUS_OK) {
goto error;
}
/* Borrow the `muxer` filter comp. class within the `utils` plugin */
comp_cls = bt_plugin_borrow_filter_component_class_by_name_const(
plugin, "muxer");
if (!comp_cls) {
goto error;
}
/* Add the filter component to the graph (no init. parameters) */
add_comp_status = bt_graph_add_filter_component(graph, comp_cls,
"muxer", NULL, BT_LOGGING_LEVEL_NONE, comp);
goto end;
error:
add_comp_status = BT_GRAPH_ADD_COMPONENT_STATUS_ERROR;
end:
bt_plugin_put_ref(plugin);
return add_comp_status;
}
/*
* Creates a trace processing graph having this layout:
*
* +------------+ +-----------------+ +--------------+
* | src.ctf.fs | | flt.utils.muxer | | Our own sink |
* | [ctf] | | [muxer] | | [relay] |
* | | | | | |
* | stream0 @--->@ in0 out @--->@ in |
* | stream1 @--->@ in1 | +--------------+
* | stream2 @--->@ in2 |
* | stream3 @--->@ in3 |
* +------------+ @ in4 |
* +-----------------+
*
* In the example above, the `src.ctf.fs` component reads a CTF trace
* having four data streams. The `trace_dir` parameter is the directory
* containing the CTF trace to read.
*
* Our own relay sink component, of which the consuming method is
* relay_consume(), consumes messages from the `flt.utils.muxer`
* component, storing them to a structure (`*relay_data`) shared with
* the bt_graph_run_once() call site.
*
* See <https://babeltrace.org/docs/v2.0/libbabeltrace2/group__api-graph.html>.
*/
static bt_graph *create_graph(const char * const trace_dir,
struct relay_data * const relay_data)
{
bt_graph *graph;
const bt_component_source *ctf_fs_comp;
const bt_component_filter *muxer_comp;
const bt_component_sink *relay_comp;
bt_graph_add_component_status add_comp_status;
bt_graph_connect_ports_status connect_ports_status;
uint64_t i;
/* Create an empty trace processing graph */
graph = bt_graph_create(0);
if (!graph) {
goto error;
}
/* Create and add the three required components to `graph` */
add_comp_status = add_ctf_fs_comp(graph, trace_dir, &ctf_fs_comp);
if (add_comp_status != BT_GRAPH_ADD_COMPONENT_STATUS_OK) {
goto error;
}
add_comp_status = add_muxer_comp(graph, &muxer_comp);
if (add_comp_status != BT_GRAPH_ADD_COMPONENT_STATUS_OK) {
goto error;
}
add_comp_status = add_relay_comp(graph, relay_data, &relay_comp);
if (add_comp_status != BT_GRAPH_ADD_COMPONENT_STATUS_OK) {
goto error;
}
/*
* Connect all the output ports of the `ctf` source component to
* the input ports of the `muxer` filter component.
*
* An `flt.utils.muxer` component adds an input port every time
* you connect one, making one always available.
*
* See <https://babeltrace.org/docs/v2.0/man7/babeltrace2-filter.utils.muxer.7/#doc-_input>.
*/
for (i = 0; i < bt_component_source_get_output_port_count(ctf_fs_comp);
i++) {
const bt_port_output * const out_port =
bt_component_source_borrow_output_port_by_index_const(
ctf_fs_comp, i);
const bt_port_input * const in_port =
bt_component_filter_borrow_input_port_by_index_const(
muxer_comp, i);
/* Connect ports */
connect_ports_status = bt_graph_connect_ports(graph,
out_port, in_port, NULL);
if (connect_ports_status != BT_GRAPH_CONNECT_PORTS_STATUS_OK) {
goto error;
}
}
/* Connect the `muxer` output port to the `relay` input port */
connect_ports_status = bt_graph_connect_ports(graph,
bt_component_filter_borrow_output_port_by_index_const(
muxer_comp, 0),
bt_component_sink_borrow_input_port_by_index_const(
relay_comp, 0), NULL);
if (connect_ports_status != BT_GRAPH_CONNECT_PORTS_STATUS_OK) {
goto error;
}
goto end;
error:
BT_GRAPH_PUT_REF_AND_RESET(graph);
end:
return graph;
}
/*
* Handles a single message `msg`, printing its name if it's an event
* message.
*
* See <https://babeltrace.org/docs/v2.0/libbabeltrace2/group__api-msg.html>.
*/
static void handle_msg(const bt_message * const msg)
{
const bt_event *event;
if (bt_message_get_type(msg) != BT_MESSAGE_TYPE_EVENT) {
goto end;
}
event = bt_message_event_borrow_event_const(msg);
puts(bt_event_class_get_name(bt_event_borrow_class_const(event)));
end:
return;
}
/*
* Runs the trace processing graph `graph`, our relay sink component
* transferring its consumed messages to `*relay_data`.
*
* See <https://babeltrace.org/docs/v2.0/libbabeltrace2/group__api-graph.html#api-graph-lc-run>.
*/
static int run_graph(bt_graph * const graph,
struct relay_data * const relay_data)
{
bt_graph_run_once_status status;
while (true) {
uint64_t i;
/*
* bt_graph_run_once() calls the consuming method of
* our relay sink component (relay_consume()).
*
* relay_consume() consumes a batch of messages from the
* `flt.utils.muxer` component and stores them in
* `*relay_data`.
*/
status = bt_graph_run_once(graph);
assert(status != BT_GRAPH_RUN_ONCE_STATUS_AGAIN);
if (status != BT_GRAPH_RUN_ONCE_STATUS_OK) {
break;
}
/* Handle each consumed message */
for (i = 0; i < relay_data->msg_count; i++) {
const bt_message * const msg = relay_data->msgs[i];
handle_msg(msg);
/*
* The message reference `msg` is ours: release
* it now.
*/
bt_message_put_ref(msg);
}
}
return status == BT_GRAPH_RUN_ONCE_STATUS_END ? 0 : -1;
}
/*
* Reads the CTF trace located in the directory `argv[1]`, printing one
* event name per line to the standard output.
*/
int main(const int argc, const char * const * const argv)
{
int ret = EXIT_SUCCESS;
struct relay_data relay_data = { 0 };
bt_graph *graph;
assert(argc == 2);
/* Create the trace processing graph */
graph = create_graph(argv[1], &relay_data);
if (!graph) {
ret = EXIT_FAILURE;
goto end;
}
/* Run the graph, printing one event name per line */
if (run_graph(graph, &relay_data)) {
ret = EXIT_FAILURE;
}
end:
bt_graph_put_ref(graph);
return ret;
}
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Build as such:
$ cc -Wextra -Wall -o read-ctf-example read-ctf-example.c \
$(pkg-config babeltrace2 --cflags --libs)
Feel free to ask us any questions.
Philippe Proulx
EfficiOS Inc.
https://www.efficios.com/
>
> -- Steve
>
>>
>> Phil
>>
>> [1]: https://babeltrace.org/docs/v2.0/libbabeltrace2/
>> [2]:
>> https://babeltrace.org/docs/v2.0/libbabeltrace2/group__api-graph.html#gaa8432f03a967d01b764fb1bc959c8e89
>>
>> >
>> > Thanks,
>> >
>> > Mathieu
>> >
>> > --
>> > Mathieu Desnoyers
>> > EfficiOS Inc.
>> > http://www.efficios.com
>> > _______________________________________________
>> > lttng-dev mailing list
>> > lttng-dev at lists.lttng.org
> > > https://lists.lttng.org/cgi-bin/mailman/listinfo/lttng-dev
More information about the lttng-dev
mailing list