f2a74ed3 |
1 | /* This file is part of the Linux Trace Toolkit viewer |
2 | * Copyright (C) 2007 Mathieu Desnoyers |
d2007fbd |
3 | * |
f2a74ed3 |
4 | * Complete rewrite from the original version made by XangXiu Yang. |
5 | * |
6 | * This library is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Lesser General Public |
8 | * License Version 2.1 as published by the Free Software Foundation. |
9 | * |
10 | * This library is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * Lesser General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU Lesser General Public |
16 | * License along with this library; if not, write to the |
17 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
18 | * Boston, MA 02111-1307, USA. |
d2007fbd |
19 | */ |
20 | |
f2a74ed3 |
21 | #ifdef HAVE_CONFIG_H |
22 | #include <config.h> |
23 | #endif |
24 | |
d2007fbd |
25 | #include <glib.h> |
3c165eaf |
26 | #include <stdio.h> |
27 | #include <string.h> |
28 | #include <errno.h> |
d2007fbd |
29 | #include <ltt/compiler.h> |
bb38a290 |
30 | #include <ltt/marker.h> |
3c165eaf |
31 | #include <ltt/ltt-private.h> |
d2007fbd |
32 | |
33 | #define DEFAULT_MARKERS_NUM 100 |
3c165eaf |
34 | #define DEFAULT_FIELDS_NUM 1 |
35 | #define MAX_NAME_LEN 1024 |
d2007fbd |
36 | |
3c165eaf |
37 | static inline const char *parse_trace_type(struct marker_info *info, |
38 | const char *fmt, |
39 | char *trace_size, enum ltt_type *trace_type, |
40 | unsigned long *attributes) |
41 | { |
42 | int qualifier; /* 'h', 'l', or 'L' for integer fields */ |
43 | /* 'z' support added 23/7/1999 S.H. */ |
44 | /* 'z' changed to 'Z' --davidm 1/25/99 */ |
45 | /* 't' added for ptrdiff_t */ |
46 | |
47 | /* parse attributes. */ |
48 | repeat: |
49 | switch (*fmt) { |
3c165eaf |
50 | case 'n': |
51 | *attributes |= LTT_ATTRIBUTE_NETWORK_BYTE_ORDER; |
52 | ++fmt; |
53 | goto repeat; |
54 | } |
55 | |
56 | /* get the conversion qualifier */ |
57 | qualifier = -1; |
58 | if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || |
59 | *fmt =='Z' || *fmt == 'z' || *fmt == 't' || |
60 | *fmt == 'S' || *fmt == '1' || *fmt == '2' || |
8d2ebd3d |
61 | *fmt == '4' || *fmt == '8') { |
3c165eaf |
62 | qualifier = *fmt; |
63 | ++fmt; |
64 | if (qualifier == 'l' && *fmt == 'l') { |
65 | qualifier = 'L'; |
66 | ++fmt; |
67 | } |
68 | } |
69 | |
70 | switch (*fmt) { |
71 | case 'c': |
72 | *trace_type = LTT_TYPE_UNSIGNED_INT; |
73 | *trace_size = sizeof(char); |
74 | goto parse_end; |
75 | case 's': |
76 | *trace_type = LTT_TYPE_STRING; |
77 | goto parse_end; |
78 | case 'p': |
91346f59 |
79 | *trace_type = LTT_TYPE_POINTER; |
3c165eaf |
80 | *trace_size = info->pointer_size; |
81 | goto parse_end; |
82 | case 'd': |
83 | case 'i': |
84 | *trace_type = LTT_TYPE_SIGNED_INT; |
85 | break; |
86 | case 'o': |
87 | case 'u': |
88 | case 'x': |
89 | case 'X': |
90 | *trace_type = LTT_TYPE_UNSIGNED_INT; |
91 | break; |
92 | default: |
93 | if (!*fmt) |
94 | --fmt; |
95 | goto parse_end; |
96 | } |
97 | switch (qualifier) { |
98 | case 'L': |
99 | *trace_size = sizeof(long long); |
100 | break; |
101 | case 'l': |
102 | *trace_size = info->long_size; |
103 | break; |
104 | case 'Z': |
105 | case 'z': |
106 | *trace_size = info->size_t_size; |
107 | break; |
108 | case 't': |
109 | *trace_size = info->pointer_size; |
110 | break; |
111 | case 'h': |
112 | *trace_size = sizeof(short); |
113 | break; |
114 | case '1': |
115 | *trace_size = sizeof(uint8_t); |
116 | break; |
117 | case '2': |
118 | *trace_size = sizeof(guint16); |
119 | break; |
120 | case '4': |
121 | *trace_size = sizeof(uint32_t); |
122 | break; |
123 | case '8': |
124 | *trace_size = sizeof(uint64_t); |
125 | break; |
126 | default: |
127 | *trace_size = info->int_size; |
128 | } |
129 | |
130 | parse_end: |
131 | return fmt; |
132 | } |
133 | |
134 | /* |
135 | * Restrictions: |
136 | * Field width and precision are *not* supported. |
137 | * %n not supported. |
138 | */ |
139 | __attribute__((no_instrument_function)) |
140 | static inline const char *parse_c_type(struct marker_info *info, |
141 | const char *fmt, |
4d683428 |
142 | char *c_size, enum ltt_type *c_type, GString *field_fmt) |
3c165eaf |
143 | { |
144 | int qualifier; /* 'h', 'l', or 'L' for integer fields */ |
145 | /* 'z' support added 23/7/1999 S.H. */ |
146 | /* 'z' changed to 'Z' --davidm 1/25/99 */ |
147 | /* 't' added for ptrdiff_t */ |
148 | |
149 | /* process flags : ignore standard print formats for now. */ |
150 | repeat: |
151 | switch (*fmt) { |
152 | case '-': |
153 | case '+': |
154 | case ' ': |
155 | case '#': |
156 | case '0': |
4d683428 |
157 | g_string_append_c(field_fmt, *fmt); |
3c165eaf |
158 | ++fmt; |
159 | goto repeat; |
160 | } |
161 | |
162 | /* get the conversion qualifier */ |
163 | qualifier = -1; |
164 | if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || |
165 | *fmt =='Z' || *fmt == 'z' || *fmt == 't' || |
166 | *fmt == 'S') { |
167 | qualifier = *fmt; |
168 | ++fmt; |
169 | if (qualifier == 'l' && *fmt == 'l') { |
170 | qualifier = 'L'; |
171 | ++fmt; |
172 | } |
173 | } |
174 | |
175 | switch (*fmt) { |
176 | case 'c': |
177 | *c_type = LTT_TYPE_UNSIGNED_INT; |
178 | *c_size = sizeof(unsigned char); |
4d683428 |
179 | g_string_append_c(field_fmt, *fmt); |
3c165eaf |
180 | goto parse_end; |
181 | case 's': |
182 | *c_type = LTT_TYPE_STRING; |
183 | goto parse_end; |
184 | case 'p': |
91346f59 |
185 | *c_type = LTT_TYPE_POINTER; |
3c165eaf |
186 | *c_size = info->pointer_size; |
187 | goto parse_end; |
188 | case 'd': |
189 | case 'i': |
190 | *c_type = LTT_TYPE_SIGNED_INT; |
4d683428 |
191 | g_string_append_c(field_fmt, 'l'); |
192 | g_string_append_c(field_fmt, 'l'); |
193 | g_string_append_c(field_fmt, *fmt); |
3c165eaf |
194 | break; |
195 | case 'o': |
196 | case 'u': |
197 | case 'x': |
198 | case 'X': |
4d683428 |
199 | g_string_append_c(field_fmt, 'l'); |
200 | g_string_append_c(field_fmt, 'l'); |
201 | g_string_append_c(field_fmt, *fmt); |
3c165eaf |
202 | *c_type = LTT_TYPE_UNSIGNED_INT; |
203 | break; |
204 | default: |
205 | if (!*fmt) |
206 | --fmt; |
207 | goto parse_end; |
208 | } |
209 | switch (qualifier) { |
210 | case 'L': |
211 | *c_size = sizeof(long long); |
212 | break; |
213 | case 'l': |
214 | *c_size = info->long_size; |
215 | break; |
216 | case 'Z': |
217 | case 'z': |
218 | *c_size = info->size_t_size; |
219 | break; |
220 | case 't': |
221 | *c_size = info->pointer_size; |
222 | break; |
223 | case 'h': |
224 | *c_size = sizeof(short); |
225 | break; |
226 | default: |
227 | *c_size = info->int_size; |
228 | } |
229 | |
230 | parse_end: |
231 | return fmt; |
232 | } |
233 | |
234 | static inline long add_type(struct marker_info *info, |
235 | long offset, const char *name, |
236 | char trace_size, enum ltt_type trace_type, |
237 | char c_size, enum ltt_type c_type, unsigned long attributes, |
4d683428 |
238 | unsigned int field_count, GString *field_fmt) |
3c165eaf |
239 | { |
240 | struct marker_field *field; |
241 | char tmpname[MAX_NAME_LEN]; |
242 | |
243 | info->fields = g_array_set_size(info->fields, info->fields->len+1); |
244 | field = &g_array_index(info->fields, struct marker_field, |
245 | info->fields->len-1); |
246 | if (name) |
247 | field->name = g_quark_from_string(name); |
248 | else { |
249 | snprintf(tmpname, MAX_NAME_LEN-1, "field %u", field_count); |
250 | field->name = g_quark_from_string(tmpname); |
251 | } |
252 | field->type = trace_type; |
4d683428 |
253 | field->fmt = g_string_new(field_fmt->str); |
3c165eaf |
254 | |
255 | switch (trace_type) { |
256 | case LTT_TYPE_SIGNED_INT: |
257 | case LTT_TYPE_UNSIGNED_INT: |
7f440a66 |
258 | case LTT_TYPE_POINTER: |
3c165eaf |
259 | field->size = trace_size; |
260 | field->alignment = trace_size; |
2fc874ab |
261 | info->largest_align = max((guint8)field->alignment, |
262 | (guint8)info->largest_align); |
3c165eaf |
263 | field->attributes = attributes; |
264 | if (offset == -1) { |
265 | field->offset = -1; |
266 | field->static_offset = 0; |
267 | return -1; |
268 | } else { |
269 | field->offset = offset + ltt_align(offset, field->alignment, |
270 | info->alignment); |
271 | field->static_offset = 1; |
272 | return field->offset + trace_size; |
273 | } |
274 | case LTT_TYPE_STRING: |
275 | field->offset = offset; |
276 | field->size = 0; /* Variable length, size is 0 */ |
277 | field->alignment = 1; |
278 | if (offset == -1) |
279 | field->static_offset = 0; |
280 | else |
281 | field->static_offset = 1; |
282 | return -1; |
283 | default: |
2fc874ab |
284 | g_error("Unexpected type"); |
3c165eaf |
285 | return 0; |
286 | } |
287 | } |
288 | |
289 | long marker_update_fields_offsets(struct marker_info *info, const char *data) |
290 | { |
291 | struct marker_field *field; |
292 | unsigned int i; |
293 | long offset = 0; |
294 | |
256a5b3a |
295 | /* Find the last field with a static offset, then update from there. */ |
296 | for (i = info->fields->len - 1; i >= 0; i--) { |
3c165eaf |
297 | field = &g_array_index(info->fields, struct marker_field, i); |
256a5b3a |
298 | if (field->static_offset) { |
299 | offset = field->offset; |
300 | break; |
301 | } |
302 | } |
3c165eaf |
303 | |
256a5b3a |
304 | for (; i < info->fields->len; i++) { |
305 | field = &g_array_index(info->fields, struct marker_field, i); |
3c165eaf |
306 | |
307 | switch (field->type) { |
308 | case LTT_TYPE_SIGNED_INT: |
309 | case LTT_TYPE_UNSIGNED_INT: |
7f440a66 |
310 | case LTT_TYPE_POINTER: |
3c165eaf |
311 | field->offset = offset + ltt_align(offset, field->alignment, |
312 | info->alignment); |
313 | offset = field->offset + field->size; |
314 | break; |
315 | case LTT_TYPE_STRING: |
316 | field->offset = offset; |
317 | offset = offset + strlen(&data[offset]) + 1; |
318 | // not aligning on pointer size, breaking genevent backward compatibility. |
319 | break; |
320 | default: |
2fc874ab |
321 | g_error("Unexpected type"); |
3c165eaf |
322 | return -1; |
323 | } |
324 | } |
325 | return offset; |
326 | } |
327 | |
328 | static void format_parse(const char *fmt, struct marker_info *info) |
329 | { |
330 | char trace_size = 0, c_size = 0; /* |
331 | * 0 (unset), 1, 2, 4, 8 bytes. |
332 | */ |
333 | enum ltt_type trace_type = LTT_TYPE_NONE, c_type = LTT_TYPE_NONE; |
334 | unsigned long attributes = 0; |
335 | long offset = 0; |
336 | const char *name_begin = NULL, *name_end = NULL; |
337 | char *name = NULL; |
338 | unsigned int field_count = 1; |
4d683428 |
339 | GString *field_fmt = g_string_new(""); |
3c165eaf |
340 | |
2e13d6af |
341 | name_begin = fmt; |
3c165eaf |
342 | for (; *fmt ; ++fmt) { |
343 | switch (*fmt) { |
344 | case '#': |
345 | /* tracetypes (#) */ |
346 | ++fmt; /* skip first '#' */ |
4d683428 |
347 | if (*fmt == '#') { /* Escaped ## */ |
348 | g_string_append_c(field_fmt, *fmt); |
349 | g_string_append_c(field_fmt, *fmt); |
3c165eaf |
350 | break; |
4d683428 |
351 | } |
3c165eaf |
352 | attributes = 0; |
353 | fmt = parse_trace_type(info, fmt, &trace_size, &trace_type, |
354 | &attributes); |
355 | break; |
356 | case '%': |
357 | /* c types (%) */ |
4d683428 |
358 | g_string_append_c(field_fmt, *fmt); |
3c165eaf |
359 | ++fmt; /* skip first '%' */ |
4d683428 |
360 | if (*fmt == '%') { /* Escaped %% */ |
361 | g_string_append_c(field_fmt, *fmt); |
3c165eaf |
362 | break; |
4d683428 |
363 | } |
364 | fmt = parse_c_type(info, fmt, &c_size, &c_type, field_fmt); |
3c165eaf |
365 | /* |
366 | * Output c types if no trace types has been |
367 | * specified. |
368 | */ |
369 | if (!trace_size) |
370 | trace_size = c_size; |
371 | if (trace_type == LTT_TYPE_NONE) |
372 | trace_type = c_type; |
373 | if (c_type == LTT_TYPE_STRING) |
374 | trace_type = LTT_TYPE_STRING; |
375 | /* perform trace write */ |
376 | offset = add_type(info, offset, name, trace_size, |
4d683428 |
377 | trace_type, c_size, c_type, attributes, field_count++, |
378 | field_fmt); |
3c165eaf |
379 | trace_size = c_size = 0; |
380 | trace_type = c_size = LTT_TYPE_NONE; |
4d683428 |
381 | g_string_truncate(field_fmt, 0); |
3c165eaf |
382 | attributes = 0; |
383 | name_begin = NULL; |
384 | if (name) { |
385 | g_free(name); |
386 | name = NULL; |
387 | } |
388 | break; |
389 | case ' ': |
4d683428 |
390 | g_string_truncate(field_fmt, 0); |
565460ea |
391 | if (!name_end && name_begin) { |
3c165eaf |
392 | name_end = fmt; |
393 | if (name) |
394 | g_free(name); |
395 | name = g_new(char, name_end - name_begin + 1); |
396 | memcpy(name, name_begin, name_end - name_begin); |
397 | name[name_end - name_begin] = '\0'; |
398 | } |
399 | break; /* Skip white spaces */ |
400 | default: |
4d683428 |
401 | g_string_append_c(field_fmt, *fmt); |
565460ea |
402 | if (!name_begin) { |
3c165eaf |
403 | name_begin = fmt; |
404 | name_end = NULL; |
405 | } |
406 | } |
407 | } |
408 | info->size = offset; |
409 | if (name) |
410 | g_free(name); |
4d683428 |
411 | g_string_free(field_fmt, TRUE); |
3c165eaf |
412 | } |
413 | |
414 | int marker_parse_format(const char *format, struct marker_info *info) |
415 | { |
416 | if (info->fields) |
417 | g_array_free(info->fields, TRUE); |
418 | info->fields = g_array_sized_new(FALSE, TRUE, |
419 | sizeof(struct marker_field), DEFAULT_FIELDS_NUM); |
420 | format_parse(format, info); |
2e13d6af |
421 | return 0; |
3c165eaf |
422 | } |
423 | |
750eb11a |
424 | int marker_format_event(LttTrace *trace, GQuark channel, GQuark name, |
425 | const char *format) |
d2007fbd |
426 | { |
427 | struct marker_info *info; |
750eb11a |
428 | struct marker_data *mdata; |
c09a11e9 |
429 | char *fquery; |
430 | char *fcopy; |
750eb11a |
431 | GArray *group; |
432 | |
433 | group = g_datalist_id_get_data(&trace->tracefiles, channel); |
0919a063 |
434 | if (!group) |
435 | return -ENOENT; |
750eb11a |
436 | g_assert(group->len > 0); |
437 | mdata = g_array_index (group, LttTracefile, 0).mdata; |
438 | |
439 | fquery = marker_get_format_from_name(mdata, name); |
37e04c8b |
440 | if (fquery) { |
c09a11e9 |
441 | if (strcmp(fquery, format) != 0) |
750eb11a |
442 | g_error("Marker format mismatch \"%s\" vs \"%s\" for marker %s.%s. " |
443 | "Kernel issue.", fquery, format, |
444 | g_quark_to_string(channel), g_quark_to_string(name)); |
37e04c8b |
445 | else |
446 | return 0; /* Already exists. Nothing to do. */ |
447 | } |
c09a11e9 |
448 | fcopy = g_new(char, strlen(format)+1); |
449 | strcpy(fcopy, format); |
750eb11a |
450 | g_hash_table_insert(mdata->markers_format_hash, (gpointer)(gulong)name, |
c09a11e9 |
451 | (gpointer)fcopy); |
452 | |
750eb11a |
453 | info = marker_get_info_from_name(mdata, name); |
3c165eaf |
454 | for (; info != NULL; info = info->next) { |
c09a11e9 |
455 | info->format = fcopy; |
3c165eaf |
456 | if (marker_parse_format(format, info)) |
750eb11a |
457 | g_error("Error parsing marker format \"%s\" for marker \"%.s.%s\"", |
458 | format, g_quark_to_string(channel), g_quark_to_string(name)); |
3c165eaf |
459 | } |
03dab2c1 |
460 | return 0; |
d2007fbd |
461 | } |
462 | |
750eb11a |
463 | int marker_id_event(LttTrace *trace, GQuark channel, GQuark name, guint16 id, |
3c165eaf |
464 | uint8_t int_size, uint8_t long_size, uint8_t pointer_size, |
465 | uint8_t size_t_size, uint8_t alignment) |
d2007fbd |
466 | { |
750eb11a |
467 | struct marker_data *mdata; |
3c165eaf |
468 | struct marker_info *info, *head; |
03dab2c1 |
469 | int found = 0; |
750eb11a |
470 | GArray *group; |
471 | |
472 | g_debug("Add channel %s event %s %hu\n", g_quark_to_string(channel), |
473 | g_quark_to_string(name), id); |
474 | |
475 | group = g_datalist_id_get_data(&trace->tracefiles, channel); |
0919a063 |
476 | if (!group) |
477 | return -ENOENT; |
750eb11a |
478 | g_assert(group->len > 0); |
479 | mdata = g_array_index (group, LttTracefile, 0).mdata; |
d2007fbd |
480 | |
750eb11a |
481 | if (mdata->markers->len <= id) |
482 | mdata->markers = g_array_set_size(mdata->markers, |
483 | max(mdata->markers->len * 2, id + 1)); |
484 | info = &g_array_index(mdata->markers, struct marker_info, id); |
3c165eaf |
485 | info->name = name; |
486 | info->int_size = int_size; |
487 | info->long_size = long_size; |
488 | info->pointer_size = pointer_size; |
489 | info->size_t_size = size_t_size; |
490 | info->alignment = alignment; |
dcf96842 |
491 | info->fields = NULL; |
3c165eaf |
492 | info->next = NULL; |
750eb11a |
493 | info->format = marker_get_format_from_name(mdata, name); |
2fc874ab |
494 | info->largest_align = 1; |
c09a11e9 |
495 | if (info->format && marker_parse_format(info->format, info)) |
750eb11a |
496 | g_error("Error parsing marker format \"%s\" for marker \"%s.%s\"", |
497 | info->format, g_quark_to_string(channel), g_quark_to_string(name)); |
498 | head = marker_get_info_from_name(mdata, name); |
3c165eaf |
499 | if (!head) |
750eb11a |
500 | g_hash_table_insert(mdata->markers_hash, (gpointer)(gulong)name, |
abc34be7 |
501 | (gpointer)(gulong)id); |
d7913a10 |
502 | else { |
03dab2c1 |
503 | struct marker_info *iter; |
504 | for (iter = head; iter != NULL; iter = iter->next) |
505 | if (iter->name == name) |
506 | found = 1; |
507 | if (!found) { |
750eb11a |
508 | g_hash_table_replace(mdata->markers_hash, (gpointer)(gulong)name, |
abc34be7 |
509 | (gpointer)(gulong)id); |
03dab2c1 |
510 | info->next = head; |
511 | } |
d7913a10 |
512 | } |
03dab2c1 |
513 | return 0; |
d2007fbd |
514 | } |
515 | |
750eb11a |
516 | struct marker_data *allocate_marker_data(void) |
d2007fbd |
517 | { |
750eb11a |
518 | struct marker_data *data; |
519 | |
520 | data = g_new(struct marker_data, 1); |
d2007fbd |
521 | /* Init array to 0 */ |
750eb11a |
522 | data->markers = g_array_sized_new(FALSE, TRUE, |
d2007fbd |
523 | sizeof(struct marker_info), DEFAULT_MARKERS_NUM); |
750eb11a |
524 | if (!data->markers) |
525 | goto free_data; |
526 | data->markers_hash = g_hash_table_new(g_direct_hash, g_direct_equal); |
527 | if (!data->markers_hash) |
528 | goto free_markers; |
529 | data->markers_format_hash = g_hash_table_new_full(g_direct_hash, |
c09a11e9 |
530 | g_direct_equal, NULL, g_free); |
750eb11a |
531 | if (!data->markers_format_hash) |
532 | goto free_markers_hash; |
533 | return data; |
534 | |
535 | /* error handling */ |
536 | free_markers_hash: |
537 | g_hash_table_destroy(data->markers_hash); |
538 | free_markers: |
539 | g_array_free(data->markers, TRUE); |
540 | free_data: |
541 | g_free(data); |
542 | return NULL; |
d2007fbd |
543 | } |
544 | |
750eb11a |
545 | void destroy_marker_data(struct marker_data *data) |
d2007fbd |
546 | { |
4d683428 |
547 | unsigned int i, j; |
d2007fbd |
548 | struct marker_info *info; |
4d683428 |
549 | struct marker_field *field; |
d2007fbd |
550 | |
750eb11a |
551 | for (i=0; i<data->markers->len; i++) { |
552 | info = &g_array_index(data->markers, struct marker_info, i); |
4d683428 |
553 | if (info->fields) { |
554 | for (j = 0; j < info->fields->len; j++) { |
555 | field = &g_array_index(info->fields, struct marker_field, j); |
556 | g_string_free(field->fmt, TRUE); |
557 | } |
3c165eaf |
558 | g_array_free(info->fields, TRUE); |
4d683428 |
559 | } |
d2007fbd |
560 | } |
750eb11a |
561 | g_hash_table_destroy(data->markers_format_hash); |
562 | g_hash_table_destroy(data->markers_hash); |
563 | g_array_free(data->markers, TRUE); |
564 | g_free(data); |
d2007fbd |
565 | } |