recorder_configure_type(3)

NAME
record_configure_type - Configure format characters for event records

SYNOPSIS
#include <recorder/recorder.h>

typedef size_t (*recorder_type_fn)(intptr_t trace,
                                   const char *format,
                                    char *buffer,
                                    size_tlength,
                                    uintptr_tdata);
recorder_type_fn   recorder_configure_type(uint8_tid, recorder_type_fntype);

DESCRIPTION

The record_configure_type configures a type formatting character identified by id that can then be used in record(3) format strings. This is normally used to add special extensions to the printf format that deal with to frequently used data types in the program. In the example below, we use record_configure_type('t', todo_format) so that the %t format sequence will format struct todo * pointers.

The function identified by type is called to perform the actual data formatting. It receives the following arguments:

trace
    is the tracing value associated to the recorder. This can be used to implement safe pointer rendering similar to %+s i.e. only derefrence the given pointer if you know that it is safe to do so. Note that if the format string is %+id then the value of trace will be non-zero.

format
    is the actual format string, which allows your type function to handle format modifiers or precision indicators similar to the way printf does.

buffer
    is the buffer where your function should write its output.

length
    is the amount of space available in the buffer.

data
    is the actual value stored in the recorder event, for example a pointer to your data.

RETURN VALUE

The return value of recorder_configure_type() is the previous function handling the given format identifier.

The type function should return the size it would have written, in the same way snprintf(3) does, i.e. the number of characters that it would have written, irrespective of the size of the buffer.

EXAMPLES

The following program uses recorder_configure_type() to associate the function todo_format() to the %t format sequence. The function interprets its argument as a struct todo * pointer. The main program builds a to-do list from command-line arguments, but it contains a subtle bug that causes it to crash if any of the arguments happens to be "crash".

    #include <recorder/recorder.h>
    #include <string.h>
    #include <stdlib.h>
    #include <stdio.h>
    RECORDER(todo, 32, "To-Do List");
    typedef struct todo
    {
        const char *name;
        struct todo *next;
    } todo;
    size_t todo_format(intptr_t tracing,
                       const char *format,
                       char *buffer, size_t size,
                       uintptr_t arg)
    {
        struct todo *t = (struct todo *) arg;
        if (!tracing || !t)
            return snprintf(buffer, size, "%p", arg);
        return snprintf(buffer, size, "todo@%p('%s')", t, t->name);
    }
    int main(int argc, char **argv)
    {
        int a;
        todo *list = NULL;
        int crash = 0;
        recorder_dump_on_common_signals(0, 0);
        recorder_configure_type('t', todo_format);
        for (a = 1; a < argc; a++)
        {
            record(todo, "Argument %d is '%+s'", a, argv[a]);
            todo *next = malloc(sizeof(todo));
            next->name = argv[a];
            next->next = list;
            record(todo, "Created entry %t previous was %t", next, list);
            list = next;
            crash += strcmp(argv[a], "crash") == 0;
        }
        while (list || crash)
        {
            todo *next = list->next;
            record(todo, "Had item %t", list);
            free(list);
            list = next;
        }
    }

The following shows what happens if you run the program normally

    % todo eat pray love

The following shows what happens if you trace the program: the struct todo * values are formatted, showing additional information.
    % RECORDER_TRACES=todo todo eat pray love
    [34 0.000273] todo: Argument 1 is 'eat'
    [35 0.000339] todo: Created entry todo@0x50ba80('eat') previous was (nil)
    [36 0.000352] todo: Argument 2 is 'pray'
    [37 0.000360] todo: Created entry todo@0x50ba60('pray') previous was todo@0x50ba80('eat')
    [38 0.000368] todo: Argument 3 is 'love'
    [39 0.000373] todo: Created entry todo@0x50ba40('love') previous was todo@0x50ba60('pray')
    [40 0.000386] todo: Had item todo@0x50ba40('love')
    [41 0.000394] todo: Had item todo@0x50ba60('pray')
    [42 0.000477] todo: Had item todo@0x50ba80('eat')

