lib: add enum bt_resolve_field_xref_status
[deliverable/babeltrace.git] / src / lib / trace-ir / resolve-field-path.c
1 /*
2 * SPDX-License-Identifier: MIT
3 *
4 * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
5 */
6
7 #define BT_LOG_TAG "LIB/RESOLVE-FIELD-PATH"
8 #include "lib/logging.h"
9
10 #include "lib/assert-cond.h"
11 #include "common/assert.h"
12 #include <babeltrace2/trace-ir/field-path.h>
13 #include <limits.h>
14 #include <stdbool.h>
15 #include <stdint.h>
16 #include <inttypes.h>
17 #include <glib.h>
18
19 #include "field-class.h"
20 #include "field-path.h"
21 #include "resolve-field-path.h"
22
23 static
24 bool find_field_class_recursive(struct bt_field_class *fc,
25 struct bt_field_class *tgt_fc, struct bt_field_path *field_path)
26 {
27 bool found = false;
28
29 if (tgt_fc == fc) {
30 found = true;
31 goto end;
32 }
33
34 if (bt_field_class_type_is(fc->type, BT_FIELD_CLASS_TYPE_OPTION)) {
35 struct bt_field_class_option *opt_fc = (void *) fc;
36 struct bt_field_path_item item = {
37 .type = BT_FIELD_PATH_ITEM_TYPE_CURRENT_OPTION_CONTENT,
38 .index = UINT64_C(-1),
39 };
40
41 bt_field_path_append_item(field_path, &item);
42 found = find_field_class_recursive(opt_fc->content_fc,
43 tgt_fc, field_path);
44 if (found) {
45 goto end;
46 }
47
48 bt_field_path_remove_last_item(field_path);
49 } else if (fc->type == BT_FIELD_CLASS_TYPE_STRUCTURE ||
50 bt_field_class_type_is(fc->type,
51 BT_FIELD_CLASS_TYPE_VARIANT)) {
52 struct bt_field_class_named_field_class_container *container_fc =
53 (void *) fc;
54 uint64_t i;
55
56 for (i = 0; i < container_fc->named_fcs->len; i++) {
57 struct bt_named_field_class *named_fc =
58 container_fc->named_fcs->pdata[i];
59 struct bt_field_path_item item = {
60 .type = BT_FIELD_PATH_ITEM_TYPE_INDEX,
61 .index = i,
62 };
63
64 bt_field_path_append_item(field_path, &item);
65 found = find_field_class_recursive(named_fc->fc,
66 tgt_fc, field_path);
67 if (found) {
68 goto end;
69 }
70
71 bt_field_path_remove_last_item(field_path);
72 }
73 } else if (bt_field_class_type_is(fc->type, BT_FIELD_CLASS_TYPE_ARRAY)) {
74 struct bt_field_class_array *array_fc = (void *) fc;
75 struct bt_field_path_item item = {
76 .type = BT_FIELD_PATH_ITEM_TYPE_CURRENT_ARRAY_ELEMENT,
77 .index = UINT64_C(-1),
78 };
79
80 bt_field_path_append_item(field_path, &item);
81 found = find_field_class_recursive(array_fc->element_fc,
82 tgt_fc, field_path);
83 if (found) {
84 goto end;
85 }
86
87 bt_field_path_remove_last_item(field_path);
88 }
89
90 end:
91 return found;
92 }
93
94 static
95 enum bt_resolve_field_xref_status find_field_class(
96 struct bt_field_class *root_fc,
97 enum bt_field_path_scope root_scope,
98 struct bt_field_class *tgt_fc,
99 struct bt_field_path **ret_field_path)
100 {
101 enum bt_resolve_field_xref_status ret;
102 struct bt_field_path *field_path = NULL;
103
104 if (!root_fc) {
105 ret = BT_RESOLVE_FIELD_XREF_STATUS_OK;
106 goto end;
107 }
108
109 field_path = bt_field_path_create();
110 if (!field_path) {
111 ret = BT_RESOLVE_FIELD_XREF_STATUS_MEMORY_ERROR;
112 goto end;
113 }
114
115 field_path->root = root_scope;
116 if (!find_field_class_recursive(root_fc, tgt_fc, field_path)) {
117 /* Not found here */
118 BT_OBJECT_PUT_REF_AND_RESET(field_path);
119 }
120
121 ret = BT_RESOLVE_FIELD_XREF_STATUS_OK;
122
123 end:
124 *ret_field_path = field_path;
125 return ret;
126 }
127
128 static
129 enum bt_resolve_field_xref_status find_field_class_in_ctx(
130 struct bt_field_class *fc,
131 struct bt_resolve_field_xref_context *ctx,
132 struct bt_field_path **ret_field_path)
133 {
134 enum bt_resolve_field_xref_status ret;
135
136 *ret_field_path = NULL;
137
138 ret = find_field_class(ctx->packet_context,
139 BT_FIELD_PATH_SCOPE_PACKET_CONTEXT, fc, ret_field_path);
140 if (ret != BT_RESOLVE_FIELD_XREF_STATUS_OK || *ret_field_path) {
141 goto end;
142 }
143
144 ret = find_field_class(ctx->event_common_context,
145 BT_FIELD_PATH_SCOPE_EVENT_COMMON_CONTEXT, fc, ret_field_path);
146 if (ret != BT_RESOLVE_FIELD_XREF_STATUS_OK || *ret_field_path) {
147 goto end;
148 }
149
150 ret = find_field_class(ctx->event_specific_context,
151 BT_FIELD_PATH_SCOPE_EVENT_SPECIFIC_CONTEXT, fc, ret_field_path);
152 if (ret != BT_RESOLVE_FIELD_XREF_STATUS_OK || *ret_field_path) {
153 goto end;
154 }
155
156 ret = find_field_class(ctx->event_payload,
157 BT_FIELD_PATH_SCOPE_EVENT_PAYLOAD, fc, ret_field_path);
158 if (ret != BT_RESOLVE_FIELD_XREF_STATUS_OK || *ret_field_path) {
159 goto end;
160 }
161
162 end:
163 return ret;
164 }
165
166 BT_ASSERT_COND_DEV_FUNC
167 static inline
168 bool target_is_before_source(struct bt_field_path *src_field_path,
169 struct bt_field_path *tgt_field_path)
170 {
171 bool is_valid = true;
172 uint64_t src_i = 0, tgt_i = 0;
173
174 if (tgt_field_path->root < src_field_path->root) {
175 goto end;
176 }
177
178 if (tgt_field_path->root > src_field_path->root) {
179 is_valid = false;
180 goto end;
181 }
182
183 BT_ASSERT(tgt_field_path->root == src_field_path->root);
184
185 for (src_i = 0, tgt_i = 0; src_i < src_field_path->items->len &&
186 tgt_i < tgt_field_path->items->len; src_i++, tgt_i++) {
187 struct bt_field_path_item *src_fp_item =
188 bt_field_path_borrow_item_by_index_inline(
189 src_field_path, src_i);
190 struct bt_field_path_item *tgt_fp_item =
191 bt_field_path_borrow_item_by_index_inline(
192 tgt_field_path, tgt_i);
193
194 if (src_fp_item->type == BT_FIELD_PATH_ITEM_TYPE_INDEX &&
195 tgt_fp_item->type == BT_FIELD_PATH_ITEM_TYPE_INDEX) {
196 if (tgt_fp_item->index > src_fp_item->index) {
197 is_valid = false;
198 goto end;
199 }
200 }
201
202 src_i++;
203 tgt_i++;
204 }
205
206 end:
207 return is_valid;
208 }
209
210 BT_ASSERT_COND_DEV_FUNC
211 static inline
212 struct bt_field_class *borrow_root_field_class(
213 struct bt_resolve_field_xref_context *ctx,
214 enum bt_field_path_scope scope)
215 {
216 switch (scope) {
217 case BT_FIELD_PATH_SCOPE_PACKET_CONTEXT:
218 return ctx->packet_context;
219 case BT_FIELD_PATH_SCOPE_EVENT_COMMON_CONTEXT:
220 return ctx->event_common_context;
221 case BT_FIELD_PATH_SCOPE_EVENT_SPECIFIC_CONTEXT:
222 return ctx->event_specific_context;
223 case BT_FIELD_PATH_SCOPE_EVENT_PAYLOAD:
224 return ctx->event_payload;
225 default:
226 bt_common_abort();
227 }
228
229 return NULL;
230 }
231
232 BT_ASSERT_COND_DEV_FUNC
233 static inline
234 struct bt_field_class *borrow_child_field_class(
235 struct bt_field_class *parent_fc,
236 struct bt_field_path_item *fp_item)
237 {
238 struct bt_field_class *child_fc = NULL;
239
240 if (bt_field_class_type_is(parent_fc->type,
241 BT_FIELD_CLASS_TYPE_OPTION)) {
242 struct bt_field_class_option *opt_fc = (void *) parent_fc;
243
244 BT_ASSERT(fp_item->type ==
245 BT_FIELD_PATH_ITEM_TYPE_CURRENT_OPTION_CONTENT);
246 child_fc = opt_fc->content_fc;
247 } else if (parent_fc->type == BT_FIELD_CLASS_TYPE_STRUCTURE ||
248 bt_field_class_type_is(parent_fc->type,
249 BT_FIELD_CLASS_TYPE_VARIANT)) {
250 struct bt_field_class_named_field_class_container *container_fc =
251 (void *) parent_fc;
252 struct bt_named_field_class *named_fc;
253
254 BT_ASSERT(fp_item->type == BT_FIELD_PATH_ITEM_TYPE_INDEX);
255 named_fc = container_fc->named_fcs->pdata[fp_item->index];
256 child_fc = named_fc->fc;
257 } else if (bt_field_class_type_is(parent_fc->type,
258 BT_FIELD_CLASS_TYPE_ARRAY)) {
259 struct bt_field_class_array *array_fc = (void *) parent_fc;
260
261 BT_ASSERT(fp_item->type ==
262 BT_FIELD_PATH_ITEM_TYPE_CURRENT_ARRAY_ELEMENT);
263 child_fc = array_fc->element_fc;
264 }
265
266 return child_fc;
267 }
268
269 BT_ASSERT_COND_DEV_FUNC
270 static inline
271 bool target_field_path_in_different_scope_has_struct_fc_only(
272 struct bt_field_path *src_field_path,
273 struct bt_field_path *tgt_field_path,
274 struct bt_resolve_field_xref_context *ctx)
275 {
276 bool is_valid = true;
277 uint64_t i = 0;
278 struct bt_field_class *fc;
279
280 if (src_field_path->root == tgt_field_path->root) {
281 goto end;
282 }
283
284 fc = borrow_root_field_class(ctx, tgt_field_path->root);
285
286 for (i = 0; i < tgt_field_path->items->len; i++) {
287 struct bt_field_path_item *fp_item =
288 bt_field_path_borrow_item_by_index_inline(
289 tgt_field_path, i);
290
291 if (bt_field_class_type_is(fc->type,
292 BT_FIELD_CLASS_TYPE_ARRAY) ||
293 bt_field_class_type_is(fc->type,
294 BT_FIELD_CLASS_TYPE_OPTION) ||
295 bt_field_class_type_is(fc->type,
296 BT_FIELD_CLASS_TYPE_VARIANT)) {
297 is_valid = false;
298 goto end;
299 }
300
301 BT_ASSERT(fp_item->type == BT_FIELD_PATH_ITEM_TYPE_INDEX);
302 fc = borrow_child_field_class(fc, fp_item);
303 }
304
305 end:
306 return is_valid;
307 }
308
309 BT_ASSERT_COND_DEV_FUNC
310 static inline
311 bool lca_is_structure_field_class(struct bt_field_path *src_field_path,
312 struct bt_field_path *tgt_field_path,
313 struct bt_resolve_field_xref_context *ctx)
314 {
315 bool is_valid = true;
316 struct bt_field_class *src_fc;
317 struct bt_field_class *tgt_fc;
318 struct bt_field_class *prev_fc = NULL;
319 uint64_t src_i = 0, tgt_i = 0;
320
321 if (src_field_path->root != tgt_field_path->root) {
322 goto end;
323 }
324
325 src_fc = borrow_root_field_class(ctx, src_field_path->root);
326 tgt_fc = borrow_root_field_class(ctx, tgt_field_path->root);
327 BT_ASSERT(src_fc);
328 BT_ASSERT(tgt_fc);
329
330 for (src_i = 0, tgt_i = 0; src_i < src_field_path->items->len &&
331 tgt_i < tgt_field_path->items->len; src_i++, tgt_i++) {
332 struct bt_field_path_item *src_fp_item =
333 bt_field_path_borrow_item_by_index_inline(
334 src_field_path, src_i);
335 struct bt_field_path_item *tgt_fp_item =
336 bt_field_path_borrow_item_by_index_inline(
337 tgt_field_path, tgt_i);
338
339 if (src_fc != tgt_fc) {
340 if (!prev_fc) {
341 /*
342 * This is correct: the LCA is the root
343 * scope field class, which must be a
344 * structure field class.
345 */
346 break;
347 }
348
349 if (prev_fc->type != BT_FIELD_CLASS_TYPE_STRUCTURE) {
350 is_valid = false;
351 }
352
353 break;
354 }
355
356 prev_fc = src_fc;
357 src_fc = borrow_child_field_class(src_fc, src_fp_item);
358 tgt_fc = borrow_child_field_class(tgt_fc, tgt_fp_item);
359 }
360
361 end:
362 return is_valid;
363 }
364
365 BT_ASSERT_COND_DEV_FUNC
366 static inline
367 bool lca_to_target_has_struct_fc_only(struct bt_field_path *src_field_path,
368 struct bt_field_path *tgt_field_path,
369 struct bt_resolve_field_xref_context *ctx)
370 {
371 bool is_valid = true;
372 struct bt_field_class *src_fc;
373 struct bt_field_class *tgt_fc;
374 uint64_t src_i = 0, tgt_i = 0;
375
376 if (src_field_path->root != tgt_field_path->root) {
377 goto end;
378 }
379
380 src_fc = borrow_root_field_class(ctx, src_field_path->root);
381 tgt_fc = borrow_root_field_class(ctx, tgt_field_path->root);
382 BT_ASSERT(src_fc);
383 BT_ASSERT(tgt_fc);
384 BT_ASSERT(src_fc == tgt_fc);
385
386 /* Find LCA */
387 for (src_i = 0, tgt_i = 0; src_i < src_field_path->items->len &&
388 tgt_i < tgt_field_path->items->len; src_i++, tgt_i++) {
389 struct bt_field_path_item *src_fp_item =
390 bt_field_path_borrow_item_by_index_inline(
391 src_field_path, src_i);
392 struct bt_field_path_item *tgt_fp_item =
393 bt_field_path_borrow_item_by_index_inline(
394 tgt_field_path, tgt_i);
395
396 if (src_i != tgt_i) {
397 /* Next field class is different: LCA is `tgt_fc` */
398 break;
399 }
400
401 src_fc = borrow_child_field_class(src_fc, src_fp_item);
402 tgt_fc = borrow_child_field_class(tgt_fc, tgt_fp_item);
403 }
404
405 /* Only structure field classes to the target */
406 for (; tgt_i < tgt_field_path->items->len; tgt_i++) {
407 struct bt_field_path_item *tgt_fp_item =
408 bt_field_path_borrow_item_by_index_inline(
409 tgt_field_path, tgt_i);
410
411 if (bt_field_class_type_is(tgt_fc->type,
412 BT_FIELD_CLASS_TYPE_ARRAY) ||
413 bt_field_class_type_is(tgt_fc->type,
414 BT_FIELD_CLASS_TYPE_OPTION) ||
415 bt_field_class_type_is(tgt_fc->type,
416 BT_FIELD_CLASS_TYPE_VARIANT)) {
417 is_valid = false;
418 goto end;
419 }
420
421 tgt_fc = borrow_child_field_class(tgt_fc, tgt_fp_item);
422 }
423
424 end:
425 return is_valid;
426 }
427
428 BT_ASSERT_COND_DEV_FUNC
429 static inline
430 bool field_path_is_valid(struct bt_field_class *src_fc,
431 struct bt_field_class *tgt_fc,
432 struct bt_resolve_field_xref_context *ctx)
433 {
434 struct bt_field_path *src_field_path;
435 struct bt_field_path *tgt_field_path = NULL;
436 enum bt_resolve_field_xref_status status;
437 bool is_valid;
438
439 status = find_field_class_in_ctx(src_fc, ctx, &src_field_path);
440 BT_ASSERT(status == BT_RESOLVE_FIELD_XREF_STATUS_OK);
441
442 if (!src_field_path) {
443 BT_ASSERT_COND_DEV_MSG("Cannot find requesting field class in "
444 "resolving context: %!+F", src_fc);
445 is_valid = false;
446 goto end;
447 }
448
449 status = find_field_class_in_ctx(tgt_fc, ctx, &tgt_field_path);
450 BT_ASSERT(status == BT_RESOLVE_FIELD_XREF_STATUS_OK);
451
452 if (!tgt_field_path) {
453 BT_ASSERT_COND_DEV_MSG("Cannot find target field class in "
454 "resolving context: %!+F", tgt_fc);
455 is_valid = false;
456 goto end;
457 }
458
459 /* Target must be before source */
460 if (!target_is_before_source(src_field_path, tgt_field_path)) {
461 BT_ASSERT_COND_DEV_MSG("Target field class is located after "
462 "requesting field class: %![req-fc-]+F, %![tgt-fc-]+F",
463 src_fc, tgt_fc);
464 is_valid = false;
465 goto end;
466 }
467
468 /*
469 * If target is in a different scope than source, there are no
470 * array or variant field classes on the way to the target.
471 */
472 if (!target_field_path_in_different_scope_has_struct_fc_only(
473 src_field_path, tgt_field_path, ctx)) {
474 BT_ASSERT_COND_DEV_MSG("Target field class is located in a "
475 "different scope than requesting field class, "
476 "but within an array or a variant field class: "
477 "%![req-fc-]+F, %![tgt-fc-]+F",
478 src_fc, tgt_fc);
479 is_valid = false;
480 goto end;
481 }
482
483 /* Same scope: LCA must be a structure field class */
484 if (!lca_is_structure_field_class(src_field_path, tgt_field_path, ctx)) {
485 BT_ASSERT_COND_DEV_MSG("Lowest common ancestor of target and "
486 "requesting field classes is not a structure field class: "
487 "%![req-fc-]+F, %![tgt-fc-]+F",
488 src_fc, tgt_fc);
489 is_valid = false;
490 goto end;
491 }
492
493 /* Same scope: path from LCA to target has no array/variant FTs */
494 if (!lca_to_target_has_struct_fc_only(src_field_path, tgt_field_path,
495 ctx)) {
496 BT_ASSERT_COND_DEV_MSG("Path from lowest common ancestor of target "
497 "and requesting field classes to target field class "
498 "contains an array or a variant field class: "
499 "%![req-fc-]+F, %![tgt-fc-]+F", src_fc, tgt_fc);
500 is_valid = false;
501 goto end;
502 }
503
504 is_valid = true;
505
506 end:
507 bt_object_put_ref(src_field_path);
508 bt_object_put_ref(tgt_field_path);
509 return is_valid;
510 }
511
512 static
513 enum bt_resolve_field_xref_status resolve_field_path(
514 struct bt_field_class *src_fc,
515 struct bt_field_class *tgt_fc,
516 struct bt_resolve_field_xref_context *ctx,
517 const char *api_func,
518 struct bt_field_path **ret_field_path)
519 {
520 BT_ASSERT_PRE_DEV_FROM_FUNC(api_func, "valid-field-class",
521 field_path_is_valid(src_fc, tgt_fc, ctx),
522 "Invalid target field class: %![req-fc-]+F, %![tgt-fc-]+F",
523 src_fc, tgt_fc);
524 return find_field_class_in_ctx(tgt_fc, ctx, ret_field_path);
525 }
526
527 BT_HIDDEN
528 enum bt_resolve_field_xref_status bt_resolve_field_paths(
529 struct bt_field_class *fc,
530 struct bt_resolve_field_xref_context *ctx,
531 const char *api_func)
532 {
533 enum bt_resolve_field_xref_status status;
534
535 BT_ASSERT(fc);
536
537 /* Resolving part for dynamic array and variant field classes */
538 if (bt_field_class_type_is(fc->type,
539 BT_FIELD_CLASS_TYPE_OPTION_WITH_SELECTOR_FIELD)) {
540 struct bt_field_class_option_with_selector_field *opt_fc = (void *) fc;
541
542 if (opt_fc->selector_field_xref_kind == FIELD_XREF_KIND_PATH) {
543 BT_ASSERT(opt_fc->selector_field.path.class);
544 BT_ASSERT(!opt_fc->selector_field.path.path);
545 status = resolve_field_path(
546 fc, opt_fc->selector_field.path.class, ctx, __func__,
547 &opt_fc->selector_field.path.path);
548 if (status != BT_RESOLVE_FIELD_XREF_STATUS_OK) {
549 goto end;
550 }
551 }
552 } else if (fc->type == BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY_WITH_LENGTH_FIELD) {
553 struct bt_field_class_array_dynamic *dyn_array_fc = (void *) fc;
554
555 if (dyn_array_fc->length_field.xref_kind == FIELD_XREF_KIND_PATH) {
556 BT_ASSERT(dyn_array_fc->length_field.path.class);
557 BT_ASSERT(!dyn_array_fc->length_field.path.path);
558 status = resolve_field_path(
559 fc, dyn_array_fc->length_field.path.class, ctx, __func__,
560 &dyn_array_fc->length_field.path.path);
561 if (status != BT_RESOLVE_FIELD_XREF_STATUS_OK) {
562 goto end;
563 }
564 }
565 } else if (bt_field_class_type_is(fc->type,
566 BT_FIELD_CLASS_TYPE_VARIANT_WITH_SELECTOR_FIELD)) {
567 struct bt_field_class_variant_with_selector_field *var_fc =
568 (void *) fc;
569
570 if (var_fc->selector_field_xref_kind == FIELD_XREF_KIND_PATH) {
571 BT_ASSERT(var_fc->selector_field.path.class);
572 BT_ASSERT(!var_fc->selector_field.path.path);
573 status = resolve_field_path(fc,
574 (void *) var_fc->selector_field.path.class, ctx,
575 __func__, &var_fc->selector_field.path.path);
576 if (status != BT_RESOLVE_FIELD_XREF_STATUS_OK) {
577 goto end;
578 }
579 }
580 }
581
582 /* Recursive part */
583 if (bt_field_class_type_is(fc->type, BT_FIELD_CLASS_TYPE_OPTION)) {
584 struct bt_field_class_option *opt_fc = (void *) fc;
585
586 status = bt_resolve_field_paths(opt_fc->content_fc, ctx, api_func);
587 if (status != BT_RESOLVE_FIELD_XREF_STATUS_OK) {
588 goto end;
589 }
590 } else if (fc->type == BT_FIELD_CLASS_TYPE_STRUCTURE ||
591 bt_field_class_type_is(fc->type,
592 BT_FIELD_CLASS_TYPE_VARIANT)) {
593 struct bt_field_class_named_field_class_container *container_fc =
594 (void *) fc;
595 uint64_t i;
596
597 for (i = 0; i < container_fc->named_fcs->len; i++) {
598 struct bt_named_field_class *named_fc =
599 container_fc->named_fcs->pdata[i];
600
601 status = bt_resolve_field_paths(named_fc->fc, ctx,
602 api_func);
603 if (status != BT_RESOLVE_FIELD_XREF_STATUS_OK) {
604 goto end;
605 }
606 }
607 } else if (bt_field_class_type_is(fc->type,
608 BT_FIELD_CLASS_TYPE_ARRAY)) {
609 struct bt_field_class_array *array_fc = (void *) fc;
610
611 status = bt_resolve_field_paths(array_fc->element_fc, ctx,
612 api_func);
613 if (status != BT_RESOLVE_FIELD_XREF_STATUS_OK) {
614 goto end;
615 }
616 }
617
618 status = BT_RESOLVE_FIELD_XREF_STATUS_OK;
619
620 end:
621 return status;
622 }
This page took 0.046428 seconds and 5 git commands to generate.