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) { |
50 | case 'b': |
51 | *attributes |= LTT_ATTRIBUTE_COMPACT; |
52 | ++fmt; |
53 | goto repeat; |
54 | case 'n': |
55 | *attributes |= LTT_ATTRIBUTE_NETWORK_BYTE_ORDER; |
56 | ++fmt; |
57 | goto repeat; |
58 | } |
59 | |
60 | /* get the conversion qualifier */ |
61 | qualifier = -1; |
62 | if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || |
63 | *fmt =='Z' || *fmt == 'z' || *fmt == 't' || |
64 | *fmt == 'S' || *fmt == '1' || *fmt == '2' || |
8d2ebd3d |
65 | *fmt == '4' || *fmt == '8') { |
3c165eaf |
66 | qualifier = *fmt; |
67 | ++fmt; |
68 | if (qualifier == 'l' && *fmt == 'l') { |
69 | qualifier = 'L'; |
70 | ++fmt; |
71 | } |
72 | } |
73 | |
74 | switch (*fmt) { |
75 | case 'c': |
76 | *trace_type = LTT_TYPE_UNSIGNED_INT; |
77 | *trace_size = sizeof(char); |
78 | goto parse_end; |
79 | case 's': |
80 | *trace_type = LTT_TYPE_STRING; |
81 | goto parse_end; |
82 | case 'p': |
91346f59 |
83 | *trace_type = LTT_TYPE_POINTER; |
3c165eaf |
84 | *trace_size = info->pointer_size; |
85 | goto parse_end; |
86 | case 'd': |
87 | case 'i': |
88 | *trace_type = LTT_TYPE_SIGNED_INT; |
89 | break; |
90 | case 'o': |
91 | case 'u': |
92 | case 'x': |
93 | case 'X': |
94 | *trace_type = LTT_TYPE_UNSIGNED_INT; |
95 | break; |
96 | default: |
97 | if (!*fmt) |
98 | --fmt; |
99 | goto parse_end; |
100 | } |
101 | switch (qualifier) { |
102 | case 'L': |
103 | *trace_size = sizeof(long long); |
104 | break; |
105 | case 'l': |
106 | *trace_size = info->long_size; |
107 | break; |
108 | case 'Z': |
109 | case 'z': |
110 | *trace_size = info->size_t_size; |
111 | break; |
112 | case 't': |
113 | *trace_size = info->pointer_size; |
114 | break; |
115 | case 'h': |
116 | *trace_size = sizeof(short); |
117 | break; |
118 | case '1': |
119 | *trace_size = sizeof(uint8_t); |
120 | break; |
121 | case '2': |
122 | *trace_size = sizeof(guint16); |
123 | break; |
124 | case '4': |
125 | *trace_size = sizeof(uint32_t); |
126 | break; |
127 | case '8': |
128 | *trace_size = sizeof(uint64_t); |
129 | break; |
130 | default: |
131 | *trace_size = info->int_size; |
132 | } |
133 | |
134 | parse_end: |
135 | return fmt; |
136 | } |
137 | |
138 | /* |
139 | * Restrictions: |
140 | * Field width and precision are *not* supported. |
141 | * %n not supported. |
142 | */ |
143 | __attribute__((no_instrument_function)) |
144 | static inline const char *parse_c_type(struct marker_info *info, |
145 | const char *fmt, |
4d683428 |
146 | char *c_size, enum ltt_type *c_type, GString *field_fmt) |
3c165eaf |
147 | { |
148 | int qualifier; /* 'h', 'l', or 'L' for integer fields */ |
149 | /* 'z' support added 23/7/1999 S.H. */ |
150 | /* 'z' changed to 'Z' --davidm 1/25/99 */ |
151 | /* 't' added for ptrdiff_t */ |
152 | |
153 | /* process flags : ignore standard print formats for now. */ |
154 | repeat: |
155 | switch (*fmt) { |
156 | case '-': |
157 | case '+': |
158 | case ' ': |
159 | case '#': |
160 | case '0': |
4d683428 |
161 | g_string_append_c(field_fmt, *fmt); |
3c165eaf |
162 | ++fmt; |
163 | goto repeat; |
164 | } |
165 | |
166 | /* get the conversion qualifier */ |
167 | qualifier = -1; |
168 | if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || |
169 | *fmt =='Z' || *fmt == 'z' || *fmt == 't' || |
170 | *fmt == 'S') { |
171 | qualifier = *fmt; |
172 | ++fmt; |
173 | if (qualifier == 'l' && *fmt == 'l') { |
174 | qualifier = 'L'; |
175 | ++fmt; |
176 | } |
177 | } |
178 | |
179 | switch (*fmt) { |
180 | case 'c': |
181 | *c_type = LTT_TYPE_UNSIGNED_INT; |
182 | *c_size = sizeof(unsigned char); |
4d683428 |
183 | g_string_append_c(field_fmt, *fmt); |
3c165eaf |
184 | goto parse_end; |
185 | case 's': |
186 | *c_type = LTT_TYPE_STRING; |
187 | goto parse_end; |
188 | case 'p': |
91346f59 |
189 | *c_type = LTT_TYPE_POINTER; |
3c165eaf |
190 | *c_size = info->pointer_size; |
191 | goto parse_end; |
192 | case 'd': |
193 | case 'i': |
194 | *c_type = LTT_TYPE_SIGNED_INT; |
4d683428 |
195 | g_string_append_c(field_fmt, 'l'); |
196 | g_string_append_c(field_fmt, 'l'); |
197 | g_string_append_c(field_fmt, *fmt); |
3c165eaf |
198 | break; |
199 | case 'o': |
200 | case 'u': |
201 | case 'x': |
202 | case 'X': |
4d683428 |
203 | g_string_append_c(field_fmt, 'l'); |
204 | g_string_append_c(field_fmt, 'l'); |
205 | g_string_append_c(field_fmt, *fmt); |
3c165eaf |
206 | *c_type = LTT_TYPE_UNSIGNED_INT; |
207 | break; |
208 | default: |
209 | if (!*fmt) |
210 | --fmt; |
211 | goto parse_end; |
212 | } |
213 | switch (qualifier) { |
214 | case 'L': |
215 | *c_size = sizeof(long long); |
216 | break; |
217 | case 'l': |
218 | *c_size = info->long_size; |
219 | break; |
220 | case 'Z': |
221 | case 'z': |
222 | *c_size = info->size_t_size; |
223 | break; |
224 | case 't': |
225 | *c_size = info->pointer_size; |
226 | break; |
227 | case 'h': |
228 | *c_size = sizeof(short); |
229 | break; |
230 | default: |
231 | *c_size = info->int_size; |
232 | } |
233 | |
234 | parse_end: |
235 | return fmt; |
236 | } |
237 | |
238 | static inline long add_type(struct marker_info *info, |
239 | long offset, const char *name, |
240 | char trace_size, enum ltt_type trace_type, |
241 | char c_size, enum ltt_type c_type, unsigned long attributes, |
4d683428 |
242 | unsigned int field_count, GString *field_fmt) |
3c165eaf |
243 | { |
244 | struct marker_field *field; |
245 | char tmpname[MAX_NAME_LEN]; |
246 | |
247 | info->fields = g_array_set_size(info->fields, info->fields->len+1); |
248 | field = &g_array_index(info->fields, struct marker_field, |
249 | info->fields->len-1); |
250 | if (name) |
251 | field->name = g_quark_from_string(name); |
252 | else { |
253 | snprintf(tmpname, MAX_NAME_LEN-1, "field %u", field_count); |
254 | field->name = g_quark_from_string(tmpname); |
255 | } |
256 | field->type = trace_type; |
4d683428 |
257 | field->fmt = g_string_new(field_fmt->str); |
3c165eaf |
258 | |
259 | switch (trace_type) { |
260 | case LTT_TYPE_SIGNED_INT: |
261 | case LTT_TYPE_UNSIGNED_INT: |
7f440a66 |
262 | case LTT_TYPE_POINTER: |
3c165eaf |
263 | field->size = trace_size; |
264 | field->alignment = trace_size; |
265 | field->attributes = attributes; |
266 | if (offset == -1) { |
267 | field->offset = -1; |
268 | field->static_offset = 0; |
269 | return -1; |
270 | } else { |
271 | field->offset = offset + ltt_align(offset, field->alignment, |
272 | info->alignment); |
273 | field->static_offset = 1; |
274 | return field->offset + trace_size; |
275 | } |
276 | case LTT_TYPE_STRING: |
277 | field->offset = offset; |
278 | field->size = 0; /* Variable length, size is 0 */ |
279 | field->alignment = 1; |
280 | if (offset == -1) |
281 | field->static_offset = 0; |
282 | else |
283 | field->static_offset = 1; |
284 | return -1; |
285 | default: |
286 | g_error("Unexpected type"); //FIXME: compact type |
287 | return 0; |
288 | } |
289 | } |
290 | |
291 | long marker_update_fields_offsets(struct marker_info *info, const char *data) |
292 | { |
293 | struct marker_field *field; |
294 | unsigned int i; |
295 | long offset = 0; |
296 | |
256a5b3a |
297 | /* Find the last field with a static offset, then update from there. */ |
298 | for (i = info->fields->len - 1; i >= 0; i--) { |
3c165eaf |
299 | field = &g_array_index(info->fields, struct marker_field, i); |
256a5b3a |
300 | if (field->static_offset) { |
301 | offset = field->offset; |
302 | break; |
303 | } |
304 | } |
3c165eaf |
305 | |
256a5b3a |
306 | for (; i < info->fields->len; i++) { |
307 | field = &g_array_index(info->fields, struct marker_field, i); |
3c165eaf |
308 | |
309 | switch (field->type) { |
310 | case LTT_TYPE_SIGNED_INT: |
311 | case LTT_TYPE_UNSIGNED_INT: |
7f440a66 |
312 | case LTT_TYPE_POINTER: |
3c165eaf |
313 | field->offset = offset + ltt_align(offset, field->alignment, |
314 | info->alignment); |
315 | offset = field->offset + field->size; |
316 | break; |
317 | case LTT_TYPE_STRING: |
318 | field->offset = offset; |
319 | offset = offset + strlen(&data[offset]) + 1; |
320 | // not aligning on pointer size, breaking genevent backward compatibility. |
321 | break; |
322 | default: |
323 | g_error("Unexpected type"); //FIXME: compact type |
324 | return -1; |
325 | } |
326 | } |
327 | return offset; |
328 | } |
329 | |
330 | static void format_parse(const char *fmt, struct marker_info *info) |
331 | { |
332 | char trace_size = 0, c_size = 0; /* |
333 | * 0 (unset), 1, 2, 4, 8 bytes. |
334 | */ |
335 | enum ltt_type trace_type = LTT_TYPE_NONE, c_type = LTT_TYPE_NONE; |
336 | unsigned long attributes = 0; |
337 | long offset = 0; |
338 | const char *name_begin = NULL, *name_end = NULL; |
339 | char *name = NULL; |
340 | unsigned int field_count = 1; |
4d683428 |
341 | GString *field_fmt = g_string_new(""); |
3c165eaf |
342 | |
2e13d6af |
343 | name_begin = fmt; |
3c165eaf |
344 | for (; *fmt ; ++fmt) { |
345 | switch (*fmt) { |
346 | case '#': |
347 | /* tracetypes (#) */ |
348 | ++fmt; /* skip first '#' */ |
4d683428 |
349 | if (*fmt == '#') { /* Escaped ## */ |
350 | g_string_append_c(field_fmt, *fmt); |
351 | g_string_append_c(field_fmt, *fmt); |
3c165eaf |
352 | break; |
4d683428 |
353 | } |
3c165eaf |
354 | attributes = 0; |
355 | fmt = parse_trace_type(info, fmt, &trace_size, &trace_type, |
356 | &attributes); |
357 | break; |
358 | case '%': |
359 | /* c types (%) */ |
4d683428 |
360 | g_string_append_c(field_fmt, *fmt); |
3c165eaf |
361 | ++fmt; /* skip first '%' */ |
4d683428 |
362 | if (*fmt == '%') { /* Escaped %% */ |
363 | g_string_append_c(field_fmt, *fmt); |
3c165eaf |
364 | break; |
4d683428 |
365 | } |
366 | fmt = parse_c_type(info, fmt, &c_size, &c_type, field_fmt); |
3c165eaf |
367 | /* |
368 | * Output c types if no trace types has been |
369 | * specified. |
370 | */ |
371 | if (!trace_size) |
372 | trace_size = c_size; |
373 | if (trace_type == LTT_TYPE_NONE) |
374 | trace_type = c_type; |
375 | if (c_type == LTT_TYPE_STRING) |
376 | trace_type = LTT_TYPE_STRING; |
377 | /* perform trace write */ |
378 | offset = add_type(info, offset, name, trace_size, |
4d683428 |
379 | trace_type, c_size, c_type, attributes, field_count++, |
380 | field_fmt); |
3c165eaf |
381 | trace_size = c_size = 0; |
382 | trace_type = c_size = LTT_TYPE_NONE; |
4d683428 |
383 | g_string_truncate(field_fmt, 0); |
3c165eaf |
384 | attributes = 0; |
385 | name_begin = NULL; |
386 | if (name) { |
387 | g_free(name); |
388 | name = NULL; |
389 | } |
390 | break; |
391 | case ' ': |
4d683428 |
392 | g_string_truncate(field_fmt, 0); |
565460ea |
393 | if (!name_end && name_begin) { |
3c165eaf |
394 | name_end = fmt; |
395 | if (name) |
396 | g_free(name); |
397 | name = g_new(char, name_end - name_begin + 1); |
398 | memcpy(name, name_begin, name_end - name_begin); |
399 | name[name_end - name_begin] = '\0'; |
400 | } |
401 | break; /* Skip white spaces */ |
402 | default: |
4d683428 |
403 | g_string_append_c(field_fmt, *fmt); |
565460ea |
404 | if (!name_begin) { |
3c165eaf |
405 | name_begin = fmt; |
406 | name_end = NULL; |
407 | } |
408 | } |
409 | } |
410 | info->size = offset; |
411 | if (name) |
412 | g_free(name); |
4d683428 |
413 | g_string_free(field_fmt, TRUE); |
3c165eaf |
414 | } |
415 | |
416 | int marker_parse_format(const char *format, struct marker_info *info) |
417 | { |
418 | if (info->fields) |
419 | g_array_free(info->fields, TRUE); |
420 | info->fields = g_array_sized_new(FALSE, TRUE, |
421 | sizeof(struct marker_field), DEFAULT_FIELDS_NUM); |
422 | format_parse(format, info); |
2e13d6af |
423 | return 0; |
3c165eaf |
424 | } |
425 | |
426 | int marker_format_event(LttTrace *trace, GQuark name, const char *format) |
d2007fbd |
427 | { |
428 | struct marker_info *info; |
c09a11e9 |
429 | char *fquery; |
430 | char *fcopy; |
d2007fbd |
431 | |
c09a11e9 |
432 | fquery = marker_get_format_from_name(trace, name); |
37e04c8b |
433 | if (fquery) { |
c09a11e9 |
434 | if (strcmp(fquery, format) != 0) |
435 | g_error("Marker format mismatch \"%s\" vs \"%s\" for marker %s. " |
436 | "Kernel issue.", fquery, format, g_quark_to_string(name)); |
37e04c8b |
437 | else |
438 | return 0; /* Already exists. Nothing to do. */ |
439 | } |
c09a11e9 |
440 | fcopy = g_new(char, strlen(format)+1); |
441 | strcpy(fcopy, format); |
f64fedd7 |
442 | g_hash_table_insert(trace->markers_format_hash, (gpointer)(gulong)name, |
c09a11e9 |
443 | (gpointer)fcopy); |
444 | |
abc34be7 |
445 | info = marker_get_info_from_name(trace, name); |
3c165eaf |
446 | for (; info != NULL; info = info->next) { |
c09a11e9 |
447 | info->format = fcopy; |
3c165eaf |
448 | if (marker_parse_format(format, info)) |
2e13d6af |
449 | g_error("Error parsing marker format \"%s\" for marker \"%s\"", format, |
3c165eaf |
450 | g_quark_to_string(name)); |
451 | } |
03dab2c1 |
452 | return 0; |
d2007fbd |
453 | } |
454 | |
3c165eaf |
455 | int marker_id_event(LttTrace *trace, GQuark name, guint16 id, |
456 | uint8_t int_size, uint8_t long_size, uint8_t pointer_size, |
457 | uint8_t size_t_size, uint8_t alignment) |
d2007fbd |
458 | { |
3c165eaf |
459 | struct marker_info *info, *head; |
03dab2c1 |
460 | int found = 0; |
d2007fbd |
461 | |
c7146c5c |
462 | if (trace->markers->len <= id) |
abc34be7 |
463 | trace->markers = g_array_set_size(trace->markers, |
464 | max(trace->markers->len * 2, id + 1)); |
d2007fbd |
465 | info = &g_array_index(trace->markers, struct marker_info, id); |
3c165eaf |
466 | info->name = name; |
467 | info->int_size = int_size; |
468 | info->long_size = long_size; |
469 | info->pointer_size = pointer_size; |
470 | info->size_t_size = size_t_size; |
471 | info->alignment = alignment; |
dcf96842 |
472 | info->fields = NULL; |
3c165eaf |
473 | info->next = NULL; |
c09a11e9 |
474 | info->format = marker_get_format_from_name(trace, name); |
475 | if (info->format && marker_parse_format(info->format, info)) |
476 | g_error("Error parsing marker format \"%s\" for marker \"%s\"", |
477 | info->format, g_quark_to_string(name)); |
abc34be7 |
478 | head = marker_get_info_from_name(trace, name); |
3c165eaf |
479 | if (!head) |
f64fedd7 |
480 | g_hash_table_insert(trace->markers_hash, (gpointer)(gulong)name, |
abc34be7 |
481 | (gpointer)(gulong)id); |
d7913a10 |
482 | else { |
03dab2c1 |
483 | struct marker_info *iter; |
484 | for (iter = head; iter != NULL; iter = iter->next) |
485 | if (iter->name == name) |
486 | found = 1; |
487 | if (!found) { |
f64fedd7 |
488 | g_hash_table_replace(trace->markers_hash, (gpointer)(gulong)name, |
abc34be7 |
489 | (gpointer)(gulong)id); |
03dab2c1 |
490 | info->next = head; |
491 | } |
d7913a10 |
492 | } |
03dab2c1 |
493 | return 0; |
d2007fbd |
494 | } |
495 | |
496 | int allocate_marker_data(LttTrace *trace) |
497 | { |
498 | /* Init array to 0 */ |
499 | trace->markers = g_array_sized_new(FALSE, TRUE, |
500 | sizeof(struct marker_info), DEFAULT_MARKERS_NUM); |
3c165eaf |
501 | if (!trace->markers) |
502 | return -ENOMEM; |
503 | trace->markers_hash = g_hash_table_new(g_direct_hash, g_direct_equal); |
c09a11e9 |
504 | if (!trace->markers_hash) |
505 | return -ENOMEM; |
506 | trace->markers_format_hash = g_hash_table_new_full(g_direct_hash, |
507 | g_direct_equal, NULL, g_free); |
3c165eaf |
508 | if (!trace->markers_hash) |
509 | return -ENOMEM; |
510 | return 0; |
d2007fbd |
511 | } |
512 | |
3c165eaf |
513 | void destroy_marker_data(LttTrace *trace) |
d2007fbd |
514 | { |
4d683428 |
515 | unsigned int i, j; |
d2007fbd |
516 | struct marker_info *info; |
4d683428 |
517 | struct marker_field *field; |
d2007fbd |
518 | |
519 | for (i=0; i<trace->markers->len; i++) { |
520 | info = &g_array_index(trace->markers, struct marker_info, i); |
4d683428 |
521 | if (info->fields) { |
522 | for (j = 0; j < info->fields->len; j++) { |
523 | field = &g_array_index(info->fields, struct marker_field, j); |
524 | g_string_free(field->fmt, TRUE); |
525 | } |
3c165eaf |
526 | g_array_free(info->fields, TRUE); |
4d683428 |
527 | } |
d2007fbd |
528 | } |
529 | g_array_free(trace->markers, TRUE); |
530 | g_hash_table_destroy(trace->markers_hash); |
c09a11e9 |
531 | g_hash_table_destroy(trace->markers_format_hash); |
d2007fbd |
532 | } |