format string support
[lttv.git] / trunk / lttv / ltt / marker.c
1 /* This file is part of the Linux Trace Toolkit viewer
2 * Copyright (C) 2007 Mathieu Desnoyers
3 *
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.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <glib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <ltt/compiler.h>
30 #include <ltt/marker.h>
31 #include <ltt/ltt-private.h>
32
33 #define DEFAULT_MARKERS_NUM 100
34 #define DEFAULT_FIELDS_NUM 1
35 #define MAX_NAME_LEN 1024
36
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':
83 *trace_type = LTT_TYPE_POINTER;
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, GString *field_fmt)
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 g_string_append_c(field_fmt, *fmt);
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 g_string_append_c(field_fmt, *fmt);
176 ++fmt;
177 }
178 }
179
180 switch (*fmt) {
181 case 'c':
182 *c_type = LTT_TYPE_UNSIGNED_INT;
183 *c_size = sizeof(unsigned char);
184 g_string_append_c(field_fmt, *fmt);
185 goto parse_end;
186 case 's':
187 *c_type = LTT_TYPE_STRING;
188 goto parse_end;
189 case 'p':
190 *c_type = LTT_TYPE_POINTER;
191 *c_size = info->pointer_size;
192 goto parse_end;
193 case 'd':
194 case 'i':
195 *c_type = LTT_TYPE_SIGNED_INT;
196 g_string_append_c(field_fmt, 'l');
197 g_string_append_c(field_fmt, 'l');
198 g_string_append_c(field_fmt, *fmt);
199 break;
200 case 'o':
201 case 'u':
202 case 'x':
203 case 'X':
204 g_string_append_c(field_fmt, 'l');
205 g_string_append_c(field_fmt, 'l');
206 g_string_append_c(field_fmt, *fmt);
207 *c_type = LTT_TYPE_UNSIGNED_INT;
208 break;
209 default:
210 if (!*fmt)
211 --fmt;
212 goto parse_end;
213 }
214 switch (qualifier) {
215 case 'L':
216 *c_size = sizeof(long long);
217 break;
218 case 'l':
219 *c_size = info->long_size;
220 break;
221 case 'Z':
222 case 'z':
223 *c_size = info->size_t_size;
224 break;
225 case 't':
226 *c_size = info->pointer_size;
227 break;
228 case 'h':
229 *c_size = sizeof(short);
230 break;
231 default:
232 *c_size = info->int_size;
233 }
234
235 parse_end:
236 return fmt;
237 }
238
239 static inline long add_type(struct marker_info *info,
240 long offset, const char *name,
241 char trace_size, enum ltt_type trace_type,
242 char c_size, enum ltt_type c_type, unsigned long attributes,
243 unsigned int field_count, GString *field_fmt)
244 {
245 struct marker_field *field;
246 char tmpname[MAX_NAME_LEN];
247
248 info->fields = g_array_set_size(info->fields, info->fields->len+1);
249 field = &g_array_index(info->fields, struct marker_field,
250 info->fields->len-1);
251 if (name)
252 field->name = g_quark_from_string(name);
253 else {
254 snprintf(tmpname, MAX_NAME_LEN-1, "field %u", field_count);
255 field->name = g_quark_from_string(tmpname);
256 }
257 field->type = trace_type;
258 field->fmt = g_string_new(field_fmt->str);
259
260 switch (trace_type) {
261 case LTT_TYPE_SIGNED_INT:
262 case LTT_TYPE_UNSIGNED_INT:
263 case LTT_TYPE_POINTER:
264 field->size = trace_size;
265 field->alignment = trace_size;
266 field->attributes = attributes;
267 if (offset == -1) {
268 field->offset = -1;
269 field->static_offset = 0;
270 return -1;
271 } else {
272 field->offset = offset + ltt_align(offset, field->alignment,
273 info->alignment);
274 field->static_offset = 1;
275 return field->offset + trace_size;
276 }
277 case LTT_TYPE_STRING:
278 field->offset = offset;
279 field->size = 0; /* Variable length, size is 0 */
280 field->alignment = 1;
281 if (offset == -1)
282 field->static_offset = 0;
283 else
284 field->static_offset = 1;
285 return -1;
286 default:
287 g_error("Unexpected type"); //FIXME: compact type
288 return 0;
289 }
290 }
291
292 long marker_update_fields_offsets(struct marker_info *info, const char *data)
293 {
294 struct marker_field *field;
295 unsigned int i;
296 long offset = 0;
297
298 /* Find the last field with a static offset, then update from there. */
299 for (i = info->fields->len - 1; i >= 0; i--) {
300 field = &g_array_index(info->fields, struct marker_field, i);
301 if (field->static_offset) {
302 offset = field->offset;
303 break;
304 }
305 }
306
307 for (; i < info->fields->len; i++) {
308 field = &g_array_index(info->fields, struct marker_field, i);
309
310 switch (field->type) {
311 case LTT_TYPE_SIGNED_INT:
312 case LTT_TYPE_UNSIGNED_INT:
313 case LTT_TYPE_POINTER:
314 field->offset = offset + ltt_align(offset, field->alignment,
315 info->alignment);
316 offset = field->offset + field->size;
317 break;
318 case LTT_TYPE_STRING:
319 field->offset = offset;
320 offset = offset + strlen(&data[offset]) + 1;
321 // not aligning on pointer size, breaking genevent backward compatibility.
322 break;
323 default:
324 g_error("Unexpected type"); //FIXME: compact type
325 return -1;
326 }
327 }
328 return offset;
329 }
330
331 static void format_parse(const char *fmt, struct marker_info *info)
332 {
333 char trace_size = 0, c_size = 0; /*
334 * 0 (unset), 1, 2, 4, 8 bytes.
335 */
336 enum ltt_type trace_type = LTT_TYPE_NONE, c_type = LTT_TYPE_NONE;
337 unsigned long attributes = 0;
338 long offset = 0;
339 const char *name_begin = NULL, *name_end = NULL;
340 char *name = NULL;
341 unsigned int field_count = 1;
342 GString *field_fmt = g_string_new("");
343
344 name_begin = fmt;
345 for (; *fmt ; ++fmt) {
346 switch (*fmt) {
347 case '#':
348 /* tracetypes (#) */
349 ++fmt; /* skip first '#' */
350 if (*fmt == '#') { /* Escaped ## */
351 g_string_append_c(field_fmt, *fmt);
352 g_string_append_c(field_fmt, *fmt);
353 break;
354 }
355 attributes = 0;
356 fmt = parse_trace_type(info, fmt, &trace_size, &trace_type,
357 &attributes);
358 break;
359 case '%':
360 /* c types (%) */
361 g_string_append_c(field_fmt, *fmt);
362 ++fmt; /* skip first '%' */
363 if (*fmt == '%') { /* Escaped %% */
364 g_string_append_c(field_fmt, *fmt);
365 break;
366 }
367 fmt = parse_c_type(info, fmt, &c_size, &c_type, field_fmt);
368 /*
369 * Output c types if no trace types has been
370 * specified.
371 */
372 if (!trace_size)
373 trace_size = c_size;
374 if (trace_type == LTT_TYPE_NONE)
375 trace_type = c_type;
376 if (c_type == LTT_TYPE_STRING)
377 trace_type = LTT_TYPE_STRING;
378 /* perform trace write */
379 offset = add_type(info, offset, name, trace_size,
380 trace_type, c_size, c_type, attributes, field_count++,
381 field_fmt);
382 g_string_truncate(field_fmt, 0);
383 trace_size = c_size = 0;
384 trace_type = c_size = LTT_TYPE_NONE;
385 g_string_truncate(field_fmt, 0);
386 attributes = 0;
387 name_begin = NULL;
388 if (name) {
389 g_free(name);
390 name = NULL;
391 }
392 break;
393 case ' ':
394 g_string_truncate(field_fmt, 0);
395 if (!name_end && name_begin) {
396 name_end = fmt;
397 if (name)
398 g_free(name);
399 name = g_new(char, name_end - name_begin + 1);
400 memcpy(name, name_begin, name_end - name_begin);
401 name[name_end - name_begin] = '\0';
402 }
403 break; /* Skip white spaces */
404 default:
405 g_string_append_c(field_fmt, *fmt);
406 if (!name_begin) {
407 name_begin = fmt;
408 name_end = NULL;
409 }
410 }
411 }
412 info->size = offset;
413 if (name)
414 g_free(name);
415 g_string_free(field_fmt, TRUE);
416 }
417
418 int marker_parse_format(const char *format, struct marker_info *info)
419 {
420 if (info->fields)
421 g_array_free(info->fields, TRUE);
422 info->fields = g_array_sized_new(FALSE, TRUE,
423 sizeof(struct marker_field), DEFAULT_FIELDS_NUM);
424 format_parse(format, info);
425 return 0;
426 }
427
428 int marker_format_event(LttTrace *trace, GQuark name, const char *format)
429 {
430 struct marker_info *info;
431 char *fquery;
432 char *fcopy;
433
434 fquery = marker_get_format_from_name(trace, name);
435 if (fquery) {
436 if (strcmp(fquery, format) != 0)
437 g_error("Marker format mismatch \"%s\" vs \"%s\" for marker %s. "
438 "Kernel issue.", fquery, format, g_quark_to_string(name));
439 else
440 return 0; /* Already exists. Nothing to do. */
441 }
442 fcopy = g_new(char, strlen(format)+1);
443 strcpy(fcopy, format);
444 g_hash_table_insert(trace->markers_format_hash, (gpointer)(gulong)name,
445 (gpointer)fcopy);
446
447 info = marker_get_info_from_name(trace, name);
448 for (; info != NULL; info = info->next) {
449 info->format = fcopy;
450 if (marker_parse_format(format, info))
451 g_error("Error parsing marker format \"%s\" for marker \"%s\"", format,
452 g_quark_to_string(name));
453 }
454 return 0;
455 }
456
457 int marker_id_event(LttTrace *trace, GQuark name, guint16 id,
458 uint8_t int_size, uint8_t long_size, uint8_t pointer_size,
459 uint8_t size_t_size, uint8_t alignment)
460 {
461 struct marker_info *info, *head;
462 int found = 0;
463
464 if (trace->markers->len <= id)
465 trace->markers = g_array_set_size(trace->markers,
466 max(trace->markers->len * 2, id + 1));
467 info = &g_array_index(trace->markers, struct marker_info, id);
468 info->name = name;
469 info->int_size = int_size;
470 info->long_size = long_size;
471 info->pointer_size = pointer_size;
472 info->size_t_size = size_t_size;
473 info->alignment = alignment;
474 info->fields = NULL;
475 info->next = NULL;
476 info->format = marker_get_format_from_name(trace, name);
477 if (info->format && marker_parse_format(info->format, info))
478 g_error("Error parsing marker format \"%s\" for marker \"%s\"",
479 info->format, g_quark_to_string(name));
480 head = marker_get_info_from_name(trace, name);
481 if (!head)
482 g_hash_table_insert(trace->markers_hash, (gpointer)(gulong)name,
483 (gpointer)(gulong)id);
484 else {
485 struct marker_info *iter;
486 for (iter = head; iter != NULL; iter = iter->next)
487 if (iter->name == name)
488 found = 1;
489 if (!found) {
490 g_hash_table_replace(trace->markers_hash, (gpointer)(gulong)name,
491 (gpointer)(gulong)id);
492 info->next = head;
493 }
494 }
495 return 0;
496 }
497
498 int allocate_marker_data(LttTrace *trace)
499 {
500 /* Init array to 0 */
501 trace->markers = g_array_sized_new(FALSE, TRUE,
502 sizeof(struct marker_info), DEFAULT_MARKERS_NUM);
503 if (!trace->markers)
504 return -ENOMEM;
505 trace->markers_hash = g_hash_table_new(g_direct_hash, g_direct_equal);
506 if (!trace->markers_hash)
507 return -ENOMEM;
508 trace->markers_format_hash = g_hash_table_new_full(g_direct_hash,
509 g_direct_equal, NULL, g_free);
510 if (!trace->markers_hash)
511 return -ENOMEM;
512 return 0;
513 }
514
515 void destroy_marker_data(LttTrace *trace)
516 {
517 unsigned int i, j;
518 struct marker_info *info;
519 struct marker_field *field;
520
521 for (i=0; i<trace->markers->len; i++) {
522 info = &g_array_index(trace->markers, struct marker_info, i);
523 if (info->fields) {
524 for (j = 0; j < info->fields->len; j++) {
525 field = &g_array_index(info->fields, struct marker_field, j);
526 g_string_free(field->fmt, TRUE);
527 }
528 g_array_free(info->fields, TRUE);
529 }
530 }
531 g_array_free(trace->markers, TRUE);
532 g_hash_table_destroy(trace->markers_hash);
533 g_hash_table_destroy(trace->markers_format_hash);
534 }
This page took 0.048813 seconds and 4 git commands to generate.