1
2recorder_configure_type(3) Recorder Library recorder_configure_type(3)
3
4
5
7 record_configure_type - Configure format characters for event records
8
9
10
12 #include <recorder/recorder.h>
13
14 typedef size_t (*recorder_type_fn)(intptr_t trace,
15 const char *format,
16 char *buffer,
17 size_tlength,
18 uintptr_tdata);
19 recorder_type_fn recorder_configure_type(uint8_tid, recorder_type_fntype);
20
21
23 The record_configure_type configures a type formatting character iden‐
24 tified by id that can then be used in record(3) format strings. This
25 is normally used to add special extensions to the printf format that
26 deal with to frequently used data types in the program. In the example
27 below, we use record_configure_type('t', todo_format) so that the %t
28 format sequence will format struct todo * pointers.
29
30
31 The function identified by type is called to perform the actual data
32 formatting. It receives the following arguments:
33
34
35 trace is the tracing value associated to the recorder. This can be
36 used to implement safe pointer rendering similar to %+s i.e.
37 only derefrence the given pointer if you know that it is safe to
38 do so. Note that if the format string is %+id then the value of
39 trace will be non-zero.
40
41
42 format is the actual format string, which allows your type function to
43 handle format modifiers or precision indicators similar to the
44 way printf does.
45
46
47 buffer is the buffer where your function should write its output.
48
49
50 length is the amount of space available in the buffer.
51
52
53 data is the actual value stored in the recorder event, for example a
54 pointer to your data.
55
56
57
59 The return value of recorder_configure_type() is the previous function
60 handling the given format identifier.
61
62
63 The type function should return the size it would have written, in the
64 same way snprintf(3) does, i.e. the number of characters that it would
65 have written, irrespective of the size of the buffer.
66
67
68
70 The following program uses recorder_configure_type() to associate the
71 function todo_format() to the %t format sequence. The function inter‐
72 prets its argument as a struct todo * pointer. The main program builds
73 a to-do list from command-line arguments, but it contains a subtle bug
74 that causes it to crash if any of the arguments happens to be "crash".
75
76
77 #include <recorder/recorder.h>
78 #include <string.h>
79 #include <stdlib.h>
80 #include <stdio.h>
81
82 RECORDER(todo, 32, "To-Do List");
83
84 typedef struct todo
85 {
86 const char *name;
87 struct todo *next;
88 } todo;
89
90 size_t todo_format(intptr_t tracing,
91 const char *format,
92 char *buffer, size_t size,
93 uintptr_t arg)
94 {
95 struct todo *t = (struct todo *) arg;
96 if (!tracing || !t)
97 return snprintf(buffer, size, "%p", arg);
98 return snprintf(buffer, size, "todo@%p('%s')", t, t->name);
99 }
100
101 int main(int argc, char **argv)
102 {
103 int a;
104 todo *list = NULL;
105 int crash = 0;
106 recorder_dump_on_common_signals(0, 0);
107 recorder_configure_type('t', todo_format);
108 for (a = 1; a < argc; a++)
109 {
110 record(todo, "Argument %d is '%+s'", a, argv[a]);
111 todo *next = malloc(sizeof(todo));
112 next->name = argv[a];
113 next->next = list;
114 record(todo, "Created entry %t previous was %t", next, list);
115 list = next;
116 crash += strcmp(argv[a], "crash") == 0;
117 }
118
119 while (list || crash)
120 {
121 todo *next = list->next;
122 record(todo, "Had item %t", list);
123 free(list);
124 list = next;
125 }
126 }
127
128
129 The following shows what happens if you run the program normally
130
131 % todo eat pray love
132
133
134 The following shows what happens if you trace the program: the struct
135 todo * values are formatted, showing additional information.
136
137 % RECORDER_TRACES=todo todo eat pray love
138 [34 0.000273] todo: Argument 1 is 'eat'
139 [35 0.000339] todo: Created entry todo@0x50ba80('eat') previous was (nil)
140 [36 0.000352] todo: Argument 2 is 'pray'
141 [37 0.000360] todo: Created entry todo@0x50ba60('pray') previous was todo@0x50ba80('eat')
142 [38 0.000368] todo: Argument 3 is 'love'
143 [39 0.000373] todo: Created entry todo@0x50ba40('love') previous was todo@0x50ba60('pray')
144 [40 0.000386] todo: Had item todo@0x50ba40('love')
145 [41 0.000394] todo: Had item todo@0x50ba60('pray')
146 [42 0.000477] todo: Had item todo@0x50ba80('eat')
147
148
149 The following shows what happens if the program crashes due to bad
150 input: the crash dump no longer indicates the content of the todo
151 pointers, since it would be unsafe to dereference them at that time.
152
153
154 % todo crash and burn
155 [...]
156 [25 0.000052] recorders: Configure type 't' to 0x4011c6 from (nil)
157 [26 0.000067] todo: Argument 1 is 'crash'
158 [27 0.000146] todo: Created entry 0x1f9d260 previous was (nil)
159 [28 0.000150] todo: Argument 2 is 'and'
160 [29 0.000150] todo: Created entry 0x1f9d280 previous was 0x1f9d260
161 [30 0.000150] todo: Argument 3 is 'burn'
162 [31 0.000150] todo: Created entry 0x1f9d2a0 previous was 0x1f9d280
163 [32 0.000151] todo: Had item 0x1f9d2a0
164 [33 0.000152] todo: Had item 0x1f9d280
165 [34 0.000152] todo: Had item 0x1f9d260
166 [35 0.000172] signals: Received signal Segmentation fault (11) si_addr=0x8, dumping recorder
167 [36 0.000209] recorders: Recorder dump
168
169
170 Finally, the following shows what happens if you activate tracing and
171 the program crashes. In that case, the tracing part at the top shows
172 the detailed information. However, during the crash dump, the same
173 events are replayed again (putting them in context with other events),
174 this time without dereferencing the pointer.
175
176
177 % RECORDER_TRACES=todo todo crash and burn
178 [...]
179 [34 0.000184] todo: Argument 1 is 'crash'
180 [35 0.000240] todo: Created entry todo@0xf6aa80('crash') previous was (nil)
181 [36 0.000250] todo: Argument 2 is 'and'
182 [37 0.000255] todo: Created entry todo@0xf6aa60('and') previous was todo@0xf6aa80('crash')
183 [38 0.000260] todo: Argument 3 is 'burn'
184 [39 0.000265] todo: Created entry todo@0xf6aa40('burn') previous was todo@0xf6aa60('and')
185 [40 0.000270] todo: Had item todo@0xf6aa40('burn')
186 [41 0.000275] todo: Had item todo@0xf6aa60('and')
187 [42 0.000279] todo: Had item todo@0xf6aa80('crash')
188 [...]
189 [33 0.000180] recorders: Configure type 't' to 0x4011c6 from (nil)
190 [34 0.000184] todo: Argument 1 is 'crash'
191 [35 0.000240] todo: Created entry 0xf6aa80 previous was (nil)
192 [36 0.000250] todo: Argument 2 is 'and'
193 [37 0.000255] todo: Created entry 0xf6aa60 previous was 0xf6aa80
194 [38 0.000260] todo: Argument 3 is 'burn'
195 [39 0.000265] todo: Created entry 0xf6aa40 previous was 0xf6aa60
196 [40 0.000270] todo: Had item 0xf6aa40
197 [41 0.000275] todo: Had item 0xf6aa60
198 [42 0.000279] todo: Had item 0xf6aa80
199 [43 0.000297] signals: Received signal Segmentation fault (11) si_addr=0x8, dumping recorder
200 [44 0.000309] recorders: Recorder dump
201
202
203
205 Using this function will render your format strings wildly incompatible
206 with the standard printf(3) format, possibly making your code less
207 readable.
208
209
210 There is a very limited number of possible type identifiers. Using this
211 feature in a shared library may cause conflicts with other code that
212 would also want to override the same format character.
213
214
215 It is possible to override standard format characters using this func‐
216 tion. Whether this is a bug or a feature remains to be seen.
217
218
219 Bugs should be reported using https://github.com/c3d/recorder/issues.
220
221
222
224 RECORDER_DEFINE(3), RECORDER_DECLARE(3)
225 recorder_trace_set(3) RECORDER_TRACE(3)
226 recorder_dump(3), recorder_dump_for(3),
227 recorder_configure_output(3), recorder_configure_show(3)
228 recorder_configure_format(3), recorder_configure_type(3)
229
230
231 Additional documentation and tutorials can be found at
232 https://github.com/c3d/recorder.
233
234
235
237 Written by Christophe de Dinechin
238
239
240
2411.0 2019-03-09 recorder_configure_type(3)