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