Commit | Line | Data |
---|---|---|
09840de5 PP |
1 | /* |
2 | * resolve.c | |
3 | * | |
4 | * Babeltrace - CTF IR: Type resolving internal | |
5 | * | |
6 | * Copyright 2015 Jérémie Galarneau <jeremie.galarneau@efficios.com> | |
7 | * Copyright 2016 Philippe Proulx <pproulx@efficios.com> | |
8 | * | |
9 | * Authors: Jérémie Galarneau <jeremie.galarneau@efficios.com> | |
10 | * Philippe Proulx <pproulx@efficios.com> | |
11 | * | |
12 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
13 | * of this software and associated documentation files (the "Software"), to deal | |
14 | * in the Software without restriction, including without limitation the rights | |
15 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
16 | * copies of the Software, and to permit persons to whom the Software is | |
17 | * furnished to do so, subject to the following conditions: | |
18 | * | |
19 | * The above copyright notice and this permission notice shall be included in | |
20 | * all copies or substantial portions of the Software. | |
21 | * | |
22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
23 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
25 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
26 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
27 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
28 | * SOFTWARE. | |
29 | */ | |
30 | ||
31 | #include <babeltrace/ctf-ir/event.h> | |
32 | #include <babeltrace/ctf-ir/stream-class.h> | |
33 | #include <babeltrace/ctf-ir/resolve-internal.h> | |
2e33ac5a | 34 | #include <babeltrace/ctf-ir/field-types.h> |
b011f6b0 PP |
35 | #include <babeltrace/ctf-ir/field-path.h> |
36 | #include <babeltrace/ctf-ir/field-path-internal.h> | |
09840de5 PP |
37 | #include <babeltrace/ctf-ir/event-internal.h> |
38 | #include <babeltrace/ref.h> | |
39 | #include <babeltrace/babeltrace-internal.h> | |
40 | #include <babeltrace/values.h> | |
c55a9f58 | 41 | #include <babeltrace/types.h> |
09840de5 PP |
42 | #include <limits.h> |
43 | #include <glib.h> | |
44 | ||
45 | #define _printf_error(fmt, args...) \ | |
46 | printf_verbose("[resolving] " fmt, ## args) | |
47 | ||
48 | typedef GPtrArray type_stack; | |
49 | ||
50 | /* | |
51 | * A stack frame. | |
52 | * | |
53 | * `type` contains a compound field type (structure, variant, array, | |
54 | * or sequence) and `index` indicates the index of the field type in | |
55 | * the upper frame (-1 for array and sequence field types). | |
56 | * | |
57 | * `type` is owned by the stack frame. | |
58 | */ | |
59 | struct type_stack_frame { | |
60 | struct bt_ctf_field_type *type; | |
61 | int index; | |
62 | }; | |
63 | ||
64 | /* | |
65 | * The current context of the resolving engine. | |
66 | * | |
67 | * `scopes` contain the 6 CTF scope field types (see CTF, sect. 7.3.2) | |
68 | * in the following order: | |
69 | * | |
70 | * * Packet header | |
71 | * * Packet context | |
72 | * * Event header | |
73 | * * Stream event context | |
74 | * * Event context | |
75 | * * Event payload | |
76 | */ | |
77 | struct resolve_context { | |
78 | struct bt_value *environment; | |
79 | struct bt_ctf_field_type *scopes[6]; | |
80 | ||
46df6b28 PP |
81 | /* Root scope being visited */ |
82 | enum bt_ctf_scope root_scope; | |
09840de5 PP |
83 | type_stack *type_stack; |
84 | struct bt_ctf_field_type *cur_field_type; | |
85 | }; | |
86 | ||
87 | /* TSDL dynamic scope prefixes as defined in CTF Section 7.3.2 */ | |
88 | static const char * const absolute_path_prefixes[] = { | |
46df6b28 PP |
89 | [BT_CTF_SCOPE_ENV] = "env.", |
90 | [BT_CTF_SCOPE_TRACE_PACKET_HEADER] = "trace.packet.header.", | |
91 | [BT_CTF_SCOPE_STREAM_PACKET_CONTEXT] = "stream.packet.context.", | |
92 | [BT_CTF_SCOPE_STREAM_EVENT_HEADER] = "stream.event.header.", | |
93 | [BT_CTF_SCOPE_STREAM_EVENT_CONTEXT] = "stream.event.context.", | |
94 | [BT_CTF_SCOPE_EVENT_CONTEXT] = "event.context.", | |
95 | [BT_CTF_SCOPE_EVENT_FIELDS] = "event.fields.", | |
09840de5 PP |
96 | }; |
97 | ||
98 | /* Number of path tokens used for the absolute prefixes */ | |
99 | static const int absolute_path_prefix_ptoken_counts[] = { | |
46df6b28 PP |
100 | [BT_CTF_SCOPE_ENV] = 1, |
101 | [BT_CTF_SCOPE_TRACE_PACKET_HEADER] = 3, | |
102 | [BT_CTF_SCOPE_STREAM_PACKET_CONTEXT] = 3, | |
103 | [BT_CTF_SCOPE_STREAM_EVENT_HEADER] = 3, | |
104 | [BT_CTF_SCOPE_STREAM_EVENT_CONTEXT] = 3, | |
105 | [BT_CTF_SCOPE_EVENT_CONTEXT] = 2, | |
106 | [BT_CTF_SCOPE_EVENT_FIELDS] = 2, | |
09840de5 PP |
107 | }; |
108 | ||
109 | /* | |
110 | * Destroys a type stack frame. | |
111 | */ | |
112 | static | |
113 | void type_stack_destroy_notify(gpointer data) | |
114 | { | |
115 | struct type_stack_frame *frame = data; | |
116 | ||
117 | BT_PUT(frame->type); | |
118 | g_free(frame); | |
119 | } | |
120 | ||
121 | /* | |
122 | * Creates a type stack. | |
123 | * | |
124 | * Return value is owned by the caller. | |
125 | */ | |
126 | static | |
127 | type_stack *type_stack_create(void) | |
128 | { | |
129 | return g_ptr_array_new_with_free_func(type_stack_destroy_notify); | |
130 | } | |
131 | ||
132 | /* | |
133 | * Destroys a type stack. | |
134 | */ | |
135 | static | |
136 | void type_stack_destroy(type_stack *stack) | |
137 | { | |
138 | g_ptr_array_free(stack, TRUE); | |
139 | } | |
140 | ||
141 | /* | |
142 | * Pushes a field type onto a type stack. | |
143 | * | |
144 | * `type` is owned by the caller (stack frame gets a new reference). | |
145 | */ | |
146 | static | |
147 | int type_stack_push(type_stack *stack, struct bt_ctf_field_type *type) | |
148 | { | |
149 | int ret = 0; | |
150 | struct type_stack_frame *frame = NULL; | |
151 | ||
152 | if (!stack || !type) { | |
153 | ret = -1; | |
154 | goto end; | |
155 | } | |
156 | ||
157 | frame = g_new0(struct type_stack_frame, 1); | |
158 | if (!frame) { | |
159 | ret = -1; | |
160 | goto end; | |
161 | } | |
162 | ||
163 | frame->type = bt_get(type); | |
164 | g_ptr_array_add(stack, frame); | |
165 | ||
166 | end: | |
167 | return ret; | |
168 | } | |
169 | ||
170 | /* | |
171 | * Checks whether or not `stack` is empty. | |
172 | */ | |
173 | static | |
c55a9f58 | 174 | bt_bool type_stack_empty(type_stack *stack) |
09840de5 PP |
175 | { |
176 | return stack->len == 0; | |
177 | } | |
178 | ||
179 | /* | |
180 | * Returns the number of frames in `stack`. | |
181 | */ | |
182 | static | |
183 | size_t type_stack_size(type_stack *stack) | |
184 | { | |
185 | return stack->len; | |
186 | } | |
187 | ||
188 | /* | |
189 | * Returns the top frame of `stack`. | |
190 | * | |
191 | * Return value is owned by `stack`. | |
192 | */ | |
193 | static | |
194 | struct type_stack_frame *type_stack_peek(type_stack *stack) | |
195 | { | |
196 | struct type_stack_frame *entry = NULL; | |
197 | ||
198 | if (!stack || type_stack_empty(stack)) { | |
199 | goto end; | |
200 | } | |
201 | ||
202 | entry = g_ptr_array_index(stack, stack->len - 1); | |
203 | end: | |
204 | return entry; | |
205 | } | |
206 | ||
207 | /* | |
208 | * Returns the frame at index `index` in `stack`. | |
209 | * | |
210 | * Return value is owned by `stack`. | |
211 | */ | |
212 | static | |
213 | struct type_stack_frame *type_stack_at(type_stack *stack, | |
214 | size_t index) | |
215 | { | |
216 | struct type_stack_frame *entry = NULL; | |
217 | ||
218 | if (!stack || index >= stack->len) { | |
219 | goto end; | |
220 | } | |
221 | ||
222 | entry = g_ptr_array_index(stack, index); | |
223 | ||
224 | end: | |
225 | return entry; | |
226 | } | |
227 | ||
228 | /* | |
229 | * Removes the top frame of `stack`. | |
230 | */ | |
231 | static | |
232 | void type_stack_pop(type_stack *stack) | |
233 | { | |
234 | if (!type_stack_empty(stack)) { | |
235 | /* | |
236 | * This will call the frame's destructor and free it, as | |
237 | * well as put its contained field type. | |
238 | */ | |
239 | g_ptr_array_set_size(stack, stack->len - 1); | |
240 | } | |
241 | } | |
242 | ||
243 | /* | |
244 | * Returns the scope field type of `scope` in the context `ctx`. | |
245 | * | |
246 | * Return value is owned by `ctx` on success. | |
247 | */ | |
248 | static | |
249 | struct bt_ctf_field_type *get_type_from_ctx(struct resolve_context *ctx, | |
46df6b28 | 250 | enum bt_ctf_scope scope) |
09840de5 | 251 | { |
46df6b28 PP |
252 | assert(scope >= BT_CTF_SCOPE_TRACE_PACKET_HEADER && |
253 | scope <= BT_CTF_SCOPE_EVENT_FIELDS); | |
09840de5 | 254 | |
46df6b28 | 255 | return ctx->scopes[scope - BT_CTF_SCOPE_TRACE_PACKET_HEADER]; |
09840de5 PP |
256 | } |
257 | ||
258 | /* | |
259 | * Returns the CTF scope from a path string. May return | |
260 | * CTF_NODE_UNKNOWN if the path is found to be relative. | |
261 | */ | |
262 | static | |
46df6b28 | 263 | enum bt_ctf_scope get_root_scope_from_absolute_pathstr(const char *pathstr) |
09840de5 | 264 | { |
46df6b28 PP |
265 | enum bt_ctf_scope scope; |
266 | enum bt_ctf_scope ret = BT_CTF_SCOPE_UNKNOWN; | |
09840de5 PP |
267 | const size_t prefixes_count = sizeof(absolute_path_prefixes) / |
268 | sizeof(*absolute_path_prefixes); | |
269 | ||
46df6b28 PP |
270 | for (scope = BT_CTF_SCOPE_ENV; scope < BT_CTF_SCOPE_ENV + |
271 | prefixes_count; scope++) { | |
09840de5 PP |
272 | /* |
273 | * Chech if path string starts with a known absolute | |
274 | * path prefix. | |
275 | * | |
276 | * Refer to CTF 7.3.2 STATIC AND DYNAMIC SCOPES. | |
277 | */ | |
46df6b28 PP |
278 | if (strncmp(pathstr, absolute_path_prefixes[scope], |
279 | strlen(absolute_path_prefixes[scope]))) { | |
09840de5 PP |
280 | /* Prefix does not match: try the next one */ |
281 | continue; | |
282 | } | |
283 | ||
284 | /* Found it! */ | |
46df6b28 | 285 | ret = scope; |
09840de5 PP |
286 | goto end; |
287 | } | |
288 | ||
289 | end: | |
290 | return ret; | |
291 | } | |
292 | ||
293 | /* | |
294 | * Destroys a path token. | |
295 | */ | |
296 | static | |
297 | void ptokens_destroy_func(gpointer ptoken, gpointer data) | |
298 | { | |
299 | g_string_free(ptoken, TRUE); | |
300 | } | |
301 | ||
302 | /* | |
303 | * Destroys a path token list. | |
304 | */ | |
305 | static | |
306 | void ptokens_destroy(GList *ptokens) | |
307 | { | |
308 | if (!ptokens) { | |
309 | return; | |
310 | } | |
311 | ||
312 | g_list_foreach(ptokens, ptokens_destroy_func, NULL); | |
313 | g_list_free(ptokens); | |
314 | } | |
315 | ||
316 | /* | |
317 | * Returns the string contained in a path token. | |
318 | */ | |
319 | static | |
320 | const char *ptoken_get_string(GList *ptoken) | |
321 | { | |
322 | GString *tokenstr = (GString *) ptoken->data; | |
323 | ||
324 | return tokenstr->str; | |
325 | } | |
326 | ||
327 | /* | |
328 | * Converts a path string to a path token list, that is, splits the | |
329 | * individual words of a path string into a list of individual | |
330 | * strings. | |
331 | * | |
332 | * Return value is owned by the caller on success. | |
333 | */ | |
334 | static | |
335 | GList *pathstr_to_ptokens(const char *pathstr) | |
336 | { | |
337 | const char *at = pathstr; | |
338 | const char *last = at; | |
339 | GList *ptokens = NULL; | |
340 | ||
341 | for (;;) { | |
342 | if (*at == '.' || *at == '\0') { | |
343 | GString *tokenstr; | |
344 | ||
345 | if (at == last) { | |
346 | /* Error: empty token */ | |
347 | _printf_error("Empty token in path string at position %d\n", | |
348 | (int) (at - pathstr)); | |
349 | goto error; | |
350 | } | |
351 | ||
352 | tokenstr = g_string_new(NULL); | |
353 | g_string_append_len(tokenstr, last, at - last); | |
354 | ptokens = g_list_append(ptokens, tokenstr); | |
355 | last = at + 1; | |
356 | } | |
357 | ||
358 | if (*at == '\0') { | |
359 | break; | |
360 | } | |
361 | ||
362 | at++; | |
363 | } | |
364 | ||
365 | return ptokens; | |
366 | ||
367 | error: | |
368 | ptokens_destroy(ptokens); | |
369 | return NULL; | |
370 | } | |
371 | ||
372 | /* | |
373 | * Converts a path token list to a field path object. The path token | |
374 | * list is relative from `type`. The index of the source looking for | |
375 | * its target within `type` is indicated by `src_index`. This can be | |
376 | * `INT_MAX` if the source is contained in `type`. | |
377 | * | |
378 | * `ptokens` is owned by the caller. `field_path` is an output parameter | |
379 | * owned by the caller that must be filled here. `type` is owned by the | |
380 | * caller. | |
381 | */ | |
382 | static | |
383 | int ptokens_to_field_path(GList *ptokens, struct bt_ctf_field_path *field_path, | |
384 | struct bt_ctf_field_type *type, int src_index) | |
385 | { | |
386 | int ret = 0; | |
387 | GList *cur_ptoken = ptokens; | |
c55a9f58 | 388 | bt_bool first_level_done = BT_FALSE; |
09840de5 PP |
389 | |
390 | /* Get our own reference */ | |
391 | bt_get(type); | |
392 | ||
393 | /* Locate target */ | |
394 | while (cur_ptoken) { | |
395 | int child_index; | |
396 | struct bt_ctf_field_type *child_type; | |
397 | const char *field_name = ptoken_get_string(cur_ptoken); | |
1487a16a | 398 | enum bt_ctf_field_type_id type_id = |
dc3fffef | 399 | bt_ctf_field_type_get_type_id(type); |
09840de5 PP |
400 | |
401 | /* Find to which index corresponds the current path token */ | |
402 | if (type_id == CTF_TYPE_ARRAY || type_id == CTF_TYPE_SEQUENCE) { | |
403 | child_index = -1; | |
404 | } else { | |
405 | child_index = bt_ctf_field_type_get_field_index(type, | |
406 | field_name); | |
407 | if (child_index < 0) { | |
408 | /* | |
409 | * Error: field name does not exist or | |
410 | * wrong current type. | |
411 | */ | |
412 | _printf_error("Cannot get index of field type named \"%s\"\n", | |
413 | field_name); | |
414 | ret = -1; | |
415 | goto end; | |
416 | } else if (child_index > src_index && | |
417 | !first_level_done) { | |
418 | _printf_error("Child type is located after source index (%d)\n", | |
419 | src_index); | |
420 | ret = -1; | |
421 | goto end; | |
422 | } | |
423 | ||
424 | /* Next path token */ | |
425 | cur_ptoken = g_list_next(cur_ptoken); | |
c55a9f58 | 426 | first_level_done = BT_TRUE; |
09840de5 PP |
427 | } |
428 | ||
429 | /* Create new field path entry */ | |
b011f6b0 | 430 | g_array_append_val(field_path->indexes, child_index); |
09840de5 PP |
431 | |
432 | /* Get child field type */ | |
433 | child_type = bt_ctf_field_type_get_field_at_index(type, | |
434 | child_index); | |
435 | if (!child_type) { | |
436 | _printf_error("Cannot get child type at index %d (field \"%s\")\n", | |
437 | child_index, field_name); | |
438 | ret = -1; | |
439 | goto end; | |
440 | } | |
441 | ||
442 | /* Move child type to current type */ | |
443 | BT_MOVE(type, child_type); | |
444 | } | |
445 | ||
446 | end: | |
447 | bt_put(type); | |
448 | return ret; | |
449 | } | |
450 | ||
451 | /* | |
452 | * Converts a known absolute path token list to a field path object | |
453 | * within the resolving context `ctx`. | |
454 | * | |
455 | * `ptokens` is owned by the caller. `field_path` is an output parameter | |
456 | * owned by the caller that must be filled here. | |
457 | */ | |
458 | static | |
459 | int absolute_ptokens_to_field_path(GList *ptokens, | |
460 | struct bt_ctf_field_path *field_path, | |
461 | struct resolve_context *ctx) | |
462 | { | |
463 | int ret = 0; | |
464 | GList *cur_ptoken; | |
465 | struct bt_ctf_field_type *type; | |
466 | ||
467 | /* Skip absolute path tokens */ | |
468 | cur_ptoken = g_list_nth(ptokens, | |
469 | absolute_path_prefix_ptoken_counts[field_path->root]); | |
470 | ||
471 | /* Start with root type */ | |
472 | type = get_type_from_ctx(ctx, field_path->root); | |
473 | if (!type) { | |
474 | /* Error: root type is not available */ | |
46df6b28 | 475 | _printf_error("Root type with scope type %d is not available\n", |
09840de5 PP |
476 | field_path->root); |
477 | ret = -1; | |
478 | goto end; | |
479 | } | |
480 | ||
481 | /* Locate target */ | |
482 | ret = ptokens_to_field_path(cur_ptoken, field_path, type, INT_MAX); | |
483 | ||
484 | end: | |
485 | return ret; | |
486 | } | |
487 | ||
488 | /* | |
489 | * Converts a known relative path token list to a field path object | |
490 | * within the resolving context `ctx`. | |
491 | * | |
492 | * `ptokens` is owned by the caller. `field_path` is an output parameter | |
493 | * owned by the caller that must be filled here. | |
494 | */ | |
495 | static | |
496 | int relative_ptokens_to_field_path(GList *ptokens, | |
497 | struct bt_ctf_field_path *field_path, | |
498 | struct resolve_context *ctx) | |
499 | { | |
500 | int ret = 0; | |
501 | int parent_pos_in_stack; | |
502 | struct bt_ctf_field_path *tail_field_path = bt_ctf_field_path_create(); | |
503 | ||
504 | if (!tail_field_path) { | |
505 | _printf_error("Cannot create field path\n"); | |
506 | ret = -1; | |
507 | goto end; | |
508 | } | |
509 | ||
510 | parent_pos_in_stack = type_stack_size(ctx->type_stack) - 1; | |
511 | ||
512 | while (parent_pos_in_stack >= 0) { | |
513 | struct bt_ctf_field_type *parent_type = | |
514 | type_stack_at(ctx->type_stack, | |
515 | parent_pos_in_stack)->type; | |
516 | int cur_index = type_stack_at(ctx->type_stack, | |
517 | parent_pos_in_stack)->index; | |
518 | ||
519 | /* Locate target from current parent type */ | |
520 | ret = ptokens_to_field_path(ptokens, tail_field_path, | |
521 | parent_type, cur_index); | |
522 | if (ret) { | |
523 | /* Not found... yet */ | |
524 | bt_ctf_field_path_clear(tail_field_path); | |
525 | } else { | |
526 | /* Found: stitch tail field path to head field path */ | |
527 | int i = 0; | |
528 | int tail_field_path_len = | |
b011f6b0 | 529 | tail_field_path->indexes->len; |
09840de5 | 530 | |
c55a9f58 | 531 | while (BT_TRUE) { |
09840de5 PP |
532 | struct bt_ctf_field_type *cur_type = |
533 | type_stack_at(ctx->type_stack, i)->type; | |
534 | int index = type_stack_at( | |
535 | ctx->type_stack, i)->index; | |
536 | ||
537 | if (cur_type == parent_type) { | |
538 | break; | |
539 | } | |
540 | ||
b011f6b0 | 541 | g_array_append_val(field_path->indexes, |
09840de5 PP |
542 | index); |
543 | i++; | |
544 | } | |
545 | ||
546 | for (i = 0; i < tail_field_path_len; i++) { | |
547 | int index = g_array_index( | |
b011f6b0 | 548 | tail_field_path->indexes, |
09840de5 PP |
549 | int, i); |
550 | ||
b011f6b0 | 551 | g_array_append_val(field_path->indexes, |
09840de5 PP |
552 | index); |
553 | } | |
554 | break; | |
555 | } | |
556 | ||
557 | parent_pos_in_stack--; | |
558 | } | |
559 | ||
560 | if (parent_pos_in_stack < 0) { | |
561 | /* Not found: look in previous scopes */ | |
562 | field_path->root--; | |
563 | ||
46df6b28 | 564 | while (field_path->root >= BT_CTF_SCOPE_TRACE_PACKET_HEADER) { |
09840de5 PP |
565 | struct bt_ctf_field_type *root_type; |
566 | bt_ctf_field_path_clear(field_path); | |
567 | ||
568 | root_type = get_type_from_ctx(ctx, field_path->root); | |
569 | if (!root_type) { | |
570 | field_path->root--; | |
571 | continue; | |
572 | } | |
573 | ||
574 | /* Locate target in previous scope */ | |
575 | ret = ptokens_to_field_path(ptokens, field_path, | |
576 | root_type, INT_MAX); | |
577 | if (ret) { | |
578 | /* Not found yet */ | |
579 | field_path->root--; | |
580 | continue; | |
581 | } | |
582 | ||
583 | /* Found */ | |
584 | break; | |
585 | } | |
586 | } | |
587 | ||
588 | end: | |
b011f6b0 | 589 | BT_PUT(tail_field_path); |
09840de5 PP |
590 | return ret; |
591 | } | |
592 | ||
593 | /* | |
594 | * Converts a path string to a field path object within the resolving | |
595 | * context `ctx`. | |
596 | * | |
597 | * Return value is owned by the caller on success. | |
598 | */ | |
599 | static | |
600 | struct bt_ctf_field_path *pathstr_to_field_path(const char *pathstr, | |
601 | struct resolve_context *ctx) | |
602 | { | |
603 | int ret; | |
46df6b28 | 604 | enum bt_ctf_scope root_scope; |
09840de5 PP |
605 | GList *ptokens = NULL; |
606 | struct bt_ctf_field_path *field_path = NULL; | |
607 | ||
608 | /* Create field path */ | |
609 | field_path = bt_ctf_field_path_create(); | |
610 | if (!field_path) { | |
611 | _printf_error("Cannot create field path\n"); | |
612 | ret = -1; | |
613 | goto end; | |
614 | } | |
615 | ||
616 | /* Convert path string to path tokens */ | |
617 | ptokens = pathstr_to_ptokens(pathstr); | |
618 | if (!ptokens) { | |
619 | _printf_error("Cannot convert path string \"%s\" to path tokens\n", | |
620 | pathstr); | |
621 | ret = -1; | |
622 | goto end; | |
623 | } | |
624 | ||
625 | /* Absolute or relative path? */ | |
46df6b28 | 626 | root_scope = get_root_scope_from_absolute_pathstr(pathstr); |
09840de5 | 627 | |
46df6b28 PP |
628 | if (root_scope == BT_CTF_SCOPE_UNKNOWN) { |
629 | /* Relative path: start with current root scope */ | |
630 | field_path->root = ctx->root_scope; | |
09840de5 PP |
631 | ret = relative_ptokens_to_field_path(ptokens, field_path, ctx); |
632 | if (ret) { | |
633 | _printf_error("Cannot get relative field path of path string \"%s\"\n", | |
634 | pathstr); | |
46df6b28 PP |
635 | _printf_error(" Starting at root scope %d, finished at root scope %d\n", |
636 | ctx->root_scope, field_path->root); | |
09840de5 PP |
637 | goto end; |
638 | } | |
46df6b28 | 639 | } else if (root_scope == BT_CTF_SCOPE_ENV) { |
09840de5 PP |
640 | _printf_error("Sequence field types referring the trace environment are not supported as of this version\n"); |
641 | ret = -1; | |
642 | goto end; | |
643 | } else { | |
46df6b28 PP |
644 | /* Absolute path: use found root scope */ |
645 | field_path->root = root_scope; | |
09840de5 PP |
646 | ret = absolute_ptokens_to_field_path(ptokens, field_path, ctx); |
647 | if (ret) { | |
648 | _printf_error("Cannot get absolute field path of path string \"%s\"\n", | |
649 | pathstr); | |
46df6b28 | 650 | _printf_error(" Looking in root scope %d\n", root_scope); |
09840de5 PP |
651 | goto end; |
652 | } | |
653 | } | |
654 | ||
655 | end: | |
656 | if (ret) { | |
b011f6b0 | 657 | BT_PUT(field_path); |
09840de5 PP |
658 | } |
659 | ||
660 | ptokens_destroy(ptokens); | |
661 | ||
662 | return field_path; | |
663 | } | |
664 | ||
665 | /* | |
666 | * Retrieves a field type by following the field path `field_path` in | |
667 | * the resolving context `ctx`. | |
668 | * | |
669 | * Return value is owned by the caller on success. | |
670 | */ | |
671 | static | |
672 | struct bt_ctf_field_type *field_path_to_field_type( | |
673 | struct bt_ctf_field_path *field_path, | |
674 | struct resolve_context *ctx) | |
675 | { | |
676 | int i; | |
677 | struct bt_ctf_field_type *type; | |
678 | ||
679 | /* Start with root type */ | |
680 | type = get_type_from_ctx(ctx, field_path->root); | |
681 | bt_get(type); | |
682 | if (!type) { | |
683 | /* Error: root type is not available */ | |
46df6b28 | 684 | _printf_error("Root type with scope type %d is not available\n", |
09840de5 PP |
685 | field_path->root); |
686 | goto error; | |
687 | } | |
688 | ||
689 | /* Locate target */ | |
b011f6b0 | 690 | for (i = 0; i < field_path->indexes->len; i++) { |
09840de5 PP |
691 | struct bt_ctf_field_type *child_type; |
692 | int child_index = | |
b011f6b0 | 693 | g_array_index(field_path->indexes, int, i); |
09840de5 PP |
694 | |
695 | /* Get child field type */ | |
696 | child_type = bt_ctf_field_type_get_field_at_index(type, | |
697 | child_index); | |
698 | if (!child_type) { | |
699 | _printf_error("Cannot get field type field at index %d\n", | |
700 | child_index); | |
701 | goto error; | |
702 | } | |
703 | ||
704 | /* Move child type to current type */ | |
705 | BT_MOVE(type, child_type); | |
706 | } | |
707 | ||
708 | return type; | |
709 | ||
710 | error: | |
711 | BT_PUT(type); | |
712 | return type; | |
713 | } | |
714 | ||
715 | /* | |
716 | * Returns the equivalent field path object of the context type stack. | |
717 | * | |
718 | * Return value is owned by the caller on success. | |
719 | */ | |
720 | static | |
721 | struct bt_ctf_field_path *get_ctx_stack_field_path(struct resolve_context *ctx) | |
722 | { | |
723 | int i; | |
724 | struct bt_ctf_field_path *field_path; | |
725 | ||
726 | /* Create field path */ | |
727 | field_path = bt_ctf_field_path_create(); | |
728 | if (!field_path) { | |
729 | _printf_error("Cannot create field path\n"); | |
730 | goto error; | |
731 | } | |
732 | ||
46df6b28 | 733 | field_path->root = ctx->root_scope; |
09840de5 PP |
734 | |
735 | for (i = 0; i < type_stack_size(ctx->type_stack); i++) { | |
736 | struct type_stack_frame *frame; | |
737 | ||
738 | frame = type_stack_at(ctx->type_stack, i); | |
b011f6b0 | 739 | g_array_append_val(field_path->indexes, frame->index); |
09840de5 PP |
740 | } |
741 | ||
742 | return field_path; | |
743 | ||
744 | error: | |
b011f6b0 PP |
745 | BT_PUT(field_path); |
746 | return field_path; | |
09840de5 PP |
747 | } |
748 | ||
749 | /* | |
750 | * Returns the lowest common ancestor of two field path objects | |
751 | * having the same root scope. | |
752 | * | |
753 | * `field_path1` and `field_path2` are owned by the caller. | |
754 | */ | |
755 | int get_field_paths_lca_index(struct bt_ctf_field_path *field_path1, | |
756 | struct bt_ctf_field_path *field_path2) | |
757 | { | |
758 | int lca_index = 0; | |
759 | int field_path1_len, field_path2_len; | |
760 | ||
761 | /* | |
762 | * Start from both roots and find the first mismatch. | |
763 | */ | |
764 | assert(field_path1->root == field_path2->root); | |
b011f6b0 PP |
765 | field_path1_len = field_path1->indexes->len; |
766 | field_path2_len = field_path2->indexes->len; | |
09840de5 | 767 | |
c55a9f58 | 768 | while (BT_TRUE) { |
09840de5 PP |
769 | int target_index, ctx_index; |
770 | ||
771 | if (lca_index == field_path2_len || | |
772 | lca_index == field_path1_len) { | |
773 | /* | |
774 | * This means that both field paths never split. | |
775 | * This is invalid because the target cannot be | |
776 | * an ancestor of the source. | |
777 | */ | |
778 | _printf_error("In source and target: one is an ancestor of the other\n"); | |
779 | lca_index = -1; | |
780 | break; | |
781 | } | |
782 | ||
b011f6b0 | 783 | target_index = g_array_index(field_path1->indexes, int, |
09840de5 | 784 | lca_index); |
b011f6b0 | 785 | ctx_index = g_array_index(field_path2->indexes, int, |
09840de5 PP |
786 | lca_index); |
787 | ||
788 | if (target_index != ctx_index) { | |
789 | /* LCA index is the previous */ | |
790 | break; | |
791 | } | |
792 | ||
793 | lca_index++; | |
794 | } | |
795 | ||
796 | return lca_index; | |
797 | } | |
798 | ||
799 | /* | |
800 | * Validates a target field path. | |
801 | * | |
802 | * `target_field_path` and `target_type` are owned by the caller. | |
803 | */ | |
804 | static | |
805 | int validate_target_field_path(struct bt_ctf_field_path *target_field_path, | |
806 | struct bt_ctf_field_type *target_type, | |
807 | struct resolve_context *ctx) | |
808 | { | |
809 | int ret = 0; | |
810 | struct bt_ctf_field_path *ctx_field_path; | |
b011f6b0 | 811 | int target_field_path_len = target_field_path->indexes->len; |
09840de5 PP |
812 | int lca_index; |
813 | int ctx_cur_field_type_id; | |
814 | int target_type_id; | |
815 | ||
816 | /* Get context field path */ | |
817 | ctx_field_path = get_ctx_stack_field_path(ctx); | |
818 | if (!ctx_field_path) { | |
819 | _printf_error("Cannot get source field path\n"); | |
820 | ret = -1; | |
821 | goto end; | |
822 | } | |
823 | ||
824 | /* | |
825 | * Make sure the target is not a root. | |
826 | */ | |
827 | if (target_field_path_len == 0) { | |
828 | _printf_error("Target field path's length is 0 (targeting the root)\n"); | |
829 | ret = -1; | |
830 | goto end; | |
831 | } | |
832 | ||
833 | /* | |
834 | * Make sure the root of the target field path is not located | |
835 | * after the context field path's root. | |
836 | */ | |
837 | if (target_field_path->root > ctx_field_path->root) { | |
838 | _printf_error("Target is located after source\n"); | |
839 | ret = -1; | |
840 | goto end; | |
841 | } | |
842 | ||
843 | if (target_field_path->root == ctx_field_path->root) { | |
844 | int target_index, ctx_index; | |
845 | ||
846 | /* | |
847 | * Find the index of the lowest common ancestor of both field | |
848 | * paths. | |
849 | */ | |
850 | lca_index = get_field_paths_lca_index(target_field_path, | |
851 | ctx_field_path); | |
852 | if (lca_index < 0) { | |
853 | _printf_error("Cannot get least common ancestor\n"); | |
854 | ret = -1; | |
855 | goto end; | |
856 | } | |
857 | ||
858 | /* | |
859 | * Make sure the target field path is located before the | |
860 | * context field path. | |
861 | */ | |
b011f6b0 | 862 | target_index = g_array_index(target_field_path->indexes, |
09840de5 | 863 | int, lca_index); |
b011f6b0 | 864 | ctx_index = g_array_index(ctx_field_path->indexes, |
09840de5 PP |
865 | int, lca_index); |
866 | ||
867 | if (target_index >= ctx_index) { | |
868 | _printf_error("Target index (%d) is greater or equal to source index (%d) in LCA\n", | |
869 | target_index, ctx_index); | |
870 | ret = -1; | |
871 | goto end; | |
872 | } | |
873 | } | |
874 | ||
875 | /* | |
876 | * Make sure the target type has the right type and properties. | |
877 | */ | |
878 | ctx_cur_field_type_id = bt_ctf_field_type_get_type_id( | |
879 | ctx->cur_field_type); | |
880 | target_type_id = bt_ctf_field_type_get_type_id(target_type); | |
881 | ||
882 | if (ctx_cur_field_type_id == CTF_TYPE_VARIANT) { | |
883 | if (target_type_id != CTF_TYPE_ENUM) { | |
884 | _printf_error("Variant type's tag field type is not an enumeration\n"); | |
885 | ret = -1; | |
886 | goto end; | |
887 | } | |
888 | } else if (ctx_cur_field_type_id == CTF_TYPE_SEQUENCE) { | |
889 | if (target_type_id != CTF_TYPE_INTEGER || | |
890 | bt_ctf_field_type_integer_get_signed( | |
891 | target_type)) { | |
892 | _printf_error("Sequence type's length field type is not an unsigned integer\n"); | |
893 | ret = -1; | |
894 | goto end; | |
895 | } | |
896 | } else { | |
c55a9f58 | 897 | assert(BT_FALSE); |
09840de5 PP |
898 | } |
899 | ||
900 | end: | |
b011f6b0 | 901 | BT_PUT(ctx_field_path); |
09840de5 PP |
902 | return ret; |
903 | } | |
904 | ||
905 | /* | |
906 | * Resolves a variant or sequence field type `type`. | |
907 | * | |
908 | * `type` is owned by the caller. | |
909 | */ | |
910 | static | |
911 | int resolve_sequence_or_variant_type(struct bt_ctf_field_type *type, | |
912 | struct resolve_context *ctx) | |
913 | { | |
914 | int ret = 0; | |
915 | const char *pathstr; | |
916 | int type_id = bt_ctf_field_type_get_type_id(type); | |
917 | struct bt_ctf_field_path *target_field_path = NULL; | |
918 | struct bt_ctf_field_type *target_type = NULL; | |
919 | ||
920 | /* Get path string */ | |
921 | switch (type_id) { | |
922 | case CTF_TYPE_SEQUENCE: | |
923 | pathstr = | |
924 | bt_ctf_field_type_sequence_get_length_field_name(type); | |
925 | break; | |
926 | case CTF_TYPE_VARIANT: | |
927 | pathstr = | |
928 | bt_ctf_field_type_variant_get_tag_name(type); | |
929 | break; | |
930 | default: | |
c55a9f58 | 931 | assert(BT_FALSE); |
5febb458 JG |
932 | ret = -1; |
933 | goto end; | |
09840de5 PP |
934 | } |
935 | ||
936 | /* Get target field path out of path string */ | |
937 | target_field_path = pathstr_to_field_path(pathstr, ctx); | |
938 | if (!target_field_path) { | |
939 | _printf_error("Cannot get target field path for path string \"%s\"\n", | |
940 | pathstr); | |
941 | ret = -1; | |
942 | goto end; | |
943 | } | |
944 | ||
945 | /* Get target field type */ | |
946 | target_type = field_path_to_field_type(target_field_path, ctx); | |
947 | if (!target_type) { | |
948 | _printf_error("Cannot get target field type for path string \"%s\"\n", | |
949 | pathstr); | |
950 | ret = -1; | |
951 | goto end; | |
952 | } | |
953 | ||
954 | ret = validate_target_field_path(target_field_path, target_type, ctx); | |
955 | if (ret) { | |
956 | _printf_error("Invalid target field path for path string \"%s\"\n", | |
957 | pathstr); | |
958 | goto end; | |
959 | } | |
960 | ||
961 | /* Set target field path and target field type */ | |
962 | if (type_id == CTF_TYPE_SEQUENCE) { | |
963 | ret = bt_ctf_field_type_sequence_set_length_field_path( | |
964 | type, target_field_path); | |
965 | if (ret) { | |
966 | _printf_error("Cannot set sequence field type's length field path\n"); | |
967 | goto end; | |
968 | } | |
09840de5 PP |
969 | } else if (type_id == CTF_TYPE_VARIANT) { |
970 | ret = bt_ctf_field_type_variant_set_tag_field_path( | |
971 | type, target_field_path); | |
972 | if (ret) { | |
973 | _printf_error("Cannot set variant field type's tag field path\n"); | |
974 | goto end; | |
975 | } | |
976 | ||
4b5fcb78 PP |
977 | ret = bt_ctf_field_type_variant_set_tag_field_type( |
978 | type, target_type); | |
09840de5 PP |
979 | if (ret) { |
980 | _printf_error("Cannot set variant field type's tag field type\n"); | |
981 | goto end; | |
982 | } | |
983 | } else { | |
c55a9f58 | 984 | assert(BT_FALSE); |
09840de5 PP |
985 | } |
986 | ||
987 | end: | |
b011f6b0 | 988 | BT_PUT(target_field_path); |
09840de5 PP |
989 | BT_PUT(target_type); |
990 | return ret; | |
991 | } | |
992 | ||
993 | /* | |
994 | * Resolves a field type `type`. | |
995 | * | |
996 | * `type` is owned by the caller. | |
997 | */ | |
998 | static | |
999 | int resolve_type(struct bt_ctf_field_type *type, struct resolve_context *ctx) | |
1000 | { | |
1001 | int ret = 0; | |
1002 | int type_id; | |
1003 | ||
1004 | if (!type) { | |
1005 | /* Type is not available; still valid */ | |
1006 | goto end; | |
1007 | } | |
1008 | ||
1009 | type_id = bt_ctf_field_type_get_type_id(type); | |
1010 | ctx->cur_field_type = type; | |
1011 | ||
1012 | /* Resolve sequence/variant field type */ | |
1013 | switch (type_id) { | |
1014 | case CTF_TYPE_SEQUENCE: | |
1015 | case CTF_TYPE_VARIANT: | |
1016 | ret = resolve_sequence_or_variant_type(type, ctx); | |
1017 | if (ret) { | |
1018 | _printf_error("Cannot resolve sequence or variant field type's length/tag\n"); | |
1019 | goto end; | |
1020 | } | |
1021 | break; | |
1022 | default: | |
1023 | break; | |
1024 | } | |
1025 | ||
1026 | /* Recurse into compound types */ | |
1027 | switch (type_id) { | |
1028 | case CTF_TYPE_STRUCT: | |
1029 | case CTF_TYPE_VARIANT: | |
1030 | case CTF_TYPE_SEQUENCE: | |
1031 | case CTF_TYPE_ARRAY: | |
1032 | { | |
1033 | int field_count, f_index; | |
1034 | ||
1035 | ret = type_stack_push(ctx->type_stack, type); | |
1036 | if (ret) { | |
1037 | _printf_error("Cannot push field type on type stack\n"); | |
1038 | _printf_error(" Stack size: %zu\n", | |
1039 | type_stack_size(ctx->type_stack)); | |
1040 | goto end; | |
1041 | } | |
1042 | ||
1043 | field_count = bt_ctf_field_type_get_field_count(type); | |
1044 | if (field_count < 0) { | |
1045 | _printf_error("Cannot get field type field count\n"); | |
1046 | ret = field_count; | |
1047 | goto end; | |
1048 | } | |
1049 | ||
1050 | for (f_index = 0; f_index < field_count; f_index++) { | |
1051 | struct bt_ctf_field_type *child_type = | |
1052 | bt_ctf_field_type_get_field_at_index(type, | |
1053 | f_index); | |
1054 | ||
1055 | if (!child_type) { | |
1056 | _printf_error("Cannot get field type field at index %d/%d\n", | |
1057 | f_index, field_count); | |
1058 | ret = -1; | |
1059 | goto end; | |
1060 | } | |
1061 | ||
1062 | if (type_id == CTF_TYPE_ARRAY || | |
1063 | type_id == CTF_TYPE_SEQUENCE) { | |
1064 | type_stack_peek(ctx->type_stack)->index = -1; | |
1065 | } else { | |
1066 | type_stack_peek(ctx->type_stack)->index = | |
1067 | f_index; | |
1068 | } | |
1069 | ||
1070 | ret = resolve_type(child_type, ctx); | |
1071 | BT_PUT(child_type); | |
1072 | if (ret) { | |
1073 | goto end; | |
1074 | } | |
1075 | } | |
1076 | ||
1077 | type_stack_pop(ctx->type_stack); | |
1078 | break; | |
1079 | } | |
1080 | default: | |
1081 | break; | |
1082 | } | |
1083 | ||
1084 | end: | |
1085 | return ret; | |
1086 | } | |
1087 | ||
1088 | /* | |
1089 | * Resolves the root field type corresponding to the scope `root_scope`. | |
1090 | */ | |
1091 | static | |
e2d83ebb | 1092 | int resolve_root_type(enum bt_ctf_scope root_scope, struct resolve_context *ctx) |
09840de5 PP |
1093 | { |
1094 | int ret; | |
1095 | ||
1096 | assert(type_stack_size(ctx->type_stack) == 0); | |
46df6b28 PP |
1097 | ctx->root_scope = root_scope; |
1098 | ret = resolve_type(get_type_from_ctx(ctx, root_scope), ctx); | |
1099 | ctx->root_scope = BT_CTF_SCOPE_UNKNOWN; | |
09840de5 PP |
1100 | |
1101 | return ret; | |
1102 | } | |
1103 | ||
1104 | BT_HIDDEN | |
1105 | int bt_ctf_resolve_types( | |
1106 | struct bt_value *environment, | |
1107 | struct bt_ctf_field_type *packet_header_type, | |
1108 | struct bt_ctf_field_type *packet_context_type, | |
1109 | struct bt_ctf_field_type *event_header_type, | |
1110 | struct bt_ctf_field_type *stream_event_ctx_type, | |
1111 | struct bt_ctf_field_type *event_context_type, | |
1112 | struct bt_ctf_field_type *event_payload_type, | |
1113 | enum bt_ctf_resolve_flag flags) | |
1114 | { | |
1115 | int ret = 0; | |
1116 | struct resolve_context ctx = { | |
1117 | .environment = environment, | |
1118 | .scopes = { | |
1119 | packet_header_type, | |
1120 | packet_context_type, | |
1121 | event_header_type, | |
1122 | stream_event_ctx_type, | |
1123 | event_context_type, | |
1124 | event_payload_type, | |
1125 | }, | |
46df6b28 | 1126 | .root_scope = BT_CTF_SCOPE_UNKNOWN, |
09840de5 PP |
1127 | }; |
1128 | ||
1129 | /* Initialize type stack */ | |
1130 | ctx.type_stack = type_stack_create(); | |
1131 | if (!ctx.type_stack) { | |
1132 | printf_error("Cannot create type stack\n"); | |
1133 | ret = -1; | |
1134 | goto end; | |
1135 | } | |
1136 | ||
1137 | /* Resolve packet header type */ | |
1138 | if (flags & BT_CTF_RESOLVE_FLAG_PACKET_HEADER) { | |
46df6b28 | 1139 | ret = resolve_root_type(BT_CTF_SCOPE_TRACE_PACKET_HEADER, &ctx); |
09840de5 PP |
1140 | if (ret) { |
1141 | _printf_error("Cannot resolve trace packet header type\n"); | |
1142 | goto end; | |
1143 | } | |
1144 | } | |
1145 | ||
1146 | /* Resolve packet context type */ | |
1147 | if (flags & BT_CTF_RESOLVE_FLAG_PACKET_CONTEXT) { | |
46df6b28 | 1148 | ret = resolve_root_type(BT_CTF_SCOPE_STREAM_PACKET_CONTEXT, &ctx); |
09840de5 PP |
1149 | if (ret) { |
1150 | _printf_error("Cannot resolve stream packet context type\n"); | |
1151 | goto end; | |
1152 | } | |
1153 | } | |
1154 | ||
1155 | /* Resolve event header type */ | |
1156 | if (flags & BT_CTF_RESOLVE_FLAG_EVENT_HEADER) { | |
46df6b28 | 1157 | ret = resolve_root_type(BT_CTF_SCOPE_STREAM_EVENT_HEADER, &ctx); |
09840de5 PP |
1158 | if (ret) { |
1159 | _printf_error("Cannot resolve stream event header type\n"); | |
1160 | goto end; | |
1161 | } | |
1162 | } | |
1163 | ||
1164 | /* Resolve stream event context type */ | |
1165 | if (flags & BT_CTF_RESOLVE_FLAG_STREAM_EVENT_CTX) { | |
46df6b28 | 1166 | ret = resolve_root_type(BT_CTF_SCOPE_STREAM_EVENT_CONTEXT, &ctx); |
09840de5 PP |
1167 | if (ret) { |
1168 | _printf_error("Cannot resolve stream event context type\n"); | |
1169 | goto end; | |
1170 | } | |
1171 | } | |
1172 | ||
1173 | /* Resolve event context type */ | |
1174 | if (flags & BT_CTF_RESOLVE_FLAG_EVENT_CONTEXT) { | |
46df6b28 | 1175 | ret = resolve_root_type(BT_CTF_SCOPE_EVENT_CONTEXT, &ctx); |
09840de5 PP |
1176 | if (ret) { |
1177 | _printf_error("Cannot resolve event context type\n"); | |
1178 | goto end; | |
1179 | } | |
1180 | } | |
1181 | ||
1182 | /* Resolve event payload type */ | |
1183 | if (flags & BT_CTF_RESOLVE_FLAG_EVENT_PAYLOAD) { | |
46df6b28 | 1184 | ret = resolve_root_type(BT_CTF_SCOPE_EVENT_FIELDS, &ctx); |
09840de5 PP |
1185 | if (ret) { |
1186 | _printf_error("Cannot resolve event payload type\n"); | |
1187 | goto end; | |
1188 | } | |
1189 | } | |
1190 | ||
1191 | end: | |
1192 | type_stack_destroy(ctx.type_stack); | |
1193 | ||
1194 | return ret; | |
1195 | } |