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