Skip to content

Writing your own barectf platform

Philippe Proulx edited this page Sep 10, 2020 · 2 revisions

This page shows the steps involved in writing your own, custom barectf platform.

Platform initialization function

A barectf platform initialization function is responsible for initializing barectf context(s) (calling barectf_init(), where barectf_ is the configured prefix), and opening the very first packet (calling barectf_stream_open_packet() with target-specific parameters, for each stream, where stream is the stream name).

barectf generates one context C structure for each defined stream. They all contain the same first member, a structure with common properties.

barectf generates a single context initialization function:

void barectf_init(
    void *vctx,
    uint8_t *buf,
    uint32_t buf_size,
    struct barectf_platform_callbacks cbs,
    void *data
);

This function must be called with each stream-specific context structure to be used afterwards. The parameters are:

  • vctx: stream-specific barectf context (allocated by caller)
  • buf: buffer to use for this stream's packet (allocated by caller)
  • buf_size: size of buf in bytes
  • cbs: platform callback functions to be used with this stream-specific context
  • data: user data passed to platform callback functions (cbs)

Example

#define BUF_SZ 4096

void platform_init(/* ... */)
{
    struct barectf_my_stream_ctx *ctx;
    uint8_t *buf;
    struct my_data *my_data;
    struct barectf_platform_callbacks cbs = {
        /* ... */
    };

    ctx = platform_alloc(sizeof(*ctx));
    buf = platform_alloc(BUF_SZ);
    my_data = platform_alloc(sizeof(*my_data));
    my_data->ctx = ctx;
    barectf_init(ctx, buf, BUF_SZ, cbs, my_data);

    /* ... */
}

barectf generates one packet opening and one packet closing function per defined stream, since each stream may have custom parameters at the packet opening time, and custom offsets of fields to write at packet closing time.

The platform initialization should open the very first packet of each stream to use because the tracing functions expect the current packet to be opened.

Here's an example of a packet opening function prototype:

void barectf_my_stream_open_packet(
    struct barectf_my_stream_ctx *ctx,
    float pc_something
);

The function needs the stream-specific barectf context, as well as any custom trace packet header or stream packet context field; in this last example, something is a floating point number stream packet context field.

barectf packet information API

There's a small API to query stuff about the current packet of a given barectf context:

uint32_t barectf_packet_size(const void *ctx);
int barectf_packet_is_full(const void *ctx);
int barectf_packet_is_empty(const void *ctx);
uint32_t barectf_packet_events_discarded(const void *ctx);
uint8_t *barectf_packet_buf(const void *ctx);
void barectf_packet_set_buf(void *ctx, uint8_t *buf, uint32_t buf_size);
uint32_t barectf_packet_buf_size(const void *ctx);
int barectf_packet_is_open(const void *ctx);

barectf_packet_is_full() returns 1 if the context's current packet is full (no space left for any event), 0 otherwise.

barectf_packet_is_empty() returns 1 if the context's current packet is empty (no recorded events), 0 otherwise.

barectf_packet_events_discarded() returns the number of lost (discarded) events so far for a given stream.

The buffer size (buf_size parameter of barectf_packet_set_buf() and return value of barectf_packet_buf_size()) is always a number of bytes.

barectf_packet_is_open() returns 1 if the context's current packet is open (the packet opening function was called with this context).

Platform callback functions

The callback functions to implement for a given platform are in the generated barectf_platform_callbacks C structure. This structure will contain:

  • One callback function per defined clock, using the clock's return C type. Those functions must return the current clock values.
  • is_backend_full(): is the back-end full? If a new packet is opened now, does it have its reserved space in the back-end? Return 0 if it does, 1 otherwise.
  • open_packet(): this callback function must call the relevant packet opening function.
  • close_packet(): this callback function must call the relevant packet closing function and copy/move the current packet to the back-end.

What exactly is a back-end is left to the platform implementor. It could be a ring buffer of packets, or it could be dumber: close_packet() always appends the current packet to some medium, and is_backend_full() always returns 0 (back-end is never full).

Typically, if is_backend_full() returns 0, then the next call to close_packet() should be able to write the current packet. If is_backend_full() returns 1, there will be lost (discarded) events. If a stream packet context has an events_discarded field, it will be written to accordingly when a packet is closed.

If a platform needs double buffering, open_packet() is the callback function where packet buffers would be swapped (before calling the barectf packet opening function).

Platform finalization function

The platform finalization function should be called by the application when tracing is no more required. It is responsible for closing the very last packet of each stream.

Typically, assuming there's only one stream (named my_stream in this example), the finalization function will look like this:

void platform_tracing_finalize(struct platform_data *platform_data)
{
    if (barectf_packet_is_open(platform_data->ctx) &&
            !barectf_packet_is_empty(platform_data->ctx)) {
        barectf_my_stream_close_packet(platform_data->ctx);

        /*
         * Do whatever is necessary here to write the packet
         * to the platform's back-end.
         */
    }
}

That is: if the packet is still open (thus not closed and written yet) and it contains at least one event (not empty), close and write the last packet.

Note, however, that you might be interested in closing an open empty packet, since its packet context could update the discarded events count (if there were lost events between the last packet closing time and now, which is quite possible if the back-end became full after closing and writing the previous packet).