Add Trace CTF IR type
[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->trace_packet_header_type);
141 bt_ctf_field_put(trace->trace_packet_header);
142 g_free(trace);
143 }
144
145 struct bt_ctf_stream *bt_ctf_trace_create_stream(struct bt_ctf_trace *trace,
146 struct bt_ctf_stream_class *stream_class)
147 {
148 int ret;
149 int stream_class_found = 0;
150 size_t i;
151 struct bt_ctf_stream *stream = NULL;
152
153 if (!trace || !stream_class) {
154 goto error;
155 }
156
157 ret = bt_ctf_stream_class_set_byte_order(stream_class,
158 trace->byte_order == LITTLE_ENDIAN ?
159 BT_CTF_BYTE_ORDER_LITTLE_ENDIAN : BT_CTF_BYTE_ORDER_BIG_ENDIAN);
160 if (ret) {
161 goto error;
162 }
163
164 stream = bt_ctf_stream_create(stream_class);
165 if (!stream) {
166 goto error;
167 }
168
169 for (i = 0; i < trace->stream_classes->len; i++) {
170 if (trace->stream_classes->pdata[i] == stream_class) {
171 stream_class_found = 1;
172 }
173 }
174
175 if (!stream_class_found) {
176 int64_t stream_id = bt_ctf_stream_class_get_id(stream_class);
177
178 if (stream_id < 0) {
179 /* Try to assign a new stream id */
180 if (bt_ctf_stream_class_set_id(stream->stream_class,
181 trace->next_stream_id++)) {
182 goto error;
183 }
184 }
185
186 for (i = 0; i < trace->stream_classes->len; i++) {
187 if (stream_id == bt_ctf_stream_class_get_id(
188 trace->stream_classes->pdata[i])) {
189 /* Duplicate stream id found */
190 goto error;
191 }
192 }
193 bt_ctf_stream_class_get(stream->stream_class);
194 g_ptr_array_add(trace->stream_classes, stream->stream_class);
195 }
196
197 bt_ctf_stream_get(stream);
198 g_ptr_array_add(trace->streams, stream);
199 trace->frozen = 1;
200 return stream;
201
202 error:
203 bt_ctf_stream_put(stream);
204 return NULL;
205 }
206
207 int bt_ctf_trace_add_environment_field(struct bt_ctf_trace *trace,
208 const char *name,
209 const char *value)
210 {
211 struct environment_variable *var = NULL;
212 char *escaped_value = NULL;
213 int ret = 0;
214
215 if (!trace || !name || !value || validate_identifier(name)) {
216 ret = -1;
217 goto error;
218 }
219
220 if (strchr(name, ' ')) {
221 ret = -1;
222 goto error;
223 }
224
225 var = g_new0(struct environment_variable, 1);
226 if (!var) {
227 ret = -1;
228 goto error;
229 }
230
231 escaped_value = g_strescape(value, NULL);
232 if (!escaped_value) {
233 ret = -1;
234 goto error;
235 }
236
237 var->name = g_string_new(name);
238 var->value = g_string_new(escaped_value);
239 g_free(escaped_value);
240 if (!var->name || !var->value) {
241 ret = -1;
242 goto error;
243 }
244
245 g_ptr_array_add(trace->environment, var);
246 return ret;
247
248 error:
249 if (var && var->name) {
250 g_string_free(var->name, TRUE);
251 }
252
253 if (var && var->value) {
254 g_string_free(var->value, TRUE);
255 }
256
257 g_free(var);
258 return ret;
259 }
260
261 int bt_ctf_trace_add_clock(struct bt_ctf_trace *trace,
262 struct bt_ctf_clock *clock)
263 {
264 int ret = 0;
265 struct search_query query = { .value = clock, .found = 0 };
266
267 if (!trace || !clock) {
268 ret = -1;
269 goto end;
270 }
271
272 /* Check for duplicate clocks */
273 g_ptr_array_foreach(trace->clocks, value_exists, &query);
274 if (query.found) {
275 ret = -1;
276 goto end;
277 }
278
279 bt_ctf_clock_get(clock);
280 g_ptr_array_add(trace->clocks, clock);
281 end:
282 return ret;
283 }
284
285 BT_HIDDEN
286 const char *get_byte_order_string(int byte_order)
287 {
288 const char *string;
289
290 switch (byte_order) {
291 case LITTLE_ENDIAN:
292 string = "le";
293 break;
294 case BIG_ENDIAN:
295 string = "be";
296 break;
297 default:
298 string = "unknown";
299 break;
300 }
301
302 return string;
303 }
304
305 static
306 int append_trace_metadata(struct bt_ctf_trace *trace,
307 struct metadata_context *context)
308 {
309 unsigned char *uuid = trace->uuid;
310 int ret;
311
312 g_string_append(context->string, "trace {\n");
313
314 g_string_append(context->string, "\tmajor = 1;\n");
315 g_string_append(context->string, "\tminor = 8;\n");
316
317 g_string_append_printf(context->string,
318 "\tuuid = \"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\";\n",
319 uuid[0], uuid[1], uuid[2], uuid[3],
320 uuid[4], uuid[5], uuid[6], uuid[7],
321 uuid[8], uuid[9], uuid[10], uuid[11],
322 uuid[12], uuid[13], uuid[14], uuid[15]);
323 g_string_append_printf(context->string, "\tbyte_order = %s;\n",
324 get_byte_order_string(trace->byte_order));
325
326 g_string_append(context->string, "\tpacket.header := ");
327 context->current_indentation_level++;
328 g_string_assign(context->field_name, "");
329 ret = bt_ctf_field_type_serialize(trace->trace_packet_header_type,
330 context);
331 if (ret) {
332 goto end;
333 }
334 context->current_indentation_level--;
335
336 g_string_append(context->string, ";\n};\n\n");
337 end:
338 return ret;
339 }
340
341 static
342 void append_env_field_metadata(struct environment_variable *var,
343 struct metadata_context *context)
344 {
345 g_string_append_printf(context->string, "\t%s = \"%s\";\n",
346 var->name->str, var->value->str);
347 }
348
349 static
350 void append_env_metadata(struct bt_ctf_trace *trace,
351 struct metadata_context *context)
352 {
353 if (trace->environment->len == 0) {
354 return;
355 }
356
357 g_string_append(context->string, "env {\n");
358 g_ptr_array_foreach(trace->environment,
359 (GFunc)append_env_field_metadata, context);
360 g_string_append(context->string, "};\n\n");
361 }
362
363 char *bt_ctf_trace_get_metadata_string(struct bt_ctf_trace *trace)
364 {
365 char *metadata = NULL;
366 struct metadata_context *context = NULL;
367 int err = 0;
368 size_t i;
369
370 if (!trace) {
371 goto end;
372 }
373
374 context = g_new0(struct metadata_context, 1);
375 if (!context) {
376 goto end;
377 }
378
379 context->field_name = g_string_sized_new(DEFAULT_IDENTIFIER_SIZE);
380 context->string = g_string_sized_new(DEFAULT_METADATA_STRING_SIZE);
381 g_string_append(context->string, "/* CTF 1.8 */\n\n");
382 if (append_trace_metadata(trace, context)) {
383 goto error;
384 }
385 append_env_metadata(trace, context);
386 g_ptr_array_foreach(trace->clocks,
387 (GFunc)bt_ctf_clock_serialize, context);
388
389 for (i = 0; i < trace->stream_classes->len; i++) {
390 err = bt_ctf_stream_class_serialize(
391 trace->stream_classes->pdata[i], context);
392 if (err) {
393 goto error;
394 }
395 }
396
397 metadata = context->string->str;
398 error:
399 g_string_free(context->string, err ? TRUE : FALSE);
400 g_string_free(context->field_name, TRUE);
401 g_free(context);
402 end:
403 return metadata;
404 }
405
406 int bt_ctf_trace_set_byte_order(struct bt_ctf_trace *trace,
407 enum bt_ctf_byte_order byte_order)
408 {
409 int ret = 0;
410 int internal_byte_order;
411
412 if (!trace || trace->frozen) {
413 ret = -1;
414 goto end;
415 }
416
417 switch (byte_order) {
418 case BT_CTF_BYTE_ORDER_NATIVE:
419 internal_byte_order = (G_BYTE_ORDER == G_LITTLE_ENDIAN) ?
420 LITTLE_ENDIAN : BIG_ENDIAN;
421 break;
422 case BT_CTF_BYTE_ORDER_LITTLE_ENDIAN:
423 internal_byte_order = LITTLE_ENDIAN;
424 break;
425 case BT_CTF_BYTE_ORDER_BIG_ENDIAN:
426 case BT_CTF_BYTE_ORDER_NETWORK:
427 internal_byte_order = BIG_ENDIAN;
428 break;
429 default:
430 ret = -1;
431 goto end;
432 }
433
434 trace->byte_order = internal_byte_order;
435 if (trace->trace_packet_header_type ||
436 trace->trace_packet_header) {
437 init_trace_packet_header(trace);
438 }
439 end:
440 return ret;
441 }
442
443 void bt_ctf_trace_get(struct bt_ctf_trace *trace)
444 {
445 if (!trace) {
446 return;
447 }
448
449 bt_ctf_ref_get(&trace->ref_count);
450 }
451
452 void bt_ctf_trace_put(struct bt_ctf_trace *trace)
453 {
454 if (!trace) {
455 return;
456 }
457
458 bt_ctf_ref_put(&trace->ref_count, bt_ctf_trace_destroy);
459 }
460
461 BT_HIDDEN
462 int validate_identifier(const char *input_string)
463 {
464 int ret = 0;
465 char *string = NULL;
466 char *save_ptr, *token;
467
468 if (!input_string || input_string[0] == '\0') {
469 ret = -1;
470 goto end;
471 }
472
473 string = strdup(input_string);
474 if (!string) {
475 ret = -1;
476 goto end;
477 }
478
479 token = strtok_r(string, " ", &save_ptr);
480 while (token) {
481 if (g_hash_table_lookup_extended(reserved_keywords_set,
482 GINT_TO_POINTER(g_quark_from_string(token)),
483 NULL, NULL)) {
484 ret = -1;
485 goto end;
486 }
487
488 token = strtok_r(NULL, " ", &save_ptr);
489 }
490 end:
491 free(string);
492 return ret;
493 }
494
495 BT_HIDDEN
496 struct bt_ctf_field_type *get_field_type(enum field_type_alias alias)
497 {
498 unsigned int alignment, size;
499 struct bt_ctf_field_type *field_type;
500
501 if (alias >= NR_FIELD_TYPE_ALIAS) {
502 return NULL;
503 }
504
505 alignment = field_type_aliases_alignments[alias];
506 size = field_type_aliases_sizes[alias];
507 field_type = bt_ctf_field_type_integer_create(size);
508 bt_ctf_field_type_set_alignment(field_type, alignment);
509 return field_type;
510 }
511
512 static
513 int init_trace_packet_header(struct bt_ctf_trace *trace)
514 {
515 size_t i;
516 int ret = 0;
517 struct bt_ctf_field *trace_packet_header = NULL,
518 *magic = NULL, *uuid_array = NULL;
519 struct bt_ctf_field_type *_uint32_t =
520 get_field_type(FIELD_TYPE_ALIAS_UINT32_T);
521 struct bt_ctf_field_type *_uint8_t =
522 get_field_type(FIELD_TYPE_ALIAS_UINT8_T);
523 struct bt_ctf_field_type *trace_packet_header_type =
524 bt_ctf_field_type_structure_create();
525 struct bt_ctf_field_type *uuid_array_type =
526 bt_ctf_field_type_array_create(_uint8_t, 16);
527
528 if (!trace_packet_header_type || !uuid_array_type) {
529 ret = -1;
530 goto end;
531 }
532
533 ret = bt_ctf_field_type_set_byte_order(_uint32_t,
534 (trace->byte_order == LITTLE_ENDIAN ?
535 BT_CTF_BYTE_ORDER_LITTLE_ENDIAN :
536 BT_CTF_BYTE_ORDER_BIG_ENDIAN));
537 if (ret) {
538 goto end;
539 }
540
541 ret = bt_ctf_field_type_structure_add_field(trace_packet_header_type,
542 _uint32_t, "magic");
543 if (ret) {
544 goto end;
545 }
546
547 ret = bt_ctf_field_type_structure_add_field(trace_packet_header_type,
548 uuid_array_type, "uuid");
549 if (ret) {
550 goto end;
551 }
552
553 ret = bt_ctf_field_type_structure_add_field(trace_packet_header_type,
554 _uint32_t, "stream_id");
555 if (ret) {
556 goto end;
557 }
558
559 trace_packet_header = bt_ctf_field_create(trace_packet_header_type);
560 if (!trace_packet_header) {
561 ret = -1;
562 goto end;
563 }
564
565 magic = bt_ctf_field_structure_get_field(trace_packet_header, "magic");
566 ret = bt_ctf_field_unsigned_integer_set_value(magic, 0xC1FC1FC1);
567 if (ret) {
568 goto end;
569 }
570
571 uuid_array = bt_ctf_field_structure_get_field(trace_packet_header,
572 "uuid");
573 for (i = 0; i < 16; i++) {
574 struct bt_ctf_field *uuid_element =
575 bt_ctf_field_array_get_field(uuid_array, i);
576 ret = bt_ctf_field_unsigned_integer_set_value(uuid_element,
577 trace->uuid[i]);
578 bt_ctf_field_put(uuid_element);
579 if (ret) {
580 goto end;
581 }
582 }
583
584 bt_ctf_field_type_put(trace->trace_packet_header_type);
585 bt_ctf_field_put(trace->trace_packet_header);
586 trace->trace_packet_header_type = trace_packet_header_type;
587 trace->trace_packet_header = trace_packet_header;
588 end:
589 bt_ctf_field_type_put(uuid_array_type);
590 bt_ctf_field_type_put(_uint32_t);
591 bt_ctf_field_type_put(_uint8_t);
592 bt_ctf_field_put(magic);
593 bt_ctf_field_put(uuid_array);
594 if (ret) {
595 bt_ctf_field_type_put(trace_packet_header_type);
596 bt_ctf_field_put(trace_packet_header);
597 }
598
599 return ret;
600 }
601
602 static
603 void environment_variable_destroy(struct environment_variable *var)
604 {
605 g_string_free(var->name, TRUE);
606 g_string_free(var->value, TRUE);
607 g_free(var);
608 }
609
610 static __attribute__((constructor))
611 void trace_init(void)
612 {
613 size_t i;
614 const size_t reserved_keywords_count =
615 sizeof(reserved_keywords_str) / sizeof(char *);
616
617 global_data_refcount++;
618 if (init_done) {
619 return;
620 }
621
622 reserved_keywords_set = g_hash_table_new(g_direct_hash, g_direct_equal);
623 for (i = 0; i < reserved_keywords_count; i++) {
624 gpointer quark = GINT_TO_POINTER(g_quark_from_string(
625 reserved_keywords_str[i]));
626
627 g_hash_table_insert(reserved_keywords_set, quark, quark);
628 }
629
630 init_done = 1;
631 }
632
633 static __attribute__((destructor))
634 void trace_finalize(void)
635 {
636 if (--global_data_refcount == 0) {
637 g_hash_table_destroy(reserved_keywords_set);
638 }
639 }
This page took 0.042338 seconds and 4 git commands to generate.