cpp-common/bt2c/fmt.hpp: use `wise_enum::string_type` in `EnableIfIsWiseEnum` definition
[babeltrace.git] / src / plugins / ctf / common / src / metadata / tsdl / ctf-meta-resolve.cpp
1 /*
2 * SPDX-License-Identifier: MIT
3 *
4 * Copyright 2016-2018 Philippe Proulx <pproulx@efficios.com>
5 * Copyright 2015 Jérémie Galarneau <jeremie.galarneau@efficios.com>
6 */
7
8 #include <babeltrace2/babeltrace.h>
9
10 #include "common/assert.h"
11 #include "common/common.h"
12 #include "cpp-common/bt2c/logging.hpp"
13 #include "cpp-common/vendor/fmt/format.h"
14
15 #include "ctf-meta-visitors.hpp"
16
17 using field_class_stack_t = GPtrArray;
18
19 /*
20 * A stack frame.
21 *
22 * `fc` contains a compound field class (structure, variant, array,
23 * or sequence) and `index` indicates the index of the field class in
24 * the upper frame (-1 for array and sequence field classes). `name`
25 * indicates the name of the field class in the upper frame (empty
26 * string for array and sequence field classes).
27 */
28 struct field_class_stack_frame
29 {
30 struct ctf_field_class *fc;
31 int64_t index;
32 };
33
34 /*
35 * The current context of the resolving engine.
36 */
37 struct resolve_context
38 {
39 explicit resolve_context(const bt2c::Logger& parentLogger) :
40 logger {parentLogger, "PLUGIN/CTF/META/RESOLVE"}
41 {
42 }
43
44 bt2c::Logger logger;
45
46 struct ctf_trace_class *tc = nullptr;
47 struct ctf_stream_class *sc = nullptr;
48 struct ctf_event_class *ec = nullptr;
49
50 struct
51 {
52 struct ctf_field_class *packet_header = nullptr;
53 struct ctf_field_class *packet_context = nullptr;
54 struct ctf_field_class *event_header = nullptr;
55 struct ctf_field_class *event_common_context = nullptr;
56 struct ctf_field_class *event_spec_context = nullptr;
57 struct ctf_field_class *event_payload = nullptr;
58 } scopes;
59
60 /* Root scope being visited */
61 enum ctf_scope root_scope = CTF_SCOPE_PACKET_HEADER;
62 field_class_stack_t *field_class_stack = nullptr;
63 struct ctf_field_class *cur_fc = nullptr;
64 };
65
66 /* TSDL dynamic scope prefixes as defined in CTF Section 7.3.2 */
67 static const char * const absolute_path_prefixes[] = {
68 /* CTF_SCOPE_PACKET_HEADER */ "trace.packet.header.",
69 /* CTF_SCOPE_PACKET_CONTEXT */ "stream.packet.context.",
70 /* CTF_SCOPE_EVENT_HEADER */ "stream.event.header.",
71 /* CTF_SCOPE_EVENT_COMMON_CONTEXT */ "stream.event.context.",
72 /* CTF_SCOPE_EVENT_SPECIFIC_CONTEXT */ "event.context.",
73 /* CTF_SCOPE_EVENT_PAYLOAD */ "event.fields.",
74 };
75
76 /* Number of path tokens used for the absolute prefixes */
77 static const uint64_t absolute_path_prefix_ptoken_counts[] = {
78 /* CTF_SCOPE_PACKET_HEADER */ 3,
79 /* CTF_SCOPE_PACKET_CONTEXT */ 3,
80 /* CTF_SCOPE_EVENT_HEADER */ 3,
81 /* CTF_SCOPE_EVENT_COMMON_CONTEXT */ 3,
82 /* CTF_SCOPE_EVENT_SPECIFIC_CONTEXT */ 2,
83 /* CTF_SCOPE_EVENT_PAYLOAD */ 2,
84 };
85
86 static void destroy_field_class_stack_frame(struct field_class_stack_frame *frame)
87 {
88 if (!frame) {
89 return;
90 }
91
92 g_free(frame);
93 }
94
95 /*
96 * Creates a class stack.
97 */
98 static field_class_stack_t *field_class_stack_create(void)
99 {
100 return g_ptr_array_new_with_free_func((GDestroyNotify) destroy_field_class_stack_frame);
101 }
102
103 /*
104 * Destroys a class stack.
105 */
106 static void field_class_stack_destroy(field_class_stack_t *stack)
107 {
108 if (stack) {
109 g_ptr_array_free(stack, TRUE);
110 }
111 }
112
113 /*
114 * Pushes a field class onto a class stack.
115 */
116 static int field_class_stack_push(field_class_stack_t *stack, struct ctf_field_class *fc,
117 struct resolve_context *ctx)
118 {
119 int ret = 0;
120 struct field_class_stack_frame *frame = NULL;
121
122 if (!stack || !fc) {
123 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
124 "Invalid parameter: stack or field class is `NULL`.");
125 ret = -1;
126 goto end;
127 }
128
129 frame = g_new0(struct field_class_stack_frame, 1);
130 if (!frame) {
131 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
132 "Failed to allocate one field class stack frame.");
133 ret = -1;
134 goto end;
135 }
136
137 BT_CPPLOGD_SPEC(ctx->logger,
138 "Pushing field class on context's stack: "
139 "fc-addr={}, stack-size-before={}",
140 fmt::ptr(fc), stack->len);
141 frame->fc = fc;
142 g_ptr_array_add(stack, frame);
143
144 end:
145 return ret;
146 }
147
148 /*
149 * Checks whether or not `stack` is empty.
150 */
151 static bool field_class_stack_empty(field_class_stack_t *stack)
152 {
153 return stack->len == 0;
154 }
155
156 /*
157 * Returns the number of frames in `stack`.
158 */
159 static size_t field_class_stack_size(field_class_stack_t *stack)
160 {
161 return stack->len;
162 }
163
164 /*
165 * Returns the top frame of `stack`.
166 */
167 static struct field_class_stack_frame *field_class_stack_peek(field_class_stack_t *stack)
168 {
169 BT_ASSERT(stack);
170 BT_ASSERT(!field_class_stack_empty(stack));
171
172 return (field_class_stack_frame *) g_ptr_array_index(stack, stack->len - 1);
173 }
174
175 /*
176 * Returns the frame at index `index` in `stack`.
177 */
178 static struct field_class_stack_frame *field_class_stack_at(field_class_stack_t *stack,
179 size_t index)
180 {
181 BT_ASSERT(stack);
182 BT_ASSERT(index < stack->len);
183
184 return (field_class_stack_frame *) g_ptr_array_index(stack, index);
185 }
186
187 /*
188 * Removes the top frame of `stack`.
189 */
190 static void field_class_stack_pop(field_class_stack_t *stack, struct resolve_context *ctx)
191 {
192 if (!field_class_stack_empty(stack)) {
193 /*
194 * This will call the frame's destructor and free it, as
195 * well as put its contained field class.
196 */
197 BT_CPPLOGD_SPEC(ctx->logger, "Popping context's stack: stack-size-before={}", stack->len);
198 g_ptr_array_set_size(stack, stack->len - 1);
199 }
200 }
201
202 /*
203 * Returns the scope field class of `scope` in the context `ctx`.
204 */
205 static struct ctf_field_class *borrow_class_from_ctx(struct resolve_context *ctx,
206 enum ctf_scope scope)
207 {
208 switch (scope) {
209 case CTF_SCOPE_PACKET_HEADER:
210 return ctx->scopes.packet_header;
211 case CTF_SCOPE_PACKET_CONTEXT:
212 return ctx->scopes.packet_context;
213 case CTF_SCOPE_EVENT_HEADER:
214 return ctx->scopes.event_header;
215 case CTF_SCOPE_EVENT_COMMON_CONTEXT:
216 return ctx->scopes.event_common_context;
217 case CTF_SCOPE_EVENT_SPECIFIC_CONTEXT:
218 return ctx->scopes.event_spec_context;
219 case CTF_SCOPE_EVENT_PAYLOAD:
220 return ctx->scopes.event_payload;
221 default:
222 bt_common_abort();
223 }
224
225 return NULL;
226 }
227
228 /*
229 * Returns the CTF scope from a path string. May return -1 if the path
230 * is found to be relative.
231 */
232 static enum ctf_scope get_root_scope_from_absolute_pathstr(const char *pathstr,
233 struct resolve_context *ctx)
234 {
235 enum ctf_scope scope;
236 enum ctf_scope ret = CTF_SCOPE_PACKET_UNKNOWN;
237 const size_t prefixes_count = sizeof(absolute_path_prefixes) / sizeof(*absolute_path_prefixes);
238
239 for (scope = CTF_SCOPE_PACKET_HEADER; scope < CTF_SCOPE_PACKET_HEADER + prefixes_count;
240 scope = (ctf_scope) (scope + 1)) {
241 /*
242 * Check if path string starts with a known absolute
243 * path prefix.
244 *
245 * Refer to CTF 7.3.2 STATIC AND DYNAMIC SCOPES.
246 */
247 if (strncmp(pathstr, absolute_path_prefixes[scope],
248 strlen(absolute_path_prefixes[scope]))) {
249 /* Prefix does not match: try the next one */
250 BT_CPPLOGD_SPEC(ctx->logger,
251 "Prefix does not match: trying the next one: "
252 "path=\"{}\", path-prefix=\"{}\", scope={}",
253 pathstr, absolute_path_prefixes[scope], scope);
254 continue;
255 }
256
257 /* Found it! */
258 ret = scope;
259 BT_CPPLOGD_SPEC(ctx->logger,
260 "Found root scope from absolute path: "
261 "path=\"{}\", scope={}",
262 pathstr, scope);
263 goto end;
264 }
265
266 end:
267 return ret;
268 }
269
270 /*
271 * Destroys a path token.
272 */
273 static void ptokens_destroy_func(gpointer ptoken, gpointer)
274 {
275 g_string_free((GString *) ptoken, TRUE);
276 }
277
278 /*
279 * Destroys a path token list.
280 */
281 static void ptokens_destroy(GList *ptokens)
282 {
283 if (!ptokens) {
284 return;
285 }
286
287 g_list_foreach(ptokens, ptokens_destroy_func, NULL);
288 g_list_free(ptokens);
289 }
290
291 /*
292 * Returns the string contained in a path token.
293 */
294 static const char *ptoken_get_string(GList *ptoken)
295 {
296 GString *tokenstr = (GString *) ptoken->data;
297
298 return tokenstr->str;
299 }
300
301 /*
302 * Converts a path string to a path token list, that is, splits the
303 * individual words of a path string into a list of individual
304 * strings.
305 */
306 static GList *pathstr_to_ptokens(const char *pathstr, struct resolve_context *ctx)
307 {
308 const char *at = pathstr;
309 const char *last = at;
310 GList *ptokens = NULL;
311
312 for (;;) {
313 if (*at == '.' || *at == '\0') {
314 GString *tokenstr;
315
316 if (at == last) {
317 /* Error: empty token */
318 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger, "Empty path token: path=\"{}\", pos={}",
319 pathstr, (unsigned int) (at - pathstr));
320 goto error;
321 }
322
323 tokenstr = g_string_new(NULL);
324 g_string_append_len(tokenstr, last, at - last);
325 ptokens = g_list_append(ptokens, tokenstr);
326 last = at + 1;
327 }
328
329 if (*at == '\0') {
330 break;
331 }
332
333 at++;
334 }
335
336 return ptokens;
337
338 error:
339 ptokens_destroy(ptokens);
340 return NULL;
341 }
342
343 /*
344 * Converts a path token list to a field path object. The path token
345 * list is relative from `fc`. The index of the source looking for its
346 * target within `fc` is indicated by `src_index`. This can be
347 * `INT64_MAX` if the source is contained in `fc`.
348 *
349 * `field_path` is an output parameter owned by the caller that must be
350 * filled here.
351 */
352 static int ptokens_to_field_path(GList *ptokens, struct ctf_field_path *field_path,
353 struct ctf_field_class *fc, int64_t src_index,
354 struct resolve_context *ctx)
355 {
356 int ret = 0;
357 GList *cur_ptoken = ptokens;
358 bool first_level_done = false;
359
360 /* Locate target */
361 while (cur_ptoken) {
362 int64_t child_index;
363 struct ctf_field_class *child_fc;
364 const char *ft_name = ptoken_get_string(cur_ptoken);
365
366 BT_CPPLOGD_SPEC(ctx->logger, "Current path token: token=\"{}\"", ft_name);
367
368 /* Find to which index corresponds the current path token */
369 if (fc->type == CTF_FIELD_CLASS_TYPE_ARRAY || fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE) {
370 child_index = -1;
371 } else {
372 child_index =
373 ctf_field_class_compound_get_field_class_index_from_orig_name(fc, ft_name);
374 if (child_index < 0) {
375 /*
376 * Error: field name does not exist or
377 * wrong current class.
378 */
379 BT_CPPLOGD_SPEC(ctx->logger,
380 "Cannot get index of field class: "
381 "field-name=\"{}\", "
382 "src-index={}, "
383 "child-index={}, "
384 "first-level-done={}",
385 ft_name, src_index, child_index, first_level_done);
386 ret = -1;
387 goto end;
388 } else if (child_index > src_index && !first_level_done) {
389 BT_CPPLOGD_SPEC(ctx->logger,
390 "Child field class is located after source field class: "
391 "field-name=\"{}\", "
392 "src-index={}, "
393 "child-index={}, "
394 "first-level-done={}",
395 ft_name, src_index, child_index, first_level_done);
396 ret = -1;
397 goto end;
398 }
399
400 /* Next path token */
401 cur_ptoken = g_list_next(cur_ptoken);
402 first_level_done = true;
403 }
404
405 /* Create new field path entry */
406 ctf_field_path_append_index(field_path, child_index);
407
408 /* Get child field class */
409 child_fc = ctf_field_class_compound_borrow_field_class_by_index(fc, child_index);
410 BT_ASSERT(child_fc);
411
412 /* Move child class to current class */
413 fc = child_fc;
414 }
415
416 end:
417 return ret;
418 }
419
420 /*
421 * Converts a known absolute path token list to a field path object
422 * within the resolving context `ctx`.
423 *
424 * `field_path` is an output parameter owned by the caller that must be
425 * filled here.
426 */
427 static int absolute_ptokens_to_field_path(GList *ptokens, struct ctf_field_path *field_path,
428 struct resolve_context *ctx)
429 {
430 int ret = 0;
431 GList *cur_ptoken;
432 struct ctf_field_class *fc;
433
434 /*
435 * Make sure we're not referring to a scope within a translated
436 * object.
437 */
438 switch (field_path->root) {
439 case CTF_SCOPE_PACKET_HEADER:
440 if (ctx->tc->is_translated) {
441 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
442 "Trace class is already translated: "
443 "root-scope={}",
444 field_path->root);
445 ret = -1;
446 goto end;
447 }
448
449 break;
450 case CTF_SCOPE_PACKET_CONTEXT:
451 case CTF_SCOPE_EVENT_HEADER:
452 case CTF_SCOPE_EVENT_COMMON_CONTEXT:
453 if (!ctx->sc) {
454 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
455 "No current stream class: "
456 "root-scope={}",
457 field_path->root);
458 ret = -1;
459 goto end;
460 }
461
462 if (ctx->sc->is_translated) {
463 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
464 "Stream class is already translated: "
465 "root-scope={}",
466 field_path->root);
467 ret = -1;
468 goto end;
469 }
470
471 break;
472 case CTF_SCOPE_EVENT_SPECIFIC_CONTEXT:
473 case CTF_SCOPE_EVENT_PAYLOAD:
474 if (!ctx->ec) {
475 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
476 "No current event class: "
477 "root-scope={}",
478 field_path->root);
479 ret = -1;
480 goto end;
481 }
482
483 if (ctx->ec->is_translated) {
484 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
485 "Event class is already translated: "
486 "root-scope={}",
487 field_path->root);
488 ret = -1;
489 goto end;
490 }
491
492 break;
493
494 default:
495 bt_common_abort();
496 }
497
498 /* Skip absolute path tokens */
499 cur_ptoken = g_list_nth(ptokens, absolute_path_prefix_ptoken_counts[field_path->root]);
500
501 /* Start with root class */
502 fc = borrow_class_from_ctx(ctx, field_path->root);
503 if (!fc) {
504 /* Error: root class is not available */
505 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
506 "Root field class is not available: "
507 "root-scope={}",
508 field_path->root);
509 ret = -1;
510 goto end;
511 }
512
513 /* Locate target */
514 ret = ptokens_to_field_path(cur_ptoken, field_path, fc, INT64_MAX, ctx);
515
516 end:
517 return ret;
518 }
519
520 /*
521 * Converts a known relative path token list to a field path object
522 * within the resolving context `ctx`.
523 *
524 * `field_path` is an output parameter owned by the caller that must be
525 * filled here.
526 */
527 static int relative_ptokens_to_field_path(GList *ptokens, struct ctf_field_path *field_path,
528 struct resolve_context *ctx)
529 {
530 int ret = 0;
531 int64_t parent_pos_in_stack;
532 struct ctf_field_path tail_field_path;
533
534 ctf_field_path_init(&tail_field_path);
535 parent_pos_in_stack = field_class_stack_size(ctx->field_class_stack) - 1;
536
537 while (parent_pos_in_stack >= 0) {
538 struct ctf_field_class *parent_class =
539 field_class_stack_at(ctx->field_class_stack, parent_pos_in_stack)->fc;
540 int64_t cur_index =
541 field_class_stack_at(ctx->field_class_stack, parent_pos_in_stack)->index;
542
543 BT_CPPLOGD_SPEC(ctx->logger,
544 "Locating target field class from current parent field class: "
545 "parent-pos={}, parent-fc-addr={}, "
546 "cur-index={}",
547 parent_pos_in_stack, fmt::ptr(parent_class), cur_index);
548
549 /* Locate target from current parent class */
550 ret = ptokens_to_field_path(ptokens, &tail_field_path, parent_class, cur_index, ctx);
551 if (ret) {
552 /* Not found... yet */
553 BT_CPPLOGD_STR_SPEC(ctx->logger, "Not found at this point.");
554 ctf_field_path_clear(&tail_field_path);
555 } else {
556 /* Found: stitch tail field path to head field path */
557 uint64_t i = 0;
558 size_t tail_field_path_len = tail_field_path.path->len;
559
560 while (BT_TRUE) {
561 struct ctf_field_class *cur_class =
562 field_class_stack_at(ctx->field_class_stack, i)->fc;
563 int64_t index = field_class_stack_at(ctx->field_class_stack, i)->index;
564
565 if (cur_class == parent_class) {
566 break;
567 }
568
569 ctf_field_path_append_index(field_path, index);
570 i++;
571 }
572
573 for (i = 0; i < tail_field_path_len; i++) {
574 int64_t index = ctf_field_path_borrow_index_by_index(&tail_field_path, i);
575
576 ctf_field_path_append_index(field_path, (int64_t) index);
577 }
578 break;
579 }
580
581 parent_pos_in_stack--;
582 }
583
584 if (parent_pos_in_stack < 0) {
585 /* Not found */
586 ret = -1;
587 }
588
589 ctf_field_path_fini(&tail_field_path);
590 return ret;
591 }
592
593 /*
594 * Converts a path string to a field path object within the resolving
595 * context `ctx`.
596 */
597 static int pathstr_to_field_path(const char *pathstr, struct ctf_field_path *field_path,
598 struct resolve_context *ctx)
599 {
600 int ret = 0;
601 enum ctf_scope root_scope;
602 GList *ptokens = NULL;
603
604 /* Convert path string to path tokens */
605 ptokens = pathstr_to_ptokens(pathstr, ctx);
606 if (!ptokens) {
607 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
608 "Cannot convert path string to path tokens: "
609 "path=\"{}\"",
610 pathstr);
611 ret = -1;
612 goto end;
613 }
614
615 /* Absolute or relative path? */
616 root_scope = get_root_scope_from_absolute_pathstr(pathstr, ctx);
617
618 if (root_scope == CTF_SCOPE_PACKET_UNKNOWN) {
619 /* Relative path: start with current root scope */
620 field_path->root = ctx->root_scope;
621 BT_CPPLOGD_SPEC(ctx->logger,
622 "Detected relative path: starting with current root scope: "
623 "scope={}",
624 field_path->root);
625 ret = relative_ptokens_to_field_path(ptokens, field_path, ctx);
626 if (ret) {
627 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
628 "Cannot get relative field path of path string: "
629 "path=\"{}\", start-scope={}, end-scope={}",
630 pathstr, ctx->root_scope, field_path->root);
631 goto end;
632 }
633 } else {
634 /* Absolute path: use found root scope */
635 field_path->root = root_scope;
636 BT_CPPLOGD_SPEC(ctx->logger,
637 "Detected absolute path: using root scope: "
638 "scope={}",
639 field_path->root);
640 ret = absolute_ptokens_to_field_path(ptokens, field_path, ctx);
641 if (ret) {
642 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
643 "Cannot get absolute field path of path string: "
644 "path=\"{}\", root-scope={}",
645 pathstr, root_scope);
646 goto end;
647 }
648 }
649
650 if (ret == 0) {
651 BT_CPPLOGD_SPEC(ctx->logger, "Found field path: path=\"{}\", field-path=\"{}\"", pathstr,
652 *field_path);
653 }
654
655 end:
656 ptokens_destroy(ptokens);
657 return ret;
658 }
659
660 /*
661 * Retrieves a field class by following the field path `field_path` in
662 * the resolving context `ctx`.
663 */
664 static struct ctf_field_class *field_path_to_field_class(struct ctf_field_path *field_path,
665 struct resolve_context *ctx)
666 {
667 uint64_t i;
668 struct ctf_field_class *fc;
669
670 /* Start with root class */
671 fc = borrow_class_from_ctx(ctx, field_path->root);
672 if (!fc) {
673 /* Error: root class is not available */
674 BT_CPPLOGE_APPEND_CAUSE_SPEC(
675 ctx->logger, "Root field class is not available: root-scope={}", field_path->root);
676 goto end;
677 }
678
679 /* Locate target */
680 for (i = 0; i < field_path->path->len; i++) {
681 struct ctf_field_class *child_fc;
682 int64_t child_index = ctf_field_path_borrow_index_by_index(field_path, i);
683
684 /* Get child field class */
685 child_fc = ctf_field_class_compound_borrow_field_class_by_index(fc, child_index);
686 BT_ASSERT(child_fc);
687
688 /* Move child class to current class */
689 fc = child_fc;
690 }
691
692 end:
693 return fc;
694 }
695
696 /*
697 * Fills the equivalent field path object of the context class stack.
698 */
699 static void get_ctx_stack_field_path(struct resolve_context *ctx, struct ctf_field_path *field_path)
700 {
701 uint64_t i;
702
703 BT_ASSERT(field_path);
704 field_path->root = ctx->root_scope;
705 ctf_field_path_clear(field_path);
706
707 for (i = 0; i < field_class_stack_size(ctx->field_class_stack); i++) {
708 struct field_class_stack_frame *frame = field_class_stack_at(ctx->field_class_stack, i);
709
710 ctf_field_path_append_index(field_path, frame->index);
711 }
712 }
713
714 /*
715 * Returns the index of the lowest common ancestor of two field path
716 * objects having the same root scope.
717 */
718 static int64_t get_field_paths_lca_index(struct ctf_field_path *field_path1,
719 struct ctf_field_path *field_path2,
720 struct resolve_context *ctx)
721 {
722 int64_t lca_index = 0;
723 uint64_t field_path1_len, field_path2_len;
724
725 BT_CPPLOGD_SPEC(ctx->logger,
726 "Finding lowest common ancestor (LCA) between two field paths: "
727 "field-path-1=\"{}\", field-path-2=\"{}\"",
728 *field_path1, *field_path2);
729
730 /*
731 * Start from both roots and find the first mismatch.
732 */
733 BT_ASSERT(field_path1->root == field_path2->root);
734 field_path1_len = field_path1->path->len;
735 field_path2_len = field_path2->path->len;
736
737 while (true) {
738 int64_t target_index, ctx_index;
739
740 if (lca_index == (int64_t) field_path2_len || lca_index == (int64_t) field_path1_len) {
741 /*
742 * This means that both field paths never split.
743 * This is invalid because the target cannot be
744 * an ancestor of the source.
745 */
746 BT_CPPLOGE_APPEND_CAUSE_SPEC(
747 ctx->logger,
748 "Source field class is an ancestor of target field class or vice versa: "
749 "lca-index={}, "
750 "field-path-1-len={}, "
751 "field-path-2-len={}",
752 lca_index, field_path1_len, field_path2_len);
753 lca_index = -1;
754 break;
755 }
756
757 target_index = ctf_field_path_borrow_index_by_index(field_path1, lca_index);
758 ctx_index = ctf_field_path_borrow_index_by_index(field_path2, lca_index);
759
760 if (target_index != ctx_index) {
761 /* LCA index is the previous */
762 break;
763 }
764
765 lca_index++;
766 }
767
768 BT_CPPLOGD_SPEC(ctx->logger, "Found LCA: lca-index={}", lca_index);
769 return lca_index;
770 }
771
772 /*
773 * Validates a target field path.
774 */
775 static int validate_target_field_path(struct ctf_field_path *target_field_path,
776 struct ctf_field_class *target_fc,
777 struct resolve_context *ctx)
778 {
779 int ret = 0;
780 struct ctf_field_path ctx_field_path;
781 uint64_t target_field_path_len = target_field_path->path->len;
782 int64_t lca_index;
783
784 /* Get context field path */
785 ctf_field_path_init(&ctx_field_path);
786 get_ctx_stack_field_path(ctx, &ctx_field_path);
787
788 /*
789 * Make sure the target is not a root.
790 */
791 if (target_field_path_len == 0) {
792 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
793 "Target field path's length is 0 (targeting the root).");
794 ret = -1;
795 goto end;
796 }
797
798 /*
799 * Make sure the root of the target field path is not located
800 * after the context field path's root.
801 */
802 if (target_field_path->root > ctx_field_path.root) {
803 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
804 "Target field class is located after source field class: "
805 "target-root={}, source-root={}",
806 target_field_path->root, ctx_field_path.root);
807 ret = -1;
808 goto end;
809 }
810
811 if (target_field_path->root == ctx_field_path.root) {
812 int64_t target_index, ctx_index;
813
814 /*
815 * Find the index of the lowest common ancestor of both field
816 * paths.
817 */
818 lca_index = get_field_paths_lca_index(target_field_path, &ctx_field_path, ctx);
819 if (lca_index < 0) {
820 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger, "Cannot get least common ancestor.");
821 ret = -1;
822 goto end;
823 }
824
825 /*
826 * Make sure the target field path is located before the
827 * context field path.
828 */
829 target_index =
830 ctf_field_path_borrow_index_by_index(target_field_path, (uint64_t) lca_index);
831 ctx_index = ctf_field_path_borrow_index_by_index(&ctx_field_path, (uint64_t) lca_index);
832
833 if (target_index >= ctx_index) {
834 BT_CPPLOGE_APPEND_CAUSE_SPEC(
835 ctx->logger,
836 "Target field class's index is greater than or equal to source field class's index in LCA: "
837 "lca-index={}, "
838 "target-index={}, "
839 "source-index={}",
840 lca_index, target_index, ctx_index);
841 ret = -1;
842 goto end;
843 }
844 }
845
846 /*
847 * Make sure the target class has the right class and properties.
848 */
849 switch (ctx->cur_fc->type) {
850 case CTF_FIELD_CLASS_TYPE_VARIANT:
851 if (target_fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
852 BT_CPPLOGE_APPEND_CAUSE_SPEC(
853 ctx->logger,
854 "Variant field class's tag field class is not an enumeration field class: "
855 "tag-fc-addr={}, tag-fc-id={}",
856 fmt::ptr(target_fc), (int) target_fc->type);
857 ret = -1;
858 goto end;
859 }
860 break;
861 case CTF_FIELD_CLASS_TYPE_SEQUENCE:
862 {
863 if (target_fc->type != CTF_FIELD_CLASS_TYPE_INT &&
864 target_fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
865 BT_CPPLOGE_APPEND_CAUSE_SPEC(
866 ctx->logger,
867 "Sequence field class's length field class is not an unsigned integer field class: "
868 "length-fc-addr={}, length-fc-id={}",
869 fmt::ptr(target_fc), (int) target_fc->type);
870 ret = -1;
871 goto end;
872 }
873
874 ctf_field_class_int *int_fc = ctf_field_class_as_int(target_fc);
875
876 if (int_fc->is_signed) {
877 BT_CPPLOGE_APPEND_CAUSE_SPEC(
878 ctx->logger,
879 "Sequence field class's length field class is not an unsigned integer field class: "
880 "length-fc-addr={}, length-fc-id={}",
881 fmt::ptr(target_fc), (int) target_fc->type);
882 ret = -1;
883 goto end;
884 }
885 break;
886 }
887 default:
888 bt_common_abort();
889 }
890
891 end:
892 ctf_field_path_fini(&ctx_field_path);
893 return ret;
894 }
895
896 /*
897 * Resolves a variant or sequence field class `fc`.
898 */
899 static int resolve_sequence_or_variant_field_class(struct ctf_field_class *fc,
900 struct resolve_context *ctx)
901 {
902 int ret = 0;
903 const char *pathstr;
904 struct ctf_field_path target_field_path;
905 struct ctf_field_class *target_fc = NULL;
906
907 ctf_field_path_init(&target_field_path);
908
909 /* Get path string */
910 switch (fc->type) {
911 case CTF_FIELD_CLASS_TYPE_SEQUENCE:
912 {
913 struct ctf_field_class_sequence *seq_fc = ctf_field_class_as_sequence(fc);
914 pathstr = seq_fc->length_ref->str;
915 break;
916 }
917 case CTF_FIELD_CLASS_TYPE_VARIANT:
918 {
919 struct ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
920 pathstr = var_fc->tag_ref->str;
921 break;
922 }
923 default:
924 bt_common_abort();
925 }
926
927 if (!pathstr) {
928 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger, "Cannot get path string.");
929 ret = -1;
930 goto end;
931 }
932
933 /* Get target field path out of path string */
934 ret = pathstr_to_field_path(pathstr, &target_field_path, ctx);
935 if (ret) {
936 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
937 "Cannot get target field path for path string: "
938 "path=\"{}\"",
939 pathstr);
940 goto end;
941 }
942
943 /* Get target field class */
944 target_fc = field_path_to_field_class(&target_field_path, ctx);
945 if (!target_fc) {
946 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
947 "Cannot get target field class for path string: "
948 "path=\"{}\", target-field-path=\"{}\"",
949 pathstr, target_field_path);
950 ret = -1;
951 goto end;
952 }
953
954 ret = validate_target_field_path(&target_field_path, target_fc, ctx);
955 if (ret) {
956 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
957 "Invalid target field path for path string: "
958 "path=\"{}\", target-field-path=\"{}\"",
959 pathstr, target_field_path);
960 goto end;
961 }
962
963 /* Set target field path and target field class */
964 switch (fc->type) {
965 case CTF_FIELD_CLASS_TYPE_SEQUENCE:
966 {
967 ctf_field_class_sequence *seq_fc = ctf_field_class_as_sequence(fc);
968
969 ctf_field_path_copy_content(&seq_fc->length_path, &target_field_path);
970 seq_fc->length_fc = ctf_field_class_as_int(target_fc);
971 break;
972 }
973 case CTF_FIELD_CLASS_TYPE_VARIANT:
974 {
975 ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
976
977 ctf_field_path_copy_content(&var_fc->tag_path, &target_field_path);
978 ctf_field_class_variant_set_tag_field_class(var_fc, ctf_field_class_as_enum(target_fc));
979 break;
980 }
981 default:
982 bt_common_abort();
983 }
984
985 end:
986 ctf_field_path_fini(&target_field_path);
987 return ret;
988 }
989
990 /*
991 * Resolves a field class `fc`.
992 */
993 static int resolve_field_class(struct ctf_field_class *fc, struct resolve_context *ctx)
994 {
995 int ret = 0;
996
997 if (!fc) {
998 /* Field class is not available; still valid */
999 goto end;
1000 }
1001
1002 ctx->cur_fc = fc;
1003
1004 /* Resolve sequence/variant field class */
1005 switch (fc->type) {
1006 case CTF_FIELD_CLASS_TYPE_SEQUENCE:
1007 case CTF_FIELD_CLASS_TYPE_VARIANT:
1008 ret = resolve_sequence_or_variant_field_class(fc, ctx);
1009 if (ret) {
1010 BT_CPPLOGE_APPEND_CAUSE_SPEC(
1011 ctx->logger,
1012 "Cannot resolve sequence field class's length or variant field class's tag: "
1013 "ret={}, fc-addr={}",
1014 ret, fmt::ptr(fc));
1015 goto end;
1016 }
1017
1018 break;
1019 default:
1020 break;
1021 }
1022
1023 /* Recurse into compound classes */
1024 switch (fc->type) {
1025 case CTF_FIELD_CLASS_TYPE_STRUCT:
1026 case CTF_FIELD_CLASS_TYPE_VARIANT:
1027 case CTF_FIELD_CLASS_TYPE_SEQUENCE:
1028 case CTF_FIELD_CLASS_TYPE_ARRAY:
1029 {
1030 uint64_t i;
1031 uint64_t field_count = ctf_field_class_compound_get_field_class_count(fc);
1032
1033 ret = field_class_stack_push(ctx->field_class_stack, fc, ctx);
1034 if (ret) {
1035 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
1036 "Cannot push field class on context's stack: "
1037 "fc-addr={}",
1038 fmt::ptr(fc));
1039 goto end;
1040 }
1041
1042 for (i = 0; i < field_count; i++) {
1043 struct ctf_field_class *child_fc =
1044 ctf_field_class_compound_borrow_field_class_by_index(fc, i);
1045
1046 BT_ASSERT(child_fc);
1047
1048 if (fc->type == CTF_FIELD_CLASS_TYPE_ARRAY ||
1049 fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE) {
1050 field_class_stack_peek(ctx->field_class_stack)->index = -1;
1051 } else {
1052 field_class_stack_peek(ctx->field_class_stack)->index = (int64_t) i;
1053 }
1054
1055 BT_CPPLOGD_SPEC(ctx->logger,
1056 "Resolving field class's child field class: "
1057 "parent-fc-addr={}, child-fc-addr={}, "
1058 "index={}, count={}",
1059 fmt::ptr(fc), fmt::ptr(child_fc), i, field_count);
1060 ret = resolve_field_class(child_fc, ctx);
1061 if (ret) {
1062 goto end;
1063 }
1064 }
1065
1066 field_class_stack_pop(ctx->field_class_stack, ctx);
1067 break;
1068 }
1069 default:
1070 break;
1071 }
1072
1073 end:
1074 return ret;
1075 }
1076
1077 /*
1078 * Resolves the root field class corresponding to the scope `root_scope`.
1079 */
1080 static int resolve_root_class(enum ctf_scope root_scope, struct resolve_context *ctx)
1081 {
1082 int ret;
1083
1084 BT_ASSERT(field_class_stack_size(ctx->field_class_stack) == 0);
1085 ctx->root_scope = root_scope;
1086 ret = resolve_field_class(borrow_class_from_ctx(ctx, root_scope), ctx);
1087 ctx->root_scope = CTF_SCOPE_PACKET_UNKNOWN;
1088 return ret;
1089 }
1090
1091 static int resolve_event_class_field_classes(struct resolve_context *ctx,
1092 struct ctf_event_class *ec)
1093 {
1094 int ret = 0;
1095
1096 BT_ASSERT(!ctx->scopes.event_spec_context);
1097 BT_ASSERT(!ctx->scopes.event_payload);
1098
1099 if (ec->is_translated) {
1100 goto end;
1101 }
1102
1103 ctx->ec = ec;
1104 ctx->scopes.event_spec_context = ec->spec_context_fc;
1105 ret = resolve_root_class(CTF_SCOPE_EVENT_COMMON_CONTEXT, ctx);
1106 if (ret) {
1107 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
1108 "Cannot resolve event specific context field class: "
1109 "ret={}",
1110 ret);
1111 goto end;
1112 }
1113
1114 ctx->scopes.event_payload = ec->payload_fc;
1115 ret = resolve_root_class(CTF_SCOPE_EVENT_PAYLOAD, ctx);
1116 if (ret) {
1117 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
1118 "Cannot resolve event payload field class: "
1119 "ret={}",
1120 ret);
1121 goto end;
1122 }
1123
1124 end:
1125 ctx->scopes.event_spec_context = NULL;
1126 ctx->scopes.event_payload = NULL;
1127 ctx->ec = NULL;
1128 return ret;
1129 }
1130
1131 static int resolve_stream_class_field_classes(struct resolve_context *ctx,
1132 struct ctf_stream_class *sc)
1133 {
1134 int ret = 0;
1135 uint64_t i;
1136
1137 BT_ASSERT(!ctx->scopes.packet_context);
1138 BT_ASSERT(!ctx->scopes.event_header);
1139 BT_ASSERT(!ctx->scopes.event_common_context);
1140 ctx->sc = sc;
1141
1142 if (!sc->is_translated) {
1143 ctx->scopes.packet_context = sc->packet_context_fc;
1144 ret = resolve_root_class(CTF_SCOPE_PACKET_CONTEXT, ctx);
1145 if (ret) {
1146 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
1147 "Cannot resolve packet context field class: "
1148 "ret={}",
1149 ret);
1150 goto end;
1151 }
1152
1153 ctx->scopes.event_header = sc->event_header_fc;
1154 ret = resolve_root_class(CTF_SCOPE_EVENT_HEADER, ctx);
1155 if (ret) {
1156 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
1157 "Cannot resolve event header field class: "
1158 "ret={}",
1159 ret);
1160 goto end;
1161 }
1162
1163 ctx->scopes.event_common_context = sc->event_common_context_fc;
1164 ret = resolve_root_class(CTF_SCOPE_EVENT_COMMON_CONTEXT, ctx);
1165 if (ret) {
1166 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
1167 "Cannot resolve event common context field class: "
1168 "ret={}",
1169 ret);
1170 goto end;
1171 }
1172 }
1173
1174 ctx->scopes.packet_context = sc->packet_context_fc;
1175 ctx->scopes.event_header = sc->event_header_fc;
1176 ctx->scopes.event_common_context = sc->event_common_context_fc;
1177
1178 for (i = 0; i < sc->event_classes->len; i++) {
1179 ctf_event_class *ec = (ctf_event_class *) sc->event_classes->pdata[i];
1180
1181 ret = resolve_event_class_field_classes(ctx, ec);
1182 if (ret) {
1183 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
1184 "Cannot resolve event class's field classes: "
1185 "ec-id={}, ec-name=\"{}\"",
1186 ec->id, ec->name->str);
1187 goto end;
1188 }
1189 }
1190
1191 end:
1192 ctx->scopes.packet_context = NULL;
1193 ctx->scopes.event_header = NULL;
1194 ctx->scopes.event_common_context = NULL;
1195 ctx->sc = NULL;
1196 return ret;
1197 }
1198
1199 int ctf_trace_class_resolve_field_classes(struct ctf_trace_class *tc,
1200 const bt2c::Logger& parentLogger)
1201 {
1202 int ret = 0;
1203 uint64_t i;
1204
1205 resolve_context local_ctx(parentLogger);
1206 local_ctx.tc = tc;
1207 local_ctx.scopes.packet_header = tc->packet_header_fc;
1208 local_ctx.root_scope = CTF_SCOPE_PACKET_HEADER;
1209
1210 struct resolve_context *ctx = &local_ctx;
1211
1212 /* Initialize class stack */
1213 ctx->field_class_stack = field_class_stack_create();
1214 if (!ctx->field_class_stack) {
1215 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger, "Cannot create field class stack.");
1216 ret = -1;
1217 goto end;
1218 }
1219
1220 if (!tc->is_translated) {
1221 ctx->scopes.packet_header = tc->packet_header_fc;
1222 ret = resolve_root_class(CTF_SCOPE_PACKET_HEADER, ctx);
1223 if (ret) {
1224 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
1225 "Cannot resolve packet header field class: "
1226 "ret={}",
1227 ret);
1228 goto end;
1229 }
1230 }
1231
1232 ctx->scopes.packet_header = tc->packet_header_fc;
1233
1234 for (i = 0; i < tc->stream_classes->len; i++) {
1235 ctf_stream_class *sc = (ctf_stream_class *) tc->stream_classes->pdata[i];
1236
1237 ret = resolve_stream_class_field_classes(ctx, sc);
1238 if (ret) {
1239 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctx->logger,
1240 "Cannot resolve stream class's field classes: "
1241 "sc-id={}",
1242 sc->id);
1243 goto end;
1244 }
1245 }
1246
1247 end:
1248 field_class_stack_destroy(ctx->field_class_stack);
1249 return ret;
1250 }
This page took 0.10315 seconds and 4 git commands to generate.