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: |
253 | field->size = trace_size; |
254 | field->alignment = trace_size; |
255 | field->attributes = attributes; |
256 | if (offset == -1) { |
257 | field->offset = -1; |
258 | field->static_offset = 0; |
259 | return -1; |
260 | } else { |
261 | field->offset = offset + ltt_align(offset, field->alignment, |
262 | info->alignment); |
263 | field->static_offset = 1; |
264 | return field->offset + trace_size; |
265 | } |
266 | case LTT_TYPE_STRING: |
267 | field->offset = offset; |
268 | field->size = 0; /* Variable length, size is 0 */ |
269 | field->alignment = 1; |
270 | if (offset == -1) |
271 | field->static_offset = 0; |
272 | else |
273 | field->static_offset = 1; |
274 | return -1; |
275 | default: |
276 | g_error("Unexpected type"); //FIXME: compact type |
277 | return 0; |
278 | } |
279 | } |
280 | |
281 | long marker_update_fields_offsets(struct marker_info *info, const char *data) |
282 | { |
283 | struct marker_field *field; |
284 | unsigned int i; |
285 | long offset = 0; |
286 | |
256a5b3a |
287 | /* Find the last field with a static offset, then update from there. */ |
288 | for (i = info->fields->len - 1; i >= 0; i--) { |
3c165eaf |
289 | field = &g_array_index(info->fields, struct marker_field, i); |
256a5b3a |
290 | if (field->static_offset) { |
291 | offset = field->offset; |
292 | break; |
293 | } |
294 | } |
3c165eaf |
295 | |
256a5b3a |
296 | for (; i < info->fields->len; i++) { |
297 | field = &g_array_index(info->fields, struct marker_field, i); |
3c165eaf |
298 | |
299 | switch (field->type) { |
300 | case LTT_TYPE_SIGNED_INT: |
301 | case LTT_TYPE_UNSIGNED_INT: |
302 | field->offset = offset + ltt_align(offset, field->alignment, |
303 | info->alignment); |
304 | offset = field->offset + field->size; |
305 | break; |
306 | case LTT_TYPE_STRING: |
307 | field->offset = offset; |
308 | offset = offset + strlen(&data[offset]) + 1; |
309 | // not aligning on pointer size, breaking genevent backward compatibility. |
310 | break; |
311 | default: |
312 | g_error("Unexpected type"); //FIXME: compact type |
313 | return -1; |
314 | } |
315 | } |
316 | return offset; |
317 | } |
318 | |
319 | static void format_parse(const char *fmt, struct marker_info *info) |
320 | { |
321 | char trace_size = 0, c_size = 0; /* |
322 | * 0 (unset), 1, 2, 4, 8 bytes. |
323 | */ |
324 | enum ltt_type trace_type = LTT_TYPE_NONE, c_type = LTT_TYPE_NONE; |
325 | unsigned long attributes = 0; |
326 | long offset = 0; |
327 | const char *name_begin = NULL, *name_end = NULL; |
328 | char *name = NULL; |
329 | unsigned int field_count = 1; |
330 | |
2e13d6af |
331 | name_begin = fmt; |
3c165eaf |
332 | for (; *fmt ; ++fmt) { |
333 | switch (*fmt) { |
334 | case '#': |
335 | /* tracetypes (#) */ |
336 | ++fmt; /* skip first '#' */ |
337 | if (*fmt == '#') /* Escaped ## */ |
338 | break; |
339 | attributes = 0; |
340 | fmt = parse_trace_type(info, fmt, &trace_size, &trace_type, |
341 | &attributes); |
342 | break; |
343 | case '%': |
344 | /* c types (%) */ |
345 | ++fmt; /* skip first '%' */ |
346 | if (*fmt == '%') /* Escaped %% */ |
347 | break; |
348 | fmt = parse_c_type(info, fmt, &c_size, &c_type); |
349 | /* |
350 | * Output c types if no trace types has been |
351 | * specified. |
352 | */ |
353 | if (!trace_size) |
354 | trace_size = c_size; |
355 | if (trace_type == LTT_TYPE_NONE) |
356 | trace_type = c_type; |
357 | if (c_type == LTT_TYPE_STRING) |
358 | trace_type = LTT_TYPE_STRING; |
359 | /* perform trace write */ |
360 | offset = add_type(info, offset, name, trace_size, |
361 | trace_type, c_size, c_type, attributes, field_count++); |
362 | trace_size = c_size = 0; |
363 | trace_type = c_size = LTT_TYPE_NONE; |
364 | attributes = 0; |
365 | name_begin = NULL; |
366 | if (name) { |
367 | g_free(name); |
368 | name = NULL; |
369 | } |
370 | break; |
371 | case ' ': |
565460ea |
372 | if (!name_end && name_begin) { |
3c165eaf |
373 | name_end = fmt; |
374 | if (name) |
375 | g_free(name); |
376 | name = g_new(char, name_end - name_begin + 1); |
377 | memcpy(name, name_begin, name_end - name_begin); |
378 | name[name_end - name_begin] = '\0'; |
379 | } |
380 | break; /* Skip white spaces */ |
381 | default: |
565460ea |
382 | if (!name_begin) { |
3c165eaf |
383 | name_begin = fmt; |
384 | name_end = NULL; |
385 | } |
386 | } |
387 | } |
388 | info->size = offset; |
389 | if (name) |
390 | g_free(name); |
391 | } |
392 | |
393 | int marker_parse_format(const char *format, struct marker_info *info) |
394 | { |
395 | if (info->fields) |
396 | g_array_free(info->fields, TRUE); |
397 | info->fields = g_array_sized_new(FALSE, TRUE, |
398 | sizeof(struct marker_field), DEFAULT_FIELDS_NUM); |
399 | format_parse(format, info); |
2e13d6af |
400 | return 0; |
3c165eaf |
401 | } |
402 | |
403 | int marker_format_event(LttTrace *trace, GQuark name, const char *format) |
d2007fbd |
404 | { |
405 | struct marker_info *info; |
406 | |
407 | info = g_hash_table_lookup(trace->markers_hash, (gconstpointer)name); |
408 | if (!info) |
2e13d6af |
409 | g_error("Got marker format \"%s\", but marker name \"%s\" has no ID yet. " |
d2007fbd |
410 | "Kernel issue.", |
411 | format, name); |
3c165eaf |
412 | for (; info != NULL; info = info->next) { |
413 | if (info->format) |
414 | g_free(info->format); |
415 | info->format = g_new(char, strlen(format)+1); |
416 | strcpy(info->format, format); |
417 | if (marker_parse_format(format, info)) |
2e13d6af |
418 | g_error("Error parsing marker format \"%s\" for marker \"%s\"", format, |
3c165eaf |
419 | g_quark_to_string(name)); |
420 | } |
03dab2c1 |
421 | return 0; |
d2007fbd |
422 | } |
423 | |
3c165eaf |
424 | int marker_id_event(LttTrace *trace, GQuark name, guint16 id, |
425 | uint8_t int_size, uint8_t long_size, uint8_t pointer_size, |
426 | uint8_t size_t_size, uint8_t alignment) |
d2007fbd |
427 | { |
3c165eaf |
428 | struct marker_info *info, *head; |
03dab2c1 |
429 | int found = 0; |
d2007fbd |
430 | |
c7146c5c |
431 | if (trace->markers->len <= id) |
d2007fbd |
432 | trace->markers = g_array_set_size(trace->markers, id+1); |
433 | info = &g_array_index(trace->markers, struct marker_info, id); |
3c165eaf |
434 | info->name = name; |
435 | info->int_size = int_size; |
436 | info->long_size = long_size; |
437 | info->pointer_size = pointer_size; |
438 | info->size_t_size = size_t_size; |
439 | info->alignment = alignment; |
440 | info->next = NULL; |
441 | head = g_hash_table_lookup(trace->markers_hash, (gconstpointer)name); |
442 | if (!head) |
443 | g_hash_table_insert(trace->markers_hash, (gpointer)name, info); |
d7913a10 |
444 | else { |
03dab2c1 |
445 | struct marker_info *iter; |
446 | for (iter = head; iter != NULL; iter = iter->next) |
447 | if (iter->name == name) |
448 | found = 1; |
449 | if (!found) { |
450 | g_hash_table_replace(trace->markers_hash, (gpointer)name, info); |
451 | info->next = head; |
452 | } |
d7913a10 |
453 | } |
03dab2c1 |
454 | return 0; |
d2007fbd |
455 | } |
456 | |
457 | int allocate_marker_data(LttTrace *trace) |
458 | { |
459 | /* Init array to 0 */ |
460 | trace->markers = g_array_sized_new(FALSE, TRUE, |
461 | sizeof(struct marker_info), DEFAULT_MARKERS_NUM); |
3c165eaf |
462 | if (!trace->markers) |
463 | return -ENOMEM; |
464 | trace->markers_hash = g_hash_table_new(g_direct_hash, g_direct_equal); |
465 | if (!trace->markers_hash) |
466 | return -ENOMEM; |
467 | return 0; |
d2007fbd |
468 | } |
469 | |
3c165eaf |
470 | void destroy_marker_data(LttTrace *trace) |
d2007fbd |
471 | { |
472 | unsigned int i; |
473 | struct marker_info *info; |
474 | |
475 | for (i=0; i<trace->markers->len; i++) { |
476 | info = &g_array_index(trace->markers, struct marker_info, i); |
d2007fbd |
477 | if (info->format) |
478 | g_free(info->format); |
3c165eaf |
479 | if (info->fields) |
480 | g_array_free(info->fields, TRUE); |
d2007fbd |
481 | } |
482 | g_array_free(trace->markers, TRUE); |
483 | g_hash_table_destroy(trace->markers_hash); |
484 | } |