The following shows what happens if the program crashes due to bad input: the crash dump no longer indicates the content of the todo pointers, since it would be unsafe to dereference them at that time.

    % todo crash and burn
    [...]
    [25 0.000052] recorders: Configure type 't' to 0x4011c6 from (nil)
    [26 0.000067] todo: Argument 1 is 'crash'
    [27 0.000146] todo: Created entry 0x1f9d260 previous was (nil)
    [28 0.000150] todo: Argument 2 is 'and'
    [29 0.000150] todo: Created entry 0x1f9d280 previous was 0x1f9d260
    [30 0.000150] todo: Argument 3 is 'burn'
    [31 0.000150] todo: Created entry 0x1f9d2a0 previous was 0x1f9d280
    [32 0.000151] todo: Had item 0x1f9d2a0
    [33 0.000152] todo: Had item 0x1f9d280
    [34 0.000152] todo: Had item 0x1f9d260
    [35 0.000172] signals: Received signal Segmentation fault (11) si_addr=0x8, dumping recorder
    [36 0.000209] recorders: Recorder dump

Finally, the following shows what happens if you activate tracing and the program crashes. In that case, the tracing part at the top shows the detailed information. However, during the crash dump, the same events are replayed again (putting them in context with other events), this time without dereferencing the pointer.

    % RECORDER_TRACES=todo todo crash and burn
    [...]
    [34 0.000184] todo: Argument 1 is 'crash'
    [35 0.000240] todo: Created entry todo@0xf6aa80('crash') previous was (nil)
    [36 0.000250] todo: Argument 2 is 'and'
    [37 0.000255] todo: Created entry todo@0xf6aa60('and') previous was todo@0xf6aa80('crash')
    [38 0.000260] todo: Argument 3 is 'burn'
    [39 0.000265] todo: Created entry todo@0xf6aa40('burn') previous was todo@0xf6aa60('and')
    [40 0.000270] todo: Had item todo@0xf6aa40('burn')
    [41 0.000275] todo: Had item todo@0xf6aa60('and')
    [42 0.000279] todo: Had item todo@0xf6aa80('crash')
    [...]
    [33 0.000180] recorders: Configure type 't' to 0x4011c6 from (nil)
    [34 0.000184] todo: Argument 1 is 'crash'
    [35 0.000240] todo: Created entry 0xf6aa80 previous was (nil)
    [36 0.000250] todo: Argument 2 is 'and'
    [37 0.000255] todo: Created entry 0xf6aa60 previous was 0xf6aa80
    [38 0.000260] todo: Argument 3 is 'burn'
    [39 0.000265] todo: Created entry 0xf6aa40 previous was 0xf6aa60
    [40 0.000270] todo: Had item 0xf6aa40
    [41 0.000275] todo: Had item 0xf6aa60
    [42 0.000279] todo: Had item 0xf6aa80
    [43 0.000297] signals: Received signal Segmentation fault (11) si_addr=0x8, dumping recorder
    [44 0.000309] recorders: Recorder dump

BUGS

Using this function will render your format strings wildly incompatible with the standard printf(3) format, possibly making your code less readable.

There is a very limited number of possible type identifiers. Using this feature in a shared library may cause conflicts with other code that would also want to override the same format character.

It is possible to override standard format characters using this function. Whether this is a bug or a feature remains to be seen.

Bugs should be reported using https://github.com/c3d/recorder/issues.

SEE ALSO
RECORDER_DEFINE(3), RECORDER_DECLARE(3)
recorder_trace_set(3) RECORDER_TRACE(3)
recorder_dump(3), recorder_dump_for(3),
recorder_configure_output(3), recorder_configure_show(3)
recorder_configure_format(3), recorder_configure_type(3)

Additional documentation and tutorials can be found at https://github.com/c3d/recorder.

AUTHOR
Written by Christophe de Dinechin