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