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' || |
65 | *fmt == '4' || *fmt == 8) { |
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, |
146 | char *c_size, enum ltt_type *c_type) |
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': |
161 | ++fmt; |
162 | goto repeat; |
163 | } |
164 | |
165 | /* get the conversion qualifier */ |
166 | qualifier = -1; |
167 | if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || |
168 | *fmt =='Z' || *fmt == 'z' || *fmt == 't' || |
169 | *fmt == 'S') { |
170 | qualifier = *fmt; |
171 | ++fmt; |
172 | if (qualifier == 'l' && *fmt == 'l') { |
173 | qualifier = 'L'; |
174 | ++fmt; |
175 | } |
176 | } |
177 | |
178 | switch (*fmt) { |
179 | case 'c': |
180 | *c_type = LTT_TYPE_UNSIGNED_INT; |
181 | *c_size = sizeof(unsigned char); |
182 | goto parse_end; |
183 | case 's': |
184 | *c_type = LTT_TYPE_STRING; |
185 | goto parse_end; |
186 | case 'p': |
91346f59 |
187 | *c_type = LTT_TYPE_POINTER; |
3c165eaf |
188 | *c_size = info->pointer_size; |
189 | goto parse_end; |
190 | case 'd': |
191 | case 'i': |
192 | *c_type = LTT_TYPE_SIGNED_INT; |
193 | break; |
194 | case 'o': |
195 | case 'u': |
196 | case 'x': |
197 | case 'X': |
198 | *c_type = LTT_TYPE_UNSIGNED_INT; |
199 | break; |
200 | default: |
201 | if (!*fmt) |
202 | --fmt; |
203 | goto parse_end; |
204 | } |
205 | switch (qualifier) { |
206 | case 'L': |
207 | *c_size = sizeof(long long); |
208 | break; |
209 | case 'l': |
210 | *c_size = info->long_size; |
211 | break; |
212 | case 'Z': |
213 | case 'z': |
214 | *c_size = info->size_t_size; |
215 | break; |
216 | case 't': |
217 | *c_size = info->pointer_size; |
218 | break; |
219 | case 'h': |
220 | *c_size = sizeof(short); |
221 | break; |
222 | default: |
223 | *c_size = info->int_size; |
224 | } |
225 | |
226 | parse_end: |
227 | return fmt; |
228 | } |
229 | |
230 | static inline long add_type(struct marker_info *info, |
231 | long offset, const char *name, |
232 | char trace_size, enum ltt_type trace_type, |
233 | char c_size, enum ltt_type c_type, unsigned long attributes, |
234 | unsigned int field_count) |
235 | { |
236 | struct marker_field *field; |
237 | char tmpname[MAX_NAME_LEN]; |
238 | |
239 | info->fields = g_array_set_size(info->fields, info->fields->len+1); |
240 | field = &g_array_index(info->fields, struct marker_field, |
241 | info->fields->len-1); |
242 | if (name) |
243 | field->name = g_quark_from_string(name); |
244 | else { |
245 | snprintf(tmpname, MAX_NAME_LEN-1, "field %u", field_count); |
246 | field->name = g_quark_from_string(tmpname); |
247 | } |
248 | field->type = trace_type; |
249 | |
250 | switch (trace_type) { |
251 | case LTT_TYPE_SIGNED_INT: |
252 | case LTT_TYPE_UNSIGNED_INT: |
7f440a66 |
253 | case LTT_TYPE_POINTER: |
3c165eaf |
254 | field->size = trace_size; |
255 | field->alignment = trace_size; |
256 | field->attributes = attributes; |
257 | if (offset == -1) { |
258 | field->offset = -1; |
259 | field->static_offset = 0; |
260 | return -1; |
261 | } else { |
262 | field->offset = offset + ltt_align(offset, field->alignment, |
263 | info->alignment); |
264 | field->static_offset = 1; |
265 | return field->offset + trace_size; |
266 | } |
267 | case LTT_TYPE_STRING: |
268 | field->offset = offset; |
269 | field->size = 0; /* Variable length, size is 0 */ |
270 | field->alignment = 1; |
271 | if (offset == -1) |
272 | field->static_offset = 0; |
273 | else |
274 | field->static_offset = 1; |
275 | return -1; |
276 | default: |
277 | g_error("Unexpected type"); //FIXME: compact type |
278 | return 0; |
279 | } |
280 | } |
281 | |
282 | long marker_update_fields_offsets(struct marker_info *info, const char *data) |
283 | { |
284 | struct marker_field *field; |
285 | unsigned int i; |
286 | long offset = 0; |
287 | |
256a5b3a |
288 | /* Find the last field with a static offset, then update from there. */ |
289 | for (i = info->fields->len - 1; i >= 0; i--) { |
3c165eaf |
290 | field = &g_array_index(info->fields, struct marker_field, i); |
256a5b3a |
291 | if (field->static_offset) { |
292 | offset = field->offset; |
293 | break; |
294 | } |
295 | } |
3c165eaf |
296 | |
256a5b3a |
297 | for (; i < info->fields->len; i++) { |
298 | field = &g_array_index(info->fields, struct marker_field, i); |
3c165eaf |
299 | |
300 | switch (field->type) { |
301 | case LTT_TYPE_SIGNED_INT: |
302 | case LTT_TYPE_UNSIGNED_INT: |
7f440a66 |
303 | case LTT_TYPE_POINTER: |
3c165eaf |
304 | field->offset = offset + ltt_align(offset, field->alignment, |
305 | info->alignment); |
306 | offset = field->offset + field->size; |
307 | break; |
308 | case LTT_TYPE_STRING: |
309 | field->offset = offset; |
310 | offset = offset + strlen(&data[offset]) + 1; |
311 | // not aligning on pointer size, breaking genevent backward compatibility. |
312 | break; |
313 | default: |
314 | g_error("Unexpected type"); //FIXME: compact type |
315 | return -1; |
316 | } |
317 | } |
318 | return offset; |
319 | } |
320 | |
321 | static void format_parse(const char *fmt, struct marker_info *info) |
322 | { |
323 | char trace_size = 0, c_size = 0; /* |
324 | * 0 (unset), 1, 2, 4, 8 bytes. |
325 | */ |
326 | enum ltt_type trace_type = LTT_TYPE_NONE, c_type = LTT_TYPE_NONE; |
327 | unsigned long attributes = 0; |
328 | long offset = 0; |
329 | const char *name_begin = NULL, *name_end = NULL; |
330 | char *name = NULL; |
331 | unsigned int field_count = 1; |
332 | |
2e13d6af |
333 | name_begin = fmt; |
3c165eaf |
334 | for (; *fmt ; ++fmt) { |
335 | switch (*fmt) { |
336 | case '#': |
337 | /* tracetypes (#) */ |
338 | ++fmt; /* skip first '#' */ |
339 | if (*fmt == '#') /* Escaped ## */ |
340 | break; |
341 | attributes = 0; |
342 | fmt = parse_trace_type(info, fmt, &trace_size, &trace_type, |
343 | &attributes); |
344 | break; |
345 | case '%': |
346 | /* c types (%) */ |
347 | ++fmt; /* skip first '%' */ |
348 | if (*fmt == '%') /* Escaped %% */ |
349 | break; |
350 | fmt = parse_c_type(info, fmt, &c_size, &c_type); |
351 | /* |
352 | * Output c types if no trace types has been |
353 | * specified. |
354 | */ |
355 | if (!trace_size) |
356 | trace_size = c_size; |
357 | if (trace_type == LTT_TYPE_NONE) |
358 | trace_type = c_type; |
359 | if (c_type == LTT_TYPE_STRING) |
360 | trace_type = LTT_TYPE_STRING; |
361 | /* perform trace write */ |
362 | offset = add_type(info, offset, name, trace_size, |
363 | trace_type, c_size, c_type, attributes, field_count++); |
364 | trace_size = c_size = 0; |
365 | trace_type = c_size = LTT_TYPE_NONE; |
366 | attributes = 0; |
367 | name_begin = NULL; |
368 | if (name) { |
369 | g_free(name); |
370 | name = NULL; |
371 | } |
372 | break; |
373 | case ' ': |
565460ea |
374 | if (!name_end && name_begin) { |
3c165eaf |
375 | name_end = fmt; |
376 | if (name) |
377 | g_free(name); |
378 | name = g_new(char, name_end - name_begin + 1); |
379 | memcpy(name, name_begin, name_end - name_begin); |
380 | name[name_end - name_begin] = '\0'; |
381 | } |
382 | break; /* Skip white spaces */ |
383 | default: |
565460ea |
384 | if (!name_begin) { |
3c165eaf |
385 | name_begin = fmt; |
386 | name_end = NULL; |
387 | } |
388 | } |
389 | } |
390 | info->size = offset; |
391 | if (name) |
392 | g_free(name); |
393 | } |
394 | |
395 | int marker_parse_format(const char *format, struct marker_info *info) |
396 | { |
397 | if (info->fields) |
398 | g_array_free(info->fields, TRUE); |
399 | info->fields = g_array_sized_new(FALSE, TRUE, |
400 | sizeof(struct marker_field), DEFAULT_FIELDS_NUM); |
401 | format_parse(format, info); |
2e13d6af |
402 | return 0; |
3c165eaf |
403 | } |
404 | |
405 | int marker_format_event(LttTrace *trace, GQuark name, const char *format) |
d2007fbd |
406 | { |
407 | struct marker_info *info; |
408 | |
abc34be7 |
409 | info = marker_get_info_from_name(trace, name); |
d2007fbd |
410 | if (!info) |
2e13d6af |
411 | g_error("Got marker format \"%s\", but marker name \"%s\" has no ID yet. " |
d2007fbd |
412 | "Kernel issue.", |
413 | format, name); |
3c165eaf |
414 | for (; info != NULL; info = info->next) { |
415 | if (info->format) |
416 | g_free(info->format); |
417 | info->format = g_new(char, strlen(format)+1); |
418 | strcpy(info->format, format); |
419 | if (marker_parse_format(format, info)) |
2e13d6af |
420 | g_error("Error parsing marker format \"%s\" for marker \"%s\"", format, |
3c165eaf |
421 | g_quark_to_string(name)); |
422 | } |
03dab2c1 |
423 | return 0; |
d2007fbd |
424 | } |
425 | |
3c165eaf |
426 | int marker_id_event(LttTrace *trace, GQuark name, guint16 id, |
427 | uint8_t int_size, uint8_t long_size, uint8_t pointer_size, |
428 | uint8_t size_t_size, uint8_t alignment) |
d2007fbd |
429 | { |
3c165eaf |
430 | struct marker_info *info, *head; |
03dab2c1 |
431 | int found = 0; |
d2007fbd |
432 | |
c7146c5c |
433 | if (trace->markers->len <= id) |
abc34be7 |
434 | trace->markers = g_array_set_size(trace->markers, |
435 | max(trace->markers->len * 2, id + 1)); |
d2007fbd |
436 | info = &g_array_index(trace->markers, struct marker_info, id); |
3c165eaf |
437 | info->name = name; |
438 | info->int_size = int_size; |
439 | info->long_size = long_size; |
440 | info->pointer_size = pointer_size; |
441 | info->size_t_size = size_t_size; |
442 | info->alignment = alignment; |
dcf96842 |
443 | info->fields = NULL; |
3c165eaf |
444 | info->next = NULL; |
abc34be7 |
445 | head = marker_get_info_from_name(trace, name); |
3c165eaf |
446 | if (!head) |
abc34be7 |
447 | g_hash_table_insert(trace->markers_hash, (gpointer)name, |
448 | (gpointer)(gulong)id); |
d7913a10 |
449 | else { |
03dab2c1 |
450 | struct marker_info *iter; |
451 | for (iter = head; iter != NULL; iter = iter->next) |
452 | if (iter->name == name) |
453 | found = 1; |
454 | if (!found) { |
abc34be7 |
455 | g_hash_table_replace(trace->markers_hash, (gpointer)name, |
456 | (gpointer)(gulong)id); |
03dab2c1 |
457 | info->next = head; |
458 | } |
d7913a10 |
459 | } |
03dab2c1 |
460 | return 0; |
d2007fbd |
461 | } |
462 | |
463 | int allocate_marker_data(LttTrace *trace) |
464 | { |
465 | /* Init array to 0 */ |
466 | trace->markers = g_array_sized_new(FALSE, TRUE, |
467 | sizeof(struct marker_info), DEFAULT_MARKERS_NUM); |
3c165eaf |
468 | if (!trace->markers) |
469 | return -ENOMEM; |
470 | trace->markers_hash = g_hash_table_new(g_direct_hash, g_direct_equal); |
471 | if (!trace->markers_hash) |
472 | return -ENOMEM; |
473 | return 0; |
d2007fbd |
474 | } |
475 | |
3c165eaf |
476 | void destroy_marker_data(LttTrace *trace) |
d2007fbd |
477 | { |
478 | unsigned int i; |
479 | struct marker_info *info; |
480 | |
481 | for (i=0; i<trace->markers->len; i++) { |
482 | info = &g_array_index(trace->markers, struct marker_info, i); |
d2007fbd |
483 | if (info->format) |
484 | g_free(info->format); |
3c165eaf |
485 | if (info->fields) |
486 | g_array_free(info->fields, TRUE); |
d2007fbd |
487 | } |
488 | g_array_free(trace->markers, TRUE); |
489 | g_hash_table_destroy(trace->markers_hash); |
490 | } |