Fix: src.ctf.fs: use BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE when applicable
[babeltrace.git] / src / plugins / ctf / fs-src / fs.c
1 /*
2 * fs.c
3 *
4 * Babeltrace CTF file system Reader Component
5 *
6 * Copyright 2015-2017 Philippe Proulx <pproulx@efficios.com>
7 * Copyright 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a copy
10 * of this software and associated documentation files (the "Software"), to deal
11 * in the Software without restriction, including without limitation the rights
12 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 * copies of the Software, and to permit persons to whom the Software is
14 * furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 * SOFTWARE.
26 */
27
28 #define BT_COMP_LOG_SELF_COMP self_comp
29 #define BT_LOG_OUTPUT_LEVEL log_level
30 #define BT_LOG_TAG "PLUGIN/SRC.CTF.FS"
31 #include "logging/comp-logging.h"
32
33 #include "common/common.h"
34 #include <babeltrace2/babeltrace.h>
35 #include "common/uuid.h"
36 #include <glib.h>
37 #include "common/assert.h"
38 #include <inttypes.h>
39 #include <stdbool.h>
40 #include "fs.h"
41 #include "metadata.h"
42 #include "data-stream-file.h"
43 #include "file.h"
44 #include "../common/metadata/decoder.h"
45 #include "../common/metadata/ctf-meta-configure-ir-trace.h"
46 #include "../common/msg-iter/msg-iter.h"
47 #include "query.h"
48 #include "plugins/common/param-validation/param-validation.h"
49
50 struct tracer_info {
51 const char *name;
52 int64_t major;
53 int64_t minor;
54 int64_t patch;
55 };
56
57 static
58 int msg_iter_data_set_current_ds_file(struct ctf_fs_msg_iter_data *msg_iter_data)
59 {
60 struct ctf_fs_ds_file_info *ds_file_info;
61 int ret = 0;
62
63 BT_ASSERT(msg_iter_data->ds_file_info_index <
64 msg_iter_data->ds_file_group->ds_file_infos->len);
65 ds_file_info = g_ptr_array_index(
66 msg_iter_data->ds_file_group->ds_file_infos,
67 msg_iter_data->ds_file_info_index);
68
69 ctf_fs_ds_file_destroy(msg_iter_data->ds_file);
70 msg_iter_data->ds_file = ctf_fs_ds_file_create(
71 msg_iter_data->ds_file_group->ctf_fs_trace,
72 msg_iter_data->pc_msg_iter,
73 msg_iter_data->msg_iter,
74 msg_iter_data->ds_file_group->stream,
75 ds_file_info->path->str,
76 msg_iter_data->log_level);
77 if (!msg_iter_data->ds_file) {
78 ret = -1;
79 }
80
81 return ret;
82 }
83
84 static
85 void ctf_fs_msg_iter_data_destroy(
86 struct ctf_fs_msg_iter_data *msg_iter_data)
87 {
88 if (!msg_iter_data) {
89 return;
90 }
91
92 ctf_fs_ds_file_destroy(msg_iter_data->ds_file);
93
94 if (msg_iter_data->msg_iter) {
95 bt_msg_iter_destroy(msg_iter_data->msg_iter);
96 }
97
98 g_free(msg_iter_data);
99 }
100
101 static
102 void set_msg_iter_emits_stream_beginning_end_messages(
103 struct ctf_fs_msg_iter_data *msg_iter_data)
104 {
105 bt_msg_iter_set_emit_stream_beginning_message(
106 msg_iter_data->ds_file->msg_iter,
107 msg_iter_data->ds_file_info_index == 0);
108 bt_msg_iter_set_emit_stream_end_message(
109 msg_iter_data->ds_file->msg_iter,
110 msg_iter_data->ds_file_info_index ==
111 msg_iter_data->ds_file_group->ds_file_infos->len - 1);
112 }
113
114 static
115 bt_component_class_message_iterator_next_method_status ctf_fs_iterator_next_one(
116 struct ctf_fs_msg_iter_data *msg_iter_data,
117 const bt_message **out_msg)
118 {
119 bt_component_class_message_iterator_next_method_status status;
120
121 BT_ASSERT(msg_iter_data->ds_file);
122
123 while (true) {
124 bt_message *msg;
125
126 status = ctf_fs_ds_file_next(msg_iter_data->ds_file, &msg);
127 switch (status) {
128 case BT_COMPONENT_CLASS_MESSAGE_ITERATOR_NEXT_METHOD_STATUS_OK:
129 *out_msg = msg;
130 msg = NULL;
131 goto end;
132 case BT_COMPONENT_CLASS_MESSAGE_ITERATOR_NEXT_METHOD_STATUS_END:
133 {
134 int ret;
135
136 if (msg_iter_data->ds_file_info_index ==
137 msg_iter_data->ds_file_group->ds_file_infos->len - 1) {
138 /* End of all group's stream files */
139 goto end;
140 }
141
142 msg_iter_data->ds_file_info_index++;
143 bt_msg_iter_reset_for_next_stream_file(
144 msg_iter_data->msg_iter);
145 set_msg_iter_emits_stream_beginning_end_messages(
146 msg_iter_data);
147
148 /*
149 * Open and start reading the next stream file
150 * within our stream file group.
151 */
152 ret = msg_iter_data_set_current_ds_file(msg_iter_data);
153 if (ret) {
154 status = BT_COMPONENT_CLASS_MESSAGE_ITERATOR_NEXT_METHOD_STATUS_ERROR;
155 goto end;
156 }
157
158 /* Continue the loop to get the next message */
159 break;
160 }
161 default:
162 goto end;
163 }
164 }
165
166 end:
167 return status;
168 }
169
170 BT_HIDDEN
171 bt_component_class_message_iterator_next_method_status ctf_fs_iterator_next(
172 bt_self_message_iterator *iterator,
173 bt_message_array_const msgs, uint64_t capacity,
174 uint64_t *count)
175 {
176 bt_component_class_message_iterator_next_method_status status =
177 BT_COMPONENT_CLASS_MESSAGE_ITERATOR_NEXT_METHOD_STATUS_OK;
178 struct ctf_fs_msg_iter_data *msg_iter_data =
179 bt_self_message_iterator_get_data(iterator);
180 uint64_t i = 0;
181
182 while (i < capacity &&
183 status == BT_COMPONENT_CLASS_MESSAGE_ITERATOR_NEXT_METHOD_STATUS_OK) {
184 status = ctf_fs_iterator_next_one(msg_iter_data, &msgs[i]);
185 if (status == BT_COMPONENT_CLASS_MESSAGE_ITERATOR_NEXT_METHOD_STATUS_OK) {
186 i++;
187 }
188 }
189
190 if (i > 0) {
191 /*
192 * Even if ctf_fs_iterator_next_one() returned something
193 * else than BT_COMPONENT_CLASS_MESSAGE_ITERATOR_NEXT_METHOD_STATUS_OK, we
194 * accumulated message objects in the output
195 * message array, so we need to return
196 * BT_COMPONENT_CLASS_MESSAGE_ITERATOR_NEXT_METHOD_STATUS_OK so that they are
197 * transfered to downstream. This other status occurs
198 * again the next time muxer_msg_iter_do_next() is
199 * called, possibly without any accumulated
200 * message, in which case we'll return it.
201 */
202 *count = i;
203 status = BT_COMPONENT_CLASS_MESSAGE_ITERATOR_NEXT_METHOD_STATUS_OK;
204 }
205
206 return status;
207 }
208
209 static
210 int ctf_fs_iterator_reset(struct ctf_fs_msg_iter_data *msg_iter_data)
211 {
212 int ret;
213
214 msg_iter_data->ds_file_info_index = 0;
215 ret = msg_iter_data_set_current_ds_file(msg_iter_data);
216 if (ret) {
217 goto end;
218 }
219
220 bt_msg_iter_reset(msg_iter_data->msg_iter);
221 set_msg_iter_emits_stream_beginning_end_messages(msg_iter_data);
222
223 end:
224 return ret;
225 }
226
227 BT_HIDDEN
228 bt_component_class_message_iterator_seek_beginning_method_status
229 ctf_fs_iterator_seek_beginning(bt_self_message_iterator *it)
230 {
231 struct ctf_fs_msg_iter_data *msg_iter_data =
232 bt_self_message_iterator_get_data(it);
233 bt_component_class_message_iterator_seek_beginning_method_status status =
234 BT_COMPONENT_CLASS_MESSAGE_ITERATOR_SEEK_BEGINNING_METHOD_STATUS_OK;
235
236 BT_ASSERT(msg_iter_data);
237 if (ctf_fs_iterator_reset(msg_iter_data)) {
238 status = BT_COMPONENT_CLASS_MESSAGE_ITERATOR_SEEK_BEGINNING_METHOD_STATUS_ERROR;
239 }
240
241 return status;
242 }
243
244 BT_HIDDEN
245 void ctf_fs_iterator_finalize(bt_self_message_iterator *it)
246 {
247 ctf_fs_msg_iter_data_destroy(
248 bt_self_message_iterator_get_data(it));
249 }
250
251 BT_HIDDEN
252 bt_component_class_message_iterator_initialize_method_status ctf_fs_iterator_init(
253 bt_self_message_iterator *self_msg_iter,
254 bt_self_message_iterator_configuration *config,
255 bt_self_component_source *self_comp_src,
256 bt_self_component_port_output *self_port)
257 {
258 struct ctf_fs_port_data *port_data;
259 struct ctf_fs_msg_iter_data *msg_iter_data = NULL;
260 bt_component_class_message_iterator_initialize_method_status ret =
261 BT_COMPONENT_CLASS_MESSAGE_ITERATOR_INITIALIZE_METHOD_STATUS_OK;
262 bt_logging_level log_level;
263 bt_self_component *self_comp =
264 bt_self_component_source_as_self_component(self_comp_src);
265
266 port_data = bt_self_component_port_get_data(
267 bt_self_component_port_output_as_self_component_port(
268 self_port));
269 BT_ASSERT(port_data);
270 log_level = port_data->ctf_fs->log_level;
271 msg_iter_data = g_new0(struct ctf_fs_msg_iter_data, 1);
272 if (!msg_iter_data) {
273 ret = BT_COMPONENT_CLASS_MESSAGE_ITERATOR_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
274 goto error;
275 }
276
277 msg_iter_data->log_level = log_level;
278 msg_iter_data->self_comp = self_comp;
279 msg_iter_data->pc_msg_iter = self_msg_iter;
280 msg_iter_data->msg_iter = bt_msg_iter_create(
281 port_data->ds_file_group->ctf_fs_trace->metadata->tc,
282 bt_common_get_page_size(msg_iter_data->log_level) * 8,
283 ctf_fs_ds_file_medops, NULL, msg_iter_data->log_level,
284 self_comp);
285 if (!msg_iter_data->msg_iter) {
286 BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Cannot create a CTF message iterator.");
287 ret = BT_COMPONENT_CLASS_MESSAGE_ITERATOR_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
288 goto error;
289 }
290
291 msg_iter_data->ds_file_group = port_data->ds_file_group;
292 if (ctf_fs_iterator_reset(msg_iter_data)) {
293 ret = BT_COMPONENT_CLASS_MESSAGE_ITERATOR_INITIALIZE_METHOD_STATUS_ERROR;
294 goto error;
295 }
296
297 /*
298 * This iterator can seek forward if its stream class has a default
299 * clock class.
300 */
301 if (msg_iter_data->ds_file_group->sc->default_clock_class) {
302 bt_self_message_iterator_configuration_set_can_seek_forward(
303 config, true);
304 }
305
306 bt_self_message_iterator_set_data(self_msg_iter,
307 msg_iter_data);
308 if (ret != BT_COMPONENT_CLASS_MESSAGE_ITERATOR_INITIALIZE_METHOD_STATUS_OK) {
309 goto error;
310 }
311 msg_iter_data = NULL;
312 goto end;
313
314 error:
315 bt_self_message_iterator_set_data(self_msg_iter, NULL);
316
317 end:
318 ctf_fs_msg_iter_data_destroy(msg_iter_data);
319 return ret;
320 }
321
322 static
323 void ctf_fs_trace_destroy(struct ctf_fs_trace *ctf_fs_trace)
324 {
325 if (!ctf_fs_trace) {
326 return;
327 }
328
329 if (ctf_fs_trace->ds_file_groups) {
330 g_ptr_array_free(ctf_fs_trace->ds_file_groups, TRUE);
331 }
332
333 BT_TRACE_PUT_REF_AND_RESET(ctf_fs_trace->trace);
334
335 if (ctf_fs_trace->path) {
336 g_string_free(ctf_fs_trace->path, TRUE);
337 }
338
339 if (ctf_fs_trace->metadata) {
340 ctf_fs_metadata_fini(ctf_fs_trace->metadata);
341 g_free(ctf_fs_trace->metadata);
342 }
343
344 g_free(ctf_fs_trace);
345 }
346
347 BT_HIDDEN
348 void ctf_fs_destroy(struct ctf_fs_component *ctf_fs)
349 {
350 if (!ctf_fs) {
351 return;
352 }
353
354 ctf_fs_trace_destroy(ctf_fs->trace);
355
356 if (ctf_fs->port_data) {
357 g_ptr_array_free(ctf_fs->port_data, TRUE);
358 }
359
360 g_free(ctf_fs);
361 }
362
363 static
364 void port_data_destroy(struct ctf_fs_port_data *port_data)
365 {
366 if (!port_data) {
367 return;
368 }
369
370 g_free(port_data);
371 }
372
373 static
374 void port_data_destroy_notifier(void *data) {
375 port_data_destroy(data);
376 }
377
378 static
379 void ctf_fs_trace_destroy_notifier(void *data)
380 {
381 struct ctf_fs_trace *trace = data;
382 ctf_fs_trace_destroy(trace);
383 }
384
385 struct ctf_fs_component *ctf_fs_component_create(bt_logging_level log_level,
386 bt_self_component *self_comp)
387 {
388 struct ctf_fs_component *ctf_fs;
389
390 ctf_fs = g_new0(struct ctf_fs_component, 1);
391 if (!ctf_fs) {
392 goto error;
393 }
394
395 ctf_fs->log_level = log_level;
396 ctf_fs->port_data =
397 g_ptr_array_new_with_free_func(port_data_destroy_notifier);
398 if (!ctf_fs->port_data) {
399 goto error;
400 }
401
402 goto end;
403
404 error:
405 ctf_fs_destroy(ctf_fs);
406 ctf_fs = NULL;
407
408 end:
409 return ctf_fs;
410 }
411
412 void ctf_fs_finalize(bt_self_component_source *component)
413 {
414 ctf_fs_destroy(bt_self_component_get_data(
415 bt_self_component_source_as_self_component(component)));
416 }
417
418 gchar *ctf_fs_make_port_name(struct ctf_fs_ds_file_group *ds_file_group)
419 {
420 GString *name = g_string_new(NULL);
421
422 /*
423 * The unique port name is generated by concatenating unique identifiers
424 * for:
425 *
426 * - the trace
427 * - the stream class
428 * - the stream
429 */
430
431 /* For the trace, use the uuid if present, else the path. */
432 if (ds_file_group->ctf_fs_trace->metadata->tc->is_uuid_set) {
433 char uuid_str[BT_UUID_STR_LEN + 1];
434
435 bt_uuid_to_str(ds_file_group->ctf_fs_trace->metadata->tc->uuid, uuid_str);
436 g_string_assign(name, uuid_str);
437 } else {
438 g_string_assign(name, ds_file_group->ctf_fs_trace->path->str);
439 }
440
441 /*
442 * For the stream class, use the id if present. We can omit this field
443 * otherwise, as there will only be a single stream class.
444 */
445 if (ds_file_group->sc->id != UINT64_C(-1)) {
446 g_string_append_printf(name, " | %" PRIu64, ds_file_group->sc->id);
447 }
448
449 /* For the stream, use the id if present, else, use the path. */
450 if (ds_file_group->stream_id != UINT64_C(-1)) {
451 g_string_append_printf(name, " | %" PRIu64, ds_file_group->stream_id);
452 } else {
453 BT_ASSERT(ds_file_group->ds_file_infos->len == 1);
454 struct ctf_fs_ds_file_info *ds_file_info =
455 g_ptr_array_index(ds_file_group->ds_file_infos, 0);
456 g_string_append_printf(name, " | %s", ds_file_info->path->str);
457 }
458
459 return g_string_free(name, FALSE);
460 }
461
462 static
463 int create_one_port_for_trace(struct ctf_fs_component *ctf_fs,
464 struct ctf_fs_trace *ctf_fs_trace,
465 struct ctf_fs_ds_file_group *ds_file_group,
466 bt_self_component_source *self_comp_src)
467 {
468 int ret = 0;
469 struct ctf_fs_port_data *port_data = NULL;
470 gchar *port_name;
471 bt_logging_level log_level = ctf_fs->log_level;
472 bt_self_component *self_comp =
473 bt_self_component_source_as_self_component(self_comp_src);
474
475 port_name = ctf_fs_make_port_name(ds_file_group);
476 if (!port_name) {
477 goto error;
478 }
479
480 BT_COMP_LOGI("Creating one port named `%s`", port_name);
481
482 /* Create output port for this file */
483 port_data = g_new0(struct ctf_fs_port_data, 1);
484 if (!port_data) {
485 goto error;
486 }
487
488 port_data->ctf_fs = ctf_fs;
489 port_data->ds_file_group = ds_file_group;
490 ret = bt_self_component_source_add_output_port(
491 self_comp_src, port_name, port_data, NULL);
492 if (ret) {
493 goto error;
494 }
495
496 g_ptr_array_add(ctf_fs->port_data, port_data);
497 port_data = NULL;
498 goto end;
499
500 error:
501 ret = -1;
502
503 end:
504 g_free(port_name);
505
506 port_data_destroy(port_data);
507 return ret;
508 }
509
510 static
511 int create_ports_for_trace(struct ctf_fs_component *ctf_fs,
512 struct ctf_fs_trace *ctf_fs_trace,
513 bt_self_component_source *self_comp_src)
514 {
515 int ret = 0;
516 size_t i;
517 bt_logging_level log_level = ctf_fs_trace->log_level;
518 bt_self_component *self_comp =
519 bt_self_component_source_as_self_component(self_comp_src);
520
521 /* Create one output port for each stream file group */
522 for (i = 0; i < ctf_fs_trace->ds_file_groups->len; i++) {
523 struct ctf_fs_ds_file_group *ds_file_group =
524 g_ptr_array_index(ctf_fs_trace->ds_file_groups, i);
525
526 ret = create_one_port_for_trace(ctf_fs, ctf_fs_trace,
527 ds_file_group, self_comp_src);
528 if (ret) {
529 BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Cannot create output port.");
530 goto end;
531 }
532 }
533
534 end:
535 return ret;
536 }
537
538 static
539 void ctf_fs_ds_file_info_destroy(struct ctf_fs_ds_file_info *ds_file_info)
540 {
541 if (!ds_file_info) {
542 return;
543 }
544
545 if (ds_file_info->path) {
546 g_string_free(ds_file_info->path, TRUE);
547 }
548
549 g_free(ds_file_info);
550 }
551
552 static
553 struct ctf_fs_ds_file_info *ctf_fs_ds_file_info_create(const char *path,
554 int64_t begin_ns)
555 {
556 struct ctf_fs_ds_file_info *ds_file_info;
557
558 ds_file_info = g_new0(struct ctf_fs_ds_file_info, 1);
559 if (!ds_file_info) {
560 goto end;
561 }
562
563 ds_file_info->path = g_string_new(path);
564 if (!ds_file_info->path) {
565 ctf_fs_ds_file_info_destroy(ds_file_info);
566 ds_file_info = NULL;
567 goto end;
568 }
569
570 ds_file_info->begin_ns = begin_ns;
571
572 end:
573 return ds_file_info;
574 }
575
576 static
577 void ctf_fs_ds_file_group_destroy(struct ctf_fs_ds_file_group *ds_file_group)
578 {
579 if (!ds_file_group) {
580 return;
581 }
582
583 if (ds_file_group->ds_file_infos) {
584 g_ptr_array_free(ds_file_group->ds_file_infos, TRUE);
585 }
586
587 if (ds_file_group->index) {
588 if (ds_file_group->index->entries) {
589 g_ptr_array_free(ds_file_group->index->entries, TRUE);
590 }
591 g_free(ds_file_group->index);
592 }
593
594 bt_stream_put_ref(ds_file_group->stream);
595 g_free(ds_file_group);
596 }
597
598 static
599 struct ctf_fs_ds_file_group *ctf_fs_ds_file_group_create(
600 struct ctf_fs_trace *ctf_fs_trace,
601 struct ctf_stream_class *sc,
602 uint64_t stream_instance_id,
603 struct ctf_fs_ds_index *index)
604 {
605 struct ctf_fs_ds_file_group *ds_file_group;
606
607 ds_file_group = g_new0(struct ctf_fs_ds_file_group, 1);
608 if (!ds_file_group) {
609 goto error;
610 }
611
612 ds_file_group->ds_file_infos = g_ptr_array_new_with_free_func(
613 (GDestroyNotify) ctf_fs_ds_file_info_destroy);
614 if (!ds_file_group->ds_file_infos) {
615 goto error;
616 }
617
618 ds_file_group->index = index;
619
620 ds_file_group->stream_id = stream_instance_id;
621 BT_ASSERT(sc);
622 ds_file_group->sc = sc;
623 ds_file_group->ctf_fs_trace = ctf_fs_trace;
624 goto end;
625
626 error:
627 ctf_fs_ds_file_group_destroy(ds_file_group);
628 ctf_fs_ds_index_destroy(index);
629 ds_file_group = NULL;
630
631 end:
632 return ds_file_group;
633 }
634
635 /* Replace by g_ptr_array_insert when we depend on glib >= 2.40. */
636 static
637 void array_insert(GPtrArray *array, gpointer element, size_t pos)
638 {
639 size_t original_array_len = array->len;
640
641 /* Allocate an unused element at the end of the array. */
642 g_ptr_array_add(array, NULL);
643
644 /* If we are not inserting at the end, move the elements by one. */
645 if (pos < original_array_len) {
646 memmove(&(array->pdata[pos + 1]),
647 &(array->pdata[pos]),
648 (original_array_len - pos) * sizeof(gpointer));
649 }
650
651 /* Insert the value. */
652 array->pdata[pos] = element;
653 }
654
655 /*
656 * Insert ds_file_info in ds_file_group's list of ds_file_infos at the right
657 * place to keep it sorted.
658 */
659
660 static
661 void ds_file_group_insert_ds_file_info_sorted(
662 struct ctf_fs_ds_file_group *ds_file_group,
663 struct ctf_fs_ds_file_info *ds_file_info)
664 {
665 guint i;
666
667 /* Find the spot where to insert this ds_file_info. */
668 for (i = 0; i < ds_file_group->ds_file_infos->len; i++) {
669 struct ctf_fs_ds_file_info *other_ds_file_info =
670 g_ptr_array_index(ds_file_group->ds_file_infos, i);
671
672 if (ds_file_info->begin_ns < other_ds_file_info->begin_ns) {
673 break;
674 }
675 }
676
677 array_insert(ds_file_group->ds_file_infos, ds_file_info, i);
678 }
679
680 static
681 void ds_file_group_insert_ds_index_entry_sorted(
682 struct ctf_fs_ds_file_group *ds_file_group,
683 struct ctf_fs_ds_index_entry *entry)
684 {
685 guint i;
686
687 /* Find the spot where to insert this index entry. */
688 for (i = 0; i < ds_file_group->index->entries->len; i++) {
689 struct ctf_fs_ds_index_entry *other_entry = g_ptr_array_index(
690 ds_file_group->index->entries, i);
691
692 if (entry->timestamp_begin_ns < other_entry->timestamp_begin_ns) {
693 break;
694 }
695 }
696
697 array_insert(ds_file_group->index->entries, entry, i);
698 }
699
700 static
701 int add_ds_file_to_ds_file_group(struct ctf_fs_trace *ctf_fs_trace,
702 const char *path)
703 {
704 int64_t stream_instance_id = -1;
705 int64_t begin_ns = -1;
706 struct ctf_fs_ds_file_group *ds_file_group = NULL;
707 bool add_group = false;
708 int ret;
709 size_t i;
710 struct ctf_fs_ds_file *ds_file = NULL;
711 struct ctf_fs_ds_file_info *ds_file_info = NULL;
712 struct ctf_fs_ds_index *index = NULL;
713 struct bt_msg_iter *msg_iter = NULL;
714 struct ctf_stream_class *sc = NULL;
715 struct bt_msg_iter_packet_properties props;
716 bt_logging_level log_level = ctf_fs_trace->log_level;
717 bt_self_component *self_comp = ctf_fs_trace->self_comp;
718 bt_self_component_class *self_comp_class = ctf_fs_trace->self_comp_class;
719
720 msg_iter = bt_msg_iter_create(ctf_fs_trace->metadata->tc,
721 bt_common_get_page_size(log_level) * 8,
722 ctf_fs_ds_file_medops, NULL, log_level, self_comp);
723 if (!msg_iter) {
724 BT_COMP_LOGE_STR("Cannot create a CTF message iterator.");
725 goto error;
726 }
727
728 ds_file = ctf_fs_ds_file_create(ctf_fs_trace, NULL, msg_iter,
729 NULL, path, log_level);
730 if (!ds_file) {
731 goto error;
732 }
733
734 ret = bt_msg_iter_get_packet_properties(ds_file->msg_iter, &props);
735 if (ret) {
736 BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
737 "Cannot get stream file's first packet's header and context fields (`%s`).",
738 path);
739 goto error;
740 }
741
742 sc = ctf_trace_class_borrow_stream_class_by_id(ds_file->metadata->tc,
743 props.stream_class_id);
744 BT_ASSERT(sc);
745 stream_instance_id = props.data_stream_id;
746
747 if (props.snapshots.beginning_clock != UINT64_C(-1)) {
748 BT_ASSERT(sc->default_clock_class);
749 ret = bt_util_clock_cycles_to_ns_from_origin(
750 props.snapshots.beginning_clock,
751 sc->default_clock_class->frequency,
752 sc->default_clock_class->offset_seconds,
753 sc->default_clock_class->offset_cycles, &begin_ns);
754 if (ret) {
755 BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
756 "Cannot convert clock cycles to nanoseconds from origin (`%s`).",
757 path);
758 goto error;
759 }
760 }
761
762 ds_file_info = ctf_fs_ds_file_info_create(path, begin_ns);
763 if (!ds_file_info) {
764 goto error;
765 }
766
767 index = ctf_fs_ds_file_build_index(ds_file, ds_file_info);
768 if (!index) {
769 BT_COMP_LOGW("Failed to index CTF stream file \'%s\'",
770 ds_file->file->path->str);
771 }
772
773 if (begin_ns == -1) {
774 /*
775 * No beggining timestamp to sort the stream files
776 * within a stream file group, so consider that this
777 * file must be the only one within its group.
778 */
779 stream_instance_id = -1;
780 }
781
782 if (stream_instance_id == -1) {
783 /*
784 * No stream instance ID or no beginning timestamp:
785 * create a unique stream file group for this stream
786 * file because, even if there's a stream instance ID,
787 * there's no timestamp to order the file within its
788 * group.
789 */
790 ds_file_group = ctf_fs_ds_file_group_create(ctf_fs_trace,
791 sc, UINT64_C(-1), index);
792 /* Ownership of index is transferred. */
793 index = NULL;
794
795 if (!ds_file_group) {
796 goto error;
797 }
798
799 ds_file_group_insert_ds_file_info_sorted(ds_file_group, ds_file_info);
800
801 add_group = true;
802 goto end;
803 }
804
805 BT_ASSERT(stream_instance_id != -1);
806 BT_ASSERT(begin_ns != -1);
807
808 /* Find an existing stream file group with this ID */
809 for (i = 0; i < ctf_fs_trace->ds_file_groups->len; i++) {
810 ds_file_group = g_ptr_array_index(
811 ctf_fs_trace->ds_file_groups, i);
812
813 if (ds_file_group->sc == sc &&
814 ds_file_group->stream_id ==
815 stream_instance_id) {
816 break;
817 }
818
819 ds_file_group = NULL;
820 }
821
822 if (!ds_file_group) {
823 ds_file_group = ctf_fs_ds_file_group_create(ctf_fs_trace,
824 sc, stream_instance_id, index);
825 /* Ownership of index is transferred. */
826 index = NULL;
827 if (!ds_file_group) {
828 goto error;
829 }
830
831 add_group = true;
832 }
833
834 ds_file_group_insert_ds_file_info_sorted(ds_file_group, ds_file_info);
835
836 goto end;
837
838 error:
839 ctf_fs_ds_file_group_destroy(ds_file_group);
840 ds_file_group = NULL;
841 ret = -1;
842
843 end:
844 if (add_group && ds_file_group) {
845 g_ptr_array_add(ctf_fs_trace->ds_file_groups, ds_file_group);
846 }
847
848 ctf_fs_ds_file_destroy(ds_file);
849
850 if (msg_iter) {
851 bt_msg_iter_destroy(msg_iter);
852 }
853
854 ctf_fs_ds_index_destroy(index);
855 return ret;
856 }
857
858 static
859 int create_ds_file_groups(struct ctf_fs_trace *ctf_fs_trace)
860 {
861 int ret = 0;
862 const char *basename;
863 GError *error = NULL;
864 GDir *dir = NULL;
865 bt_logging_level log_level = ctf_fs_trace->log_level;
866 bt_self_component *self_comp = ctf_fs_trace->self_comp;
867 bt_self_component_class *self_comp_class = ctf_fs_trace->self_comp_class;
868
869 /* Check each file in the path directory, except specific ones */
870 dir = g_dir_open(ctf_fs_trace->path->str, 0, &error);
871 if (!dir) {
872 BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
873 "Cannot open directory `%s`: %s (code %d)",
874 ctf_fs_trace->path->str, error->message,
875 error->code);
876 goto error;
877 }
878
879 while ((basename = g_dir_read_name(dir))) {
880 struct ctf_fs_file *file;
881
882 if (strcmp(basename, CTF_FS_METADATA_FILENAME) == 0) {
883 /* Ignore the metadata stream. */
884 BT_COMP_LOGI("Ignoring metadata file `%s" G_DIR_SEPARATOR_S "%s`",
885 ctf_fs_trace->path->str, basename);
886 continue;
887 }
888
889 if (basename[0] == '.') {
890 BT_COMP_LOGI("Ignoring hidden file `%s" G_DIR_SEPARATOR_S "%s`",
891 ctf_fs_trace->path->str, basename);
892 continue;
893 }
894
895 /* Create the file. */
896 file = ctf_fs_file_create(log_level, self_comp);
897 if (!file) {
898 BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
899 "Cannot create stream file object for file `%s" G_DIR_SEPARATOR_S "%s`",
900 ctf_fs_trace->path->str, basename);
901 goto error;
902 }
903
904 /* Create full path string. */
905 g_string_append_printf(file->path, "%s" G_DIR_SEPARATOR_S "%s",
906 ctf_fs_trace->path->str, basename);
907 if (!g_file_test(file->path->str, G_FILE_TEST_IS_REGULAR)) {
908 BT_COMP_LOGI("Ignoring non-regular file `%s`",
909 file->path->str);
910 ctf_fs_file_destroy(file);
911 file = NULL;
912 continue;
913 }
914
915 ret = ctf_fs_file_open(file, "rb");
916 if (ret) {
917 BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
918 "Cannot open stream file `%s`",
919 file->path->str);
920 goto error;
921 }
922
923 if (file->size == 0) {
924 /* Skip empty stream. */
925 BT_COMP_LOGI("Ignoring empty file `%s`", file->path->str);
926 ctf_fs_file_destroy(file);
927 continue;
928 }
929
930 ret = add_ds_file_to_ds_file_group(ctf_fs_trace,
931 file->path->str);
932 if (ret) {
933 BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
934 "Cannot add stream file `%s` to stream file group",
935 file->path->str);
936 ctf_fs_file_destroy(file);
937 goto error;
938 }
939
940 ctf_fs_file_destroy(file);
941 }
942
943 goto end;
944
945 error:
946 ret = -1;
947
948 end:
949 if (dir) {
950 g_dir_close(dir);
951 dir = NULL;
952 }
953
954 if (error) {
955 g_error_free(error);
956 }
957
958 return ret;
959 }
960
961 static
962 int set_trace_name(bt_trace *trace, const char *name_suffix,
963 bt_logging_level log_level, bt_self_component *self_comp)
964 {
965 int ret = 0;
966 const bt_value *val;
967 GString *name;
968
969 name = g_string_new(NULL);
970 if (!name) {
971 BT_COMP_LOGE_STR("Failed to allocate a GString.");
972 ret = -1;
973 goto end;
974 }
975
976 /*
977 * Check if we have a trace environment string value named `hostname`.
978 * If so, use it as the trace name's prefix.
979 */
980 val = bt_trace_borrow_environment_entry_value_by_name_const(
981 trace, "hostname");
982 if (val && bt_value_is_string(val)) {
983 g_string_append(name, bt_value_string_get(val));
984
985 if (name_suffix) {
986 g_string_append_c(name, G_DIR_SEPARATOR);
987 }
988 }
989
990 if (name_suffix) {
991 g_string_append(name, name_suffix);
992 }
993
994 ret = bt_trace_set_name(trace, name->str);
995 if (ret) {
996 goto end;
997 }
998
999 goto end;
1000
1001 end:
1002 if (name) {
1003 g_string_free(name, TRUE);
1004 }
1005
1006 return ret;
1007 }
1008
1009 static
1010 struct ctf_fs_trace *ctf_fs_trace_create(
1011 bt_self_component *self_comp,
1012 bt_self_component_class *self_comp_class,
1013 const char *path, const char *name,
1014 struct ctf_fs_metadata_config *metadata_config,
1015 bt_logging_level log_level)
1016 {
1017 struct ctf_fs_trace *ctf_fs_trace;
1018 int ret;
1019
1020 /* Only one of them must be set. */
1021 BT_ASSERT(!self_comp != !self_comp_class);
1022
1023 ctf_fs_trace = g_new0(struct ctf_fs_trace, 1);
1024 if (!ctf_fs_trace) {
1025 goto end;
1026 }
1027
1028 ctf_fs_trace->log_level = log_level;
1029 ctf_fs_trace->self_comp = self_comp;
1030 ctf_fs_trace->self_comp_class = self_comp_class;
1031 ctf_fs_trace->path = g_string_new(path);
1032 if (!ctf_fs_trace->path) {
1033 goto error;
1034 }
1035
1036 ctf_fs_trace->metadata = g_new0(struct ctf_fs_metadata, 1);
1037 if (!ctf_fs_trace->metadata) {
1038 goto error;
1039 }
1040
1041 ctf_fs_metadata_init(ctf_fs_trace->metadata);
1042 ctf_fs_trace->ds_file_groups = g_ptr_array_new_with_free_func(
1043 (GDestroyNotify) ctf_fs_ds_file_group_destroy);
1044 if (!ctf_fs_trace->ds_file_groups) {
1045 goto error;
1046 }
1047
1048 ret = ctf_fs_metadata_set_trace_class(self_comp, ctf_fs_trace,
1049 metadata_config);
1050 if (ret) {
1051 goto error;
1052 }
1053
1054 if (ctf_fs_trace->metadata->trace_class) {
1055 ctf_fs_trace->trace =
1056 bt_trace_create(ctf_fs_trace->metadata->trace_class);
1057 if (!ctf_fs_trace->trace) {
1058 goto error;
1059 }
1060 }
1061
1062 if (ctf_fs_trace->trace) {
1063 ret = ctf_trace_class_configure_ir_trace(
1064 ctf_fs_trace->metadata->tc, ctf_fs_trace->trace);
1065 if (ret) {
1066 goto error;
1067 }
1068
1069 ret = set_trace_name(ctf_fs_trace->trace, name, log_level,
1070 self_comp);
1071 if (ret) {
1072 goto error;
1073 }
1074 }
1075
1076 ret = create_ds_file_groups(ctf_fs_trace);
1077 if (ret) {
1078 goto error;
1079 }
1080
1081 goto end;
1082
1083 error:
1084 ctf_fs_trace_destroy(ctf_fs_trace);
1085 ctf_fs_trace = NULL;
1086
1087 end:
1088 return ctf_fs_trace;
1089 }
1090
1091 static
1092 int path_is_ctf_trace(const char *path)
1093 {
1094 GString *metadata_path = g_string_new(NULL);
1095 int ret = 0;
1096
1097 if (!metadata_path) {
1098 ret = -1;
1099 goto end;
1100 }
1101
1102 g_string_printf(metadata_path, "%s" G_DIR_SEPARATOR_S "%s", path, CTF_FS_METADATA_FILENAME);
1103
1104 if (g_file_test(metadata_path->str, G_FILE_TEST_IS_REGULAR)) {
1105 ret = 1;
1106 goto end;
1107 }
1108
1109 end:
1110 g_string_free(metadata_path, TRUE);
1111 return ret;
1112 }
1113
1114 /* Helper for ctf_fs_component_create_ctf_fs_trace, to handle a single path. */
1115
1116 static
1117 int ctf_fs_component_create_ctf_fs_trace_one_path(
1118 struct ctf_fs_component *ctf_fs,
1119 const char *path_param,
1120 const char *trace_name,
1121 GPtrArray *traces,
1122 bt_self_component *self_comp,
1123 bt_self_component_class *self_comp_class)
1124 {
1125 struct ctf_fs_trace *ctf_fs_trace;
1126 int ret;
1127 GString *norm_path;
1128 bt_logging_level log_level = ctf_fs->log_level;
1129
1130 norm_path = bt_common_normalize_path(path_param, NULL);
1131 if (!norm_path) {
1132 BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
1133 "Failed to normalize path: `%s`.",
1134 path_param);
1135 goto error;
1136 }
1137
1138 ret = path_is_ctf_trace(norm_path->str);
1139 if (ret < 0) {
1140 BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
1141 "Failed to check if path is a CTF trace: path=%s",
1142 norm_path->str);
1143 goto error;
1144 } else if (ret == 0) {
1145 BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
1146 "Path is not a CTF trace (does not contain a metadata file): `%s`.",
1147 norm_path->str);
1148 goto error;
1149 }
1150
1151 // FIXME: Remove or ifdef for __MINGW32__
1152 if (strcmp(norm_path->str, "/") == 0) {
1153 BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
1154 "Opening a trace in `/` is not supported.");
1155 ret = -1;
1156 goto end;
1157 }
1158
1159 ctf_fs_trace = ctf_fs_trace_create(self_comp, self_comp_class, norm_path->str,
1160 trace_name, &ctf_fs->metadata_config, log_level);
1161 if (!ctf_fs_trace) {
1162 BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
1163 "Cannot create trace for `%s`.",
1164 norm_path->str);
1165 goto error;
1166 }
1167
1168 g_ptr_array_add(traces, ctf_fs_trace);
1169 ctf_fs_trace = NULL;
1170
1171 ret = 0;
1172 goto end;
1173
1174 error:
1175 ret = -1;
1176
1177 end:
1178 if (norm_path) {
1179 g_string_free(norm_path, TRUE);
1180 }
1181
1182 return ret;
1183 }
1184
1185 /*
1186 * Count the number of stream and event classes defined by this trace's metadata.
1187 *
1188 * This is used to determine which metadata is the "latest", out of multiple
1189 * traces sharing the same UUID. It is assumed that amongst all these metadatas,
1190 * a bigger metadata is a superset of a smaller metadata. Therefore, it is
1191 * enough to just count the classes.
1192 */
1193
1194 static
1195 unsigned int metadata_count_stream_and_event_classes(struct ctf_fs_trace *trace)
1196 {
1197 unsigned int num = trace->metadata->tc->stream_classes->len;
1198 guint i;
1199
1200 for (i = 0; i < trace->metadata->tc->stream_classes->len; i++) {
1201 struct ctf_stream_class *sc = trace->metadata->tc->stream_classes->pdata[i];
1202 num += sc->event_classes->len;
1203 }
1204
1205 return num;
1206 }
1207
1208 /*
1209 * Merge the src ds_file_group into dest. This consists of merging their
1210 * ds_file_infos, making sure to keep the result sorted.
1211 */
1212
1213 static
1214 void merge_ctf_fs_ds_file_groups(struct ctf_fs_ds_file_group *dest, struct ctf_fs_ds_file_group *src)
1215 {
1216 guint i;
1217
1218 for (i = 0; i < src->ds_file_infos->len; i++) {
1219 struct ctf_fs_ds_file_info *ds_file_info =
1220 g_ptr_array_index(src->ds_file_infos, i);
1221
1222 /* Ownership of the ds_file_info is transferred to dest. */
1223 g_ptr_array_index(src->ds_file_infos, i) = NULL;
1224
1225 ds_file_group_insert_ds_file_info_sorted(dest, ds_file_info);
1226 }
1227
1228 /* Merge both indexes. */
1229 for (i = 0; i < src->index->entries->len; i++) {
1230 struct ctf_fs_ds_index_entry *entry = g_ptr_array_index(
1231 src->index->entries, i);
1232
1233 /*
1234 * Ownership of the ctf_fs_ds_index_entry is transferred to
1235 * dest.
1236 */
1237 g_ptr_array_index(src->index->entries, i) = NULL;
1238
1239 ds_file_group_insert_ds_index_entry_sorted(dest, entry);
1240 }
1241 }
1242 /* Merge src_trace's data stream file groups into dest_trace's. */
1243
1244 static
1245 int merge_matching_ctf_fs_ds_file_groups(
1246 struct ctf_fs_trace *dest_trace,
1247 struct ctf_fs_trace *src_trace)
1248 {
1249
1250 GPtrArray *dest = dest_trace->ds_file_groups;
1251 GPtrArray *src = src_trace->ds_file_groups;
1252 guint s_i;
1253 int ret = 0;
1254
1255 /*
1256 * Save the initial length of dest: we only want to check against the
1257 * original elements in the inner loop.
1258 */
1259 const guint dest_len = dest->len;
1260
1261 for (s_i = 0; s_i < src->len; s_i++) {
1262 struct ctf_fs_ds_file_group *src_group = g_ptr_array_index(src, s_i);
1263 struct ctf_fs_ds_file_group *dest_group = NULL;
1264
1265 /* A stream instance without ID can't match a stream in the other trace. */
1266 if (src_group->stream_id != -1) {
1267 guint d_i;
1268
1269 /* Let's search for a matching ds_file_group in the destination. */
1270 for (d_i = 0; d_i < dest_len; d_i++) {
1271 struct ctf_fs_ds_file_group *candidate_dest = g_ptr_array_index(dest, d_i);
1272
1273 /* Can't match a stream instance without ID. */
1274 if (candidate_dest->stream_id == -1) {
1275 continue;
1276 }
1277
1278 /*
1279 * If the two groups have the same stream instance id
1280 * and belong to the same stream class (stream instance
1281 * ids are per-stream class), they represent the same
1282 * stream instance.
1283 */
1284 if (candidate_dest->stream_id != src_group->stream_id ||
1285 candidate_dest->sc->id != src_group->sc->id) {
1286 continue;
1287 }
1288
1289 dest_group = candidate_dest;
1290 break;
1291 }
1292 }
1293
1294 /*
1295 * Didn't find a friend in dest to merge our src_group into?
1296 * Create a new empty one. This can happen if a stream was
1297 * active in the source trace chunk but not in the destination
1298 * trace chunk.
1299 */
1300 if (!dest_group) {
1301 struct ctf_stream_class *sc;
1302 struct ctf_fs_ds_index *index;
1303
1304 sc = ctf_trace_class_borrow_stream_class_by_id(
1305 dest_trace->metadata->tc, src_group->sc->id);
1306 BT_ASSERT(sc);
1307
1308 index = ctf_fs_ds_index_create(dest_trace->log_level,
1309 dest_trace->self_comp);
1310 if (!index) {
1311 ret = -1;
1312 goto end;
1313 }
1314
1315 dest_group = ctf_fs_ds_file_group_create(dest_trace, sc,
1316 src_group->stream_id, index);
1317 /* Ownership of index is transferred. */
1318 index = NULL;
1319 if (!dest_group) {
1320 ret = -1;
1321 goto end;
1322 }
1323
1324 g_ptr_array_add(dest_trace->ds_file_groups, dest_group);
1325 }
1326
1327 BT_ASSERT(dest_group);
1328 merge_ctf_fs_ds_file_groups(dest_group, src_group);
1329 }
1330
1331 end:
1332 return ret;
1333 }
1334
1335 /*
1336 * Collapse the given traces, which must all share the same UUID, in a single
1337 * one.
1338 *
1339 * The trace with the most expansive metadata is chosen and all other traces
1340 * are merged into that one. The array slots of all the traces that get merged
1341 * in the chosen one are set to NULL, so only the slot of the chosen trace
1342 * remains non-NULL.
1343 */
1344
1345 static
1346 int merge_ctf_fs_traces(struct ctf_fs_trace **traces, unsigned int num_traces,
1347 struct ctf_fs_trace **out_trace)
1348 {
1349 unsigned int winner_count;
1350 struct ctf_fs_trace *winner;
1351 guint i, winner_i;
1352 int ret = 0;
1353
1354 BT_ASSERT(num_traces >= 2);
1355
1356 winner_count = metadata_count_stream_and_event_classes(traces[0]);
1357 winner = traces[0];
1358 winner_i = 0;
1359
1360 /* Find the trace with the largest metadata. */
1361 for (i = 1; i < num_traces; i++) {
1362 struct ctf_fs_trace *candidate;
1363 unsigned int candidate_count;
1364
1365 candidate = traces[i];
1366
1367 /* A bit of sanity check. */
1368 BT_ASSERT(bt_uuid_compare(winner->metadata->tc->uuid, candidate->metadata->tc->uuid) == 0);
1369
1370 candidate_count = metadata_count_stream_and_event_classes(candidate);
1371
1372 if (candidate_count > winner_count) {
1373 winner_count = candidate_count;
1374 winner = candidate;
1375 winner_i = i;
1376 }
1377 }
1378
1379 /* Merge all the other traces in the winning trace. */
1380 for (i = 0; i < num_traces; i++) {
1381 struct ctf_fs_trace *trace = traces[i];
1382
1383 /* Don't merge the winner into itself. */
1384 if (trace == winner) {
1385 continue;
1386 }
1387
1388 /* Merge trace's data stream file groups into winner's. */
1389 ret = merge_matching_ctf_fs_ds_file_groups(winner, trace);
1390 if (ret) {
1391 goto end;
1392 }
1393 }
1394
1395 /*
1396 * Move the winner out of the array, into `*out_trace`.
1397 */
1398 *out_trace = winner;
1399 traces[winner_i] = NULL;
1400
1401 end:
1402 return ret;
1403 }
1404
1405 enum target_event {
1406 FIRST_EVENT,
1407 LAST_EVENT,
1408 };
1409
1410 static
1411 int decode_clock_snapshot_after_event(struct ctf_fs_trace *ctf_fs_trace,
1412 struct ctf_clock_class *default_cc,
1413 struct ctf_fs_ds_index_entry *index_entry,
1414 enum target_event target_event, uint64_t *cs, int64_t *ts_ns)
1415 {
1416 enum bt_msg_iter_status iter_status = BT_MSG_ITER_STATUS_OK;
1417 struct ctf_fs_ds_file *ds_file = NULL;
1418 struct bt_msg_iter *msg_iter = NULL;
1419 bt_logging_level log_level = ctf_fs_trace->log_level;
1420 bt_self_component *self_comp = ctf_fs_trace->self_comp;
1421 int ret = 0;
1422
1423 BT_ASSERT(ctf_fs_trace);
1424 BT_ASSERT(ctf_fs_trace->metadata);
1425 BT_ASSERT(ctf_fs_trace->metadata->tc);
1426
1427 msg_iter = bt_msg_iter_create(ctf_fs_trace->metadata->tc,
1428 bt_common_get_page_size(log_level) * 8, ctf_fs_ds_file_medops,
1429 NULL, log_level, self_comp);
1430 if (!msg_iter) {
1431 /* bt_msg_iter_create() logs errors. */
1432 ret = -1;
1433 goto end;
1434 }
1435
1436 BT_ASSERT(index_entry);
1437 BT_ASSERT(index_entry->path);
1438
1439 ds_file = ctf_fs_ds_file_create(ctf_fs_trace, NULL, msg_iter,
1440 NULL, index_entry->path, log_level);
1441 if (!ds_file) {
1442 BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Failed to create a ctf_fs_ds_file");
1443 ret = -1;
1444 goto end;
1445 }
1446
1447 /*
1448 * Turn on dry run mode to prevent the creation and usage of Babeltrace
1449 * library objects (bt_field, bt_message_*, etc.).
1450 */
1451 bt_msg_iter_set_dry_run(msg_iter, true);
1452
1453 /* Seek to the beginning of the target packet. */
1454 iter_status = bt_msg_iter_seek(ds_file->msg_iter, index_entry->offset);
1455 if (iter_status) {
1456 /* bt_msg_iter_seek() logs errors. */
1457 ret = -1;
1458 goto end;
1459 }
1460
1461 switch (target_event) {
1462 case FIRST_EVENT:
1463 /*
1464 * Start to decode the packet until we reach the end of
1465 * the first event. To extract the first event's clock
1466 * snapshot.
1467 */
1468 iter_status = bt_msg_iter_curr_packet_first_event_clock_snapshot(
1469 ds_file->msg_iter, cs);
1470 break;
1471 case LAST_EVENT:
1472 /* Decode the packet to extract the last event's clock snapshot. */
1473 iter_status = bt_msg_iter_curr_packet_last_event_clock_snapshot(
1474 ds_file->msg_iter, cs);
1475 break;
1476 default:
1477 abort();
1478 }
1479 if (iter_status) {
1480 ret = -1;
1481 goto end;
1482 }
1483
1484 /* Convert clock snapshot to timestamp. */
1485 ret = bt_util_clock_cycles_to_ns_from_origin(*cs,
1486 default_cc->frequency, default_cc->offset_seconds,
1487 default_cc->offset_cycles, ts_ns);
1488 if (ret) {
1489 BT_COMP_LOGE_APPEND_CAUSE(self_comp,
1490 "Failed to convert clock snapshot to timestamp");
1491 goto end;
1492 }
1493
1494 end:
1495 if (ds_file) {
1496 ctf_fs_ds_file_destroy(ds_file);
1497 }
1498 if (msg_iter) {
1499 bt_msg_iter_destroy(msg_iter);
1500 }
1501
1502 return ret;
1503 }
1504
1505 static
1506 int decode_packet_first_event_timestamp(struct ctf_fs_trace *ctf_fs_trace,
1507 struct ctf_clock_class *default_cc,
1508 struct ctf_fs_ds_index_entry *index_entry, uint64_t *cs, int64_t *ts_ns)
1509 {
1510 return decode_clock_snapshot_after_event(ctf_fs_trace, default_cc,
1511 index_entry, FIRST_EVENT, cs, ts_ns);
1512 }
1513
1514 static
1515 int decode_packet_last_event_timestamp(struct ctf_fs_trace *ctf_fs_trace,
1516 struct ctf_clock_class *default_cc,
1517 struct ctf_fs_ds_index_entry *index_entry, uint64_t *cs, int64_t *ts_ns)
1518 {
1519 return decode_clock_snapshot_after_event(ctf_fs_trace, default_cc,
1520 index_entry, LAST_EVENT, cs, ts_ns);
1521 }
1522
1523 /*
1524 * Fix up packet index entries for lttng's "event-after-packet" bug.
1525 * Some buggy lttng tracer versions may emit events with a timestamp that is
1526 * larger (after) than the timestamp_end of the their packets.
1527 *
1528 * To fix up this erroneous data we do the following:
1529 * 1. If it's not the stream file's last packet: set the packet index entry's
1530 * end time to the next packet's beginning time.
1531 * 2. If it's the stream file's last packet, set the packet index entry's end
1532 * time to the packet's last event's time, if any, or to the packet's
1533 * beginning time otherwise.
1534 *
1535 * Known buggy tracer versions:
1536 * - before lttng-ust 2.11.0
1537 * - before lttng-module 2.11.0
1538 * - before lttng-module 2.10.10
1539 * - before lttng-module 2.9.13
1540 */
1541 static
1542 int fix_index_lttng_event_after_packet_bug(struct ctf_fs_trace *trace)
1543 {
1544 int ret = 0;
1545 guint ds_file_group_i;
1546 GPtrArray *ds_file_groups = trace->ds_file_groups;
1547 bt_logging_level log_level = trace->log_level;
1548
1549 for (ds_file_group_i = 0; ds_file_group_i < ds_file_groups->len;
1550 ds_file_group_i++) {
1551 guint entry_i;
1552 struct ctf_clock_class *default_cc;
1553 struct ctf_fs_ds_index_entry *last_entry;
1554 struct ctf_fs_ds_index *index;
1555
1556 struct ctf_fs_ds_file_group *ds_file_group =
1557 g_ptr_array_index(ds_file_groups, ds_file_group_i);
1558
1559 BT_ASSERT(ds_file_group);
1560 index = ds_file_group->index;
1561
1562 BT_ASSERT(index);
1563 BT_ASSERT(index->entries);
1564 BT_ASSERT(index->entries->len > 0);
1565
1566 /*
1567 * Iterate over all entries but the last one. The last one is
1568 * fixed differently after.
1569 */
1570 for (entry_i = 0; entry_i < index->entries->len - 1;
1571 entry_i++) {
1572 struct ctf_fs_ds_index_entry *curr_entry, *next_entry;
1573
1574 curr_entry = g_ptr_array_index(index->entries, entry_i);
1575 next_entry = g_ptr_array_index(index->entries, entry_i + 1);
1576
1577 /*
1578 * 1. Set the current index entry `end` timestamp to
1579 * the next index entry `begin` timestamp.
1580 */
1581 curr_entry->timestamp_end = next_entry->timestamp_begin;
1582 curr_entry->timestamp_end_ns = next_entry->timestamp_begin_ns;
1583 }
1584
1585 /*
1586 * 2. Fix the last entry by decoding the last event of the last
1587 * packet.
1588 */
1589 last_entry = g_ptr_array_index(index->entries,
1590 index->entries->len - 1);
1591 BT_ASSERT(last_entry);
1592
1593 BT_ASSERT(ds_file_group->sc->default_clock_class);
1594 default_cc = ds_file_group->sc->default_clock_class;
1595
1596 /*
1597 * Decode packet to read the timestamp of the last event of the
1598 * entry.
1599 */
1600 ret = decode_packet_last_event_timestamp(trace, default_cc,
1601 last_entry, &last_entry->timestamp_end,
1602 &last_entry->timestamp_end_ns);
1603 if (ret) {
1604 BT_COMP_LOGE_APPEND_CAUSE(trace->self_comp,
1605 "Failed to decode stream's last packet to get its last event's clock snapshot.");
1606 goto end;
1607 }
1608 }
1609
1610 end:
1611 return ret;
1612 }
1613
1614 /*
1615 * Fix up packet index entries for barectf's "event-before-packet" bug.
1616 * Some buggy barectf tracer versions may emit events with a timestamp that is
1617 * less than the timestamp_begin of the their packets.
1618 *
1619 * To fix up this erroneous data we do the following:
1620 * 1. Starting at the second index entry, set the timestamp_begin of the
1621 * current entry to the timestamp of the first event of the packet.
1622 * 2. Set the previous entry's timestamp_end to the timestamp_begin of the
1623 * current packet.
1624 *
1625 * Known buggy tracer versions:
1626 * - before barectf 2.3.1
1627 */
1628 static
1629 int fix_index_barectf_event_before_packet_bug(struct ctf_fs_trace *trace)
1630 {
1631 int ret = 0;
1632 guint ds_file_group_i;
1633 GPtrArray *ds_file_groups = trace->ds_file_groups;
1634 bt_logging_level log_level = trace->log_level;
1635
1636 for (ds_file_group_i = 0; ds_file_group_i < ds_file_groups->len;
1637 ds_file_group_i++) {
1638 guint entry_i;
1639 struct ctf_clock_class *default_cc;
1640 struct ctf_fs_ds_file_group *ds_file_group =
1641 g_ptr_array_index(ds_file_groups, ds_file_group_i);
1642
1643 struct ctf_fs_ds_index *index = ds_file_group->index;
1644
1645 BT_ASSERT(index);
1646 BT_ASSERT(index->entries);
1647 BT_ASSERT(index->entries->len > 0);
1648
1649 BT_ASSERT(ds_file_group->sc->default_clock_class);
1650 default_cc = ds_file_group->sc->default_clock_class;
1651
1652 /*
1653 * 1. Iterate over the index, starting from the second entry
1654 * (index = 1).
1655 */
1656 for (entry_i = 1; entry_i < index->entries->len;
1657 entry_i++) {
1658 struct ctf_fs_ds_index_entry *curr_entry, *prev_entry;
1659 prev_entry = g_ptr_array_index(index->entries, entry_i - 1);
1660 curr_entry = g_ptr_array_index(index->entries, entry_i);
1661 /*
1662 * 2. Set the current entry `begin` timestamp to the
1663 * timestamp of the first event of the current packet.
1664 */
1665 ret = decode_packet_first_event_timestamp(trace, default_cc,
1666 curr_entry, &curr_entry->timestamp_begin,
1667 &curr_entry->timestamp_begin_ns);
1668 if (ret) {
1669 BT_COMP_LOGE_APPEND_CAUSE(trace->self_comp,
1670 "Failed to decode first event's clock snapshot");
1671 goto end;
1672 }
1673
1674 /*
1675 * 3. Set the previous entry `end` timestamp to the
1676 * timestamp of the first event of the current packet.
1677 */
1678 prev_entry->timestamp_end = curr_entry->timestamp_begin;
1679 prev_entry->timestamp_end_ns = curr_entry->timestamp_begin_ns;
1680 }
1681 }
1682 end:
1683 return ret;
1684 }
1685
1686 /*
1687 * When using the lttng-crash feature it's likely that the last packets of each
1688 * stream have their timestamp_end set to zero. This is caused by the fact that
1689 * the tracer crashed and was not able to properly close the packets.
1690 *
1691 * To fix up this erroneous data we do the following:
1692 * For each index entry, if the entry's timestamp_end is 0 and the
1693 * timestamp_begin is not 0:
1694 * - If it's the stream file's last packet: set the packet index entry's end
1695 * time to the packet's last event's time, if any, or to the packet's
1696 * beginning time otherwise.
1697 * - If it's not the stream file's last packet: set the packet index
1698 * entry's end time to the next packet's beginning time.
1699 *
1700 * Affected versions:
1701 * - All current and future lttng-ust and lttng-modules versions.
1702 */
1703 static
1704 int fix_index_lttng_crash_quirk(struct ctf_fs_trace *trace)
1705 {
1706 int ret = 0;
1707 guint ds_file_group_idx;
1708 GPtrArray *ds_file_groups = trace->ds_file_groups;
1709 bt_logging_level log_level = trace->log_level;
1710
1711 for (ds_file_group_idx = 0; ds_file_group_idx < ds_file_groups->len;
1712 ds_file_group_idx++) {
1713 guint entry_idx;
1714 struct ctf_clock_class *default_cc;
1715 struct ctf_fs_ds_index_entry *last_entry;
1716 struct ctf_fs_ds_index *index;
1717
1718 struct ctf_fs_ds_file_group *ds_file_group =
1719 g_ptr_array_index(ds_file_groups, ds_file_group_idx);
1720
1721 BT_ASSERT(ds_file_group);
1722 index = ds_file_group->index;
1723
1724 BT_ASSERT(ds_file_group->sc->default_clock_class);
1725 default_cc = ds_file_group->sc->default_clock_class;
1726
1727 BT_ASSERT(index);
1728 BT_ASSERT(index->entries);
1729 BT_ASSERT(index->entries->len > 0);
1730
1731 last_entry = g_ptr_array_index(index->entries,
1732 index->entries->len - 1);
1733 BT_ASSERT(last_entry);
1734
1735
1736 /* 1. Fix the last entry first. */
1737 if (last_entry->timestamp_end == 0 &&
1738 last_entry->timestamp_begin != 0) {
1739 /*
1740 * Decode packet to read the timestamp of the
1741 * last event of the stream file.
1742 */
1743 ret = decode_packet_last_event_timestamp(trace,
1744 default_cc, last_entry,
1745 &last_entry->timestamp_end,
1746 &last_entry->timestamp_end_ns);
1747 if (ret) {
1748 BT_COMP_LOGE_APPEND_CAUSE(trace->self_comp,
1749 "Failed to decode last event's clock snapshot");
1750 goto end;
1751 }
1752 }
1753
1754 /* Iterate over all entries but the last one. */
1755 for (entry_idx = 0; entry_idx < index->entries->len - 1;
1756 entry_idx++) {
1757 struct ctf_fs_ds_index_entry *curr_entry, *next_entry;
1758 curr_entry = g_ptr_array_index(index->entries, entry_idx);
1759 next_entry = g_ptr_array_index(index->entries, entry_idx + 1);
1760
1761 if (curr_entry->timestamp_end == 0 &&
1762 curr_entry->timestamp_begin != 0) {
1763 /*
1764 * 2. Set the current index entry `end` timestamp to
1765 * the next index entry `begin` timestamp.
1766 */
1767 curr_entry->timestamp_end = next_entry->timestamp_begin;
1768 curr_entry->timestamp_end_ns = next_entry->timestamp_begin_ns;
1769 }
1770 }
1771 }
1772
1773 end:
1774 return ret;
1775 }
1776
1777 /*
1778 * Extract the tracer information necessary to compare versions.
1779 * Returns 0 on success, and -1 if the extraction is not successful because the
1780 * necessary fields are absents in the trace metadata.
1781 */
1782 static
1783 int extract_tracer_info(struct ctf_fs_trace *trace,
1784 struct tracer_info *current_tracer_info)
1785 {
1786 int ret = 0;
1787 struct ctf_trace_class_env_entry *entry;
1788
1789 /* Clear the current_tracer_info struct */
1790 memset(current_tracer_info, 0, sizeof(*current_tracer_info));
1791
1792 /*
1793 * To compare 2 tracer versions, at least the tracer name and it's
1794 * major version are needed. If one of these is missing, consider it an
1795 * extraction failure.
1796 */
1797 entry = ctf_trace_class_borrow_env_entry_by_name(
1798 trace->metadata->tc, "tracer_name");
1799 if (!entry || entry->type != CTF_TRACE_CLASS_ENV_ENTRY_TYPE_STR) {
1800 goto missing_bare_minimum;
1801 }
1802
1803 /* Set tracer name. */
1804 current_tracer_info->name = entry->value.str->str;
1805
1806 entry = ctf_trace_class_borrow_env_entry_by_name(
1807 trace->metadata->tc, "tracer_major");
1808 if (!entry || entry->type != CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT) {
1809 goto missing_bare_minimum;
1810 }
1811
1812 /* Set major version number. */
1813 current_tracer_info->major = entry->value.i;
1814
1815 entry = ctf_trace_class_borrow_env_entry_by_name(
1816 trace->metadata->tc, "tracer_minor");
1817 if (!entry || entry->type != CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT) {
1818 goto end;
1819 }
1820
1821 /* Set minor version number. */
1822 current_tracer_info->minor = entry->value.i;
1823
1824 entry = ctf_trace_class_borrow_env_entry_by_name(
1825 trace->metadata->tc, "tracer_patch");
1826 if (!entry) {
1827 /*
1828 * If `tracer_patch` doesn't exist `tracer_patchlevel` might.
1829 * For example, `lttng-modules` uses entry name
1830 * `tracer_patchlevel`.
1831 */
1832 entry = ctf_trace_class_borrow_env_entry_by_name(
1833 trace->metadata->tc, "tracer_patchlevel");
1834 }
1835
1836 if (!entry || entry->type != CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT) {
1837 goto end;
1838 }
1839
1840 /* Set patch version number. */
1841 current_tracer_info->patch = entry->value.i;
1842
1843 goto end;
1844
1845 missing_bare_minimum:
1846 ret = -1;
1847 end:
1848 return ret;
1849 }
1850
1851 static
1852 bool is_tracer_affected_by_lttng_event_after_packet_bug(
1853 struct tracer_info *curr_tracer_info)
1854 {
1855 bool is_affected = false;
1856
1857 if (strcmp(curr_tracer_info->name, "lttng-ust") == 0) {
1858 if (curr_tracer_info->major < 2) {
1859 is_affected = true;
1860 } else if (curr_tracer_info->major == 2) {
1861 /* fixed in lttng-ust 2.11.0 */
1862 if (curr_tracer_info->minor < 11) {
1863 is_affected = true;
1864 }
1865 }
1866 } else if (strcmp(curr_tracer_info->name, "lttng-modules") == 0) {
1867 if (curr_tracer_info->major < 2) {
1868 is_affected = true;
1869 } else if (curr_tracer_info->major == 2) {
1870 /* fixed in lttng-modules 2.11.0 */
1871 if (curr_tracer_info->minor == 10) {
1872 /* fixed in lttng-modules 2.10.10 */
1873 if (curr_tracer_info->patch < 10) {
1874 is_affected = true;
1875 }
1876 } else if (curr_tracer_info->minor == 9) {
1877 /* fixed in lttng-modules 2.9.13 */
1878 if (curr_tracer_info->patch < 13) {
1879 is_affected = true;
1880 }
1881 } else if (curr_tracer_info->minor < 9) {
1882 is_affected = true;
1883 }
1884 }
1885 }
1886
1887 return is_affected;
1888 }
1889
1890 static
1891 bool is_tracer_affected_by_barectf_event_before_packet_bug(
1892 struct tracer_info *curr_tracer_info)
1893 {
1894 bool is_affected = false;
1895
1896 if (strcmp(curr_tracer_info->name, "barectf") == 0) {
1897 if (curr_tracer_info->major < 2) {
1898 is_affected = true;
1899 } else if (curr_tracer_info->major == 2) {
1900 if (curr_tracer_info->minor < 3) {
1901 is_affected = true;
1902 } else if (curr_tracer_info->minor == 3) {
1903 /* fixed in barectf 2.3.1 */
1904 if (curr_tracer_info->patch < 1) {
1905 is_affected = true;
1906 }
1907 }
1908 }
1909 }
1910
1911 return is_affected;
1912 }
1913
1914 static
1915 bool is_tracer_affected_by_lttng_crash_quirk(
1916 struct tracer_info *curr_tracer_info)
1917 {
1918 bool is_affected = false;
1919
1920 /* All LTTng tracer may be affected by this lttng crash quirk. */
1921 if (strcmp(curr_tracer_info->name, "lttng-ust") == 0) {
1922 is_affected = true;
1923 } else if (strcmp(curr_tracer_info->name, "lttng-modules") == 0) {
1924 is_affected = true;
1925 }
1926
1927 return is_affected;
1928 }
1929
1930 /*
1931 * Looks for trace produced by known buggy tracers and fix up the index
1932 * produced earlier.
1933 */
1934 static
1935 int fix_packet_index_tracer_bugs(struct ctf_fs_component *ctf_fs,
1936 bt_self_component *self_comp,
1937 bt_self_component_class *self_comp_class)
1938 {
1939 int ret = 0;
1940 struct tracer_info current_tracer_info;
1941 bt_logging_level log_level = ctf_fs->log_level;
1942
1943 ret = extract_tracer_info(ctf_fs->trace, &current_tracer_info);
1944 if (ret) {
1945 /*
1946 * A trace may not have all the necessary environment
1947 * entries to do the tracer version comparison.
1948 * At least, the tracer name and major version number
1949 * are needed. Failing to extract these entries is not
1950 * an error.
1951 */
1952 ret = 0;
1953 BT_LOGI_STR("Cannot extract tracer information necessary to compare with buggy versions.");
1954 goto end;;
1955 }
1956
1957 /* Check if the trace may be affected by old tracer bugs. */
1958 if (is_tracer_affected_by_lttng_event_after_packet_bug(
1959 &current_tracer_info)) {
1960 BT_LOGI_STR("Trace may be affected by LTTng tracer packet timestamp bug. Fixing up.");
1961 ret = fix_index_lttng_event_after_packet_bug(ctf_fs->trace);
1962 if (ret) {
1963 BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
1964 self_comp, self_comp_class,
1965 "Failed to fix LTTng event-after-packet bug.");
1966 goto end;
1967 }
1968 ctf_fs->trace->metadata->tc->quirks.lttng_event_after_packet = true;
1969 }
1970
1971 if (is_tracer_affected_by_barectf_event_before_packet_bug(
1972 &current_tracer_info)) {
1973 BT_LOGI_STR("Trace may be affected by barectf tracer packet timestamp bug. Fixing up.");
1974 ret = fix_index_barectf_event_before_packet_bug(ctf_fs->trace);
1975 if (ret) {
1976 BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
1977 self_comp, self_comp_class,
1978 "Failed to fix barectf event-before-packet bug.");
1979 goto end;
1980 }
1981 ctf_fs->trace->metadata->tc->quirks.barectf_event_before_packet = true;
1982 }
1983
1984 if (is_tracer_affected_by_lttng_crash_quirk(
1985 &current_tracer_info)) {
1986 ret = fix_index_lttng_crash_quirk(ctf_fs->trace);
1987 if (ret) {
1988 BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
1989 self_comp, self_comp_class,
1990 "Failed to fix lttng-crash timestamp quirks.");
1991 goto end;
1992 }
1993 ctf_fs->trace->metadata->tc->quirks.lttng_crash = true;
1994 }
1995
1996 end:
1997 return ret;
1998 }
1999
2000 static
2001 gint compare_ds_file_groups_by_first_path(gconstpointer a, gconstpointer b)
2002 {
2003 struct ctf_fs_ds_file_group * const *ds_file_group_a = a;
2004 struct ctf_fs_ds_file_group * const *ds_file_group_b = b;
2005 const struct ctf_fs_ds_file_info *first_ds_file_info_a;
2006 const struct ctf_fs_ds_file_info *first_ds_file_info_b;
2007
2008 BT_ASSERT((*ds_file_group_a)->ds_file_infos->len > 0);
2009 BT_ASSERT((*ds_file_group_b)->ds_file_infos->len > 0);
2010 first_ds_file_info_a = (*ds_file_group_a)->ds_file_infos->pdata[0];
2011 first_ds_file_info_b = (*ds_file_group_b)->ds_file_infos->pdata[0];
2012 return strcmp(first_ds_file_info_a->path->str,
2013 first_ds_file_info_b->path->str);
2014 }
2015
2016 int ctf_fs_component_create_ctf_fs_trace(
2017 struct ctf_fs_component *ctf_fs,
2018 const bt_value *paths_value,
2019 const bt_value *trace_name_value,
2020 bt_self_component *self_comp,
2021 bt_self_component_class *self_comp_class)
2022 {
2023 int ret = 0;
2024 uint64_t i;
2025 bt_logging_level log_level = ctf_fs->log_level;
2026 GPtrArray *traces;
2027 const char *trace_name;
2028
2029 BT_ASSERT(bt_value_get_type(paths_value) == BT_VALUE_TYPE_ARRAY);
2030 BT_ASSERT(!bt_value_array_is_empty(paths_value));
2031
2032 traces = g_ptr_array_new_with_free_func(ctf_fs_trace_destroy_notifier);
2033 if (!traces) {
2034 BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
2035 "Failed to allocate a GPtrArray.");
2036 goto error;
2037 }
2038
2039 trace_name = trace_name_value ? bt_value_string_get(trace_name_value) : NULL;
2040
2041 /* Start by creating a separate ctf_fs_trace object for each path. */
2042 for (i = 0; i < bt_value_array_get_length(paths_value); i++) {
2043 const bt_value *path_value = bt_value_array_borrow_element_by_index_const(paths_value, i);
2044 const char *input = bt_value_string_get(path_value);
2045
2046 ret = ctf_fs_component_create_ctf_fs_trace_one_path(ctf_fs,
2047 input, trace_name, traces, self_comp, self_comp_class);
2048 if (ret) {
2049 goto end;
2050 }
2051 }
2052
2053 if (traces->len > 1) {
2054 struct ctf_fs_trace *first_trace = (struct ctf_fs_trace *) traces->pdata[0];
2055 const uint8_t *first_trace_uuid = first_trace->metadata->tc->uuid;
2056 struct ctf_fs_trace *trace;
2057
2058 /*
2059 * We have more than one trace, they must all share the same
2060 * UUID, verify that.
2061 */
2062 for (i = 0; i < traces->len; i++) {
2063 struct ctf_fs_trace *this_trace =
2064 (struct ctf_fs_trace *) traces->pdata[i];
2065 const uint8_t *this_trace_uuid = this_trace->metadata->tc->uuid;
2066
2067 if (!this_trace->metadata->tc->is_uuid_set) {
2068 BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
2069 "Multiple traces given, but a trace does not have a UUID: path=%s",
2070 this_trace->path->str);
2071 goto error;
2072 }
2073
2074 if (bt_uuid_compare(first_trace_uuid, this_trace_uuid) != 0) {
2075 char first_trace_uuid_str[BT_UUID_STR_LEN + 1];
2076 char this_trace_uuid_str[BT_UUID_STR_LEN + 1];
2077
2078 bt_uuid_to_str(first_trace_uuid, first_trace_uuid_str);
2079 bt_uuid_to_str(this_trace_uuid, this_trace_uuid_str);
2080
2081 BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
2082 "Multiple traces given, but UUIDs don't match: "
2083 "first-trace-uuid=%s, first-trace-path=%s, "
2084 "trace-uuid=%s, trace-path=%s",
2085 first_trace_uuid_str, first_trace->path->str,
2086 this_trace_uuid_str, this_trace->path->str);
2087 goto error;
2088 }
2089 }
2090
2091 ret = merge_ctf_fs_traces((struct ctf_fs_trace **) traces->pdata,
2092 traces->len, &trace);
2093 if (ret) {
2094 BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
2095 "Failed to merge traces with the same UUID.");
2096 goto error;
2097 }
2098
2099 ctf_fs->trace = trace;
2100 } else {
2101 /* Just one trace, it may or may not have a UUID, both are fine. */
2102 ctf_fs->trace = traces->pdata[0];
2103 traces->pdata[0] = NULL;
2104 }
2105
2106 ret = fix_packet_index_tracer_bugs(ctf_fs, self_comp, self_comp_class);
2107 if (ret) {
2108 BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
2109 "Failed to fix packet index tracer bugs.");
2110 }
2111
2112 /*
2113 * Sort data stream file groups by first data stream file info
2114 * path to get a deterministic order. This order influences the
2115 * order of the output ports. It also influences the order of
2116 * the automatic stream IDs if the trace's packet headers do not
2117 * contain a `stream_instance_id` field, in which case the data
2118 * stream file to stream ID association is always the same,
2119 * whatever the build and the system.
2120 *
2121 * Having a deterministic order here can help debugging and
2122 * testing.
2123 */
2124 g_ptr_array_sort(ctf_fs->trace->ds_file_groups,
2125 compare_ds_file_groups_by_first_path);
2126 goto end;
2127 error:
2128 ret = -1;
2129
2130 end:
2131 g_ptr_array_free(traces, TRUE);
2132 return ret;
2133 }
2134
2135 static
2136 GString *get_stream_instance_unique_name(
2137 struct ctf_fs_ds_file_group *ds_file_group)
2138 {
2139 GString *name;
2140 struct ctf_fs_ds_file_info *ds_file_info;
2141
2142 name = g_string_new(NULL);
2143 if (!name) {
2144 goto end;
2145 }
2146
2147 /*
2148 * If there's more than one stream file in the stream file
2149 * group, the first (earliest) stream file's path is used as
2150 * the stream's unique name.
2151 */
2152 BT_ASSERT(ds_file_group->ds_file_infos->len > 0);
2153 ds_file_info = g_ptr_array_index(ds_file_group->ds_file_infos, 0);
2154 g_string_assign(name, ds_file_info->path->str);
2155
2156 end:
2157 return name;
2158 }
2159
2160 /* Create the IR stream objects for ctf_fs_trace. */
2161
2162 static
2163 int create_streams_for_trace(struct ctf_fs_trace *ctf_fs_trace)
2164 {
2165 int ret;
2166 GString *name = NULL;
2167 guint i;
2168 bt_logging_level log_level = ctf_fs_trace->log_level;
2169 bt_self_component *self_comp = ctf_fs_trace->self_comp;
2170
2171 for (i = 0; i < ctf_fs_trace->ds_file_groups->len; i++) {
2172 struct ctf_fs_ds_file_group *ds_file_group =
2173 g_ptr_array_index(ctf_fs_trace->ds_file_groups, i);
2174 name = get_stream_instance_unique_name(ds_file_group);
2175
2176 if (!name) {
2177 goto error;
2178 }
2179
2180 if (ds_file_group->sc->ir_sc) {
2181 BT_ASSERT(ctf_fs_trace->trace);
2182
2183 if (ds_file_group->stream_id == UINT64_C(-1)) {
2184 /* No stream ID: use 0 */
2185 ds_file_group->stream = bt_stream_create_with_id(
2186 ds_file_group->sc->ir_sc,
2187 ctf_fs_trace->trace,
2188 ctf_fs_trace->next_stream_id);
2189 ctf_fs_trace->next_stream_id++;
2190 } else {
2191 /* Specific stream ID */
2192 ds_file_group->stream = bt_stream_create_with_id(
2193 ds_file_group->sc->ir_sc,
2194 ctf_fs_trace->trace,
2195 (uint64_t) ds_file_group->stream_id);
2196 }
2197 } else {
2198 ds_file_group->stream = NULL;
2199 }
2200
2201 if (!ds_file_group->stream) {
2202 BT_COMP_LOGE_APPEND_CAUSE(self_comp,
2203 "Cannot create stream for DS file group: "
2204 "addr=%p, stream-name=\"%s\"",
2205 ds_file_group, name->str);
2206 goto error;
2207 }
2208
2209 ret = bt_stream_set_name(ds_file_group->stream,
2210 name->str);
2211 if (ret) {
2212 BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Cannot set stream's name: "
2213 "addr=%p, stream-name=\"%s\"",
2214 ds_file_group->stream, name->str);
2215 goto error;
2216 }
2217
2218 g_string_free(name, TRUE);
2219 name = NULL;
2220 }
2221
2222 ret = 0;
2223 goto end;
2224
2225 error:
2226 ret = -1;
2227
2228 end:
2229
2230 if (name) {
2231 g_string_free(name, TRUE);
2232 }
2233 return ret;
2234 }
2235
2236 static const struct bt_param_validation_value_descr inputs_elem_descr = {
2237 .type = BT_VALUE_TYPE_STRING,
2238 };
2239
2240 static const struct bt_param_validation_map_value_entry_descr fs_params_entries_descr[] = {
2241 { "inputs", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, {
2242 BT_VALUE_TYPE_ARRAY,
2243 .array = {
2244 .min_length = 1,
2245 .max_length = BT_PARAM_VALIDATION_INFINITE,
2246 .element_type = &inputs_elem_descr,
2247 }
2248 }},
2249 { "trace-name", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_STRING } },
2250 { "clock-class-offset-s", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_SIGNED_INTEGER } },
2251 { "clock-class-offset-ns", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_SIGNED_INTEGER } },
2252 { "force-clock-class-origin-unix-epoch", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_BOOL } },
2253 BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
2254 };
2255
2256
2257 bool read_src_fs_parameters(const bt_value *params,
2258 const bt_value **inputs,
2259 const bt_value **trace_name,
2260 struct ctf_fs_component *ctf_fs,
2261 bt_self_component *self_comp,
2262 bt_self_component_class *self_comp_class) {
2263 bool ret;
2264 const bt_value *value;
2265 bt_logging_level log_level = ctf_fs->log_level;
2266 enum bt_param_validation_status validate_value_status;
2267 gchar *error = NULL;
2268
2269 validate_value_status = bt_param_validation_validate(params,
2270 fs_params_entries_descr, &error);
2271 if (validate_value_status != BT_PARAM_VALIDATION_STATUS_OK) {
2272 BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
2273 "%s", error);
2274 ret = false;
2275 goto end;
2276 }
2277
2278 /* inputs parameter */
2279 *inputs = bt_value_map_borrow_entry_value_const(params, "inputs");
2280
2281 /* clock-class-offset-s parameter */
2282 value = bt_value_map_borrow_entry_value_const(params,
2283 "clock-class-offset-s");
2284 if (value) {
2285 ctf_fs->metadata_config.clock_class_offset_s =
2286 bt_value_integer_signed_get(value);
2287 }
2288
2289 /* clock-class-offset-ns parameter */
2290 value = bt_value_map_borrow_entry_value_const(params,
2291 "clock-class-offset-ns");
2292 if (value) {
2293 ctf_fs->metadata_config.clock_class_offset_ns =
2294 bt_value_integer_signed_get(value);
2295 }
2296
2297 /* force-clock-class-origin-unix-epoch parameter */
2298 value = bt_value_map_borrow_entry_value_const(params,
2299 "force-clock-class-origin-unix-epoch");
2300 if (value) {
2301 ctf_fs->metadata_config.force_clock_class_origin_unix_epoch =
2302 bt_value_bool_get(value);
2303 }
2304
2305 /* trace-name parameter */
2306 *trace_name = bt_value_map_borrow_entry_value_const(params, "trace-name");
2307
2308 ret = true;
2309
2310 end:
2311 g_free(error);
2312 return ret;
2313 }
2314
2315 static
2316 struct ctf_fs_component *ctf_fs_create(
2317 const bt_value *params,
2318 bt_self_component_source *self_comp_src)
2319 {
2320 struct ctf_fs_component *ctf_fs = NULL;
2321 const bt_value *inputs_value;
2322 const bt_value *trace_name_value;
2323 bt_self_component *self_comp =
2324 bt_self_component_source_as_self_component(self_comp_src);
2325
2326 ctf_fs = ctf_fs_component_create(bt_component_get_logging_level(
2327 bt_self_component_as_component(self_comp)), self_comp);
2328 if (!ctf_fs) {
2329 goto error;
2330 }
2331
2332 if (!read_src_fs_parameters(params, &inputs_value, &trace_name_value,
2333 ctf_fs, self_comp, NULL)) {
2334 goto error;
2335 }
2336
2337 bt_self_component_set_data(self_comp, ctf_fs);
2338
2339 if (ctf_fs_component_create_ctf_fs_trace(ctf_fs, inputs_value,
2340 trace_name_value, self_comp, NULL)) {
2341 goto error;
2342 }
2343
2344 if (create_streams_for_trace(ctf_fs->trace)) {
2345 goto error;
2346 }
2347
2348 if (create_ports_for_trace(ctf_fs, ctf_fs->trace, self_comp_src)) {
2349 goto error;
2350 }
2351
2352 goto end;
2353
2354 error:
2355 ctf_fs_destroy(ctf_fs);
2356 ctf_fs = NULL;
2357 bt_self_component_set_data(self_comp, NULL);
2358
2359 end:
2360 return ctf_fs;
2361 }
2362
2363 BT_HIDDEN
2364 bt_component_class_initialize_method_status ctf_fs_init(
2365 bt_self_component_source *self_comp_src,
2366 bt_self_component_source_configuration *config,
2367 const bt_value *params, __attribute__((unused)) void *init_method_data)
2368 {
2369 struct ctf_fs_component *ctf_fs;
2370 bt_component_class_initialize_method_status ret =
2371 BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
2372
2373 ctf_fs = ctf_fs_create(params, self_comp_src);
2374 if (!ctf_fs) {
2375 ret = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
2376 }
2377
2378 return ret;
2379 }
2380
2381 BT_HIDDEN
2382 bt_component_class_query_method_status ctf_fs_query(
2383 bt_self_component_class_source *comp_class,
2384 bt_private_query_executor *priv_query_exec,
2385 const char *object, const bt_value *params,
2386 __attribute__((unused)) void *method_data,
2387 const bt_value **result)
2388 {
2389 bt_component_class_query_method_status status =
2390 BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_OK;
2391 bt_logging_level log_level = bt_query_executor_get_logging_level(
2392 bt_private_query_executor_as_query_executor_const(
2393 priv_query_exec));
2394
2395 if (strcmp(object, "metadata-info") == 0) {
2396 status = metadata_info_query(comp_class, params, log_level,
2397 result);
2398 } else if (strcmp(object, "babeltrace.trace-infos") == 0) {
2399 status = trace_infos_query(comp_class, params, log_level,
2400 result);
2401 } else if (!strcmp(object, "babeltrace.support-info")) {
2402 status = support_info_query(comp_class, params, log_level, result);
2403 } else {
2404 BT_LOGE("Unknown query object `%s`", object);
2405 status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_UNKNOWN_OBJECT;
2406 goto end;
2407 }
2408 end:
2409 return status;
2410 }
This page took 0.111821 seconds and 4 git commands to generate.