Commit | Line | Data |
---|---|---|
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 | ||
2e2088e1 MD |
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; | |
2e2088e1 MD |
431 | GArray *group; |
432 | ||
433 | group = g_datalist_id_get_data(&trace->tracefiles, channel); | |
434 | if (!group) | |
435 | return -ENOENT; | |
436 | g_assert(group->len > 0); | |
437 | mdata = g_array_index (group, LttTracefile, 0).mdata; | |
750eb11a | 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 | ||
2e2088e1 | 463 | int marker_id_event(LttTrace *trace, GQuark channel, GQuark name, guint16 id, |
f972ab5e MD |
464 | uint8_t int_size, uint8_t long_size, uint8_t pointer_size, |
465 | uint8_t size_t_size, uint8_t alignment) | |
466 | { | |
467 | struct marker_data *mdata; | |
468 | struct marker_info *info, *head; | |
469 | int found = 0; | |
2e2088e1 | 470 | GArray *group; |
f972ab5e | 471 | |
2e2088e1 MD |
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); | |
476 | if (!group) | |
477 | return -ENOENT; | |
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 | } |