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