d2007fbd |
1 | /* |
2 | * Marker support code. |
3 | * |
4 | * Mathieu Desnoyers, August 2007 |
5 | * License: LGPL. |
6 | */ |
7 | |
8 | #include <glib.h> |
3c165eaf |
9 | #include <stdio.h> |
10 | #include <string.h> |
11 | #include <errno.h> |
d2007fbd |
12 | #include <ltt/compiler.h> |
13 | #include <ltt/markers.h> |
3c165eaf |
14 | #include <ltt/ltt-private.h> |
d2007fbd |
15 | |
16 | #define DEFAULT_MARKERS_NUM 100 |
3c165eaf |
17 | #define DEFAULT_FIELDS_NUM 1 |
18 | #define MAX_NAME_LEN 1024 |
d2007fbd |
19 | |
3c165eaf |
20 | static inline const char *parse_trace_type(struct marker_info *info, |
21 | const char *fmt, |
22 | char *trace_size, enum ltt_type *trace_type, |
23 | unsigned long *attributes) |
24 | { |
25 | int qualifier; /* 'h', 'l', or 'L' for integer fields */ |
26 | /* 'z' support added 23/7/1999 S.H. */ |
27 | /* 'z' changed to 'Z' --davidm 1/25/99 */ |
28 | /* 't' added for ptrdiff_t */ |
29 | |
30 | /* parse attributes. */ |
31 | repeat: |
32 | switch (*fmt) { |
33 | case 'b': |
34 | *attributes |= LTT_ATTRIBUTE_COMPACT; |
35 | ++fmt; |
36 | goto repeat; |
37 | case 'n': |
38 | *attributes |= LTT_ATTRIBUTE_NETWORK_BYTE_ORDER; |
39 | ++fmt; |
40 | goto repeat; |
41 | } |
42 | |
43 | /* get the conversion qualifier */ |
44 | qualifier = -1; |
45 | if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || |
46 | *fmt =='Z' || *fmt == 'z' || *fmt == 't' || |
47 | *fmt == 'S' || *fmt == '1' || *fmt == '2' || |
48 | *fmt == '4' || *fmt == 8) { |
49 | qualifier = *fmt; |
50 | ++fmt; |
51 | if (qualifier == 'l' && *fmt == 'l') { |
52 | qualifier = 'L'; |
53 | ++fmt; |
54 | } |
55 | } |
56 | |
57 | switch (*fmt) { |
58 | case 'c': |
59 | *trace_type = LTT_TYPE_UNSIGNED_INT; |
60 | *trace_size = sizeof(char); |
61 | goto parse_end; |
62 | case 's': |
63 | *trace_type = LTT_TYPE_STRING; |
64 | goto parse_end; |
65 | case 'p': |
66 | *trace_type = LTT_TYPE_UNSIGNED_INT; |
67 | *trace_size = info->pointer_size; |
68 | goto parse_end; |
69 | case 'd': |
70 | case 'i': |
71 | *trace_type = LTT_TYPE_SIGNED_INT; |
72 | break; |
73 | case 'o': |
74 | case 'u': |
75 | case 'x': |
76 | case 'X': |
77 | *trace_type = LTT_TYPE_UNSIGNED_INT; |
78 | break; |
79 | default: |
80 | if (!*fmt) |
81 | --fmt; |
82 | goto parse_end; |
83 | } |
84 | switch (qualifier) { |
85 | case 'L': |
86 | *trace_size = sizeof(long long); |
87 | break; |
88 | case 'l': |
89 | *trace_size = info->long_size; |
90 | break; |
91 | case 'Z': |
92 | case 'z': |
93 | *trace_size = info->size_t_size; |
94 | break; |
95 | case 't': |
96 | *trace_size = info->pointer_size; |
97 | break; |
98 | case 'h': |
99 | *trace_size = sizeof(short); |
100 | break; |
101 | case '1': |
102 | *trace_size = sizeof(uint8_t); |
103 | break; |
104 | case '2': |
105 | *trace_size = sizeof(guint16); |
106 | break; |
107 | case '4': |
108 | *trace_size = sizeof(uint32_t); |
109 | break; |
110 | case '8': |
111 | *trace_size = sizeof(uint64_t); |
112 | break; |
113 | default: |
114 | *trace_size = info->int_size; |
115 | } |
116 | |
117 | parse_end: |
118 | return fmt; |
119 | } |
120 | |
121 | /* |
122 | * Restrictions: |
123 | * Field width and precision are *not* supported. |
124 | * %n not supported. |
125 | */ |
126 | __attribute__((no_instrument_function)) |
127 | static inline const char *parse_c_type(struct marker_info *info, |
128 | const char *fmt, |
129 | char *c_size, enum ltt_type *c_type) |
130 | { |
131 | int qualifier; /* 'h', 'l', or 'L' for integer fields */ |
132 | /* 'z' support added 23/7/1999 S.H. */ |
133 | /* 'z' changed to 'Z' --davidm 1/25/99 */ |
134 | /* 't' added for ptrdiff_t */ |
135 | |
136 | /* process flags : ignore standard print formats for now. */ |
137 | repeat: |
138 | switch (*fmt) { |
139 | case '-': |
140 | case '+': |
141 | case ' ': |
142 | case '#': |
143 | case '0': |
144 | ++fmt; |
145 | goto repeat; |
146 | } |
147 | |
148 | /* get the conversion qualifier */ |
149 | qualifier = -1; |
150 | if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || |
151 | *fmt =='Z' || *fmt == 'z' || *fmt == 't' || |
152 | *fmt == 'S') { |
153 | qualifier = *fmt; |
154 | ++fmt; |
155 | if (qualifier == 'l' && *fmt == 'l') { |
156 | qualifier = 'L'; |
157 | ++fmt; |
158 | } |
159 | } |
160 | |
161 | switch (*fmt) { |
162 | case 'c': |
163 | *c_type = LTT_TYPE_UNSIGNED_INT; |
164 | *c_size = sizeof(unsigned char); |
165 | goto parse_end; |
166 | case 's': |
167 | *c_type = LTT_TYPE_STRING; |
168 | goto parse_end; |
169 | case 'p': |
170 | *c_type = LTT_TYPE_UNSIGNED_INT; |
171 | *c_size = info->pointer_size; |
172 | goto parse_end; |
173 | case 'd': |
174 | case 'i': |
175 | *c_type = LTT_TYPE_SIGNED_INT; |
176 | break; |
177 | case 'o': |
178 | case 'u': |
179 | case 'x': |
180 | case 'X': |
181 | *c_type = LTT_TYPE_UNSIGNED_INT; |
182 | break; |
183 | default: |
184 | if (!*fmt) |
185 | --fmt; |
186 | goto parse_end; |
187 | } |
188 | switch (qualifier) { |
189 | case 'L': |
190 | *c_size = sizeof(long long); |
191 | break; |
192 | case 'l': |
193 | *c_size = info->long_size; |
194 | break; |
195 | case 'Z': |
196 | case 'z': |
197 | *c_size = info->size_t_size; |
198 | break; |
199 | case 't': |
200 | *c_size = info->pointer_size; |
201 | break; |
202 | case 'h': |
203 | *c_size = sizeof(short); |
204 | break; |
205 | default: |
206 | *c_size = info->int_size; |
207 | } |
208 | |
209 | parse_end: |
210 | return fmt; |
211 | } |
212 | |
213 | static inline long add_type(struct marker_info *info, |
214 | long offset, const char *name, |
215 | char trace_size, enum ltt_type trace_type, |
216 | char c_size, enum ltt_type c_type, unsigned long attributes, |
217 | unsigned int field_count) |
218 | { |
219 | struct marker_field *field; |
220 | char tmpname[MAX_NAME_LEN]; |
221 | |
222 | info->fields = g_array_set_size(info->fields, info->fields->len+1); |
223 | field = &g_array_index(info->fields, struct marker_field, |
224 | info->fields->len-1); |
225 | if (name) |
226 | field->name = g_quark_from_string(name); |
227 | else { |
228 | snprintf(tmpname, MAX_NAME_LEN-1, "field %u", field_count); |
229 | field->name = g_quark_from_string(tmpname); |
230 | } |
231 | field->type = trace_type; |
232 | |
233 | switch (trace_type) { |
234 | case LTT_TYPE_SIGNED_INT: |
235 | case LTT_TYPE_UNSIGNED_INT: |
236 | field->size = trace_size; |
237 | field->alignment = trace_size; |
238 | field->attributes = attributes; |
239 | if (offset == -1) { |
240 | field->offset = -1; |
241 | field->static_offset = 0; |
242 | return -1; |
243 | } else { |
244 | field->offset = offset + ltt_align(offset, field->alignment, |
245 | info->alignment); |
246 | field->static_offset = 1; |
247 | return field->offset + trace_size; |
248 | } |
249 | case LTT_TYPE_STRING: |
250 | field->offset = offset; |
251 | field->size = 0; /* Variable length, size is 0 */ |
252 | field->alignment = 1; |
253 | if (offset == -1) |
254 | field->static_offset = 0; |
255 | else |
256 | field->static_offset = 1; |
257 | return -1; |
258 | default: |
259 | g_error("Unexpected type"); //FIXME: compact type |
260 | return 0; |
261 | } |
262 | } |
263 | |
264 | long marker_update_fields_offsets(struct marker_info *info, const char *data) |
265 | { |
266 | struct marker_field *field; |
267 | unsigned int i; |
268 | long offset = 0; |
269 | |
270 | for (i = 0; i < info->fields->len; i++) { |
271 | field = &g_array_index(info->fields, struct marker_field, i); |
272 | |
273 | if (field->static_offset) |
274 | continue; |
275 | |
276 | switch (field->type) { |
277 | case LTT_TYPE_SIGNED_INT: |
278 | case LTT_TYPE_UNSIGNED_INT: |
279 | field->offset = offset + ltt_align(offset, field->alignment, |
280 | info->alignment); |
281 | offset = field->offset + field->size; |
282 | break; |
283 | case LTT_TYPE_STRING: |
284 | field->offset = offset; |
285 | offset = offset + strlen(&data[offset]) + 1; |
286 | // not aligning on pointer size, breaking genevent backward compatibility. |
287 | break; |
288 | default: |
289 | g_error("Unexpected type"); //FIXME: compact type |
290 | return -1; |
291 | } |
292 | } |
293 | return offset; |
294 | } |
295 | |
296 | static void format_parse(const char *fmt, struct marker_info *info) |
297 | { |
298 | char trace_size = 0, c_size = 0; /* |
299 | * 0 (unset), 1, 2, 4, 8 bytes. |
300 | */ |
301 | enum ltt_type trace_type = LTT_TYPE_NONE, c_type = LTT_TYPE_NONE; |
302 | unsigned long attributes = 0; |
303 | long offset = 0; |
304 | const char *name_begin = NULL, *name_end = NULL; |
305 | char *name = NULL; |
306 | unsigned int field_count = 1; |
307 | |
308 | for (; *fmt ; ++fmt) { |
309 | switch (*fmt) { |
310 | case '#': |
311 | /* tracetypes (#) */ |
312 | ++fmt; /* skip first '#' */ |
313 | if (*fmt == '#') /* Escaped ## */ |
314 | break; |
315 | attributes = 0; |
316 | fmt = parse_trace_type(info, fmt, &trace_size, &trace_type, |
317 | &attributes); |
318 | break; |
319 | case '%': |
320 | /* c types (%) */ |
321 | ++fmt; /* skip first '%' */ |
322 | if (*fmt == '%') /* Escaped %% */ |
323 | break; |
324 | fmt = parse_c_type(info, fmt, &c_size, &c_type); |
325 | /* |
326 | * Output c types if no trace types has been |
327 | * specified. |
328 | */ |
329 | if (!trace_size) |
330 | trace_size = c_size; |
331 | if (trace_type == LTT_TYPE_NONE) |
332 | trace_type = c_type; |
333 | if (c_type == LTT_TYPE_STRING) |
334 | trace_type = LTT_TYPE_STRING; |
335 | /* perform trace write */ |
336 | offset = add_type(info, offset, name, trace_size, |
337 | trace_type, c_size, c_type, attributes, field_count++); |
338 | trace_size = c_size = 0; |
339 | trace_type = c_size = LTT_TYPE_NONE; |
340 | attributes = 0; |
341 | name_begin = NULL; |
342 | if (name) { |
343 | g_free(name); |
344 | name = NULL; |
345 | } |
346 | break; |
347 | case ' ': |
348 | if (!name_end) { |
349 | name_end = fmt; |
350 | if (name) |
351 | g_free(name); |
352 | name = g_new(char, name_end - name_begin + 1); |
353 | memcpy(name, name_begin, name_end - name_begin); |
354 | name[name_end - name_begin] = '\0'; |
355 | } |
356 | break; /* Skip white spaces */ |
357 | default: |
358 | if (!name) { |
359 | name_begin = fmt; |
360 | name_end = NULL; |
361 | } |
362 | } |
363 | } |
364 | info->size = offset; |
365 | if (name) |
366 | g_free(name); |
367 | } |
368 | |
369 | int marker_parse_format(const char *format, struct marker_info *info) |
370 | { |
371 | if (info->fields) |
372 | g_array_free(info->fields, TRUE); |
373 | info->fields = g_array_sized_new(FALSE, TRUE, |
374 | sizeof(struct marker_field), DEFAULT_FIELDS_NUM); |
375 | format_parse(format, info); |
376 | } |
377 | |
378 | int marker_format_event(LttTrace *trace, GQuark name, const char *format) |
d2007fbd |
379 | { |
380 | struct marker_info *info; |
381 | |
382 | info = g_hash_table_lookup(trace->markers_hash, (gconstpointer)name); |
383 | if (!info) |
384 | g_error("Got marker format %s, but marker name %s has no ID yet. " |
385 | "Kernel issue.", |
386 | format, name); |
3c165eaf |
387 | for (; info != NULL; info = info->next) { |
388 | if (info->format) |
389 | g_free(info->format); |
390 | info->format = g_new(char, strlen(format)+1); |
391 | strcpy(info->format, format); |
392 | if (marker_parse_format(format, info)) |
393 | g_error("Error parsing marker format %s for marker %s", format, |
394 | g_quark_to_string(name)); |
395 | } |
d2007fbd |
396 | } |
397 | |
3c165eaf |
398 | int marker_id_event(LttTrace *trace, GQuark name, guint16 id, |
399 | uint8_t int_size, uint8_t long_size, uint8_t pointer_size, |
400 | uint8_t size_t_size, uint8_t alignment) |
d2007fbd |
401 | { |
3c165eaf |
402 | struct marker_info *info, *head; |
d2007fbd |
403 | |
404 | if (trace->markers->len < id) |
405 | trace->markers = g_array_set_size(trace->markers, id+1); |
406 | info = &g_array_index(trace->markers, struct marker_info, id); |
3c165eaf |
407 | info->name = name; |
408 | info->int_size = int_size; |
409 | info->long_size = long_size; |
410 | info->pointer_size = pointer_size; |
411 | info->size_t_size = size_t_size; |
412 | info->alignment = alignment; |
413 | info->next = NULL; |
414 | head = g_hash_table_lookup(trace->markers_hash, (gconstpointer)name); |
415 | if (!head) |
416 | g_hash_table_insert(trace->markers_hash, (gpointer)name, info); |
d7913a10 |
417 | else { |
418 | g_hash_table_replace(trace->markers_hash, (gpointer)name, info); |
419 | info->next = head; |
420 | } |
d2007fbd |
421 | } |
422 | |
423 | int allocate_marker_data(LttTrace *trace) |
424 | { |
425 | /* Init array to 0 */ |
426 | trace->markers = g_array_sized_new(FALSE, TRUE, |
427 | sizeof(struct marker_info), DEFAULT_MARKERS_NUM); |
3c165eaf |
428 | if (!trace->markers) |
429 | return -ENOMEM; |
430 | trace->markers_hash = g_hash_table_new(g_direct_hash, g_direct_equal); |
431 | if (!trace->markers_hash) |
432 | return -ENOMEM; |
433 | return 0; |
d2007fbd |
434 | } |
435 | |
3c165eaf |
436 | void destroy_marker_data(LttTrace *trace) |
d2007fbd |
437 | { |
438 | unsigned int i; |
439 | struct marker_info *info; |
440 | |
441 | for (i=0; i<trace->markers->len; i++) { |
442 | info = &g_array_index(trace->markers, struct marker_info, i); |
d2007fbd |
443 | if (info->format) |
444 | g_free(info->format); |
3c165eaf |
445 | if (info->fields) |
446 | g_array_free(info->fields, TRUE); |
d2007fbd |
447 | } |
448 | g_array_free(trace->markers, TRUE); |
449 | g_hash_table_destroy(trace->markers_hash); |
450 | } |