assert-pre-internal.h: add BT_ASSERT_PRE_VALID_INDEX()
[babeltrace.git] / lib / ctf-writer / trace.c
CommitLineData
3dca2276
PP
1/*
2 * Copyright 2013, 2014 Jérémie Galarneau <jeremie.galarneau@efficios.com>
3 * Copyright 2017-2018 Philippe Proulx <pproulx@efficios.com>
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 */
23
24#define BT_LOG_TAG "CTF-WRITER-TRACE"
25#include <babeltrace/lib-logging-internal.h>
26
16ca5ff0
PP
27#include <babeltrace/assert-internal.h>
28#include <babeltrace/compiler-internal.h>
29#include <babeltrace/ctf-writer/attributes-internal.h>
30#include <babeltrace/ctf-writer/clock-class-internal.h>
3dca2276 31#include <babeltrace/ctf-writer/clock-internal.h>
16ca5ff0
PP
32#include <babeltrace/ctf-writer/event-class-internal.h>
33#include <babeltrace/ctf-writer/event.h>
34#include <babeltrace/ctf-writer/event-internal.h>
35#include <babeltrace/ctf-writer/field-types-internal.h>
36#include <babeltrace/ctf-writer/field-wrapper-internal.h>
37#include <babeltrace/ctf-writer/functor-internal.h>
3dca2276 38#include <babeltrace/ctf-writer/stream-class-internal.h>
16ca5ff0
PP
39#include <babeltrace/ctf-writer/stream-internal.h>
40#include <babeltrace/ctf-writer/trace-internal.h>
41#include <babeltrace/ctf-writer/utils-internal.h>
42#include <babeltrace/ctf-writer/utils.h>
43#include <babeltrace/ctf-writer/validation-internal.h>
44#include <babeltrace/ctf-writer/visitor-internal.h>
3dca2276 45#include <babeltrace/ctf-writer/writer-internal.h>
16ca5ff0
PP
46#include <babeltrace/endian-internal.h>
47#include <babeltrace/ref.h>
48#include <babeltrace/types.h>
49#include <babeltrace/values-internal.h>
50#include <babeltrace/values.h>
51#include <inttypes.h>
52#include <stdint.h>
53#include <stdlib.h>
54#include <string.h>
55
56#define DEFAULT_IDENTIFIER_SIZE 128
57#define DEFAULT_METADATA_STRING_SIZE 4096
58
59BT_HIDDEN
60int bt_ctf_trace_common_initialize(struct bt_ctf_trace_common *trace,
61 bt_object_release_func release_func)
62{
63 int ret = 0;
64
65 BT_LOGD_STR("Initializing common trace object.");
66 trace->native_byte_order = BT_CTF_BYTE_ORDER_UNSPECIFIED;
67 bt_object_init_shared_with_parent(&trace->base, release_func);
68 trace->clock_classes = g_ptr_array_new_with_free_func(
69 (GDestroyNotify) bt_put);
70 if (!trace->clock_classes) {
71 BT_LOGE_STR("Failed to allocate one GPtrArray.");
72 goto error;
73 }
74
75 trace->streams = g_ptr_array_new_with_free_func(
76 (GDestroyNotify) bt_object_try_spec_release);
77 if (!trace->streams) {
78 BT_LOGE_STR("Failed to allocate one GPtrArray.");
79 goto error;
80 }
81
82 trace->stream_classes = g_ptr_array_new_with_free_func(
83 (GDestroyNotify) bt_object_try_spec_release);
84 if (!trace->stream_classes) {
85 BT_LOGE_STR("Failed to allocate one GPtrArray.");
86 goto error;
87 }
88
89 /* Create the environment array object */
90 trace->environment = bt_ctf_attributes_create();
91 if (!trace->environment) {
92 BT_LOGE_STR("Cannot create empty attributes object.");
93 goto error;
94 }
95
96 BT_LOGD("Initialized common trace object: addr=%p", trace);
97 goto end;
98
99error:
100 ret = -1;
101
102end:
103 return ret;
104}
105
106BT_HIDDEN
107void bt_ctf_trace_common_finalize(struct bt_ctf_trace_common *trace)
108{
109 BT_LOGD("Finalizing common trace object: addr=%p, name=\"%s\"",
110 trace, bt_ctf_trace_common_get_name(trace));
111
112 if (trace->environment) {
113 BT_LOGD_STR("Destroying environment attributes.");
114 bt_ctf_attributes_destroy(trace->environment);
115 }
116
117 if (trace->name) {
118 g_string_free(trace->name, TRUE);
119 }
120
121 if (trace->clock_classes) {
122 BT_LOGD_STR("Putting clock classes.");
123 g_ptr_array_free(trace->clock_classes, TRUE);
124 }
125
126 if (trace->streams) {
127 BT_LOGD_STR("Destroying streams.");
128 g_ptr_array_free(trace->streams, TRUE);
129 }
130
131 if (trace->stream_classes) {
132 BT_LOGD_STR("Destroying stream classes.");
133 g_ptr_array_free(trace->stream_classes, TRUE);
134 }
135
136 BT_LOGD_STR("Putting packet header field type.");
137 bt_put(trace->packet_header_field_type);
138}
139
140BT_HIDDEN
141int bt_ctf_trace_common_set_name(struct bt_ctf_trace_common *trace, const char *name)
142{
143 int ret = 0;
144
145 if (!trace) {
146 BT_LOGW_STR("Invalid parameter: trace is NULL.");
147 ret = -1;
148 goto end;
149 }
150
151 if (!name) {
152 BT_LOGW_STR("Invalid parameter: name is NULL.");
153 ret = -1;
154 goto end;
155 }
156
157 if (trace->frozen) {
158 BT_LOGW("Invalid parameter: trace is frozen: "
159 "addr=%p, name=\"%s\"",
160 trace, bt_ctf_trace_common_get_name(trace));
161 ret = -1;
162 goto end;
163 }
164
165 trace->name = trace->name ? g_string_assign(trace->name, name) :
166 g_string_new(name);
167 if (!trace->name) {
168 BT_LOGE_STR("Failed to allocate one GString.");
169 ret = -1;
170 goto end;
171 }
172
173 BT_LOGV("Set trace's name: addr=%p, name=\"%s\"", trace, name);
174
175end:
176 return ret;
177}
178
179BT_HIDDEN
180int bt_ctf_trace_common_set_uuid(struct bt_ctf_trace_common *trace,
181 const unsigned char *uuid)
182{
183 int ret = 0;
184
185 if (!trace) {
186 BT_LOGW_STR("Invalid parameter: trace is NULL.");
187 ret = -1;
188 goto end;
189 }
190
191 if (!uuid) {
192 BT_LOGW_STR("Invalid parameter: UUID is NULL.");
193 ret = -1;
194 goto end;
195 }
196
197 if (trace->frozen) {
198 BT_LOGW("Invalid parameter: trace is frozen: "
199 "addr=%p, name=\"%s\"",
200 trace, bt_ctf_trace_common_get_name(trace));
201 ret = -1;
202 goto end;
203 }
204
205 memcpy(trace->uuid, uuid, BABELTRACE_UUID_LEN);
206 trace->uuid_set = BT_TRUE;
207 BT_LOGV("Set trace's UUID: addr=%p, name=\"%s\", "
208 "uuid=\"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\"",
209 trace, bt_ctf_trace_common_get_name(trace),
210 (unsigned int) uuid[0],
211 (unsigned int) uuid[1],
212 (unsigned int) uuid[2],
213 (unsigned int) uuid[3],
214 (unsigned int) uuid[4],
215 (unsigned int) uuid[5],
216 (unsigned int) uuid[6],
217 (unsigned int) uuid[7],
218 (unsigned int) uuid[8],
219 (unsigned int) uuid[9],
220 (unsigned int) uuid[10],
221 (unsigned int) uuid[11],
222 (unsigned int) uuid[12],
223 (unsigned int) uuid[13],
224 (unsigned int) uuid[14],
225 (unsigned int) uuid[15]);
226
227end:
228 return ret;
229}
230
231BT_HIDDEN
232int bt_ctf_trace_common_set_environment_field(struct bt_ctf_trace_common *trace,
233 const char *name, struct bt_value *value)
234{
235 int ret = 0;
236
237 if (!trace) {
238 BT_LOGW_STR("Invalid parameter: trace is NULL.");
239 ret = -1;
240 goto end;
241 }
242
243 if (!name) {
244 BT_LOGW_STR("Invalid parameter: name is NULL.");
245 ret = -1;
246 goto end;
247 }
248
249 if (!value) {
250 BT_LOGW_STR("Invalid parameter: value is NULL.");
251 ret = -1;
252 goto end;
253 }
254
255 if (!bt_ctf_identifier_is_valid(name)) {
256 BT_LOGW("Invalid parameter: environment field's name is not a valid CTF identifier: "
257 "trace-addr=%p, trace-name=\"%s\", "
258 "env-name=\"%s\"",
259 trace, bt_ctf_trace_common_get_name(trace), name);
260 ret = -1;
261 goto end;
262 }
263
264 if (!bt_value_is_integer(value) && !bt_value_is_string(value)) {
265 BT_LOGW("Invalid parameter: environment field's value is not an integer or string value: "
266 "trace-addr=%p, trace-name=\"%s\", "
267 "env-name=\"%s\", env-value-type=%s",
268 trace, bt_ctf_trace_common_get_name(trace), name,
269 bt_value_type_string(bt_value_get_type(value)));
270 ret = -1;
271 goto end;
272 }
273
274 if (trace->frozen) {
275 /*
276 * New environment fields may be added to a frozen trace,
277 * but existing fields may not be changed.
278 *
279 * The object passed is frozen like all other attributes.
280 */
281 struct bt_value *attribute =
282 bt_ctf_attributes_borrow_field_value_by_name(
283 trace->environment, name);
284
285 if (attribute) {
286 BT_LOGW("Invalid parameter: trace is frozen and environment field already exists with this name: "
287 "trace-addr=%p, trace-name=\"%s\", "
288 "env-name=\"%s\"",
289 trace, bt_ctf_trace_common_get_name(trace), name);
290 ret = -1;
291 goto end;
292 }
293
294 bt_value_freeze(value);
295 }
296
297 ret = bt_ctf_attributes_set_field_value(trace->environment, name,
298 value);
299 if (ret) {
300 BT_LOGE("Cannot set environment field's value: "
301 "trace-addr=%p, trace-name=\"%s\", "
302 "env-name=\"%s\"",
303 trace, bt_ctf_trace_common_get_name(trace), name);
304 } else {
305 BT_LOGV("Set environment field's value: "
306 "trace-addr=%p, trace-name=\"%s\", "
307 "env-name=\"%s\", value-addr=%p",
308 trace, bt_ctf_trace_common_get_name(trace), name, value);
309 }
310
311end:
312 return ret;
313}
314
315BT_HIDDEN
316int bt_ctf_trace_common_set_environment_field_string(struct bt_ctf_trace_common *trace,
317 const char *name, const char *value)
318{
319 int ret = 0;
320 struct bt_value *env_value_string_obj = NULL;
321
322 if (!value) {
323 BT_LOGW_STR("Invalid parameter: value is NULL.");
324 ret = -1;
325 goto end;
326 }
327
328 env_value_string_obj = bt_value_string_create_init(value);
329 if (!env_value_string_obj) {
330 BT_LOGE_STR("Cannot create string value object.");
331 ret = -1;
332 goto end;
333 }
334
335 /* bt_ctf_trace_common_set_environment_field() logs errors */
336 ret = bt_ctf_trace_common_set_environment_field(trace, name,
337 env_value_string_obj);
338
339end:
340 bt_put(env_value_string_obj);
341 return ret;
342}
343
344BT_HIDDEN
345int bt_ctf_trace_common_set_environment_field_integer(
346 struct bt_ctf_trace_common *trace, const char *name, int64_t value)
347{
348 int ret = 0;
349 struct bt_value *env_value_integer_obj = NULL;
350
351 env_value_integer_obj = bt_value_integer_create_init(value);
352 if (!env_value_integer_obj) {
353 BT_LOGE_STR("Cannot create integer value object.");
354 ret = -1;
355 goto end;
356 }
357
358 /* bt_ctf_trace_common_set_environment_field() logs errors */
359 ret = bt_ctf_trace_common_set_environment_field(trace, name,
360 env_value_integer_obj);
361
362end:
363 bt_put(env_value_integer_obj);
364 return ret;
365}
366
367BT_HIDDEN
368int bt_ctf_trace_common_add_clock_class(struct bt_ctf_trace_common *trace,
369 struct bt_ctf_clock_class *clock_class)
370{
371 int ret = 0;
372
373 if (!trace) {
374 BT_LOGW_STR("Invalid parameter: trace is NULL.");
375 ret = -1;
376 goto end;
377 }
378
379 if (!bt_ctf_clock_class_is_valid(clock_class)) {
380 BT_LOGW("Invalid parameter: clock class is invalid: "
381 "trace-addr=%p, trace-name=\"%s\", "
382 "clock-class-addr=%p, clock-class-name=\"%s\"",
383 trace, bt_ctf_trace_common_get_name(trace),
384 clock_class, bt_ctf_clock_class_get_name(clock_class));
385 ret = -1;
386 goto end;
387 }
388
389 /* Check for duplicate clock classes */
390 if (bt_ctf_trace_common_has_clock_class(trace, clock_class)) {
391 BT_LOGW("Invalid parameter: clock class already exists in trace: "
392 "trace-addr=%p, trace-name=\"%s\", "
393 "clock-class-addr=%p, clock-class-name=\"%s\"",
394 trace, bt_ctf_trace_common_get_name(trace),
395 clock_class, bt_ctf_clock_class_get_name(clock_class));
396 ret = -1;
397 goto end;
398 }
399
400 bt_get(clock_class);
401 g_ptr_array_add(trace->clock_classes, clock_class);
402
403 if (trace->frozen) {
404 BT_LOGV_STR("Freezing added clock class because trace is frozen.");
405 bt_ctf_clock_class_freeze(clock_class);
406 }
407
408 BT_LOGV("Added clock class to trace: "
409 "trace-addr=%p, trace-name=\"%s\", "
410 "clock-class-addr=%p, clock-class-name=\"%s\"",
411 trace, bt_ctf_trace_common_get_name(trace),
412 clock_class, bt_ctf_clock_class_get_name(clock_class));
413
414end:
415 return ret;
416}
417
418static
419bool packet_header_field_type_is_valid(struct bt_ctf_trace_common *trace,
420 struct bt_ctf_field_type_common *packet_header_type)
421{
422 int ret;
423 bool is_valid = true;
424 struct bt_ctf_field_type_common *field_type = NULL;
425
426 if (!packet_header_type) {
427 /*
428 * No packet header field type: trace must have only
429 * one stream. At this point the stream class being
430 * added is not part of the trace yet, so we validate
431 * that the trace contains no stream classes yet.
432 */
433 if (trace->stream_classes->len >= 1) {
434 BT_LOGW_STR("Invalid packet header field type: "
435 "packet header field type does not exist but there's more than one stream class in the trace.");
436 goto invalid;
437 }
438
439 /* No packet header field type: valid at this point */
440 goto end;
441 }
442
443 /* Packet header field type, if it exists, must be a structure */
444 if (packet_header_type->id != BT_CTF_FIELD_TYPE_ID_STRUCT) {
445 BT_LOGW("Invalid packet header field type: must be a structure field type if it exists: "
446 "ft-addr=%p, ft-id=%s",
447 packet_header_type,
448 bt_ctf_field_type_id_string(packet_header_type->id));
449 goto invalid;
450 }
451
452 /*
453 * If there's a `magic` field, it must be a 32-bit unsigned
454 * integer field type. Also it must be the first field of the
455 * packet header field type.
456 */
457 field_type = bt_ctf_field_type_common_structure_borrow_field_type_by_name(
458 packet_header_type, "magic");
459 if (field_type) {
460 const char *field_name;
461
462 if (field_type->id != BT_CTF_FIELD_TYPE_ID_INTEGER) {
463 BT_LOGW("Invalid packet header field type: `magic` field must be an integer field type: "
464 "magic-ft-addr=%p, magic-ft-id=%s",
465 field_type,
466 bt_ctf_field_type_id_string(field_type->id));
467 goto invalid;
468 }
469
470 if (bt_ctf_field_type_common_integer_is_signed(field_type)) {
471 BT_LOGW("Invalid packet header field type: `magic` field must be an unsigned integer field type: "
472 "magic-ft-addr=%p", field_type);
473 goto invalid;
474 }
475
476 if (bt_ctf_field_type_common_integer_get_size(field_type) != 32) {
477 BT_LOGW("Invalid packet header field type: `magic` field must be a 32-bit unsigned integer field type: "
478 "magic-ft-addr=%p, magic-ft-size=%u",
479 field_type,
480 bt_ctf_field_type_common_integer_get_size(field_type));
481 goto invalid;
482 }
483
484 ret = bt_ctf_field_type_common_structure_borrow_field_by_index(
485 packet_header_type, &field_name, NULL, 0);
486 BT_ASSERT(ret == 0);
487
488 if (strcmp(field_name, "magic") != 0) {
489 BT_LOGW("Invalid packet header field type: `magic` field must be the first field: "
490 "magic-ft-addr=%p, first-field-name=\"%s\"",
491 field_type, field_name);
492 goto invalid;
493 }
494 }
495
496 /*
497 * If there's a `uuid` field, it must be an array field type of
498 * length 16 with an 8-bit unsigned integer element field type.
499 */
500 field_type = bt_ctf_field_type_common_structure_borrow_field_type_by_name(
501 packet_header_type, "uuid");
502 if (field_type) {
503 struct bt_ctf_field_type_common *elem_ft;
504
505 if (field_type->id != BT_CTF_FIELD_TYPE_ID_ARRAY) {
506 BT_LOGW("Invalid packet header field type: `uuid` field must be an array field type: "
507 "uuid-ft-addr=%p, uuid-ft-id=%s",
508 field_type,
509 bt_ctf_field_type_id_string(field_type->id));
510 goto invalid;
511 }
512
513 if (bt_ctf_field_type_common_array_get_length(field_type) != 16) {
514 BT_LOGW("Invalid packet header field type: `uuid` array field type's length must be 16: "
515 "uuid-ft-addr=%p, uuid-ft-length=%" PRId64,
516 field_type,
517 bt_ctf_field_type_common_array_get_length(field_type));
518 goto invalid;
519 }
520
521 elem_ft = bt_ctf_field_type_common_array_borrow_element_field_type(field_type);
522 BT_ASSERT(elem_ft);
523
524 if (elem_ft->id != BT_CTF_FIELD_TYPE_ID_INTEGER) {
525 BT_LOGW("Invalid packet header field type: `uuid` field's element field type must be an integer field type: "
526 "elem-ft-addr=%p, elem-ft-id=%s",
527 elem_ft,
528 bt_ctf_field_type_id_string(elem_ft->id));
529 goto invalid;
530 }
531
532 if (bt_ctf_field_type_common_integer_is_signed(elem_ft)) {
533 BT_LOGW("Invalid packet header field type: `uuid` field's element field type must be an unsigned integer field type: "
534 "elem-ft-addr=%p", elem_ft);
535 goto invalid;
536 }
537
538 if (bt_ctf_field_type_common_integer_get_size(elem_ft) != 8) {
539 BT_LOGW("Invalid packet header field type: `uuid` field's element field type must be an 8-bit unsigned integer field type: "
540 "elem-ft-addr=%p, elem-ft-size=%u",
541 elem_ft,
542 bt_ctf_field_type_common_integer_get_size(elem_ft));
543 goto invalid;
544 }
545 }
546
547 /*
548 * The `stream_id` field must exist if there's more than one
549 * stream classes in the trace.
550 */
551 field_type = bt_ctf_field_type_common_structure_borrow_field_type_by_name(
552 packet_header_type, "stream_id");
553
554 if (!field_type && trace->stream_classes->len >= 1) {
555 BT_LOGW_STR("Invalid packet header field type: "
556 "`stream_id` field does not exist but there's more than one stream class in the trace.");
557 goto invalid;
558 }
559
560 /*
561 * If there's a `stream_id` field, it must be an unsigned
562 * integer field type.
563 */
564 if (field_type) {
565 if (field_type->id != BT_CTF_FIELD_TYPE_ID_INTEGER) {
566 BT_LOGW("Invalid packet header field type: `stream_id` field must be an integer field type: "
567 "stream-id-ft-addr=%p, stream-id-ft-id=%s",
568 field_type,
569 bt_ctf_field_type_id_string(field_type->id));
570 goto invalid;
571 }
572
573 if (bt_ctf_field_type_common_integer_is_signed(field_type)) {
574 BT_LOGW("Invalid packet header field type: `stream_id` field must be an unsigned integer field type: "
575 "stream-id-ft-addr=%p", field_type);
576 goto invalid;
577 }
578 }
579
580 /*
581 * If there's a `packet_seq_num` field, it must be an unsigned
582 * integer field type.
583 */
584 field_type = bt_ctf_field_type_common_structure_borrow_field_type_by_name(
585 packet_header_type, "packet_seq_num");
586 if (field_type) {
587 if (field_type->id != BT_CTF_FIELD_TYPE_ID_INTEGER) {
588 BT_LOGW("Invalid packet header field type: `packet_seq_num` field must be an integer field type: "
589 "stream-id-ft-addr=%p, packet-seq-num-ft-id=%s",
590 field_type,
591 bt_ctf_field_type_id_string(field_type->id));
592 goto invalid;
593 }
594
595 if (bt_ctf_field_type_common_integer_is_signed(field_type)) {
596 BT_LOGW("Invalid packet header field type: `packet_seq_num` field must be an unsigned integer field type: "
597 "packet-seq-num-ft-addr=%p", field_type);
598 goto invalid;
599 }
600 }
601
602 goto end;
603
604invalid:
605 is_valid = false;
606
607end:
608 return is_valid;
609}
610
611static
612bool packet_context_field_type_is_valid(struct bt_ctf_trace_common *trace,
613 struct bt_ctf_stream_class_common *stream_class,
614 struct bt_ctf_field_type_common *packet_context_type,
615 bool check_ts_begin_end_mapped)
616{
617 bool is_valid = true;
618 struct bt_ctf_field_type_common *field_type = NULL;
619
620 if (!packet_context_type) {
621 /* No packet context field type: valid at this point */
622 goto end;
623 }
624
625 /* Packet context field type, if it exists, must be a structure */
626 if (packet_context_type->id != BT_CTF_FIELD_TYPE_ID_STRUCT) {
627 BT_LOGW("Invalid packet context field type: must be a structure field type if it exists: "
628 "ft-addr=%p, ft-id=%s",
629 packet_context_type,
630 bt_ctf_field_type_id_string(packet_context_type->id));
631 goto invalid;
632 }
633
634 /*
635 * If there's a `packet_size` field, it must be an unsigned
636 * integer field type.
637 */
638 field_type = bt_ctf_field_type_common_structure_borrow_field_type_by_name(
639 packet_context_type, "packet_size");
640 if (field_type) {
641 if (field_type->id != BT_CTF_FIELD_TYPE_ID_INTEGER) {
642 BT_LOGW("Invalid packet context field type: `packet_size` field must be an integer field type: "
643 "packet-size-ft-addr=%p, packet-size-ft-id=%s",
644 field_type,
645 bt_ctf_field_type_id_string(field_type->id));
646 goto invalid;
647 }
648
649 if (bt_ctf_field_type_common_integer_is_signed(field_type)) {
650 BT_LOGW("Invalid packet context field type: `packet_size` field must be an unsigned integer field type: "
651 "packet-size-ft-addr=%p", field_type);
652 goto invalid;
653 }
654 }
655
656 /*
657 * If there's a `content_size` field, it must be an unsigned
658 * integer field type.
659 */
660 field_type = bt_ctf_field_type_common_structure_borrow_field_type_by_name(
661 packet_context_type, "content_size");
662 if (field_type) {
663 if (field_type->id != BT_CTF_FIELD_TYPE_ID_INTEGER) {
664 BT_LOGW("Invalid packet context field type: `content_size` field must be an integer field type: "
665 "content-size-ft-addr=%p, content-size-ft-id=%s",
666 field_type,
667 bt_ctf_field_type_id_string(field_type->id));
668 goto invalid;
669 }
670
671 if (bt_ctf_field_type_common_integer_is_signed(field_type)) {
672 BT_LOGW("Invalid packet context field type: `content_size` field must be an unsigned integer field type: "
673 "content-size-ft-addr=%p", field_type);
674 goto invalid;
675 }
676 }
677
678 /*
679 * If there's a `events_discarded` field, it must be an unsigned
680 * integer field type.
681 */
682 field_type = bt_ctf_field_type_common_structure_borrow_field_type_by_name(
683 packet_context_type, "events_discarded");
684 if (field_type) {
685 if (field_type->id != BT_CTF_FIELD_TYPE_ID_INTEGER) {
686 BT_LOGW("Invalid packet context field type: `events_discarded` field must be an integer field type: "
687 "events-discarded-ft-addr=%p, events-discarded-ft-id=%s",
688 field_type,
689 bt_ctf_field_type_id_string(field_type->id));
690 goto invalid;
691 }
692
693 if (bt_ctf_field_type_common_integer_is_signed(field_type)) {
694 BT_LOGW("Invalid packet context field type: `events_discarded` field must be an unsigned integer field type: "
695 "events-discarded-ft-addr=%p", field_type);
696 goto invalid;
697 }
698 }
699
700 /*
701 * If there's a `timestamp_begin` field, it must be an unsigned
702 * integer field type. Also, if the trace is not a CTF writer's
703 * trace, then we cannot automatically set the mapped clock
704 * class of this field, so it must have a mapped clock class.
705 */
706 field_type = bt_ctf_field_type_common_structure_borrow_field_type_by_name(
707 packet_context_type, "timestamp_begin");
708 if (field_type) {
709 if (field_type->id != BT_CTF_FIELD_TYPE_ID_INTEGER) {
710 BT_LOGW("Invalid packet context field type: `timestamp_begin` field must be an integer field type: "
711 "timestamp-begin-ft-addr=%p, timestamp-begin-ft-id=%s",
712 field_type,
713 bt_ctf_field_type_id_string(field_type->id));
714 goto invalid;
715 }
716
717 if (bt_ctf_field_type_common_integer_is_signed(field_type)) {
718 BT_LOGW("Invalid packet context field type: `timestamp_begin` field must be an unsigned integer field type: "
719 "timestamp-begin-ft-addr=%p", field_type);
720 goto invalid;
721 }
722
723 if (check_ts_begin_end_mapped) {
724 struct bt_ctf_clock_class *clock_class =
725 bt_ctf_field_type_common_integer_borrow_mapped_clock_class(
726 field_type);
727
728 if (!clock_class) {
729 BT_LOGW("Invalid packet context field type: `timestamp_begin` field must be mapped to a clock class: "
730 "timestamp-begin-ft-addr=%p", field_type);
731 goto invalid;
732 }
733 }
734 }
3dca2276 735
16ca5ff0
PP
736 /*
737 * If there's a `timestamp_end` field, it must be an unsigned
738 * integer field type. Also, if the trace is not a CTF writer's
739 * trace, then we cannot automatically set the mapped clock
740 * class of this field, so it must have a mapped clock class.
741 */
742 field_type = bt_ctf_field_type_common_structure_borrow_field_type_by_name(
743 packet_context_type, "timestamp_end");
744 if (field_type) {
745 if (field_type->id != BT_CTF_FIELD_TYPE_ID_INTEGER) {
746 BT_LOGW("Invalid packet context field type: `timestamp_end` field must be an integer field type: "
747 "timestamp-end-ft-addr=%p, timestamp-end-ft-id=%s",
748 field_type,
749 bt_ctf_field_type_id_string(field_type->id));
750 goto invalid;
751 }
752
753 if (bt_ctf_field_type_common_integer_is_signed(field_type)) {
754 BT_LOGW("Invalid packet context field type: `timestamp_end` field must be an unsigned integer field type: "
755 "timestamp-end-ft-addr=%p", field_type);
756 goto invalid;
757 }
758
759 if (check_ts_begin_end_mapped) {
760 struct bt_ctf_clock_class *clock_class =
761 bt_ctf_field_type_common_integer_borrow_mapped_clock_class(
762 field_type);
763
764 if (!clock_class) {
765 BT_LOGW("Invalid packet context field type: `timestamp_end` field must be mapped to a clock class: "
766 "timestamp-end-ft-addr=%p", field_type);
767 goto invalid;
768 }
769 }
770 }
771
772 goto end;
773
774invalid:
775 is_valid = false;
776
777end:
778 return is_valid;
779}
780
781static
782bool event_header_field_type_is_valid(struct bt_ctf_trace_common *trace,
783 struct bt_ctf_stream_class_common *stream_class,
784 struct bt_ctf_field_type_common *event_header_type)
785{
786 bool is_valid = true;
787 struct bt_ctf_field_type_common *field_type = NULL;
788
789 /*
790 * We do not validate that the `timestamp` field exists here
791 * because CTF does not require this exact name to be mapped to
792 * a clock class.
793 */
794
795 if (!event_header_type) {
796 /*
797 * No event header field type: stream class must have
798 * only one event class.
799 */
800 if (bt_ctf_stream_class_common_get_event_class_count(stream_class) > 1) {
801 BT_LOGW_STR("Invalid event header field type: "
802 "event header field type does not exist but there's more than one event class in the stream class.");
803 goto invalid;
804 }
805
806 /* No event header field type: valid at this point */
807 goto end;
808 }
809
810 /* Event header field type, if it exists, must be a structure */
811 if (event_header_type->id != BT_CTF_FIELD_TYPE_ID_STRUCT) {
812 BT_LOGW("Invalid event header field type: must be a structure field type if it exists: "
813 "ft-addr=%p, ft-id=%s",
814 event_header_type,
815 bt_ctf_field_type_id_string(event_header_type->id));
816 goto invalid;
817 }
818
819 /*
820 * If there's an `id` field, it must be an unsigned integer
821 * field type or an enumeration field type with an unsigned
822 * integer container field type.
823 */
824 field_type = bt_ctf_field_type_common_structure_borrow_field_type_by_name(
825 event_header_type, "id");
826 if (field_type) {
827 struct bt_ctf_field_type_common *int_ft;
828
829 if (field_type->id == BT_CTF_FIELD_TYPE_ID_INTEGER) {
830 int_ft = field_type;
831 } else if (field_type->id == BT_CTF_FIELD_TYPE_ID_ENUM) {
832 int_ft = bt_ctf_field_type_common_enumeration_borrow_container_field_type(
833 field_type);
834 } else {
835 BT_LOGW("Invalid event header field type: `id` field must be an integer or enumeration field type: "
836 "id-ft-addr=%p, id-ft-id=%s",
837 field_type,
838 bt_ctf_field_type_id_string(field_type->id));
839 goto invalid;
840 }
841
842 BT_ASSERT(int_ft);
843 if (bt_ctf_field_type_common_integer_is_signed(int_ft)) {
844 BT_LOGW("Invalid event header field type: `id` field must be an unsigned integer or enumeration field type: "
845 "id-ft-addr=%p", int_ft);
846 goto invalid;
847 }
848 }
849
850 goto end;
851
852invalid:
853 is_valid = false;
854
855end:
856 return is_valid;
857}
858
859static
860int check_packet_header_type_has_no_clock_class(struct bt_ctf_trace_common *trace)
861{
862 int ret = 0;
863
864 if (trace->packet_header_field_type) {
865 struct bt_ctf_clock_class *clock_class = NULL;
866
867 ret = bt_ctf_field_type_common_validate_single_clock_class(
868 trace->packet_header_field_type,
869 &clock_class);
870 bt_put(clock_class);
871 if (ret || clock_class) {
872 BT_LOGW("Trace's packet header field type cannot "
873 "contain a field type which is mapped to "
874 "a clock class: "
875 "trace-addr=%p, trace-name=\"%s\", "
876 "clock-class-name=\"%s\"",
877 trace, bt_ctf_trace_common_get_name(trace),
878 clock_class ?
879 bt_ctf_clock_class_get_name(clock_class) :
880 NULL);
881 ret = -1;
882 }
883 }
884
885 return ret;
886}
887
888BT_HIDDEN
889int bt_ctf_trace_common_add_stream_class(struct bt_ctf_trace_common *trace,
890 struct bt_ctf_stream_class_common *stream_class,
891 bt_ctf_validation_flag_copy_field_type_func copy_field_type_func,
892 struct bt_ctf_clock_class *init_expected_clock_class,
893 int (*map_clock_classes_func)(struct bt_ctf_stream_class_common *stream_class,
894 struct bt_ctf_field_type_common *packet_context_field_type,
895 struct bt_ctf_field_type_common *event_header_field_type),
896 bool check_ts_begin_end_mapped)
897{
898 int ret;
899 int64_t i;
900 int64_t stream_id;
901 struct bt_ctf_validation_output trace_sc_validation_output = { 0 };
902 struct bt_ctf_validation_output *ec_validation_outputs = NULL;
903 const enum bt_ctf_validation_flag trace_sc_validation_flags =
904 BT_CTF_VALIDATION_FLAG_TRACE |
905 BT_CTF_VALIDATION_FLAG_STREAM;
906 const enum bt_ctf_validation_flag ec_validation_flags =
907 BT_CTF_VALIDATION_FLAG_EVENT;
908 struct bt_ctf_field_type_common *packet_header_type = NULL;
909 struct bt_ctf_field_type_common *packet_context_type = NULL;
910 struct bt_ctf_field_type_common *event_header_type = NULL;
911 struct bt_ctf_field_type_common *stream_event_ctx_type = NULL;
912 int64_t event_class_count;
913 struct bt_ctf_trace_common *current_parent_trace = NULL;
914 struct bt_ctf_clock_class *expected_clock_class =
915 bt_get(init_expected_clock_class);
916
917 BT_ASSERT(copy_field_type_func);
918
919 if (!trace) {
920 BT_LOGW_STR("Invalid parameter: trace is NULL.");
921 ret = -1;
922 goto end;
923 }
924
925 if (!stream_class) {
926 BT_LOGW_STR("Invalid parameter: stream class is NULL.");
927 ret = -1;
928 goto end;
929 }
930
931 BT_LOGD("Adding stream class to trace: "
932 "trace-addr=%p, trace-name=\"%s\", "
933 "stream-class-addr=%p, stream-class-name=\"%s\", "
934 "stream-class-id=%" PRId64,
935 trace, bt_ctf_trace_common_get_name(trace),
936 stream_class, bt_ctf_stream_class_common_get_name(stream_class),
937 bt_ctf_stream_class_common_get_id(stream_class));
938
939 current_parent_trace = bt_ctf_stream_class_common_borrow_trace(stream_class);
940 if (current_parent_trace) {
941 /* Stream class is already associated to a trace, abort. */
942 BT_LOGW("Invalid parameter: stream class is already part of a trace: "
943 "stream-class-trace-addr=%p, "
944 "stream-class-trace-name=\"%s\"",
945 current_parent_trace,
946 bt_ctf_trace_common_get_name(current_parent_trace));
947 ret = -1;
948 goto end;
949 }
950
951 event_class_count =
952 bt_ctf_stream_class_common_get_event_class_count(stream_class);
953 BT_ASSERT(event_class_count >= 0);
954
955 if (!stream_class->frozen) {
956 /*
957 * Stream class is not frozen yet. Validate that the
958 * stream class contains at most a single clock class
959 * because the previous
960 * bt_ctf_stream_class_common_add_event_class() calls did
961 * not make this validation since the stream class's
962 * direct field types (packet context, event header,
963 * event context) could change afterwards. This stream
964 * class is about to be frozen and those field types
965 * won't be changed if this function succeeds.
966 *
967 * At this point we're also sure that the stream class's
968 * clock, if any, has the same class as the stream
969 * class's expected clock class, if any. This is why, if
970 * bt_ctf_stream_class_common_validate_single_clock_class()
971 * succeeds below, the call to
972 * bt_ctf_stream_class_map_clock_class() at the end of this
973 * function is safe because it maps to the same, single
974 * clock class.
975 */
976 ret = bt_ctf_stream_class_common_validate_single_clock_class(
977 stream_class, &expected_clock_class);
978 if (ret) {
979 BT_LOGW("Invalid parameter: stream class or one of its "
980 "event classes contains a field type which is "
981 "not recursively mapped to the expected "
982 "clock class: "
983 "stream-class-addr=%p, "
984 "stream-class-id=%" PRId64 ", "
985 "stream-class-name=\"%s\", "
986 "expected-clock-class-addr=%p, "
987 "expected-clock-class-name=\"%s\"",
988 stream_class, bt_ctf_stream_class_common_get_id(stream_class),
989 bt_ctf_stream_class_common_get_name(stream_class),
990 expected_clock_class,
991 expected_clock_class ?
992 bt_ctf_clock_class_get_name(expected_clock_class) :
993 NULL);
994 goto end;
995 }
996 }
997
998 ret = check_packet_header_type_has_no_clock_class(trace);
999 if (ret) {
1000 /* check_packet_header_type_has_no_clock_class() logs errors */
1001 goto end;
1002 }
1003
1004 /*
1005 * We're about to freeze both the trace and the stream class.
1006 * Also, each event class contained in this stream class are
1007 * already frozen.
1008 *
1009 * This trace, this stream class, and all its event classes
1010 * should be valid at this point.
1011 *
1012 * Validate trace and stream class first, then each event
1013 * class of this stream class can be validated individually.
1014 */
1015 packet_header_type =
1016 bt_ctf_trace_common_borrow_packet_header_field_type(trace);
1017 packet_context_type =
1018 bt_ctf_stream_class_common_borrow_packet_context_field_type(stream_class);
1019 event_header_type =
1020 bt_ctf_stream_class_common_borrow_event_header_field_type(stream_class);
1021 stream_event_ctx_type =
1022 bt_ctf_stream_class_common_borrow_event_context_field_type(stream_class);
1023
1024 BT_LOGD("Validating trace and stream class field types.");
1025 ret = bt_ctf_validate_class_types(trace->environment,
1026 packet_header_type, packet_context_type, event_header_type,
1027 stream_event_ctx_type, NULL, NULL, trace->valid,
1028 stream_class->valid, 1, &trace_sc_validation_output,
1029 trace_sc_validation_flags, copy_field_type_func);
1030
1031 if (ret) {
1032 /*
1033 * This means something went wrong during the validation
1034 * process, not that the objects are invalid.
1035 */
1036 BT_LOGE("Failed to validate trace and stream class field types: "
1037 "ret=%d", ret);
1038 goto end;
1039 }
1040
1041 if ((trace_sc_validation_output.valid_flags &
1042 trace_sc_validation_flags) !=
1043 trace_sc_validation_flags) {
1044 /* Invalid trace/stream class */
1045 BT_LOGW("Invalid trace or stream class field types: "
1046 "valid-flags=0x%x",
1047 trace_sc_validation_output.valid_flags);
1048 ret = -1;
1049 goto end;
1050 }
1051
1052 if (event_class_count > 0) {
1053 ec_validation_outputs = g_new0(struct bt_ctf_validation_output,
1054 event_class_count);
1055 if (!ec_validation_outputs) {
1056 BT_LOGE_STR("Failed to allocate one validation output structure.");
1057 ret = -1;
1058 goto end;
1059 }
1060 }
1061
1062 /* Validate each event class individually */
1063 for (i = 0; i < event_class_count; i++) {
1064 struct bt_ctf_event_class_common *event_class =
1065 bt_ctf_stream_class_common_borrow_event_class_by_index(
1066 stream_class, i);
1067 struct bt_ctf_field_type_common *event_context_type = NULL;
1068 struct bt_ctf_field_type_common *event_payload_type = NULL;
1069
1070 event_context_type =
1071 bt_ctf_event_class_common_borrow_context_field_type(
1072 event_class);
1073 event_payload_type =
1074 bt_ctf_event_class_common_borrow_payload_field_type(
1075 event_class);
1076
1077 /*
1078 * It is important to use the field types returned by
1079 * the previous trace and stream class validation here
1080 * because copies could have been made.
1081 */
1082 BT_LOGD("Validating event class's field types: "
1083 "addr=%p, name=\"%s\", id=%" PRId64,
1084 event_class, bt_ctf_event_class_common_get_name(event_class),
1085 bt_ctf_event_class_common_get_id(event_class));
1086 ret = bt_ctf_validate_class_types(trace->environment,
1087 trace_sc_validation_output.packet_header_type,
1088 trace_sc_validation_output.packet_context_type,
1089 trace_sc_validation_output.event_header_type,
1090 trace_sc_validation_output.stream_event_ctx_type,
1091 event_context_type, event_payload_type,
1092 1, 1, event_class->valid, &ec_validation_outputs[i],
1093 ec_validation_flags, copy_field_type_func);
1094
1095 if (ret) {
1096 BT_LOGE("Failed to validate event class field types: "
1097 "ret=%d", ret);
1098 goto end;
1099 }
1100
1101 if ((ec_validation_outputs[i].valid_flags &
1102 ec_validation_flags) != ec_validation_flags) {
1103 /* Invalid event class */
1104 BT_LOGW("Invalid event class field types: "
1105 "valid-flags=0x%x",
1106 ec_validation_outputs[i].valid_flags);
1107 ret = -1;
1108 goto end;
1109 }
1110 }
1111
1112 stream_id = bt_ctf_stream_class_common_get_id(stream_class);
1113 if (stream_id < 0) {
1114 stream_id = trace->next_stream_id++;
1115 if (stream_id < 0) {
1116 BT_LOGE_STR("No more stream class IDs available.");
1117 ret = -1;
1118 goto end;
1119 }
1120
1121 /* Try to assign a new stream id */
1122 for (i = 0; i < trace->stream_classes->len; i++) {
1123 if (stream_id == bt_ctf_stream_class_common_get_id(
1124 trace->stream_classes->pdata[i])) {
1125 /* Duplicate stream id found */
1126 BT_LOGW("Duplicate stream class ID: "
1127 "id=%" PRId64, (int64_t) stream_id);
1128 ret = -1;
1129 goto end;
1130 }
1131 }
1132
1133 if (bt_ctf_stream_class_common_set_id_no_check(stream_class,
1134 stream_id)) {
1135 /* TODO Should retry with a different stream id */
1136 BT_LOGE("Cannot set stream class's ID: "
1137 "id=%" PRId64, (int64_t) stream_id);
1138 ret = -1;
1139 goto end;
1140 }
1141 }
1142
1143 /*
1144 * At this point all the field types in the validation output
1145 * are valid. Validate the semantics of some scopes according to
1146 * the CTF specification.
1147 */
1148 if (!packet_header_field_type_is_valid(trace,
1149 trace_sc_validation_output.packet_header_type)) {
1150 BT_LOGW_STR("Invalid trace's packet header field type.");
1151 ret = -1;
1152 goto end;
1153 }
1154
1155 if (!packet_context_field_type_is_valid(trace,
1156 stream_class,
1157 trace_sc_validation_output.packet_context_type,
1158 check_ts_begin_end_mapped)) {
1159 BT_LOGW_STR("Invalid stream class's packet context field type.");
1160 ret = -1;
1161 goto end;
1162 }
1163
1164 if (!event_header_field_type_is_valid(trace,
1165 stream_class,
1166 trace_sc_validation_output.event_header_type)) {
1167 BT_LOGW_STR("Invalid steam class's event header field type.");
1168 ret = -1;
1169 goto end;
1170 }
1171
1172 /*
1173 * Now is the time to automatically map specific field types of
1174 * the stream class's packet context and event header field
1175 * types to the stream class's clock's class if they are not
1176 * mapped to a clock class yet. We do it here because we know
1177 * that after this point, everything is frozen so it won't be
1178 * possible for the user to modify the stream class's clock, or
1179 * to map those field types to other clock classes.
1180 */
1181 if (map_clock_classes_func) {
1182 if (map_clock_classes_func(stream_class,
1183 trace_sc_validation_output.packet_context_type,
1184 trace_sc_validation_output.event_header_type)) {
1185 /* map_clock_classes_func() logs errors */
1186 ret = -1;
1187 goto end;
1188 }
1189 }
1190
1191 bt_object_set_parent(&stream_class->base, &trace->base);
1192 g_ptr_array_add(trace->stream_classes, stream_class);
1193
1194 /*
1195 * At this point we know that the function will be successful.
1196 * Therefore we can replace the trace and stream class field
1197 * types with what's in their validation output structure and
1198 * mark them as valid. We can also replace the field types of
1199 * all the event classes of the stream class and mark them as
1200 * valid.
1201 */
1202 bt_ctf_validation_replace_types(trace, stream_class, NULL,
1203 &trace_sc_validation_output, trace_sc_validation_flags);
1204 trace->valid = 1;
1205 stream_class->valid = 1;
1206
1207 /*
1208 * Put what was not moved in bt_ctf_validation_replace_types().
1209 */
1210 bt_ctf_validation_output_put_types(&trace_sc_validation_output);
1211
1212 for (i = 0; i < event_class_count; i++) {
1213 struct bt_ctf_event_class_common *event_class =
1214 bt_ctf_stream_class_common_borrow_event_class_by_index(
1215 stream_class, i);
1216
1217 bt_ctf_validation_replace_types(NULL, NULL, event_class,
1218 &ec_validation_outputs[i], ec_validation_flags);
1219 event_class->valid = 1;
1220
1221 /*
1222 * Put what was not moved in
1223 * bt_ctf_validation_replace_types().
1224 */
1225 bt_ctf_validation_output_put_types(&ec_validation_outputs[i]);
1226 }
1227
1228 /*
1229 * Freeze the trace and the stream class.
1230 */
1231 bt_ctf_stream_class_common_freeze(stream_class);
1232 bt_ctf_trace_common_freeze(trace);
1233
1234 /*
1235 * It is safe to set the stream class's unique clock class
1236 * now because the stream class is frozen.
1237 */
1238 if (expected_clock_class) {
1239 BT_MOVE(stream_class->clock_class, expected_clock_class);
1240 }
1241
1242 BT_LOGD("Added stream class to trace: "
1243 "trace-addr=%p, trace-name=\"%s\", "
1244 "stream-class-addr=%p, stream-class-name=\"%s\", "
1245 "stream-class-id=%" PRId64,
1246 trace, bt_ctf_trace_common_get_name(trace),
1247 stream_class, bt_ctf_stream_class_common_get_name(stream_class),
1248 bt_ctf_stream_class_common_get_id(stream_class));
1249
1250end:
1251 if (ret) {
1252 bt_object_set_parent(&stream_class->base, NULL);
1253
1254 if (ec_validation_outputs) {
1255 for (i = 0; i < event_class_count; i++) {
1256 bt_ctf_validation_output_put_types(
1257 &ec_validation_outputs[i]);
1258 }
1259 }
1260 }
1261
1262 g_free(ec_validation_outputs);
1263 bt_ctf_validation_output_put_types(&trace_sc_validation_output);
1264 bt_put(expected_clock_class);
1265 return ret;
1266}
1267
1268BT_HIDDEN
1269bt_bool bt_ctf_trace_common_has_clock_class(struct bt_ctf_trace_common *trace,
1270 struct bt_ctf_clock_class *clock_class)
1271{
1272 struct bt_ctf_search_query query = { .value = clock_class, .found = 0 };
1273
1274 BT_ASSERT(trace);
1275 BT_ASSERT(clock_class);
1276
1277 g_ptr_array_foreach(trace->clock_classes, value_exists, &query);
1278 return query.found;
1279}
1280
1281BT_HIDDEN
1282int bt_ctf_trace_common_set_native_byte_order(struct bt_ctf_trace_common *trace,
1283 enum bt_ctf_byte_order byte_order, bool allow_unspecified)
1284{
1285 int ret = 0;
1286
1287 if (!trace) {
1288 BT_LOGW_STR("Invalid parameter: trace is NULL.");
1289 ret = -1;
1290 goto end;
1291 }
1292
1293 if (trace->frozen) {
1294 BT_LOGW("Invalid parameter: trace is frozen: "
1295 "addr=%p, name=\"%s\"",
1296 trace, bt_ctf_trace_common_get_name(trace));
1297 ret = -1;
1298 goto end;
1299 }
1300
1301 if (byte_order == BT_CTF_BYTE_ORDER_UNSPECIFIED && !allow_unspecified) {
1302 BT_LOGW("Invalid parameter: BT_CTF_BYTE_ORDER_UNSPECIFIED byte order is not allowed: "
1303 "addr=%p, name=\"%s\"",
1304 trace, bt_ctf_trace_common_get_name(trace));
1305 ret = -1;
1306 goto end;
1307 }
1308
1309 if (byte_order != BT_CTF_BYTE_ORDER_LITTLE_ENDIAN &&
1310 byte_order != BT_CTF_BYTE_ORDER_BIG_ENDIAN &&
1311 byte_order != BT_CTF_BYTE_ORDER_NETWORK) {
1312 BT_LOGW("Invalid parameter: invalid byte order: "
1313 "addr=%p, name=\"%s\", bo=%s",
1314 trace, bt_ctf_trace_common_get_name(trace),
1315 bt_ctf_byte_order_string(byte_order));
1316 ret = -1;
1317 goto end;
1318 }
1319
1320 trace->native_byte_order = byte_order;
1321 BT_LOGV("Set trace's native byte order: "
1322 "addr=%p, name=\"%s\", bo=%s",
1323 trace, bt_ctf_trace_common_get_name(trace),
1324 bt_ctf_byte_order_string(byte_order));
1325
1326end:
1327 return ret;
1328}
1329
1330BT_HIDDEN
1331int bt_ctf_trace_common_set_packet_header_field_type(struct bt_ctf_trace_common *trace,
1332 struct bt_ctf_field_type_common *packet_header_type)
1333{
1334 int ret = 0;
1335
1336 if (!trace) {
1337 BT_LOGW_STR("Invalid parameter: trace is NULL.");
1338 ret = -1;
1339 goto end;
1340 }
1341
1342 if (trace->frozen) {
1343 BT_LOGW("Invalid parameter: trace is frozen: "
1344 "addr=%p, name=\"%s\"",
1345 trace, bt_ctf_trace_common_get_name(trace));
1346 ret = -1;
1347 goto end;
1348 }
1349
1350 /* packet_header_type must be a structure. */
1351 if (packet_header_type &&
1352 packet_header_type->id != BT_CTF_FIELD_TYPE_ID_STRUCT) {
1353 BT_LOGW("Invalid parameter: packet header field type must be a structure field type if it exists: "
1354 "addr=%p, name=\"%s\", ft-addr=%p, ft-id=%s",
1355 trace, bt_ctf_trace_common_get_name(trace),
1356 packet_header_type,
1357 bt_ctf_field_type_id_string(packet_header_type->id));
1358 ret = -1;
1359 goto end;
1360 }
1361
1362 bt_put(trace->packet_header_field_type);
1363 trace->packet_header_field_type = bt_get(packet_header_type);
1364 BT_LOGV("Set trace's packet header field type: "
1365 "addr=%p, name=\"%s\", packet-context-ft-addr=%p",
1366 trace, bt_ctf_trace_common_get_name(trace), packet_header_type);
1367end:
1368 return ret;
1369}
1370
1371static
1372int64_t get_stream_class_count(void *element)
1373{
1374 return bt_ctf_trace_get_stream_class_count(
1375 (struct bt_ctf_trace *) element);
1376}
1377
1378static
1379void *get_stream_class(void *element, int i)
1380{
1381 return bt_ctf_trace_get_stream_class_by_index(
1382 (struct bt_ctf_trace *) element, i);
1383}
1384
1385static
1386int visit_stream_class(void *object, bt_ctf_visitor visitor,void *data)
1387{
1388 return bt_ctf_stream_class_visit(object, visitor, data);
1389}
1390
1391int bt_ctf_trace_visit(struct bt_ctf_trace *trace,
1392 bt_ctf_visitor visitor, void *data)
1393{
1394 int ret;
1395 struct bt_ctf_visitor_object obj = {
1396 .object = trace,
1397 .type = BT_CTF_VISITOR_OBJECT_TYPE_TRACE
1398 };
1399
1400 if (!trace) {
1401 BT_LOGW_STR("Invalid parameter: trace is NULL.");
1402 ret = -1;
1403 goto end;
1404 }
1405
1406 if (!visitor) {
1407 BT_LOGW_STR("Invalid parameter: visitor is NULL.");
1408 ret = -1;
1409 goto end;
1410 }
1411
1412 BT_LOGV("Visiting trace: addr=%p, name=\"%s\"",
1413 trace, bt_ctf_trace_get_name(trace));
1414 ret = visitor_helper(&obj, get_stream_class_count,
1415 get_stream_class, visit_stream_class, visitor, data);
1416end:
1417 return ret;
1418}
3dca2276
PP
1419
1420static
1421void bt_ctf_trace_destroy(struct bt_object *obj)
1422{
1423 struct bt_ctf_trace *trace = (void *) obj;
1424
1425 BT_LOGD("Destroying CTF writer trace object: addr=%p, name=\"%s\"",
1426 trace, bt_ctf_trace_get_name(trace));
16ca5ff0 1427 bt_ctf_trace_common_finalize(BT_CTF_TO_COMMON(trace));
3dca2276
PP
1428 g_free(trace);
1429}
1430
1431BT_HIDDEN
1432struct bt_ctf_trace *bt_ctf_trace_create(void)
1433{
1434 struct bt_ctf_trace *trace = NULL;
1435 int ret;
1436
1437 BT_LOGD_STR("Creating CTF writer trace object.");
1438 trace = g_new0(struct bt_ctf_trace, 1);
1439 if (!trace) {
1440 BT_LOGE_STR("Failed to allocate one CTF writer trace.");
1441 goto error;
1442 }
1443
16ca5ff0 1444 ret = bt_ctf_trace_common_initialize(BT_CTF_TO_COMMON(trace),
3dca2276
PP
1445 bt_ctf_trace_destroy);
1446 if (ret) {
16ca5ff0 1447 /* bt_ctf_trace_common_initialize() logs errors */
3dca2276
PP
1448 goto error;
1449 }
1450
1451 BT_LOGD("Created CTF writer trace object: addr=%p", trace);
1452 return trace;
1453
1454error:
1455 BT_PUT(trace);
1456 return trace;
1457}
1458
1459const unsigned char *bt_ctf_trace_get_uuid(struct bt_ctf_trace *trace)
1460{
16ca5ff0 1461 return bt_ctf_trace_common_get_uuid(BT_CTF_TO_COMMON(trace));
3dca2276
PP
1462}
1463
1464int bt_ctf_trace_set_uuid(struct bt_ctf_trace *trace,
1465 const unsigned char *uuid)
1466{
16ca5ff0 1467 return bt_ctf_trace_common_set_uuid(BT_CTF_TO_COMMON(trace), uuid);
3dca2276
PP
1468}
1469
1470int bt_ctf_trace_set_environment_field(struct bt_ctf_trace *trace,
1471 const char *name, struct bt_value *value)
1472{
16ca5ff0 1473 return bt_ctf_trace_common_set_environment_field(BT_CTF_TO_COMMON(trace),
3dca2276
PP
1474 name, value);
1475}
1476
1477int bt_ctf_trace_set_environment_field_string(struct bt_ctf_trace *trace,
1478 const char *name, const char *value)
1479{
16ca5ff0 1480 return bt_ctf_trace_common_set_environment_field_string(BT_CTF_TO_COMMON(trace),
3dca2276
PP
1481 name, value);
1482}
1483
1484int bt_ctf_trace_set_environment_field_integer(
1485 struct bt_ctf_trace *trace, const char *name, int64_t value)
1486{
16ca5ff0
PP
1487 return bt_ctf_trace_common_set_environment_field_integer(
1488 BT_CTF_TO_COMMON(trace), name, value);
3dca2276
PP
1489}
1490
1491int64_t bt_ctf_trace_get_environment_field_count(struct bt_ctf_trace *trace)
1492{
16ca5ff0 1493 return bt_ctf_trace_common_get_environment_field_count(BT_CTF_TO_COMMON(trace));
3dca2276
PP
1494}
1495
1496const char *
1497bt_ctf_trace_get_environment_field_name_by_index(struct bt_ctf_trace *trace,
1498 uint64_t index)
1499{
16ca5ff0
PP
1500 return bt_ctf_trace_common_get_environment_field_name_by_index(
1501 BT_CTF_TO_COMMON(trace), index);
3dca2276
PP
1502}
1503
1504struct bt_value *bt_ctf_trace_get_environment_field_value_by_index(
1505 struct bt_ctf_trace *trace, uint64_t index)
1506{
16ca5ff0
PP
1507 return bt_get(bt_ctf_trace_common_borrow_environment_field_value_by_index(
1508 BT_CTF_TO_COMMON(trace), index));
3dca2276
PP
1509}
1510
1511struct bt_value *bt_ctf_trace_get_environment_field_value_by_name(
1512 struct bt_ctf_trace *trace, const char *name)
1513{
16ca5ff0
PP
1514 return bt_get(bt_ctf_trace_common_borrow_environment_field_value_by_name(
1515 BT_CTF_TO_COMMON(trace), name));
3dca2276
PP
1516}
1517
16ca5ff0 1518BT_HIDDEN
3dca2276
PP
1519int bt_ctf_trace_add_clock_class(struct bt_ctf_trace *trace,
1520 struct bt_ctf_clock_class *clock_class)
1521{
16ca5ff0 1522 return bt_ctf_trace_common_add_clock_class(BT_CTF_TO_COMMON(trace),
3dca2276
PP
1523 (void *) clock_class);
1524}
1525
16ca5ff0 1526BT_HIDDEN
3dca2276
PP
1527int64_t bt_ctf_trace_get_clock_class_count(struct bt_ctf_trace *trace)
1528{
16ca5ff0 1529 return bt_ctf_trace_common_get_clock_class_count(BT_CTF_TO_COMMON(trace));
3dca2276
PP
1530}
1531
16ca5ff0 1532BT_HIDDEN
3dca2276
PP
1533struct bt_ctf_clock_class *bt_ctf_trace_get_clock_class_by_index(
1534 struct bt_ctf_trace *trace, uint64_t index)
1535{
16ca5ff0
PP
1536 return bt_get(bt_ctf_trace_common_borrow_clock_class_by_index(
1537 BT_CTF_TO_COMMON(trace), index));
3dca2276
PP
1538}
1539
1540static
16ca5ff0
PP
1541int map_clock_classes_func(struct bt_ctf_stream_class_common *stream_class,
1542 struct bt_ctf_field_type_common *packet_context_type,
1543 struct bt_ctf_field_type_common *event_header_type)
3dca2276
PP
1544{
1545 int ret = bt_ctf_stream_class_map_clock_class(
16ca5ff0
PP
1546 BT_CTF_FROM_COMMON(stream_class),
1547 BT_CTF_FROM_COMMON(packet_context_type),
1548 BT_CTF_FROM_COMMON(event_header_type));
3dca2276
PP
1549
1550 if (ret) {
1551 BT_LOGW_STR("Cannot automatically map selected stream class's field types to stream class's clock's class.");
1552 }
1553
1554 return ret;
1555}
1556
1557int bt_ctf_trace_add_stream_class(struct bt_ctf_trace *trace,
1558 struct bt_ctf_stream_class *stream_class)
1559{
1560 int ret = 0;
16ca5ff0 1561 struct bt_ctf_clock_class *expected_clock_class = NULL;
3dca2276
PP
1562
1563 if (!trace) {
1564 BT_LOGW_STR("Invalid parameter: trace is NULL.");
1565 ret = -1;
1566 goto end;
1567 }
1568
1569 if (!stream_class) {
1570 BT_LOGW_STR("Invalid parameter: stream class is NULL.");
1571 ret = -1;
1572 goto end;
1573 }
1574
1575 if (stream_class->clock) {
1576 struct bt_ctf_clock_class *stream_clock_class =
1577 stream_class->clock->clock_class;
1578
1579 /*
1580 * Make sure this clock was also added to the
1581 * trace (potentially through its CTF writer
1582 * owner).
1583 */
1584 size_t i;
1585
1586 for (i = 0; i < trace->common.clock_classes->len; i++) {
1587 if (trace->common.clock_classes->pdata[i] ==
1588 stream_clock_class) {
1589 /* Found! */
1590 break;
1591 }
1592 }
1593
1594 if (i == trace->common.clock_classes->len) {
1595 /* Not found */
1596 BT_LOGW("Stream class's clock's class is not part of the trace: "
1597 "clock-class-addr=%p, clock-class-name=\"%s\"",
1598 stream_clock_class,
16ca5ff0 1599 bt_ctf_clock_class_get_name(stream_clock_class));
3dca2276
PP
1600 ret = -1;
1601 goto end;
1602 }
1603
1604 if (stream_class->common.clock_class &&
1605 stream_class->common.clock_class !=
16ca5ff0 1606 stream_class->clock->clock_class) {
3dca2276
PP
1607 /*
1608 * Stream class already has an expected clock
1609 * class, but it does not match its clock's
1610 * class.
1611 */
1612 BT_LOGW("Invalid parameter: stream class's clock's "
1613 "class does not match stream class's "
1614 "expected clock class: "
1615 "stream-class-addr=%p, "
1616 "stream-class-id=%" PRId64 ", "
1617 "stream-class-name=\"%s\", "
1618 "expected-clock-class-addr=%p, "
1619 "expected-clock-class-name=\"%s\"",
1620 stream_class,
1621 bt_ctf_stream_class_get_id(stream_class),
1622 bt_ctf_stream_class_get_name(stream_class),
1623 stream_class->common.clock_class,
16ca5ff0 1624 bt_ctf_clock_class_get_name(stream_class->common.clock_class));
3dca2276
PP
1625 } else if (!stream_class->common.clock_class) {
1626 /*
1627 * Set expected clock class to stream class's
1628 * clock's class.
1629 */
16ca5ff0 1630 expected_clock_class = stream_class->clock->clock_class;
3dca2276
PP
1631 }
1632 }
1633
1634
16ca5ff0
PP
1635 ret = bt_ctf_trace_common_add_stream_class(BT_CTF_TO_COMMON(trace),
1636 BT_CTF_TO_COMMON(stream_class),
1637 (bt_ctf_validation_flag_copy_field_type_func) bt_ctf_field_type_copy,
3dca2276
PP
1638 expected_clock_class, map_clock_classes_func,
1639 false);
1640
1641end:
1642 return ret;
1643}
1644
1645int64_t bt_ctf_trace_get_stream_count(struct bt_ctf_trace *trace)
1646{
16ca5ff0 1647 return bt_ctf_trace_common_get_stream_count(BT_CTF_TO_COMMON(trace));
3dca2276
PP
1648}
1649
1650struct bt_ctf_stream *bt_ctf_trace_get_stream_by_index(
1651 struct bt_ctf_trace *trace, uint64_t index)
1652{
16ca5ff0
PP
1653 return bt_get(bt_ctf_trace_common_borrow_stream_by_index(
1654 BT_CTF_TO_COMMON(trace), index));
3dca2276
PP
1655}
1656
1657int64_t bt_ctf_trace_get_stream_class_count(struct bt_ctf_trace *trace)
1658{
16ca5ff0 1659 return bt_ctf_trace_common_get_stream_class_count(BT_CTF_TO_COMMON(trace));
3dca2276
PP
1660}
1661
1662struct bt_ctf_stream_class *bt_ctf_trace_get_stream_class_by_index(
1663 struct bt_ctf_trace *trace, uint64_t index)
1664{
16ca5ff0
PP
1665 return bt_get(bt_ctf_trace_common_borrow_stream_class_by_index(
1666 BT_CTF_TO_COMMON(trace), index));
3dca2276
PP
1667}
1668
1669struct bt_ctf_stream_class *bt_ctf_trace_get_stream_class_by_id(
1670 struct bt_ctf_trace *trace, uint64_t id)
1671{
16ca5ff0
PP
1672 return bt_get(bt_ctf_trace_common_borrow_stream_class_by_id(
1673 BT_CTF_TO_COMMON(trace), id));
3dca2276
PP
1674}
1675
16ca5ff0 1676BT_HIDDEN
3dca2276
PP
1677struct bt_ctf_clock_class *bt_ctf_trace_get_clock_class_by_name(
1678 struct bt_ctf_trace *trace, const char *name)
1679{
094ff7c0 1680 return bt_get(
16ca5ff0 1681 bt_ctf_trace_common_borrow_clock_class_by_name(BT_CTF_TO_COMMON(trace),
3dca2276
PP
1682 name));
1683}
1684
1685static
1686int append_trace_metadata(struct bt_ctf_trace *trace,
1687 struct metadata_context *context)
1688{
1689 unsigned char *uuid = trace->common.uuid;
1690 int ret = 0;
1691
16ca5ff0
PP
1692 if (trace->common.native_byte_order == BT_CTF_BYTE_ORDER_NATIVE ||
1693 trace->common.native_byte_order == BT_CTF_BYTE_ORDER_UNSPECIFIED) {
3dca2276
PP
1694 BT_LOGW("Invalid parameter: trace's byte order cannot be BT_CTF_BYTE_ORDER_NATIVE or BT_CTF_BYTE_ORDER_UNSPECIFIED at this point; "
1695 "set it with bt_ctf_trace_set_native_byte_order(): "
1696 "addr=%p, name=\"%s\"",
1697 trace, bt_ctf_trace_get_name(trace));
1698 ret = -1;
1699 goto end;
1700 }
1701
1702 g_string_append(context->string, "trace {\n");
1703 g_string_append(context->string, "\tmajor = 1;\n");
1704 g_string_append(context->string, "\tminor = 8;\n");
16ca5ff0
PP
1705 BT_ASSERT(trace->common.native_byte_order == BT_CTF_BYTE_ORDER_LITTLE_ENDIAN ||
1706 trace->common.native_byte_order == BT_CTF_BYTE_ORDER_BIG_ENDIAN ||
1707 trace->common.native_byte_order == BT_CTF_BYTE_ORDER_NETWORK);
3dca2276
PP
1708
1709 if (trace->common.uuid_set) {
1710 g_string_append_printf(context->string,
1711 "\tuuid = \"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\";\n",
1712 uuid[0], uuid[1], uuid[2], uuid[3],
1713 uuid[4], uuid[5], uuid[6], uuid[7],
1714 uuid[8], uuid[9], uuid[10], uuid[11],
1715 uuid[12], uuid[13], uuid[14], uuid[15]);
1716 }
1717
1718 g_string_append_printf(context->string, "\tbyte_order = %s;\n",
16ca5ff0 1719 bt_ctf_get_byte_order_string(trace->common.native_byte_order));
3dca2276
PP
1720
1721 if (trace->common.packet_header_field_type) {
1722 g_string_append(context->string, "\tpacket.header := ");
1723 context->current_indentation_level++;
1724 g_string_assign(context->field_name, "");
1725 BT_LOGD_STR("Serializing trace's packet header field type's metadata.");
1726 ret = bt_ctf_field_type_serialize_recursive(
1727 (void *) trace->common.packet_header_field_type,
1728 context);
1729 if (ret) {
1730 goto end;
1731 }
1732 context->current_indentation_level--;
1733 }
1734
1735 g_string_append(context->string, ";\n};\n\n");
1736end:
1737 return ret;
1738}
1739
1740static
1741void append_env_metadata(struct bt_ctf_trace *trace,
1742 struct metadata_context *context)
1743{
1744 int64_t i;
1745 int64_t env_size;
1746
16ca5ff0 1747 env_size = bt_ctf_attributes_get_count(trace->common.environment);
3dca2276
PP
1748 if (env_size <= 0) {
1749 return;
1750 }
1751
1752 g_string_append(context->string, "env {\n");
1753
1754 for (i = 0; i < env_size; i++) {
1755 struct bt_value *env_field_value_obj = NULL;
1756 const char *entry_name;
1757
16ca5ff0 1758 entry_name = bt_ctf_attributes_get_field_name(
3dca2276 1759 trace->common.environment, i);
16ca5ff0 1760 env_field_value_obj = bt_ctf_attributes_borrow_field_value(
3dca2276
PP
1761 trace->common.environment, i);
1762
1763 BT_ASSERT(entry_name);
1764 BT_ASSERT(env_field_value_obj);
1765
1766 switch (bt_value_get_type(env_field_value_obj)) {
1767 case BT_VALUE_TYPE_INTEGER:
1768 {
1769 int ret;
1770 int64_t int_value;
1771
1772 ret = bt_value_integer_get(env_field_value_obj,
1773 &int_value);
1774 BT_ASSERT(ret == 0);
1775 g_string_append_printf(context->string,
1776 "\t%s = %" PRId64 ";\n", entry_name,
1777 int_value);
1778 break;
1779 }
1780 case BT_VALUE_TYPE_STRING:
1781 {
1782 int ret;
1783 const char *str_value;
1784 char *escaped_str = NULL;
1785
1786 ret = bt_value_string_get(env_field_value_obj,
1787 &str_value);
1788 BT_ASSERT(ret == 0);
1789 escaped_str = g_strescape(str_value, NULL);
1790 if (!escaped_str) {
1791 BT_LOGE("Cannot escape string: string=\"%s\"",
1792 str_value);
094ff7c0 1793 continue;
3dca2276
PP
1794 }
1795
1796 g_string_append_printf(context->string,
1797 "\t%s = \"%s\";\n", entry_name, escaped_str);
1798 free(escaped_str);
1799 break;
1800 }
1801 default:
094ff7c0 1802 continue;
3dca2276 1803 }
3dca2276
PP
1804 }
1805
1806 g_string_append(context->string, "};\n\n");
1807}
1808
1809char *bt_ctf_trace_get_metadata_string(struct bt_ctf_trace *trace)
1810{
1811 char *metadata = NULL;
1812 struct metadata_context *context = NULL;
1813 int err = 0;
1814 size_t i;
1815
1816 if (!trace) {
1817 BT_LOGW_STR("Invalid parameter: trace is NULL.");
1818 goto end;
1819 }
1820
1821 context = g_new0(struct metadata_context, 1);
1822 if (!context) {
1823 BT_LOGE_STR("Failed to allocate one metadata context.");
1824 goto end;
1825 }
1826
1827 context->field_name = g_string_sized_new(DEFAULT_IDENTIFIER_SIZE);
1828 context->string = g_string_sized_new(DEFAULT_METADATA_STRING_SIZE);
1829 g_string_append(context->string, "/* CTF 1.8 */\n\n");
1830 if (append_trace_metadata(trace, context)) {
1831 /* append_trace_metadata() logs errors */
1832 goto error;
1833 }
1834 append_env_metadata(trace, context);
1835 g_ptr_array_foreach(trace->common.clock_classes,
1836 (GFunc) bt_ctf_clock_class_serialize, context);
1837
1838 for (i = 0; i < trace->common.stream_classes->len; i++) {
16ca5ff0 1839 /* bt_ctf_stream_class_serialize() logs details */
3dca2276
PP
1840 err = bt_ctf_stream_class_serialize(
1841 trace->common.stream_classes->pdata[i], context);
1842 if (err) {
16ca5ff0 1843 /* bt_ctf_stream_class_serialize() logs errors */
3dca2276
PP
1844 goto error;
1845 }
1846 }
1847
1848 metadata = context->string->str;
1849
1850error:
1851 g_string_free(context->string, err ? TRUE : FALSE);
1852 g_string_free(context->field_name, TRUE);
1853 g_free(context);
1854
1855end:
1856 return metadata;
1857}
1858
1859enum bt_ctf_byte_order bt_ctf_trace_get_native_byte_order(
1860 struct bt_ctf_trace *trace)
1861{
16ca5ff0 1862 return (int) bt_ctf_trace_common_get_native_byte_order(BT_CTF_TO_COMMON(trace));
3dca2276
PP
1863}
1864
1865int bt_ctf_trace_set_native_byte_order(struct bt_ctf_trace *trace,
1866 enum bt_ctf_byte_order byte_order)
1867{
16ca5ff0 1868 return bt_ctf_trace_common_set_native_byte_order(BT_CTF_TO_COMMON(trace),
3dca2276
PP
1869 (int) byte_order, false);
1870}
1871
1872struct bt_ctf_field_type *bt_ctf_trace_get_packet_header_field_type(
1873 struct bt_ctf_trace *trace)
1874{
16ca5ff0
PP
1875 return bt_get(bt_ctf_trace_common_borrow_packet_header_field_type(
1876 BT_CTF_TO_COMMON(trace)));
3dca2276
PP
1877}
1878
1879int bt_ctf_trace_set_packet_header_field_type(struct bt_ctf_trace *trace,
1880 struct bt_ctf_field_type *packet_header_type)
1881{
16ca5ff0 1882 return bt_ctf_trace_common_set_packet_header_field_type(BT_CTF_TO_COMMON(trace),
3dca2276
PP
1883 (void *) packet_header_type);
1884}
1885
1886const char *bt_ctf_trace_get_name(struct bt_ctf_trace *trace)
1887{
16ca5ff0 1888 return bt_ctf_trace_common_get_name(BT_CTF_TO_COMMON(trace));
3dca2276 1889}
This page took 0.097738 seconds and 4 git commands to generate.