Cleanup: Dead assignments
[babeltrace.git] / src / plugins / ctf / fs-sink / translate-trace-ir-to-ctf-ir.c
CommitLineData
46bdd3e0
PP
1/*
2 * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 */
22
85623cd6 23#define BT_COMP_LOG_SELF_COMP (ctx->self_comp)
a708ff31 24#define BT_LOG_OUTPUT_LEVEL (ctx->log_level)
b03487ab 25#define BT_LOG_TAG "PLUGIN/SINK.CTF.FS/TRANSLATE-TRACE-IR-TO-CTF-IR"
3fa1b6a3 26#include "logging/comp-logging.h"
46bdd3e0 27
71c5da58 28#include <babeltrace2/babeltrace.h>
85e7137b 29#include "common/macros.h"
57952005
MJ
30#include "common/common.h"
31#include "common/assert.h"
46bdd3e0
PP
32#include <stdio.h>
33#include <stdbool.h>
34#include <string.h>
35#include <glib.h>
36
85623cd6 37#include "fs-sink.h"
46bdd3e0
PP
38#include "fs-sink-ctf-meta.h"
39
40struct field_path_elem {
41 uint64_t index_in_parent;
42 GString *name;
43
44 /* Weak */
45 const bt_field_class *ir_fc;
46
47 /* Weak */
48 struct fs_sink_ctf_field_class *parent_fc;
49};
50
51struct ctx {
a708ff31 52 bt_logging_level log_level;
85623cd6 53 bt_self_component *self_comp;
a708ff31 54
46bdd3e0
PP
55 /* Weak */
56 struct fs_sink_ctf_stream_class *cur_sc;
57
58 /* Weak */
59 struct fs_sink_ctf_event_class *cur_ec;
60
056995e5 61 bt_field_path_scope cur_scope;
46bdd3e0
PP
62
63 /*
64 * Array of `struct field_path_elem` */
65 GArray *cur_path;
66};
67
68static inline
69struct field_path_elem *cur_path_stack_at(struct ctx *ctx, uint64_t i)
70{
71 BT_ASSERT(i < ctx->cur_path->len);
72 return &g_array_index(ctx->cur_path, struct field_path_elem, i);
73}
74
75static inline
76struct field_path_elem *cur_path_stack_top(struct ctx *ctx)
77{
78 BT_ASSERT(ctx->cur_path->len > 0);
79 return cur_path_stack_at(ctx, ctx->cur_path->len - 1);
80}
81
82static inline
83bool is_reserved_member_name(const char *name, const char *reserved_name)
84{
85 bool is_reserved = false;
86
87 if (strcmp(name, reserved_name) == 0) {
88 is_reserved = true;
89 goto end;
90 }
91
92 if (name[0] == '_' && strcmp(&name[1], reserved_name) == 0) {
93 is_reserved = true;
94 goto end;
95 }
96
97end:
98 return is_reserved;
99}
100
02b61fe0
PP
101static const char *reserved_tsdl_keywords[] = {
102 "align",
103 "callsite",
104 "const",
105 "char",
106 "clock",
107 "double",
108 "enum",
109 "env",
110 "event",
111 "floating_point",
112 "float",
113 "integer",
114 "int",
115 "long",
116 "short",
117 "signed",
118 "stream",
119 "string",
120 "struct",
121 "trace",
122 "typealias",
123 "typedef",
124 "unsigned",
125 "variant",
126 "void",
127 "_Bool",
128 "_Complex",
129 "_Imaginary",
130};
131
132static inline
133bool ist_valid_identifier(const char *name)
134{
135 const char *at;
136 uint64_t i;
137 bool ist_valid = true;
138
139 /* Make sure the name is not a reserved keyword */
140 for (i = 0; i < sizeof(reserved_tsdl_keywords) / sizeof(*reserved_tsdl_keywords);
141 i++) {
142 if (strcmp(name, reserved_tsdl_keywords[i]) == 0) {
143 ist_valid = false;
144 goto end;
145 }
146 }
147
148 /* Make sure the name is not an empty string */
149 if (strlen(name) == 0) {
150 ist_valid = false;
151 goto end;
152 }
153
154 /* Make sure the name starts with a letter or `_` */
155 if (!isalpha(name[0]) && name[0] != '_') {
156 ist_valid = false;
157 goto end;
158 }
159
160 /* Make sure the name only contains letters, digits, and `_` */
161 for (at = name; *at != '\0'; at++) {
162 if (!isalnum(*at) && *at != '_') {
163 ist_valid = false;
164 goto end;
165 }
166 }
167
168end:
169 return ist_valid;
170}
171
172static inline
173bool must_protect_identifier(const char *name)
174{
175 uint64_t i;
176 bool must_protect = false;
177
178 /* Protect a reserved keyword */
179 for (i = 0; i < sizeof(reserved_tsdl_keywords) / sizeof(*reserved_tsdl_keywords);
180 i++) {
181 if (strcmp(name, reserved_tsdl_keywords[i]) == 0) {
182 must_protect = true;
183 goto end;
184 }
185 }
186
187 /* Protect an identifier which already starts with `_` */
188 if (name[0] == '_') {
189 must_protect = true;
190 goto end;
191 }
192
193end:
194 return must_protect;
195}
196
46bdd3e0
PP
197static inline
198int cur_path_stack_push(struct ctx *ctx,
02b61fe0
PP
199 uint64_t index_in_parent, const char *name,
200 bool force_protect_name, const bt_field_class *ir_fc,
46bdd3e0
PP
201 struct fs_sink_ctf_field_class *parent_fc)
202{
203 int ret = 0;
204 struct field_path_elem *field_path_elem;
205
206 g_array_set_size(ctx->cur_path, ctx->cur_path->len + 1);
207 field_path_elem = cur_path_stack_top(ctx);
208 field_path_elem->index_in_parent = index_in_parent;
02b61fe0
PP
209 field_path_elem->name = g_string_new(NULL);
210
211 if (name) {
212 if (force_protect_name) {
213 g_string_assign(field_path_elem->name, "_");
214 }
215
216 g_string_append(field_path_elem->name, name);
46bdd3e0 217
056995e5 218 if (ctx->cur_scope == BT_FIELD_PATH_SCOPE_PACKET_CONTEXT) {
02b61fe0
PP
219 if (is_reserved_member_name(name, "packet_size") ||
220 is_reserved_member_name(name, "content_size") ||
221 is_reserved_member_name(name, "timestamp_begin") ||
222 is_reserved_member_name(name, "timestamp_end") ||
223 is_reserved_member_name(name, "events_discarded") ||
224 is_reserved_member_name(name, "packet_seq_num")) {
85623cd6 225 BT_COMP_LOGE("Unsupported reserved TSDL structure field class member "
46bdd3e0 226 "or variant field class option name: name=\"%s\"",
02b61fe0 227 name);
46bdd3e0
PP
228 ret = -1;
229 goto end;
230 }
231 }
232
02b61fe0
PP
233 if (!ist_valid_identifier(field_path_elem->name->str)) {
234 ret = -1;
85623cd6 235 BT_COMP_LOGE("Unsupported non-TSDL structure field class member "
46bdd3e0 236 "or variant field class option name: name=\"%s\"",
02b61fe0 237 field_path_elem->name->str);
46bdd3e0
PP
238 goto end;
239 }
240 }
241
242 field_path_elem->ir_fc = ir_fc;
243 field_path_elem->parent_fc = parent_fc;
244
245end:
246 return ret;
247}
248
249static inline
250void cur_path_stack_pop(struct ctx *ctx)
251{
252 struct field_path_elem *field_path_elem;
253
254 BT_ASSERT(ctx->cur_path->len > 0);
255 field_path_elem = cur_path_stack_top(ctx);
256
257 if (field_path_elem->name) {
258 g_string_free(field_path_elem->name, TRUE);
259 field_path_elem->name = NULL;
260 }
261
262 g_array_set_size(ctx->cur_path, ctx->cur_path->len - 1);
263}
264
46bdd3e0
PP
265/*
266 * Creates a relative field ref (a single name) from IR field path
267 * `tgt_ir_field_path`.
268 *
269 * This function tries to locate the target field class recursively from
270 * the top to the bottom of the context's current path using only the
271 * target field class's own name. This is because many CTF reading tools
272 * do not support a relative field ref with more than one element, for
273 * example `prev_struct.len`.
274 *
275 * Returns a negative value if this resolving operation failed.
276 */
277static
278int create_relative_field_ref(struct ctx *ctx,
02b61fe0
PP
279 const bt_field_path *tgt_ir_field_path, GString *tgt_field_ref,
280 struct fs_sink_ctf_field_class **user_tgt_fc)
46bdd3e0
PP
281{
282 int ret = 0;
283 struct fs_sink_ctf_field_class *tgt_fc = NULL;
284 uint64_t i;
285 int64_t si;
08cdcf01 286 const char *tgt_fc_name = NULL;
46bdd3e0
PP
287 struct field_path_elem *field_path_elem;
288
289 /* Get target field class's name */
290 switch (bt_field_path_get_root_scope(tgt_ir_field_path)) {
056995e5 291 case BT_FIELD_PATH_SCOPE_PACKET_CONTEXT:
46bdd3e0
PP
292 BT_ASSERT(ctx->cur_sc);
293 tgt_fc = ctx->cur_sc->packet_context_fc;
294 break;
056995e5 295 case BT_FIELD_PATH_SCOPE_EVENT_COMMON_CONTEXT:
46bdd3e0
PP
296 BT_ASSERT(ctx->cur_sc);
297 tgt_fc = ctx->cur_sc->event_common_context_fc;
298 break;
056995e5 299 case BT_FIELD_PATH_SCOPE_EVENT_SPECIFIC_CONTEXT:
46bdd3e0
PP
300 BT_ASSERT(ctx->cur_ec);
301 tgt_fc = ctx->cur_ec->spec_context_fc;
302 break;
056995e5 303 case BT_FIELD_PATH_SCOPE_EVENT_PAYLOAD:
46bdd3e0
PP
304 BT_ASSERT(ctx->cur_ec);
305 tgt_fc = ctx->cur_ec->payload_fc;
306 break;
307 default:
308 abort();
309 }
310
311 i = 0;
312
1d4afa5f
PP
313 while (i < bt_field_path_get_item_count(tgt_ir_field_path)) {
314 const bt_field_path_item *fp_item =
315 bt_field_path_borrow_item_by_index_const(
316 tgt_ir_field_path, i);
46bdd3e0
PP
317 struct fs_sink_ctf_named_field_class *named_fc = NULL;
318
319 BT_ASSERT(tgt_fc);
1d4afa5f 320 BT_ASSERT(fp_item);
46bdd3e0 321
c27259b0
PP
322 if (bt_field_path_item_get_type(fp_item) ==
323 BT_FIELD_PATH_ITEM_TYPE_CURRENT_OPTION_CONTENT) {
324 /* Not supported by CTF 1.8 */
325 ret = -1;
326 goto end;
327 }
328
567cee92 329 switch (tgt_fc->type) {
46bdd3e0 330 case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT:
1d4afa5f
PP
331 BT_ASSERT(bt_field_path_item_get_type(fp_item) ==
332 BT_FIELD_PATH_ITEM_TYPE_INDEX);
46bdd3e0 333 named_fc = fs_sink_ctf_field_class_struct_borrow_member_by_index(
1d4afa5f
PP
334 (void *) tgt_fc,
335 bt_field_path_item_index_get_index(fp_item));
46bdd3e0
PP
336 break;
337 case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT:
1d4afa5f
PP
338 BT_ASSERT(bt_field_path_item_get_type(fp_item) ==
339 BT_FIELD_PATH_ITEM_TYPE_INDEX);
46bdd3e0 340 named_fc = fs_sink_ctf_field_class_variant_borrow_option_by_index(
1d4afa5f
PP
341 (void *) tgt_fc,
342 bt_field_path_item_index_get_index(fp_item));
46bdd3e0
PP
343 break;
344 case FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY:
345 case FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE:
346 {
347 struct fs_sink_ctf_field_class_array_base *array_base_fc =
348 (void *) tgt_fc;
349
1d4afa5f
PP
350 BT_ASSERT(bt_field_path_item_get_type(fp_item) ==
351 BT_FIELD_PATH_ITEM_TYPE_CURRENT_ARRAY_ELEMENT);
46bdd3e0
PP
352 tgt_fc = array_base_fc->elem_fc;
353 break;
354 }
355 default:
356 abort();
357 }
358
359 if (named_fc) {
360 tgt_fc = named_fc->fc;
361 tgt_fc_name = named_fc->name->str;
362 i++;
363 }
364 }
365
366 BT_ASSERT(tgt_fc);
567cee92 367 BT_ASSERT(tgt_fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_INT);
46bdd3e0
PP
368 BT_ASSERT(tgt_fc_name);
369
370 /* Find target field class having this name in current context */
371 for (si = ctx->cur_path->len - 1; si >= 0; si--) {
372 struct fs_sink_ctf_field_class *fc;
ce706c97
JG
373 struct fs_sink_ctf_field_class_struct *struct_fc = NULL;
374 struct fs_sink_ctf_field_class_variant *var_fc = NULL;
46bdd3e0
PP
375 struct fs_sink_ctf_named_field_class *named_fc;
376 uint64_t len;
377
378 field_path_elem = cur_path_stack_at(ctx, (uint64_t) si);
379 fc = field_path_elem->parent_fc;
380 if (!fc) {
381 /* Reached stack's bottom */
382 ret = -1;
383 goto end;
384 }
385
386 switch (fc->type) {
387 case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT:
388 case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT:
389 break;
390 case FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY:
391 case FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE:
392 continue;
393 default:
394 /* Not supported by TSDL 1.8 */
395 ret = -1;
396 goto end;
397 }
398
399 if (fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT) {
400 struct_fc = (void *) fc;
401 len = struct_fc->members->len;
402 } else {
403 var_fc = (void *) fc;
404 len = var_fc->options->len;
405 }
406
407 for (i = 0; i < len; i++) {
408 if (fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT) {
409 named_fc = fs_sink_ctf_field_class_struct_borrow_member_by_index(
410 struct_fc, i);
411 } else {
412 named_fc = fs_sink_ctf_field_class_variant_borrow_option_by_index(
413 var_fc, i);
414 }
415
416 if (strcmp(named_fc->name->str, tgt_fc_name) == 0) {
417 if (named_fc->fc == tgt_fc) {
418 g_string_assign(tgt_field_ref,
419 tgt_fc_name);
02b61fe0
PP
420
421 if (user_tgt_fc) {
422 *user_tgt_fc = tgt_fc;
423 }
46bdd3e0
PP
424 } else {
425 /*
426 * Using only the target field
427 * class's name, we're not
428 * reaching the target field
429 * class. This is not supported
430 * by TSDL 1.8.
431 */
432 ret = -1;
433 }
434
435 goto end;
436 }
437 }
438 }
439
440end:
441 return ret;
442}
443
444/*
445 * Creates an absolute field ref from IR field path `tgt_ir_field_path`.
446 *
447 * Returns a negative value if this resolving operation failed.
448 */
449static
450int create_absolute_field_ref(struct ctx *ctx,
02b61fe0
PP
451 const bt_field_path *tgt_ir_field_path, GString *tgt_field_ref,
452 struct fs_sink_ctf_field_class **user_tgt_fc)
46bdd3e0
PP
453{
454 int ret = 0;
455 struct fs_sink_ctf_field_class *fc = NULL;
456 uint64_t i;
457
458 switch (bt_field_path_get_root_scope(tgt_ir_field_path)) {
056995e5 459 case BT_FIELD_PATH_SCOPE_PACKET_CONTEXT:
46bdd3e0
PP
460 BT_ASSERT(ctx->cur_sc);
461 fc = ctx->cur_sc->packet_context_fc;
462 g_string_assign(tgt_field_ref, "stream.packet.context");
463 break;
056995e5 464 case BT_FIELD_PATH_SCOPE_EVENT_COMMON_CONTEXT:
46bdd3e0
PP
465 BT_ASSERT(ctx->cur_sc);
466 fc = ctx->cur_sc->event_common_context_fc;
467 g_string_assign(tgt_field_ref, "stream.event.context");
468 break;
056995e5 469 case BT_FIELD_PATH_SCOPE_EVENT_SPECIFIC_CONTEXT:
46bdd3e0
PP
470 BT_ASSERT(ctx->cur_ec);
471 fc = ctx->cur_ec->spec_context_fc;
472 g_string_assign(tgt_field_ref, "event.context");
473 break;
056995e5 474 case BT_FIELD_PATH_SCOPE_EVENT_PAYLOAD:
46bdd3e0
PP
475 BT_ASSERT(ctx->cur_ec);
476 fc = ctx->cur_ec->payload_fc;
477 g_string_assign(tgt_field_ref, "event.fields");
478 break;
479 default:
480 abort();
481 }
482
483 BT_ASSERT(fc);
484
1d4afa5f
PP
485 for (i = 0; i < bt_field_path_get_item_count(tgt_ir_field_path); i++) {
486 const bt_field_path_item *fp_item =
487 bt_field_path_borrow_item_by_index_const(
488 tgt_ir_field_path, i);
46bdd3e0
PP
489 struct fs_sink_ctf_named_field_class *named_fc = NULL;
490
1d4afa5f
PP
491 if (bt_field_path_item_get_type(fp_item) !=
492 BT_FIELD_PATH_ITEM_TYPE_INDEX) {
493 /* Not supported by TSDL 1.8 */
494 ret = -1;
495 goto end;
496 }
497
46bdd3e0
PP
498 switch (fc->type) {
499 case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT:
1d4afa5f
PP
500 BT_ASSERT(bt_field_path_item_get_type(fp_item) ==
501 BT_FIELD_PATH_ITEM_TYPE_INDEX);
46bdd3e0 502 named_fc = fs_sink_ctf_field_class_struct_borrow_member_by_index(
1d4afa5f
PP
503 (void *) fc,
504 bt_field_path_item_index_get_index(fp_item));
46bdd3e0
PP
505 break;
506 case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT:
1d4afa5f
PP
507 BT_ASSERT(bt_field_path_item_get_type(fp_item) ==
508 BT_FIELD_PATH_ITEM_TYPE_INDEX);
46bdd3e0 509 named_fc = fs_sink_ctf_field_class_variant_borrow_option_by_index(
1d4afa5f
PP
510 (void *) fc,
511 bt_field_path_item_index_get_index(fp_item));
46bdd3e0 512 break;
46bdd3e0
PP
513 default:
514 abort();
515 }
516
517 BT_ASSERT(named_fc);
518 g_string_append_c(tgt_field_ref, '.');
519 g_string_append(tgt_field_ref, named_fc->name->str);
520 fc = named_fc->fc;
521 }
522
02b61fe0
PP
523 if (user_tgt_fc) {
524 *user_tgt_fc = fc;
525 }
526
46bdd3e0
PP
527end:
528 return ret;
529}
530
531/*
532 * Resolves a target field class located at `tgt_ir_field_path`, writing
533 * the resolved field ref to `tgt_field_ref` and setting
534 * `*create_before` according to whether or not the target field must be
535 * created immediately before (in which case `tgt_field_ref` is
536 * irrelevant).
537 */
538static
539void resolve_field_class(struct ctx *ctx,
540 const bt_field_path *tgt_ir_field_path,
02b61fe0
PP
541 GString *tgt_field_ref, bool *create_before,
542 struct fs_sink_ctf_field_class **user_tgt_fc)
46bdd3e0
PP
543{
544 int ret;
056995e5 545 bt_field_path_scope tgt_scope;
46bdd3e0
PP
546
547 *create_before = false;
548
549 if (!tgt_ir_field_path) {
550 *create_before = true;
551 goto end;
552 }
553
554 tgt_scope = bt_field_path_get_root_scope(tgt_ir_field_path);
555
556 if (tgt_scope == ctx->cur_scope) {
557 /*
558 * Try, in this order:
559 *
560 * 1. Use a relative path, using only the target field
561 * class's name. This is what is the most commonly
562 * supported by popular CTF reading tools.
563 *
564 * 2. Use an absolute path. This could fail if there's
565 * an array field class from the current root's field
566 * class to the target field class.
567 *
568 * 3. Create the target field class before the
569 * requesting field class (fallback).
570 */
571 ret = create_relative_field_ref(ctx, tgt_ir_field_path,
02b61fe0 572 tgt_field_ref, user_tgt_fc);
46bdd3e0
PP
573 if (ret) {
574 ret = create_absolute_field_ref(ctx, tgt_ir_field_path,
02b61fe0 575 tgt_field_ref, user_tgt_fc);
46bdd3e0
PP
576 if (ret) {
577 *create_before = true;
46bdd3e0
PP
578 goto end;
579 }
580 }
581 } else {
582 ret = create_absolute_field_ref(ctx, tgt_ir_field_path,
02b61fe0 583 tgt_field_ref, user_tgt_fc);
46bdd3e0
PP
584
585 /* It must always work in previous scopes */
586 BT_ASSERT(ret == 0);
587 }
588
589end:
590 return;
591}
592
593static
594int translate_field_class(struct ctx *ctx);
595
596static inline
597void append_to_parent_field_class(struct ctx *ctx,
598 struct fs_sink_ctf_field_class *fc)
599{
600 struct fs_sink_ctf_field_class *parent_fc =
601 cur_path_stack_top(ctx)->parent_fc;
602
603 switch (parent_fc->type) {
604 case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT:
605 fs_sink_ctf_field_class_struct_append_member((void *) parent_fc,
606 cur_path_stack_top(ctx)->name->str, fc);
607 break;
c27259b0
PP
608 case FS_SINK_CTF_FIELD_CLASS_TYPE_OPTION:
609 {
610 struct fs_sink_ctf_field_class_option *opt_fc =
611 (void *) parent_fc;
612
613 BT_ASSERT(!opt_fc->content_fc);
614 opt_fc->content_fc = fc;
615 opt_fc->base.alignment = fc->alignment;
616 break;
617 }
46bdd3e0
PP
618 case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT:
619 fs_sink_ctf_field_class_variant_append_option((void *) parent_fc,
620 cur_path_stack_top(ctx)->name->str, fc);
621 break;
622 case FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY:
623 case FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE:
624 {
625 struct fs_sink_ctf_field_class_array_base *array_base_fc =
626 (void *) parent_fc;
627
628 BT_ASSERT(!array_base_fc->elem_fc);
629 array_base_fc->elem_fc = fc;
630 array_base_fc->base.alignment = fc->alignment;
631 break;
632 }
633 default:
634 abort();
635 }
636}
637
638static inline
639void update_parent_field_class_alignment(struct ctx *ctx,
640 unsigned int alignment)
641{
642 struct fs_sink_ctf_field_class *parent_fc =
643 cur_path_stack_top(ctx)->parent_fc;
644
645 switch (parent_fc->type) {
646 case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT:
647 fs_sink_ctf_field_class_struct_align_at_least(
648 (void *) parent_fc, alignment);
649 break;
650 case FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY:
651 case FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE:
652 {
653 struct fs_sink_ctf_field_class_array_base *array_base_fc =
654 (void *) parent_fc;
655
656 array_base_fc->base.alignment = alignment;
657 break;
658 }
659 default:
660 break;
661 }
662}
663
664static inline
665int translate_structure_field_class_members(struct ctx *ctx,
666 struct fs_sink_ctf_field_class_struct *struct_fc,
667 const bt_field_class *ir_fc)
668{
669 int ret = 0;
670 uint64_t i;
671
672 for (i = 0; i < bt_field_class_structure_get_member_count(ir_fc); i++) {
673 const bt_field_class_structure_member *member;
674 const char *name;
675 const bt_field_class *memb_ir_fc;
676
677 member =
678 bt_field_class_structure_borrow_member_by_index_const(
679 ir_fc, i);
680 name = bt_field_class_structure_member_get_name(member);
681 memb_ir_fc = bt_field_class_structure_member_borrow_field_class_const(
682 member);
02b61fe0 683 ret = cur_path_stack_push(ctx, i, name, true, memb_ir_fc,
46bdd3e0
PP
684 (void *) struct_fc);
685 if (ret) {
85623cd6 686 BT_COMP_LOGE("Cannot translate structure field class member: "
46bdd3e0
PP
687 "name=\"%s\"", name);
688 goto end;
689 }
690
691 ret = translate_field_class(ctx);
692 if (ret) {
85623cd6 693 BT_COMP_LOGE("Cannot translate structure field class member: "
46bdd3e0
PP
694 "name=\"%s\"", name);
695 goto end;
696 }
697
698 cur_path_stack_pop(ctx);
699 }
700
701end:
702 return ret;
703}
704
705static inline
706int translate_structure_field_class(struct ctx *ctx)
707{
708 int ret;
709 struct fs_sink_ctf_field_class_struct *fc =
710 fs_sink_ctf_field_class_struct_create_empty(
711 cur_path_stack_top(ctx)->ir_fc,
712 cur_path_stack_top(ctx)->index_in_parent);
713
714 BT_ASSERT(fc);
715 append_to_parent_field_class(ctx, (void *) fc);
716 ret = translate_structure_field_class_members(ctx, fc, fc->base.ir_fc);
717 if (ret) {
718 goto end;
719 }
720
721 update_parent_field_class_alignment(ctx, fc->base.alignment);
722
723end:
724 return ret;
725}
726
02b61fe0 727/*
2388fb87
PP
728 * This function protects a given variant FC option name (with the `_`
729 * prefix) if required. On success, `name_buf->str` contains the variant
730 * FC option name to use (original option name or protected if
731 * required).
02b61fe0
PP
732 *
733 * One of the goals of `sink.ctf.fs` is to write a CTF trace which is as
734 * close as possible to an original CTF trace as decoded by
735 * `src.ctf.fs`.
736 *
737 * This scenario is valid in CTF 1.8:
738 *
739 * enum {
740 * HELLO,
741 * MEOW
742 * } tag;
743 *
744 * variant <tag> {
745 * int HELLO;
746 * string MEOW;
747 * };
748 *
749 * Once in trace IR, the enumeration FC mapping names and variant FC
750 * option names are kept as is. For this reason, we don't want to
751 * protect the variant FC option names here (by prepending `_`): this
752 * would make the variant FC option name and the enumeration FC mapping
753 * name not match.
754 *
755 * This scenario is also valid in CTF 1.8:
756 *
757 * enum {
758 * _HELLO,
759 * MEOW
760 * } tag;
761 *
762 * variant <tag> {
763 * int _HELLO;
764 * string MEOW;
765 * };
766 *
767 * Once in trace IR, the enumeration FC mapping names are kept as is,
768 * but the `_HELLO` variant FC option name becomes `HELLO` (unprotected
769 * for presentation, as recommended by CTF 1.8). When going back to
770 * TSDL, we need to protect `HELLO` so that it becomes `_HELLO` to match
771 * the corresponding enumeration FC mapping name.
772 *
773 * This scenario is also valid in CTF 1.8:
774 *
775 * enum {
776 * __HELLO,
777 * MEOW
778 * } tag;
779 *
780 * variant <tag> {
781 * int __HELLO;
782 * string MEOW;
783 * };
784 *
785 * Once in trace IR, the enumeration FC mapping names are kept as is,
786 * but the `__HELLO` variant FC option name becomes `_HELLO`
787 * (unprotected). When going back to TSDL, we need to protect `_HELLO`
788 * so that it becomes `__HELLO` to match the corresponding enumeration
789 * FC mapping name.
790 *
791 * `src.ctf.fs` always uses the _same_ integer range sets for a selector
792 * FC mapping and a corresponding variant FC option. We can use that
793 * fact to find the original variant FC option names by matching variant
794 * FC options and enumeration FC mappings by range set.
795 */
796static
2388fb87 797int maybe_protect_variant_option_name(const bt_field_class *ir_var_fc,
02b61fe0 798 const bt_field_class *ir_tag_fc, uint64_t opt_i,
2388fb87 799 GString *name_buf)
02b61fe0
PP
800{
801 int ret = 0;
802 uint64_t i;
803 bt_field_class_type ir_var_fc_type;
804 const void *opt_ranges = NULL;
805 const char *mapping_label = NULL;
806 const char *ir_opt_name;
807 const bt_field_class_variant_option *base_var_opt;
808 bool force_protect = false;
809
02b61fe0
PP
810 ir_var_fc_type = bt_field_class_get_type(ir_var_fc);
811 base_var_opt = bt_field_class_variant_borrow_option_by_index_const(
812 ir_var_fc, opt_i);
813 BT_ASSERT(base_var_opt);
814 ir_opt_name = bt_field_class_variant_option_get_name(base_var_opt);
815 BT_ASSERT(ir_opt_name);
816
817 /*
818 * Check if the variant FC option name is required to be
819 * protected (reserved TSDL keyword or starts with `_`). In that
820 * case, the name of the selector FC mapping we find must match
821 * exactly the protected name.
822 */
823 force_protect = must_protect_identifier(ir_opt_name);
824 if (force_protect) {
02b61fe0
PP
825 g_string_assign(name_buf, "_");
826 g_string_append(name_buf, ir_opt_name);
827 } else {
828 g_string_assign(name_buf, ir_opt_name);
829 }
830
831 /* Borrow option's ranges */
8bc207af 832 if (ir_var_fc_type == BT_FIELD_CLASS_TYPE_VARIANT_WITHOUT_SELECTOR_FIELD) {
02b61fe0
PP
833 /* No ranges: we're done */
834 goto end;
8bc207af
PP
835 } if (ir_var_fc_type == BT_FIELD_CLASS_TYPE_VARIANT_WITH_UNSIGNED_INTEGER_SELECTOR_FIELD) {
836 const bt_field_class_variant_with_selector_field_integer_unsigned_option *var_opt =
837 bt_field_class_variant_with_selector_field_integer_unsigned_borrow_option_by_index_const(
02b61fe0
PP
838 ir_var_fc, opt_i);
839 opt_ranges =
8bc207af 840 bt_field_class_variant_with_selector_field_integer_unsigned_option_borrow_ranges_const(
02b61fe0
PP
841 var_opt);
842 } else {
8bc207af
PP
843 const bt_field_class_variant_with_selector_field_integer_signed_option *var_opt =
844 bt_field_class_variant_with_selector_field_integer_signed_borrow_option_by_index_const(
02b61fe0
PP
845 ir_var_fc, opt_i);
846 opt_ranges =
8bc207af 847 bt_field_class_variant_with_selector_field_integer_signed_option_borrow_ranges_const(
02b61fe0
PP
848 var_opt);
849 }
850
851 /* Find corresponding mapping by range set in selector FC */
852 for (i = 0; i < bt_field_class_enumeration_get_mapping_count(ir_tag_fc);
853 i++) {
8bc207af 854 if (ir_var_fc_type == BT_FIELD_CLASS_TYPE_VARIANT_WITH_UNSIGNED_INTEGER_SELECTOR_FIELD) {
02b61fe0 855 const bt_field_class_enumeration_mapping *mapping_base;
60bbfc7c 856 const bt_field_class_enumeration_unsigned_mapping *mapping;
02b61fe0
PP
857 const bt_integer_range_set_unsigned *mapping_ranges;
858
60bbfc7c 859 mapping = bt_field_class_enumeration_unsigned_borrow_mapping_by_index_const(
02b61fe0 860 ir_tag_fc, i);
60bbfc7c 861 mapping_ranges = bt_field_class_enumeration_unsigned_mapping_borrow_ranges_const(
02b61fe0
PP
862 mapping);
863
e1c423f9 864 if (bt_integer_range_set_unsigned_is_equal(opt_ranges,
02b61fe0
PP
865 mapping_ranges)) {
866 /* We have a winner */
867 mapping_base =
60bbfc7c 868 bt_field_class_enumeration_unsigned_mapping_as_mapping_const(
02b61fe0
PP
869 mapping);
870 mapping_label =
871 bt_field_class_enumeration_mapping_get_label(
872 mapping_base);
873 break;
874 }
875 } else {
876 const bt_field_class_enumeration_mapping *mapping_base;
60bbfc7c 877 const bt_field_class_enumeration_signed_mapping *mapping;
02b61fe0
PP
878 const bt_integer_range_set_signed *mapping_ranges;
879
60bbfc7c 880 mapping = bt_field_class_enumeration_signed_borrow_mapping_by_index_const(
02b61fe0 881 ir_tag_fc, i);
60bbfc7c 882 mapping_ranges = bt_field_class_enumeration_signed_mapping_borrow_ranges_const(
02b61fe0
PP
883 mapping);
884
e1c423f9 885 if (bt_integer_range_set_signed_is_equal(opt_ranges,
02b61fe0
PP
886 mapping_ranges)) {
887 /* We have a winner */
888 mapping_base =
60bbfc7c 889 bt_field_class_enumeration_signed_mapping_as_mapping_const(
02b61fe0
PP
890 mapping);
891 mapping_label =
892 bt_field_class_enumeration_mapping_get_label(
893 mapping_base);
894 break;
895 }
896 }
897 }
898
899 if (!mapping_label) {
900 /* Range set not found: invalid selector for CTF 1.8 */
901 ret = -1;
902 goto end;
903 }
904
905 /*
906 * If the enumeration FC mapping name is not the same as the
907 * variant FC option name and we didn't protect already, try
908 * protecting the option name and check again.
909 */
910 if (strcmp(mapping_label, name_buf->str) != 0) {
911 if (force_protect) {
912 ret = -1;
913 goto end;
914 }
915
916 if (mapping_label[0] == '\0') {
917 ret = -1;
918 goto end;
919 }
920
921 g_string_assign(name_buf, "_");
922 g_string_append(name_buf, ir_opt_name);
923
924 if (strcmp(mapping_label, name_buf->str) != 0) {
925 ret = -1;
926 goto end;
927 }
02b61fe0
PP
928 }
929
930end:
931 return ret;
932}
933
c27259b0
PP
934static inline
935int translate_option_field_class(struct ctx *ctx)
936{
937 struct fs_sink_ctf_field_class_option *fc =
938 fs_sink_ctf_field_class_option_create_empty(
939 cur_path_stack_top(ctx)->ir_fc,
940 cur_path_stack_top(ctx)->index_in_parent);
941 const bt_field_class *content_ir_fc =
942 bt_field_class_option_borrow_field_class_const(fc->base.ir_fc);
943 int ret;
944
945 BT_ASSERT(fc);
946
947 /*
948 * CTF 1.8 does not support the option field class type. To
949 * write something anyway, this component translates this type
950 * to a variant field class where the options are:
951 *
952 * * An empty structure field class.
953 * * The optional field class itself.
954 *
955 * The "tag" is always generated/before in that case (an 8-bit
956 * unsigned enumeration field class).
957 */
958 append_to_parent_field_class(ctx, (void *) fc);
959 ret = cur_path_stack_push(ctx, UINT64_C(-1), NULL, false, content_ir_fc,
960 (void *) fc);
961 if (ret) {
962 BT_COMP_LOGE_STR("Cannot translate option field class content.");
963 goto end;
964 }
965
966 ret = translate_field_class(ctx);
967 if (ret) {
968 BT_COMP_LOGE_STR("Cannot translate option field class content.");
969 goto end;
970 }
971
972 cur_path_stack_pop(ctx);
973 update_parent_field_class_alignment(ctx, fc->base.alignment);
974
975end:
976 return ret;
977}
978
46bdd3e0
PP
979static inline
980int translate_variant_field_class(struct ctx *ctx)
981{
982 int ret = 0;
983 uint64_t i;
984 struct fs_sink_ctf_field_class_variant *fc =
985 fs_sink_ctf_field_class_variant_create_empty(
986 cur_path_stack_top(ctx)->ir_fc,
987 cur_path_stack_top(ctx)->index_in_parent);
02b61fe0
PP
988 bt_field_class_type ir_fc_type;
989 const bt_field_path *ir_selector_field_path = NULL;
990 struct fs_sink_ctf_field_class *tgt_fc = NULL;
991 GString *name_buf = g_string_new(NULL);
992 bt_value *prot_opt_names = bt_value_array_create();
993 uint64_t opt_count;
46bdd3e0
PP
994
995 BT_ASSERT(fc);
02b61fe0
PP
996 BT_ASSERT(name_buf);
997 BT_ASSERT(prot_opt_names);
998 ir_fc_type = bt_field_class_get_type(fc->base.ir_fc);
999 opt_count = bt_field_class_variant_get_option_count(fc->base.ir_fc);
1000
94847783
PP
1001 if (bt_field_class_type_is(ir_fc_type,
1002 BT_FIELD_CLASS_TYPE_VARIANT_WITH_SELECTOR_FIELD)) {
8bc207af 1003 ir_selector_field_path = bt_field_class_variant_with_selector_field_borrow_selector_field_path_const(
02b61fe0
PP
1004 fc->base.ir_fc);
1005 BT_ASSERT(ir_selector_field_path);
1006 }
46bdd3e0
PP
1007
1008 /* Resolve tag field class before appending to parent */
02b61fe0
PP
1009 resolve_field_class(ctx, ir_selector_field_path, fc->tag_ref,
1010 &fc->tag_is_before, &tgt_fc);
1011
1012 if (ir_selector_field_path && tgt_fc) {
1013 uint64_t mapping_count;
1014 uint64_t option_count;
1015
1016 /* CTF 1.8: selector FC must be an enumeration FC */
1017 bt_field_class_type type = bt_field_class_get_type(
1018 tgt_fc->ir_fc);
1019
94847783
PP
1020 if (!bt_field_class_type_is(type,
1021 BT_FIELD_CLASS_TYPE_ENUMERATION)) {
02b61fe0
PP
1022 fc->tag_is_before = true;
1023 goto validate_opts;
1024 }
1025
1026 /*
2388fb87 1027 * Call maybe_protect_variant_option_name() for each
02b61fe0
PP
1028 * option below. In that case we also want selector FC
1029 * to contain as many mappings as the variant FC has
1030 * options.
1031 */
1032 mapping_count = bt_field_class_enumeration_get_mapping_count(
1033 tgt_fc->ir_fc);
1034 option_count = bt_field_class_variant_get_option_count(
1035 fc->base.ir_fc);
1036
1037 if (mapping_count != option_count) {
1038 fc->tag_is_before = true;
1039 goto validate_opts;
1040 }
1041 } else {
1042 /*
1043 * No compatible selector field class for CTF 1.8:
1044 * create the appropriate selector field class.
1045 */
1046 fc->tag_is_before = true;
2388fb87 1047 goto validate_opts;
02b61fe0
PP
1048 }
1049
1050validate_opts:
1051 /*
1052 * First pass: detect any option name clash with option name
1053 * protection. In that case, we don't fail: just create the
1054 * selector field class before the variant field class.
1055 *
1056 * After this, `prot_opt_names` contains the final option names,
1057 * potentially protected if needed. They can still be invalid
1058 * TSDL identifiers however; this will be checked by
1059 * cur_path_stack_push().
1060 */
1061 for (i = 0; i < opt_count; i++) {
2388fb87
PP
1062 if (!fc->tag_is_before) {
1063 BT_ASSERT(tgt_fc->ir_fc);
1064 ret = maybe_protect_variant_option_name(fc->base.ir_fc,
1065 tgt_fc->ir_fc, i, name_buf);
1066 if (ret) {
1067 fc->tag_is_before = true;
1068 }
02b61fe0
PP
1069 }
1070
1071 ret = bt_value_array_append_string_element(prot_opt_names,
1072 name_buf->str);
1073 if (ret) {
1074 goto end;
1075 }
1076 }
1077
1078 for (i = 0; i < opt_count; i++) {
1079 uint64_t j;
1080 const bt_value *opt_name_a =
1081 bt_value_array_borrow_element_by_index_const(
1082 prot_opt_names, i);
1083
1084 for (j = 0; j < opt_count; j++) {
1085 const bt_value *opt_name_b;
1086
1087 if (i == j) {
1088 continue;
1089 }
46bdd3e0 1090
02b61fe0
PP
1091 opt_name_b =
1092 bt_value_array_borrow_element_by_index_const(
1093 prot_opt_names, j);
e1c423f9 1094 if (bt_value_is_equal(opt_name_a, opt_name_b)) {
02b61fe0
PP
1095 /*
1096 * Variant FC option names are not
1097 * unique when protected.
1098 */
1099 fc->tag_is_before = true;
1100 goto append_to_parent;
1101 }
1102 }
1103 }
1104
1105append_to_parent:
46bdd3e0
PP
1106 append_to_parent_field_class(ctx, (void *) fc);
1107
02b61fe0 1108 for (i = 0; i < opt_count; i++) {
46bdd3e0 1109 const bt_field_class_variant_option *opt;
46bdd3e0 1110 const bt_field_class *opt_ir_fc;
02b61fe0
PP
1111 const bt_value *prot_opt_name_val =
1112 bt_value_array_borrow_element_by_index_const(
1113 prot_opt_names, i);
1114 const char *prot_opt_name = bt_value_string_get(
1115 prot_opt_name_val);
46bdd3e0 1116
02b61fe0 1117 BT_ASSERT(prot_opt_name);
46bdd3e0
PP
1118 opt = bt_field_class_variant_borrow_option_by_index_const(
1119 fc->base.ir_fc, i);
46bdd3e0
PP
1120 opt_ir_fc = bt_field_class_variant_option_borrow_field_class_const(
1121 opt);
02b61fe0
PP
1122
1123 /*
1124 * We don't ask cur_path_stack_push() to protect the
1125 * option name because it's already protected at this
1126 * point.
1127 */
1128 ret = cur_path_stack_push(ctx, i, prot_opt_name, false,
1129 opt_ir_fc, (void *) fc);
46bdd3e0 1130 if (ret) {
85623cd6 1131 BT_COMP_LOGE("Cannot translate variant field class option: "
02b61fe0 1132 "name=\"%s\"", prot_opt_name);
46bdd3e0
PP
1133 goto end;
1134 }
1135
1136 ret = translate_field_class(ctx);
1137 if (ret) {
85623cd6 1138 BT_COMP_LOGE("Cannot translate variant field class option: "
02b61fe0 1139 "name=\"%s\"", prot_opt_name);
46bdd3e0
PP
1140 goto end;
1141 }
1142
1143 cur_path_stack_pop(ctx);
1144 }
1145
1146end:
02b61fe0
PP
1147 if (name_buf) {
1148 g_string_free(name_buf, TRUE);
1149 }
1150
1151 bt_value_put_ref(prot_opt_names);
46bdd3e0
PP
1152 return ret;
1153}
1154
1155static inline
1156int translate_static_array_field_class(struct ctx *ctx)
1157{
1158 struct fs_sink_ctf_field_class_array *fc =
1159 fs_sink_ctf_field_class_array_create_empty(
1160 cur_path_stack_top(ctx)->ir_fc,
1161 cur_path_stack_top(ctx)->index_in_parent);
1162 const bt_field_class *elem_ir_fc =
1163 bt_field_class_array_borrow_element_field_class_const(
1164 fc->base.base.ir_fc);
1165 int ret;
1166
1167 BT_ASSERT(fc);
1168 append_to_parent_field_class(ctx, (void *) fc);
02b61fe0 1169 ret = cur_path_stack_push(ctx, UINT64_C(-1), NULL, false, elem_ir_fc,
46bdd3e0
PP
1170 (void *) fc);
1171 if (ret) {
85623cd6 1172 BT_COMP_LOGE_STR("Cannot translate static array field class element.");
46bdd3e0
PP
1173 goto end;
1174 }
1175
1176 ret = translate_field_class(ctx);
1177 if (ret) {
85623cd6 1178 BT_COMP_LOGE_STR("Cannot translate static array field class element.");
46bdd3e0
PP
1179 goto end;
1180 }
1181
1182 cur_path_stack_pop(ctx);
1183 update_parent_field_class_alignment(ctx, fc->base.base.alignment);
1184
1185end:
1186 return ret;
1187}
1188
1189static inline
1190int translate_dynamic_array_field_class(struct ctx *ctx)
1191{
1192 struct fs_sink_ctf_field_class_sequence *fc =
1193 fs_sink_ctf_field_class_sequence_create_empty(
1194 cur_path_stack_top(ctx)->ir_fc,
1195 cur_path_stack_top(ctx)->index_in_parent);
1196 const bt_field_class *elem_ir_fc =
1197 bt_field_class_array_borrow_element_field_class_const(
1198 fc->base.base.ir_fc);
1199 int ret;
1200
1201 BT_ASSERT(fc);
1202
1203 /* Resolve length field class before appending to parent */
b8ddb4f0
PP
1204 if (bt_field_class_get_type(cur_path_stack_top(ctx)->ir_fc) ==
1205 BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY_WITH_LENGTH_FIELD) {
1206 resolve_field_class(ctx,
1207 bt_field_class_array_dynamic_with_length_field_borrow_length_field_path_const(
1208 fc->base.base.ir_fc),
1209 fc->length_ref, &fc->length_is_before, NULL);
1210 }
46bdd3e0
PP
1211
1212 append_to_parent_field_class(ctx, (void *) fc);
02b61fe0 1213 ret = cur_path_stack_push(ctx, UINT64_C(-1), NULL, false, elem_ir_fc,
46bdd3e0
PP
1214 (void *) fc);
1215 if (ret) {
85623cd6 1216 BT_COMP_LOGE_STR("Cannot translate dynamic array field class element.");
46bdd3e0
PP
1217 goto end;
1218 }
1219
1220 ret = translate_field_class(ctx);
1221 if (ret) {
85623cd6 1222 BT_COMP_LOGE_STR("Cannot translate dynamic array field class element.");
46bdd3e0
PP
1223 goto end;
1224 }
1225
1226 cur_path_stack_pop(ctx);
1227 update_parent_field_class_alignment(ctx, fc->base.base.alignment);
1228
1229end:
1230 return ret;
1231}
1232
dc9cc972
PP
1233static inline
1234int translate_bool_field_class(struct ctx *ctx)
1235{
1236 struct fs_sink_ctf_field_class_bool *fc =
1237 fs_sink_ctf_field_class_bool_create(
1238 cur_path_stack_top(ctx)->ir_fc,
1239 cur_path_stack_top(ctx)->index_in_parent);
1240
1241 BT_ASSERT(fc);
1242 append_to_parent_field_class(ctx, (void *) fc);
1243 return 0;
1244}
1245
adec7d09
PP
1246static inline
1247int translate_bit_array_field_class(struct ctx *ctx)
1248{
1249 struct fs_sink_ctf_field_class_bit_array *fc =
1250 fs_sink_ctf_field_class_bit_array_create(
1251 cur_path_stack_top(ctx)->ir_fc,
1252 cur_path_stack_top(ctx)->index_in_parent);
1253
1254 BT_ASSERT(fc);
1255 append_to_parent_field_class(ctx, (void *) fc);
1256 return 0;
1257}
1258
46bdd3e0
PP
1259static inline
1260int translate_integer_field_class(struct ctx *ctx)
1261{
1262 struct fs_sink_ctf_field_class_int *fc =
1263 fs_sink_ctf_field_class_int_create(
1264 cur_path_stack_top(ctx)->ir_fc,
1265 cur_path_stack_top(ctx)->index_in_parent);
1266
1267 BT_ASSERT(fc);
1268 append_to_parent_field_class(ctx, (void *) fc);
1269 return 0;
1270}
1271
1272static inline
1273int translate_real_field_class(struct ctx *ctx)
1274{
1275 struct fs_sink_ctf_field_class_float *fc =
1276 fs_sink_ctf_field_class_float_create(
1277 cur_path_stack_top(ctx)->ir_fc,
1278 cur_path_stack_top(ctx)->index_in_parent);
1279
1280 BT_ASSERT(fc);
1281 append_to_parent_field_class(ctx, (void *) fc);
1282 return 0;
1283}
1284
1285static inline
1286int translate_string_field_class(struct ctx *ctx)
1287{
1288 struct fs_sink_ctf_field_class_string *fc =
1289 fs_sink_ctf_field_class_string_create(
1290 cur_path_stack_top(ctx)->ir_fc,
1291 cur_path_stack_top(ctx)->index_in_parent);
1292
1293 BT_ASSERT(fc);
1294 append_to_parent_field_class(ctx, (void *) fc);
1295 return 0;
1296}
1297
1298/*
1299 * Translates a field class, recursively.
1300 *
1301 * The field class's IR field class, parent field class, and index
1302 * within its parent are in the context's current path's top element
1303 * (cur_path_stack_top()).
1304 */
1305static
1306int translate_field_class(struct ctx *ctx)
1307{
1308 int ret;
94847783
PP
1309 bt_field_class_type ir_fc_type =
1310 bt_field_class_get_type(cur_path_stack_top(ctx)->ir_fc);
46bdd3e0 1311
94847783 1312 if (ir_fc_type == BT_FIELD_CLASS_TYPE_BOOL) {
dc9cc972 1313 ret = translate_bool_field_class(ctx);
94847783 1314 } else if (ir_fc_type == BT_FIELD_CLASS_TYPE_BIT_ARRAY) {
adec7d09 1315 ret = translate_bit_array_field_class(ctx);
94847783
PP
1316 } else if (bt_field_class_type_is(ir_fc_type,
1317 BT_FIELD_CLASS_TYPE_INTEGER)) {
46bdd3e0 1318 ret = translate_integer_field_class(ctx);
94847783
PP
1319 } else if (bt_field_class_type_is(ir_fc_type,
1320 BT_FIELD_CLASS_TYPE_REAL)) {
46bdd3e0 1321 ret = translate_real_field_class(ctx);
94847783 1322 } else if (ir_fc_type == BT_FIELD_CLASS_TYPE_STRING) {
46bdd3e0 1323 ret = translate_string_field_class(ctx);
94847783 1324 } else if (ir_fc_type == BT_FIELD_CLASS_TYPE_STRUCTURE) {
46bdd3e0 1325 ret = translate_structure_field_class(ctx);
94847783 1326 } else if (ir_fc_type == BT_FIELD_CLASS_TYPE_STATIC_ARRAY) {
46bdd3e0 1327 ret = translate_static_array_field_class(ctx);
94847783
PP
1328 } else if (bt_field_class_type_is(ir_fc_type,
1329 BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY)) {
46bdd3e0 1330 ret = translate_dynamic_array_field_class(ctx);
94847783
PP
1331 } else if (bt_field_class_type_is(ir_fc_type,
1332 BT_FIELD_CLASS_TYPE_OPTION)) {
c27259b0 1333 ret = translate_option_field_class(ctx);
94847783
PP
1334 } else if (bt_field_class_type_is(ir_fc_type,
1335 BT_FIELD_CLASS_TYPE_VARIANT)) {
46bdd3e0 1336 ret = translate_variant_field_class(ctx);
94847783 1337 } else {
46bdd3e0
PP
1338 abort();
1339 }
1340
1341 return ret;
1342}
1343
1344static
1345int set_field_ref(struct fs_sink_ctf_field_class *fc, const char *fc_name,
1346 struct fs_sink_ctf_field_class *parent_fc)
1347{
1348 int ret = 0;
1349 GString *field_ref = NULL;
1350 bool is_before;
1351 const char *tgt_type;
1352 struct fs_sink_ctf_field_class_struct *parent_struct_fc =
1353 (void *) parent_fc;
1354 uint64_t i;
1355 unsigned int suffix = 0;
1356
1357 if (!fc_name || !parent_fc ||
1358 parent_fc->type != FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT) {
1359 /* Not supported */
1360 ret = -1;
1361 goto end;
1362 }
1363
1364 switch (fc->type) {
c27259b0
PP
1365 case FS_SINK_CTF_FIELD_CLASS_TYPE_OPTION:
1366 {
1367 /*
1368 * CTF 1.8 does not support the option field class type.
1369 * To write something anyway, this component translates
1370 * this type to a variant field class where the options
1371 * are:
1372 *
1373 * * An empty structure field class.
1374 * * The optional field class itself.
1375 *
1376 * Because the option field class becomes a CTF variant
1377 * field class, we use the term "tag" too here.
1378 *
1379 * The "tag" is always generated/before in that case (an
1380 * 8-bit unsigned enumeration field class).
1381 */
1382 struct fs_sink_ctf_field_class_option *opt_fc = (void *) fc;
1383
1384 field_ref = opt_fc->tag_ref;
1385 is_before = true;
1386 tgt_type = "tag";
1387 break;
1388 }
46bdd3e0
PP
1389 case FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE:
1390 {
1391 struct fs_sink_ctf_field_class_sequence *seq_fc = (void *) fc;
1392
1393 field_ref = seq_fc->length_ref;
1394 is_before = seq_fc->length_is_before;
1395 tgt_type = "len";
1396 break;
1397 }
1398 case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT:
1399 {
1400 struct fs_sink_ctf_field_class_variant *var_fc = (void *) fc;
1401
1402 field_ref = var_fc->tag_ref;
1403 is_before = var_fc->tag_is_before;
1404 tgt_type = "tag";
1405 break;
1406 }
1407 default:
1408 abort();
1409 }
1410
1411 BT_ASSERT(field_ref);
1412
1413 if (!is_before) {
1414 goto end;
1415 }
1416
1417 /* Initial field ref */
1418 g_string_printf(field_ref, "__%s_%s", fc_name, tgt_type);
1419
1420 /*
1421 * Make sure field ref does not clash with an existing field
1422 * class name within the same parent structure field class.
1423 */
1424 while (true) {
1425 bool name_ok = true;
1426
1427 for (i = 0; i < parent_struct_fc->members->len; i++) {
1428 struct fs_sink_ctf_named_field_class *named_fc =
1429 fs_sink_ctf_field_class_struct_borrow_member_by_index(
1430 parent_struct_fc, i);
1431
1432 if (strcmp(field_ref->str, named_fc->name->str) == 0) {
1433 /* Name clash */
1434 name_ok = false;
1435 break;
1436 }
1437 }
1438
1439 if (name_ok) {
1440 /* No clash: we're done */
1441 break;
1442 }
1443
1444 /* Append suffix and try again */
1445 g_string_printf(field_ref, "__%s_%s_%u", fc_name, tgt_type,
1446 suffix);
1447 suffix++;
1448 }
1449
1450end:
1451 return ret;
1452}
1453
1454/*
1455 * This function recursively sets field refs of sequence and variant
1456 * field classes when they are immediately before, avoiding name clashes
1457 * with existing field class names.
1458 *
1459 * It can fail at this point if, for example, a sequence field class of
1460 * which to set the length's field ref has something else than a
1461 * structure field class as its parent: in this case, there's no
1462 * location to place the length field class immediately before the
1463 * sequence field class.
1464 */
1465static
60439780
PP
1466int set_field_refs(struct fs_sink_ctf_field_class * const fc,
1467 const char *fc_name, struct fs_sink_ctf_field_class *parent_fc)
46bdd3e0
PP
1468{
1469 int ret = 0;
c5fe2e6f 1470 enum fs_sink_ctf_field_class_type fc_type;
46bdd3e0
PP
1471 BT_ASSERT(fc);
1472
c5fe2e6f
FD
1473 fc_type = fc->type;
1474
1475 switch (fc_type) {
c27259b0
PP
1476 case FS_SINK_CTF_FIELD_CLASS_TYPE_OPTION:
1477 {
1478 struct fs_sink_ctf_field_class_option *opt_fc = (void *) fc;
1479
1480 ret = set_field_ref(fc, fc_name, parent_fc);
1481 if (ret) {
1482 goto end;
1483 }
1484
1485 ret = set_field_refs(opt_fc->content_fc, NULL, fc);
1486 if (ret) {
1487 goto end;
1488 }
1489
1490 break;
1491 }
46bdd3e0
PP
1492 case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT:
1493 case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT:
1494 {
1495 uint64_t i;
1496 uint64_t len;
a5e289cf 1497 struct fs_sink_ctf_field_class_struct *struct_fc = NULL;
5ccbae23 1498 struct fs_sink_ctf_field_class_variant *var_fc = NULL;
46bdd3e0
PP
1499 struct fs_sink_ctf_named_field_class *named_fc;
1500
c5fe2e6f 1501 if (fc_type == FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT) {
46bdd3e0
PP
1502 struct_fc = (void *) fc;
1503 len = struct_fc->members->len;
1504 } else {
1505 var_fc = (void *) fc;
1506 len = var_fc->options->len;
1507 ret = set_field_ref(fc, fc_name, parent_fc);
1508 if (ret) {
1509 goto end;
1510 }
1511 }
1512
1513 for (i = 0; i < len; i++) {
c5fe2e6f 1514 if (fc_type == FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT) {
46bdd3e0
PP
1515 named_fc = fs_sink_ctf_field_class_struct_borrow_member_by_index(
1516 struct_fc, i);
1517 } else {
1518 named_fc = fs_sink_ctf_field_class_variant_borrow_option_by_index(
1519 var_fc, i);
1520 }
1521
1522 ret = set_field_refs(named_fc->fc, named_fc->name->str,
1523 fc);
1524 if (ret) {
1525 goto end;
1526 }
1527 }
1528
1529 break;
1530 }
1531 case FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY:
1532 case FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE:
1533 {
1534 struct fs_sink_ctf_field_class_array_base *array_base_fc =
1535 (void *) fc;
1536
c5fe2e6f 1537 if (fc_type == FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE) {
46bdd3e0
PP
1538 ret = set_field_ref(fc, fc_name, parent_fc);
1539 if (ret) {
1540 goto end;
1541 }
1542 }
1543
1544 ret = set_field_refs(array_base_fc->elem_fc, NULL, fc);
1545 if (ret) {
1546 goto end;
1547 }
1548
1549 break;
1550 }
1551 default:
1552 break;
1553 }
1554
1555end:
1556 return ret;
1557}
1558
1559/*
1560 * This function translates a root scope trace IR field class to
1561 * a CTF IR field class.
1562 *
1563 * The resulting CTF IR field class is written to `*fc` so that it
1564 * exists as the parent object's (stream class or event class) true root
1565 * field class during the recursive translation for resolving purposes.
1566 * This is also why this function creates the empty structure field
1567 * class and then calls translate_structure_field_class_members() to
1568 * fill it.
1569 */
1570static
056995e5 1571int translate_scope_field_class(struct ctx *ctx, bt_field_path_scope scope,
46bdd3e0
PP
1572 struct fs_sink_ctf_field_class **fc,
1573 const bt_field_class *ir_fc)
1574{
1575 int ret = 0;
1576
1577 if (!ir_fc) {
1578 goto end;
1579 }
1580
1581 BT_ASSERT(bt_field_class_get_type(ir_fc) ==
1582 BT_FIELD_CLASS_TYPE_STRUCTURE);
1583 BT_ASSERT(fc);
1584 *fc = (void *) fs_sink_ctf_field_class_struct_create_empty(
1585 ir_fc, UINT64_C(-1));
1586 BT_ASSERT(*fc);
1587 ctx->cur_scope = scope;
1588 BT_ASSERT(ctx->cur_path->len == 0);
02b61fe0 1589 ret = cur_path_stack_push(ctx, UINT64_C(-1), NULL, false, ir_fc, NULL);
46bdd3e0 1590 if (ret) {
85623cd6 1591 BT_COMP_LOGE("Cannot translate scope structure field class: "
46bdd3e0
PP
1592 "scope=%d", scope);
1593 goto end;
1594 }
1595
1596 ret = translate_structure_field_class_members(ctx, (void *) *fc, ir_fc);
1597 if (ret) {
85623cd6 1598 BT_COMP_LOGE("Cannot translate scope structure field class: "
46bdd3e0
PP
1599 "scope=%d", scope);
1600 goto end;
1601 }
1602
1603 cur_path_stack_pop(ctx);
1604
1605 /* Set field refs for preceding targets */
1606 ret = set_field_refs(*fc, NULL, NULL);
1607
1608end:
1609 return ret;
1610}
1611
1612static inline
85623cd6 1613void ctx_init(struct ctx *ctx, struct fs_sink_comp *fs_sink)
46bdd3e0
PP
1614{
1615 memset(ctx, 0, sizeof(struct ctx));
1616 ctx->cur_path = g_array_new(FALSE, TRUE,
1617 sizeof(struct field_path_elem));
1618 BT_ASSERT(ctx->cur_path);
85623cd6
PP
1619 ctx->log_level = fs_sink->log_level;
1620 ctx->self_comp = fs_sink->self_comp;
46bdd3e0
PP
1621}
1622
1623static inline
1624void ctx_fini(struct ctx *ctx)
1625{
1626 if (ctx->cur_path) {
1627 g_array_free(ctx->cur_path, TRUE);
1628 ctx->cur_path = NULL;
1629 }
1630}
1631
1632static
85623cd6
PP
1633int translate_event_class(struct fs_sink_comp *fs_sink,
1634 struct fs_sink_ctf_stream_class *sc,
46bdd3e0 1635 const bt_event_class *ir_ec,
85623cd6 1636 struct fs_sink_ctf_event_class **out_ec)
46bdd3e0
PP
1637{
1638 int ret = 0;
1639 struct ctx ctx;
1640 struct fs_sink_ctf_event_class *ec;
1641
1642 BT_ASSERT(sc);
1643 BT_ASSERT(ir_ec);
1644
85623cd6 1645 ctx_init(&ctx, fs_sink);
46bdd3e0
PP
1646 ec = fs_sink_ctf_event_class_create(sc, ir_ec);
1647 BT_ASSERT(ec);
1648 ctx.cur_sc = sc;
1649 ctx.cur_ec = ec;
056995e5 1650 ret = translate_scope_field_class(&ctx, BT_FIELD_PATH_SCOPE_EVENT_SPECIFIC_CONTEXT,
46bdd3e0
PP
1651 &ec->spec_context_fc,
1652 bt_event_class_borrow_specific_context_field_class_const(
1653 ir_ec));
1654 if (ret) {
1655 goto end;
1656 }
1657
056995e5 1658 ret = translate_scope_field_class(&ctx, BT_FIELD_PATH_SCOPE_EVENT_PAYLOAD,
46bdd3e0
PP
1659 &ec->payload_fc,
1660 bt_event_class_borrow_payload_field_class_const(ir_ec));
1661 if (ret) {
1662 goto end;
1663 }
1664
1665end:
1666 ctx_fini(&ctx);
1667 *out_ec = ec;
1668 return ret;
1669}
1670
1671BT_HIDDEN
1672int try_translate_event_class_trace_ir_to_ctf_ir(
85623cd6 1673 struct fs_sink_comp *fs_sink,
46bdd3e0
PP
1674 struct fs_sink_ctf_stream_class *sc,
1675 const bt_event_class *ir_ec,
85623cd6 1676 struct fs_sink_ctf_event_class **out_ec)
46bdd3e0
PP
1677{
1678 int ret = 0;
1679
1680 BT_ASSERT(sc);
1681 BT_ASSERT(ir_ec);
1682
1683 /* Check in hash table first */
1684 *out_ec = g_hash_table_lookup(sc->event_classes_from_ir, ir_ec);
85e7137b 1685 if (G_LIKELY(*out_ec)) {
46bdd3e0
PP
1686 goto end;
1687 }
1688
85623cd6 1689 ret = translate_event_class(fs_sink, sc, ir_ec, out_ec);
46bdd3e0
PP
1690
1691end:
1692 return ret;
1693}
1694
cd03c43c 1695bool default_clock_class_name_exists(struct fs_sink_ctf_trace *trace,
46bdd3e0
PP
1696 const char *name)
1697{
1698 bool exists = false;
1699 uint64_t i;
1700
cd03c43c 1701 for (i = 0; i < trace->stream_classes->len; i++) {
46bdd3e0 1702 struct fs_sink_ctf_stream_class *sc =
cd03c43c 1703 trace->stream_classes->pdata[i];
46bdd3e0
PP
1704
1705 if (sc->default_clock_class_name->len == 0) {
1706 /* No default clock class */
1707 continue;
1708 }
1709
1710 if (strcmp(sc->default_clock_class_name->str, name) == 0) {
1711 exists = true;
1712 goto end;
1713 }
1714 }
1715
1716end:
1717 return exists;
1718}
1719
1720static
1721void make_unique_default_clock_class_name(struct fs_sink_ctf_stream_class *sc)
1722{
1723 unsigned int suffix = 0;
1724 char buf[16];
1725
1726 g_string_assign(sc->default_clock_class_name, "");
1727 sprintf(buf, "default");
1728
cd03c43c 1729 while (default_clock_class_name_exists(sc->trace, buf)) {
46bdd3e0
PP
1730 sprintf(buf, "default%u", suffix);
1731 suffix++;
1732 }
1733
1734 g_string_assign(sc->default_clock_class_name, buf);
1735}
1736
1737static
85623cd6 1738int translate_stream_class(struct fs_sink_comp *fs_sink,
cd03c43c 1739 struct fs_sink_ctf_trace *trace,
46bdd3e0 1740 const bt_stream_class *ir_sc,
85623cd6 1741 struct fs_sink_ctf_stream_class **out_sc)
46bdd3e0
PP
1742{
1743 int ret = 0;
1744 struct ctx ctx;
1745
cd03c43c 1746 BT_ASSERT(trace);
46bdd3e0 1747 BT_ASSERT(ir_sc);
85623cd6 1748 ctx_init(&ctx, fs_sink);
cd03c43c 1749 *out_sc = fs_sink_ctf_stream_class_create(trace, ir_sc);
46bdd3e0
PP
1750 BT_ASSERT(*out_sc);
1751
1752 /* Set default clock class's protected name, if any */
1753 if ((*out_sc)->default_clock_class) {
1754 const char *name = bt_clock_class_get_name(
1755 (*out_sc)->default_clock_class);
1756
46bdd3e0
PP
1757 if (name) {
1758 /* Try original name, protected */
02b61fe0
PP
1759 g_string_assign((*out_sc)->default_clock_class_name,
1760 "");
1761
1762 if (must_protect_identifier(name)) {
1763 g_string_assign(
1764 (*out_sc)->default_clock_class_name,
1765 "_");
1766 }
1767
46bdd3e0
PP
1768 g_string_assign((*out_sc)->default_clock_class_name,
1769 name);
02b61fe0
PP
1770 if (!ist_valid_identifier(
1771 (*out_sc)->default_clock_class_name->str)) {
46bdd3e0
PP
1772 /* Invalid: create a new name */
1773 make_unique_default_clock_class_name(*out_sc);
46bdd3e0
PP
1774 }
1775 } else {
1776 /* No name: create a name */
1777 make_unique_default_clock_class_name(*out_sc);
1778 }
1779 }
1780
1781 ctx.cur_sc = *out_sc;
056995e5 1782 ret = translate_scope_field_class(&ctx, BT_FIELD_PATH_SCOPE_PACKET_CONTEXT,
46bdd3e0
PP
1783 &(*out_sc)->packet_context_fc,
1784 bt_stream_class_borrow_packet_context_field_class_const(ir_sc));
1785 if (ret) {
1786 goto error;
1787 }
1788
1789 if ((*out_sc)->packet_context_fc) {
1790 /*
1791 * Make sure the structure field class's alignment is
1792 * enough: 8 is what we use for our own special members
1793 * in the packet context.
1794 */
1795 fs_sink_ctf_field_class_struct_align_at_least(
1796 (void *) (*out_sc)->packet_context_fc, 8);
1797 }
1798
056995e5 1799 ret = translate_scope_field_class(&ctx, BT_FIELD_PATH_SCOPE_EVENT_COMMON_CONTEXT,
46bdd3e0
PP
1800 &(*out_sc)->event_common_context_fc,
1801 bt_stream_class_borrow_event_common_context_field_class_const(
1802 ir_sc));
1803 if (ret) {
1804 goto error;
1805 }
1806
1807 goto end;
1808
1809error:
1810 fs_sink_ctf_stream_class_destroy(*out_sc);
1811 *out_sc = NULL;
1812
1813end:
1814 ctx_fini(&ctx);
1815 return ret;
1816}
1817
1818BT_HIDDEN
1819int try_translate_stream_class_trace_ir_to_ctf_ir(
85623cd6 1820 struct fs_sink_comp *fs_sink,
cd03c43c 1821 struct fs_sink_ctf_trace *trace,
46bdd3e0 1822 const bt_stream_class *ir_sc,
85623cd6 1823 struct fs_sink_ctf_stream_class **out_sc)
46bdd3e0
PP
1824{
1825 int ret = 0;
1826 uint64_t i;
1827
cd03c43c 1828 BT_ASSERT(trace);
46bdd3e0
PP
1829 BT_ASSERT(ir_sc);
1830
cd03c43c
PP
1831 for (i = 0; i < trace->stream_classes->len; i++) {
1832 *out_sc = trace->stream_classes->pdata[i];
46bdd3e0
PP
1833
1834 if ((*out_sc)->ir_sc == ir_sc) {
1835 goto end;
1836 }
1837 }
1838
cd03c43c 1839 ret = translate_stream_class(fs_sink, trace, ir_sc, out_sc);
46bdd3e0
PP
1840
1841end:
1842 return ret;
1843}
1844
1845BT_HIDDEN
cd03c43c
PP
1846struct fs_sink_ctf_trace *translate_trace_trace_ir_to_ctf_ir(
1847 struct fs_sink_comp *fs_sink, const bt_trace *ir_trace)
46bdd3e0
PP
1848{
1849 uint64_t count;
1850 uint64_t i;
cd03c43c 1851 struct fs_sink_ctf_trace *trace = NULL;
46bdd3e0 1852
cd03c43c
PP
1853 /* Check that trace's environment is TSDL-compatible */
1854 count = bt_trace_get_environment_entry_count(ir_trace);
46bdd3e0
PP
1855 for (i = 0; i < count; i++) {
1856 const char *name;
1857 const bt_value *val;
1858
cd03c43c
PP
1859 bt_trace_borrow_environment_entry_by_index_const(
1860 ir_trace, i, &name, &val);
46bdd3e0 1861
02b61fe0 1862 if (!ist_valid_identifier(name)) {
85623cd6
PP
1863 BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, fs_sink->log_level,
1864 fs_sink->self_comp,
a708ff31 1865 "Unsupported trace class's environment entry name: "
46bdd3e0
PP
1866 "name=\"%s\"", name);
1867 goto end;
1868 }
1869
1870 switch (bt_value_get_type(val)) {
68d9d039 1871 case BT_VALUE_TYPE_SIGNED_INTEGER:
46bdd3e0
PP
1872 case BT_VALUE_TYPE_STRING:
1873 break;
1874 default:
85623cd6
PP
1875 BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, fs_sink->log_level,
1876 fs_sink->self_comp,
a708ff31 1877 "Unsupported trace class's environment entry value type: "
46bdd3e0
PP
1878 "type=%s",
1879 bt_common_value_type_string(
1880 bt_value_get_type(val)));
1881 goto end;
1882 }
1883 }
1884
cd03c43c
PP
1885 trace = fs_sink_ctf_trace_create(ir_trace);
1886 BT_ASSERT(trace);
46bdd3e0
PP
1887
1888end:
cd03c43c 1889 return trace;
46bdd3e0 1890}
This page took 0.125322 seconds and 4 git commands to generate.