This example shows a basic source component class packaged as a shared object plugin.
The name of the plugin is dust
and the name of the source component class is input
. Therefore the component class is identified in the babeltrace2
command-line tool as source.dust.input
.
A source.dust.input
component reads a text file having this fictitious format:
1578694237 154215 send-msg Jowl pig filet mignon, turducken capicola.
1578694237 200774 recv-msg Pork belly pig burgdoggen venison bacon.
1578694241 001831 send-msg Bacon ipsum dolor amet strip steak.
1578694241 944187 send-msg Spare ribs filet mignon boudin bresaola.
1578694245 115406 recv-msg Rump cow t-bone hamburger short tenderloin.
That is:
- Each line represents an event record.
- For a given line:
- The first token is the Unix timestamp (seconds since the Unix epoch) of the record.
- The second token is a number of microseconds to add to the Unix timestamp.
- The third token is the event record's name: only
send-msg
and recv-msg
are possible.
- The remaining characters form the event record's message (payload).
A source.dust.input
component accepts a single initialization parameter, path
, which is the path of the file to open and read.
A source.dust.input
component creates a single output port named out
.
For each line of the input file, a source.dust.input
component's message iterator emits an event message.
To simplify this example, a source.dust.input
component is not 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 source component class implementation and the shared object plugin macros are in the same file, dust.c
:
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <string.h>
#include <babeltrace2/babeltrace.h>
struct dust_in {
};
static
const char *name)
{
msg_field_class);
return event_class;
}
static
struct dust_in *dust_in)
{
dust_in->send_msg_event_class = create_event_class(stream_class,
"send-msg");
dust_in->recv_msg_event_class = create_event_class(stream_class,
"recv-msg");
}
static
const bt_value *params,
void *initialize_method_data)
{
struct dust_in *dust_in = malloc(sizeof(*dust_in));
dust_in->path_value =
create_metadata_and_stream(self_component, dust_in);
"out", NULL, NULL);
}
static
{
free(dust_in);
}
enum dust_in_message_iterator_state {
DUST_IN_MESSAGE_ITERATOR_STATE_STREAM_BEGINNING,
DUST_IN_MESSAGE_ITERATOR_STATE_EVENT,
DUST_IN_MESSAGE_ITERATOR_STATE_ENDED,
};
struct dust_in_message_iterator {
struct dust_in *dust_in;
enum dust_in_message_iterator_state state;
FILE *file;
char name_buffer[32];
char msg_buffer[1024];
};
static
dust_in_message_iterator_initialize(
{
struct dust_in_message_iterator *dust_in_iter =
malloc(sizeof(*dust_in_iter));
dust_in_iter->dust_in = dust_in;
dust_in_iter->state = DUST_IN_MESSAGE_ITERATOR_STATE_STREAM_BEGINNING;
dust_in_iter->file = fopen(path, "r");
}
static
void dust_in_message_iterator_finalize(
{
struct dust_in_message_iterator *dust_in_iter =
fclose(dust_in_iter->file);
free(dust_in_iter);
}
static
struct dust_in_message_iterator *dust_in_iter,
{
uint64_t timestamp;
uint64_t extra_us;
int count = fscanf(dust_in_iter->file, "%" PRIu64 " %" PRIu64 " %s %[^\n]",
×tamp, &extra_us, &dust_in_iter->name_buffer[0],
&dust_in_iter->msg_buffer[0]);
if (count == EOF || feof(dust_in_iter->file)) {
dust_in_iter->dust_in->stream);
dust_in_iter->state = DUST_IN_MESSAGE_ITERATOR_STATE_ENDED;
goto end;
}
if (strcmp(dust_in_iter->name_buffer, "send-msg") == 0) {
event_class = dust_in_iter->dust_in->send_msg_event_class;
} else {
event_class = dust_in_iter->dust_in->recv_msg_event_class;
}
timestamp *= UINT64_C(1000000000);
timestamp += extra_us * UINT64_C(1000);
self_message_iterator, event_class, dust_in_iter->dust_in->stream,
timestamp);
payload_field, 0);
end:
return message;
}
static
uint64_t *count)
{
struct dust_in_message_iterator *dust_in_iter =
switch (dust_in_iter->state) {
case DUST_IN_MESSAGE_ITERATOR_STATE_STREAM_BEGINNING:
dust_in_iter->dust_in->stream);
dust_in_iter->state = DUST_IN_MESSAGE_ITERATOR_STATE_EVENT;
break;
case DUST_IN_MESSAGE_ITERATOR_STATE_EVENT:
message = create_message_from_line(dust_in_iter,
self_message_iterator);
break;
case DUST_IN_MESSAGE_ITERATOR_STATE_ENDED:
status =
goto end;
}
if (message) {
messages[0] = message;
*count = 1;
}
end:
return status;
}
dust_in_message_iterator_initialize);
dust_in_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 dust.c -fPIC -c $(pkg-config --cflags babeltrace2)
2 $ ld dust.o -o dust.so -shared $(pkg-config --libs babeltrace2)
With the babeltrace2
tool, assuming you have a valid input file named dust
, you can view the event messages that a source.dust.input
message iterator emits:
1 $ babeltrace2 --plugin-path=. --component=source.dust.input --params='path="dust"'
The output is similar to:
1 [17:10:37.154215000] (+?.?????????) send-msg: { msg = "Jowl pig filet mignon, turducken capicola." }
2 [17:10:37.200774000] (+0.046559000) recv-msg: { msg = "Pork belly pig burgdoggen venison bacon." }
3 [17:10:41.001831000] (+3.801057000) send-msg: { msg = "Bacon ipsum dolor amet strip steak." }
4 [17:10:41.944187000] (+0.942356000) send-msg: { msg = "Spare ribs filet mignon boudin bresaola." }
5 [17:10:45.115406000] (+3.171219000) recv-msg: { msg = "Rump cow t-bone hamburger short tenderloin." }
You can also view more details with
1 $ babeltrace2 --plugin-path=. --component=source.dust.input --params='path="dust"' \
2 --component=sink.text.details