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