Add trace packet header accessors and support custom headers
[babeltrace.git] / formats / ctf / ir / trace.c
1 /*
2 * trace.c
3 *
4 * Babeltrace CTF IR - Trace
5 *
6 * Copyright 2014 Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 *
8 * Author: Jérémie Galarneau <jeremie.galarneau@efficios.com>
9 *
10 * Permission is hereby granted, free of charge, to any person obtaining a copy
11 * of this software and associated documentation files (the "Software"), to deal
12 * in the Software without restriction, including without limitation the rights
13 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 * copies of the Software, and to permit persons to whom the Software is
15 * furnished to do so, subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be included in
18 * all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 * SOFTWARE.
27 */
28
29 #include <babeltrace/ctf-ir/trace-internal.h>
30 #include <babeltrace/ctf-ir/clock-internal.h>
31 #include <babeltrace/ctf-ir/stream-internal.h>
32 #include <babeltrace/ctf-ir/stream-class-internal.h>
33 #include <babeltrace/ctf-writer/functor-internal.h>
34 #include <babeltrace/ctf-ir/event-types-internal.h>
35 #include <babeltrace/compiler.h>
36
37 #define DEFAULT_IDENTIFIER_SIZE 128
38 #define DEFAULT_METADATA_STRING_SIZE 4096
39
40 static
41 void environment_variable_destroy(struct environment_variable *var);
42 static
43 void bt_ctf_trace_destroy(struct bt_ctf_ref *ref);
44 static
45 int init_trace_packet_header(struct bt_ctf_trace *trace);
46
47 static
48 const char * const reserved_keywords_str[] = {"align", "callsite",
49 "const", "char", "clock", "double", "enum", "env", "event",
50 "floating_point", "float", "integer", "int", "long", "short", "signed",
51 "stream", "string", "struct", "trace", "typealias", "typedef",
52 "unsigned", "variant", "void" "_Bool", "_Complex", "_Imaginary"};
53
54 static
55 const unsigned int field_type_aliases_alignments[] = {
56 [FIELD_TYPE_ALIAS_UINT5_T] = 1,
57 [FIELD_TYPE_ALIAS_UINT8_T ... FIELD_TYPE_ALIAS_UINT16_T] = 8,
58 [FIELD_TYPE_ALIAS_UINT27_T] = 1,
59 [FIELD_TYPE_ALIAS_UINT32_T ... FIELD_TYPE_ALIAS_UINT64_T] = 8,
60 };
61
62 static
63 const unsigned int field_type_aliases_sizes[] = {
64 [FIELD_TYPE_ALIAS_UINT5_T] = 5,
65 [FIELD_TYPE_ALIAS_UINT8_T] = 8,
66 [FIELD_TYPE_ALIAS_UINT16_T] = 16,
67 [FIELD_TYPE_ALIAS_UINT27_T] = 27,
68 [FIELD_TYPE_ALIAS_UINT32_T] = 32,
69 [FIELD_TYPE_ALIAS_UINT64_T] = 64,
70 };
71
72 static GHashTable *reserved_keywords_set;
73 static int init_done;
74 static int global_data_refcount;
75
76 struct bt_ctf_trace *bt_ctf_trace_create(void)
77 {
78 struct bt_ctf_trace *trace = NULL;
79
80 trace = g_new0(struct bt_ctf_trace, 1);
81 if (!trace) {
82 goto error;
83 }
84
85 bt_ctf_trace_set_byte_order(trace, BT_CTF_BYTE_ORDER_NATIVE);
86 bt_ctf_ref_init(&trace->ref_count);
87 trace->environment = g_ptr_array_new_with_free_func(
88 (GDestroyNotify)environment_variable_destroy);
89 trace->clocks = g_ptr_array_new_with_free_func(
90 (GDestroyNotify)bt_ctf_clock_put);
91 trace->streams = g_ptr_array_new_with_free_func(
92 (GDestroyNotify)bt_ctf_stream_put);
93 trace->stream_classes = g_ptr_array_new_with_free_func(
94 (GDestroyNotify)bt_ctf_stream_class_put);
95 if (!trace->environment || !trace->clocks ||
96 !trace->stream_classes || !trace->streams) {
97 goto error_destroy;
98 }
99
100 /* Generate a trace UUID */
101 uuid_generate(trace->uuid);
102 if (init_trace_packet_header(trace)) {
103 goto error_destroy;
104 }
105
106 return trace;
107
108 error_destroy:
109 bt_ctf_trace_destroy(&trace->ref_count);
110 trace = NULL;
111 error:
112 return trace;
113 }
114
115 void bt_ctf_trace_destroy(struct bt_ctf_ref *ref)
116 {
117 struct bt_ctf_trace *trace;
118
119 if (!ref) {
120 return;
121 }
122
123 trace = container_of(ref, struct bt_ctf_trace, ref_count);
124 if (trace->environment) {
125 g_ptr_array_free(trace->environment, TRUE);
126 }
127
128 if (trace->clocks) {
129 g_ptr_array_free(trace->clocks, TRUE);
130 }
131
132 if (trace->streams) {
133 g_ptr_array_free(trace->streams, TRUE);
134 }
135
136 if (trace->stream_classes) {
137 g_ptr_array_free(trace->stream_classes, TRUE);
138 }
139
140 bt_ctf_field_type_put(trace->packet_header_type);
141 g_free(trace);
142 }
143
144 struct bt_ctf_stream *bt_ctf_trace_create_stream(struct bt_ctf_trace *trace,
145 struct bt_ctf_stream_class *stream_class)
146 {
147 int ret;
148 int stream_class_found = 0;
149 size_t i;
150 struct bt_ctf_stream *stream = NULL;
151
152 if (!trace || !stream_class) {
153 goto error;
154 }
155
156 ret = bt_ctf_stream_class_set_byte_order(stream_class,
157 trace->byte_order == LITTLE_ENDIAN ?
158 BT_CTF_BYTE_ORDER_LITTLE_ENDIAN : BT_CTF_BYTE_ORDER_BIG_ENDIAN);
159 if (ret) {
160 goto error;
161 }
162
163 stream = bt_ctf_stream_create(stream_class, trace);
164 if (!stream) {
165 goto error;
166 }
167
168 for (i = 0; i < trace->stream_classes->len; i++) {
169 if (trace->stream_classes->pdata[i] == stream_class) {
170 stream_class_found = 1;
171 }
172 }
173
174 if (!stream_class_found) {
175 int64_t stream_id = bt_ctf_stream_class_get_id(stream_class);
176
177 if (stream_id < 0) {
178 /* Try to assign a new stream id */
179 if (bt_ctf_stream_class_set_id(stream->stream_class,
180 trace->next_stream_id++)) {
181 goto error;
182 }
183 }
184
185 for (i = 0; i < trace->stream_classes->len; i++) {
186 if (stream_id == bt_ctf_stream_class_get_id(
187 trace->stream_classes->pdata[i])) {
188 /* Duplicate stream id found */
189 goto error;
190 }
191 }
192 bt_ctf_stream_class_get(stream->stream_class);
193 g_ptr_array_add(trace->stream_classes, stream->stream_class);
194 }
195
196 bt_ctf_stream_get(stream);
197 g_ptr_array_add(trace->streams, stream);
198 trace->frozen = 1;
199 return stream;
200
201 error:
202 bt_ctf_stream_put(stream);
203 return NULL;
204 }
205
206 int bt_ctf_trace_add_environment_field(struct bt_ctf_trace *trace,
207 const char *name,
208 const char *value)
209 {
210 struct environment_variable *var = NULL;
211 char *escaped_value = NULL;
212 int ret = 0;
213
214 if (!trace || !name || !value || validate_identifier(name)) {
215 ret = -1;
216 goto error;
217 }
218
219 if (strchr(name, ' ')) {
220 ret = -1;
221 goto error;
222 }
223
224 var = g_new0(struct environment_variable, 1);
225 if (!var) {
226 ret = -1;
227 goto error;
228 }
229
230 escaped_value = g_strescape(value, NULL);
231 if (!escaped_value) {
232 ret = -1;
233 goto error;
234 }
235
236 var->name = g_string_new(name);
237 var->value = g_string_new(escaped_value);
238 g_free(escaped_value);
239 if (!var->name || !var->value) {
240 ret = -1;
241 goto error;
242 }
243
244 g_ptr_array_add(trace->environment, var);
245 return ret;
246
247 error:
248 if (var && var->name) {
249 g_string_free(var->name, TRUE);
250 }
251
252 if (var && var->value) {
253 g_string_free(var->value, TRUE);
254 }
255
256 g_free(var);
257 return ret;
258 }
259
260 int bt_ctf_trace_add_clock(struct bt_ctf_trace *trace,
261 struct bt_ctf_clock *clock)
262 {
263 int ret = 0;
264 struct search_query query = { .value = clock, .found = 0 };
265
266 if (!trace || !clock) {
267 ret = -1;
268 goto end;
269 }
270
271 /* Check for duplicate clocks */
272 g_ptr_array_foreach(trace->clocks, value_exists, &query);
273 if (query.found) {
274 ret = -1;
275 goto end;
276 }
277
278 bt_ctf_clock_get(clock);
279 g_ptr_array_add(trace->clocks, clock);
280 end:
281 return ret;
282 }
283
284 int bt_ctf_trace_get_clock_count(struct bt_ctf_trace *trace)
285 {
286 int ret = -1;
287
288 if (!trace) {
289 goto end;
290 }
291
292 ret = trace->clocks->len;
293 end:
294 return ret;
295 }
296
297 struct bt_ctf_clock *bt_ctf_trace_get_clock(struct bt_ctf_trace *trace,
298 int index)
299 {
300 struct bt_ctf_clock *clock = NULL;
301
302 if (!trace || index < 0 || index >= trace->clocks->len) {
303 goto end;
304 }
305
306 clock = g_ptr_array_index(trace->clocks, index);
307 bt_ctf_clock_get(clock);
308 end:
309 return clock;
310 }
311
312 BT_HIDDEN
313 const char *get_byte_order_string(int byte_order)
314 {
315 const char *string;
316
317 switch (byte_order) {
318 case LITTLE_ENDIAN:
319 string = "le";
320 break;
321 case BIG_ENDIAN:
322 string = "be";
323 break;
324 default:
325 string = "unknown";
326 break;
327 }
328
329 return string;
330 }
331
332 static
333 int append_trace_metadata(struct bt_ctf_trace *trace,
334 struct metadata_context *context)
335 {
336 unsigned char *uuid = trace->uuid;
337 int ret;
338
339 g_string_append(context->string, "trace {\n");
340
341 g_string_append(context->string, "\tmajor = 1;\n");
342 g_string_append(context->string, "\tminor = 8;\n");
343
344 g_string_append_printf(context->string,
345 "\tuuid = \"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\";\n",
346 uuid[0], uuid[1], uuid[2], uuid[3],
347 uuid[4], uuid[5], uuid[6], uuid[7],
348 uuid[8], uuid[9], uuid[10], uuid[11],
349 uuid[12], uuid[13], uuid[14], uuid[15]);
350 g_string_append_printf(context->string, "\tbyte_order = %s;\n",
351 get_byte_order_string(trace->byte_order));
352
353 g_string_append(context->string, "\tpacket.header := ");
354 context->current_indentation_level++;
355 g_string_assign(context->field_name, "");
356 ret = bt_ctf_field_type_serialize(trace->packet_header_type,
357 context);
358 if (ret) {
359 goto end;
360 }
361 context->current_indentation_level--;
362
363 g_string_append(context->string, ";\n};\n\n");
364 end:
365 return ret;
366 }
367
368 static
369 void append_env_field_metadata(struct environment_variable *var,
370 struct metadata_context *context)
371 {
372 g_string_append_printf(context->string, "\t%s = \"%s\";\n",
373 var->name->str, var->value->str);
374 }
375
376 static
377 void append_env_metadata(struct bt_ctf_trace *trace,
378 struct metadata_context *context)
379 {
380 if (trace->environment->len == 0) {
381 return;
382 }
383
384 g_string_append(context->string, "env {\n");
385 g_ptr_array_foreach(trace->environment,
386 (GFunc)append_env_field_metadata, context);
387 g_string_append(context->string, "};\n\n");
388 }
389
390 char *bt_ctf_trace_get_metadata_string(struct bt_ctf_trace *trace)
391 {
392 char *metadata = NULL;
393 struct metadata_context *context = NULL;
394 int err = 0;
395 size_t i;
396
397 if (!trace) {
398 goto end;
399 }
400
401 context = g_new0(struct metadata_context, 1);
402 if (!context) {
403 goto end;
404 }
405
406 context->field_name = g_string_sized_new(DEFAULT_IDENTIFIER_SIZE);
407 context->string = g_string_sized_new(DEFAULT_METADATA_STRING_SIZE);
408 g_string_append(context->string, "/* CTF 1.8 */\n\n");
409 if (append_trace_metadata(trace, context)) {
410 goto error;
411 }
412 append_env_metadata(trace, context);
413 g_ptr_array_foreach(trace->clocks,
414 (GFunc)bt_ctf_clock_serialize, context);
415
416 for (i = 0; i < trace->stream_classes->len; i++) {
417 err = bt_ctf_stream_class_serialize(
418 trace->stream_classes->pdata[i], context);
419 if (err) {
420 goto error;
421 }
422 }
423
424 metadata = context->string->str;
425 error:
426 g_string_free(context->string, err ? TRUE : FALSE);
427 g_string_free(context->field_name, TRUE);
428 g_free(context);
429 end:
430 return metadata;
431 }
432
433 int bt_ctf_trace_set_byte_order(struct bt_ctf_trace *trace,
434 enum bt_ctf_byte_order byte_order)
435 {
436 int ret = 0;
437 int internal_byte_order;
438
439 if (!trace || trace->frozen) {
440 ret = -1;
441 goto end;
442 }
443
444 switch (byte_order) {
445 case BT_CTF_BYTE_ORDER_NATIVE:
446 internal_byte_order = (G_BYTE_ORDER == G_LITTLE_ENDIAN) ?
447 LITTLE_ENDIAN : BIG_ENDIAN;
448 break;
449 case BT_CTF_BYTE_ORDER_LITTLE_ENDIAN:
450 internal_byte_order = LITTLE_ENDIAN;
451 break;
452 case BT_CTF_BYTE_ORDER_BIG_ENDIAN:
453 case BT_CTF_BYTE_ORDER_NETWORK:
454 internal_byte_order = BIG_ENDIAN;
455 break;
456 default:
457 ret = -1;
458 goto end;
459 }
460
461 trace->byte_order = internal_byte_order;
462 if (trace->packet_header_type) {
463 init_trace_packet_header(trace);
464 }
465 end:
466 return ret;
467 }
468
469 struct bt_ctf_field_type *bt_ctf_trace_get_packet_header_type(
470 struct bt_ctf_trace *trace)
471 {
472 struct bt_ctf_field_type *field_type = NULL;
473
474 if (!trace) {
475 goto end;
476 }
477
478 bt_ctf_field_type_get(trace->packet_header_type);
479 field_type = trace->packet_header_type;
480 end:
481 return field_type;
482 }
483
484 int bt_ctf_trace_set_packet_header_type(struct bt_ctf_trace *trace,
485 struct bt_ctf_field_type *packet_header_type)
486 {
487 int ret = 0;
488
489 if (!trace || !packet_header_type || trace->frozen) {
490 ret = -1;
491 goto end;
492 }
493
494 /* packet_header_type must be a structure */
495 if (bt_ctf_field_type_get_type_id(packet_header_type) !=
496 CTF_TYPE_STRUCT) {
497 ret = -1;
498 goto end;
499 }
500
501 bt_ctf_field_type_get(packet_header_type);
502 bt_ctf_field_type_put(trace->packet_header_type);
503 trace->packet_header_type = packet_header_type;
504 end:
505 return ret;
506 }
507
508 void bt_ctf_trace_get(struct bt_ctf_trace *trace)
509 {
510 if (!trace) {
511 return;
512 }
513
514 bt_ctf_ref_get(&trace->ref_count);
515 }
516
517 void bt_ctf_trace_put(struct bt_ctf_trace *trace)
518 {
519 if (!trace) {
520 return;
521 }
522
523 bt_ctf_ref_put(&trace->ref_count, bt_ctf_trace_destroy);
524 }
525
526 BT_HIDDEN
527 int validate_identifier(const char *input_string)
528 {
529 int ret = 0;
530 char *string = NULL;
531 char *save_ptr, *token;
532
533 if (!input_string || input_string[0] == '\0') {
534 ret = -1;
535 goto end;
536 }
537
538 string = strdup(input_string);
539 if (!string) {
540 ret = -1;
541 goto end;
542 }
543
544 token = strtok_r(string, " ", &save_ptr);
545 while (token) {
546 if (g_hash_table_lookup_extended(reserved_keywords_set,
547 GINT_TO_POINTER(g_quark_from_string(token)),
548 NULL, NULL)) {
549 ret = -1;
550 goto end;
551 }
552
553 token = strtok_r(NULL, " ", &save_ptr);
554 }
555 end:
556 free(string);
557 return ret;
558 }
559
560 BT_HIDDEN
561 struct bt_ctf_field_type *get_field_type(enum field_type_alias alias)
562 {
563 unsigned int alignment, size;
564 struct bt_ctf_field_type *field_type;
565
566 if (alias >= NR_FIELD_TYPE_ALIAS) {
567 return NULL;
568 }
569
570 alignment = field_type_aliases_alignments[alias];
571 size = field_type_aliases_sizes[alias];
572 field_type = bt_ctf_field_type_integer_create(size);
573 bt_ctf_field_type_set_alignment(field_type, alignment);
574 return field_type;
575 }
576
577 static
578 int init_trace_packet_header(struct bt_ctf_trace *trace)
579 {
580 int ret = 0;
581 struct bt_ctf_field *magic = NULL, *uuid_array = NULL;
582 struct bt_ctf_field_type *_uint32_t =
583 get_field_type(FIELD_TYPE_ALIAS_UINT32_T);
584 struct bt_ctf_field_type *_uint8_t =
585 get_field_type(FIELD_TYPE_ALIAS_UINT8_T);
586 struct bt_ctf_field_type *trace_packet_header_type =
587 bt_ctf_field_type_structure_create();
588 struct bt_ctf_field_type *uuid_array_type =
589 bt_ctf_field_type_array_create(_uint8_t, 16);
590
591 if (!trace_packet_header_type || !uuid_array_type) {
592 ret = -1;
593 goto end;
594 }
595
596 ret = bt_ctf_field_type_set_byte_order(_uint32_t,
597 (trace->byte_order == LITTLE_ENDIAN ?
598 BT_CTF_BYTE_ORDER_LITTLE_ENDIAN :
599 BT_CTF_BYTE_ORDER_BIG_ENDIAN));
600 if (ret) {
601 goto end;
602 }
603
604 ret = bt_ctf_field_type_structure_add_field(trace_packet_header_type,
605 _uint32_t, "magic");
606 if (ret) {
607 goto end;
608 }
609
610 ret = bt_ctf_field_type_structure_add_field(trace_packet_header_type,
611 uuid_array_type, "uuid");
612 if (ret) {
613 goto end;
614 }
615
616 ret = bt_ctf_field_type_structure_add_field(trace_packet_header_type,
617 _uint32_t, "stream_id");
618 if (ret) {
619 goto end;
620 }
621
622 bt_ctf_field_type_put(trace->packet_header_type);
623 trace->packet_header_type = trace_packet_header_type;
624 end:
625 bt_ctf_field_type_put(uuid_array_type);
626 bt_ctf_field_type_put(_uint32_t);
627 bt_ctf_field_type_put(_uint8_t);
628 bt_ctf_field_put(magic);
629 bt_ctf_field_put(uuid_array);
630 if (ret) {
631 bt_ctf_field_type_put(trace_packet_header_type);
632 }
633
634 return ret;
635 }
636
637 static
638 void environment_variable_destroy(struct environment_variable *var)
639 {
640 g_string_free(var->name, TRUE);
641 g_string_free(var->value, TRUE);
642 g_free(var);
643 }
644
645 static __attribute__((constructor))
646 void trace_init(void)
647 {
648 size_t i;
649 const size_t reserved_keywords_count =
650 sizeof(reserved_keywords_str) / sizeof(char *);
651
652 global_data_refcount++;
653 if (init_done) {
654 return;
655 }
656
657 reserved_keywords_set = g_hash_table_new(g_direct_hash, g_direct_equal);
658 for (i = 0; i < reserved_keywords_count; i++) {
659 gpointer quark = GINT_TO_POINTER(g_quark_from_string(
660 reserved_keywords_str[i]));
661
662 g_hash_table_insert(reserved_keywords_set, quark, quark);
663 }
664
665 init_done = 1;
666 }
667
668 static __attribute__((destructor))
669 void trace_finalize(void)
670 {
671 if (--global_data_refcount == 0) {
672 g_hash_table_destroy(reserved_keywords_set);
673 }
674 }
This page took 0.043509 seconds and 4 git commands to generate.