.gitignore: add some missing files
[babeltrace.git] / src / plugins / ctf / fs-src / fs.cpp
1 /*
2 * SPDX-License-Identifier: MIT
3 *
4 * Copyright 2015-2017 Philippe Proulx <pproulx@efficios.com>
5 * Copyright 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
6 *
7 * Babeltrace CTF file system Reader Component
8 */
9
10 #include <sstream>
11
12 #include <glib.h>
13
14 #include <babeltrace2/babeltrace.h>
15
16 #include "common/assert.h"
17 #include "common/common.h"
18 #include "cpp-common/bt2/message.hpp"
19 #include "cpp-common/bt2/private-query-executor.hpp"
20 #include "cpp-common/bt2/wrap.hpp"
21 #include "cpp-common/bt2c/file-utils.hpp"
22 #include "cpp-common/bt2c/glib-up.hpp"
23 #include "cpp-common/bt2s/make-unique.hpp"
24
25 #include "plugins/common/param-validation/param-validation.h"
26
27 #include "../common/src/metadata/ctf-ir.hpp"
28 #include "../common/src/metadata/tsdl/ctf-meta-configure-ir-trace.hpp"
29 #include "../common/src/msg-iter.hpp"
30 #include "../common/src/pkt-props.hpp"
31 #include "data-stream-file.hpp"
32 #include "file.hpp"
33 #include "fs.hpp"
34 #include "metadata.hpp"
35 #include "query.hpp"
36
37 using namespace bt2c::literals::datalen;
38 using namespace ctf::src;
39 using namespace ctf;
40
41 struct tracer_info
42 {
43 const char *name;
44 int64_t major;
45 int64_t minor;
46 int64_t patch;
47 };
48
49 bt_message_iterator_class_next_method_status
50 ctf_fs_iterator_next(bt_self_message_iterator *iterator, bt_message_array_const msgs,
51 uint64_t capacity, uint64_t *count)
52 {
53 struct ctf_fs_msg_iter_data *msg_iter_data =
54 (struct ctf_fs_msg_iter_data *) bt_self_message_iterator_get_data(iterator);
55 uint64_t i = 0;
56
57 if (G_UNLIKELY(msg_iter_data->next_saved_error)) {
58 /*
59 * Last time we were called, we hit an error but had some
60 * messages to deliver, so we stashed the error here. Return
61 * it now.
62 */
63 BT_CURRENT_THREAD_MOVE_ERROR_AND_RESET(msg_iter_data->next_saved_error);
64 return msg_iter_data->next_saved_status;
65 }
66
67 bt_message_iterator_class_next_method_status status =
68 BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK;
69
70 do {
71 try {
72 bt2::ConstMessage::Shared msg = msg_iter_data->msgIter->next();
73 if (G_LIKELY(msg)) {
74 msgs[i] = msg.release().libObjPtr();
75 ++i;
76 } else {
77 status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_END;
78 }
79 } catch (const bt2::Error&) {
80 status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR;
81 break;
82 } catch (const std::bad_alloc&) {
83 status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_MEMORY_ERROR;
84 break;
85 }
86 } while (i < capacity && status == BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK);
87
88 if (i > 0) {
89 /*
90 * Even if ctf_fs_iterator_next_one() returned something
91 * else than BT_MESSAGE_ITERATOR_NEXT_METHOD_STATUS_OK, we
92 * accumulated message objects in the output
93 * message array, so we need to return
94 * BT_MESSAGE_ITERATOR_NEXT_METHOD_STATUS_OK so that they are
95 * transferred to downstream. This other status occurs
96 * again the next time muxer_msg_iter_do_next() is
97 * called, possibly without any accumulated
98 * message, in which case we'll return it.
99 */
100 if (status < 0) {
101 /*
102 * Save this error for the next _next call. Assume that
103 * this component always appends error causes when
104 * returning an error status code, which will cause the
105 * current thread error to be non-NULL.
106 */
107 msg_iter_data->next_saved_error = bt_current_thread_take_error();
108 BT_ASSERT(msg_iter_data->next_saved_error);
109 msg_iter_data->next_saved_status = status;
110 }
111
112 *count = i;
113 status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK;
114 }
115
116 return status;
117 }
118
119 static void instantiateMsgIter(ctf_fs_msg_iter_data *msg_iter_data)
120 {
121 ctf_fs_ds_file_group *ds_file_group = msg_iter_data->port_data->ds_file_group;
122
123 Medium::UP medium = bt2s::make_unique<fs::Medium>(ds_file_group->index, msg_iter_data->logger);
124 msg_iter_data->msgIter.emplace(msg_iter_data->selfMsgIter, *ds_file_group->ctf_fs_trace->cls(),
125 ds_file_group->ctf_fs_trace->metadataStreamUuid(),
126 *ds_file_group->stream, std::move(medium),
127 msg_iter_data->port_data->ctf_fs->quirks, msg_iter_data->logger);
128 }
129
130 bt_message_iterator_class_seek_beginning_method_status
131 ctf_fs_iterator_seek_beginning(bt_self_message_iterator *it)
132 {
133 try {
134 struct ctf_fs_msg_iter_data *msg_iter_data =
135 (struct ctf_fs_msg_iter_data *) bt_self_message_iterator_get_data(it);
136
137 BT_ASSERT(msg_iter_data);
138
139 instantiateMsgIter(msg_iter_data);
140
141 return BT_MESSAGE_ITERATOR_CLASS_SEEK_BEGINNING_METHOD_STATUS_OK;
142 } catch (const std::bad_alloc&) {
143 return BT_MESSAGE_ITERATOR_CLASS_SEEK_BEGINNING_METHOD_STATUS_MEMORY_ERROR;
144 } catch (const bt2::Error&) {
145 return BT_MESSAGE_ITERATOR_CLASS_SEEK_BEGINNING_METHOD_STATUS_ERROR;
146 }
147 }
148
149 void ctf_fs_iterator_finalize(bt_self_message_iterator *it)
150 {
151 ctf_fs_msg_iter_data::UP {
152 static_cast<ctf_fs_msg_iter_data *>(bt_self_message_iterator_get_data(it))};
153 }
154
155 bt_message_iterator_class_initialize_method_status
156 ctf_fs_iterator_init(bt_self_message_iterator *self_msg_iter,
157 bt_self_message_iterator_configuration *config,
158 bt_self_component_port_output *self_port)
159 {
160 try {
161 ctf_fs_port_data *port_data = (struct ctf_fs_port_data *) bt_self_component_port_get_data(
162 bt_self_component_port_output_as_self_component_port(self_port));
163 BT_ASSERT(port_data);
164
165 auto msg_iter_data = bt2s::make_unique<ctf_fs_msg_iter_data>(bt2::wrap(self_msg_iter));
166 msg_iter_data->port_data = port_data;
167
168 instantiateMsgIter(msg_iter_data.get());
169
170 /*
171 * This iterator can seek forward if its stream class has a default
172 * clock class.
173 */
174 if (msg_iter_data->port_data->ds_file_group->dataStreamCls->defClkCls()) {
175 bt_self_message_iterator_configuration_set_can_seek_forward(config, true);
176 }
177
178 bt_self_message_iterator_set_data(self_msg_iter, msg_iter_data.release());
179
180 return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_OK;
181 } catch (const std::bad_alloc&) {
182 return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
183 } catch (const bt2::Error&) {
184 return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
185 }
186 }
187
188 void ctf_fs_finalize(bt_self_component_source *component)
189 {
190 ctf_fs_component::UP {static_cast<ctf_fs_component *>(
191 bt_self_component_get_data(bt_self_component_source_as_self_component(component)))};
192 }
193
194 std::string ctf_fs_make_port_name(ctf_fs_ds_file_group *ds_file_group)
195 {
196 std::stringstream name;
197
198 /*
199 * The unique port name is generated by concatenating unique identifiers
200 * for:
201 *
202 * - the trace
203 * - the stream class
204 * - the stream
205 */
206
207 /* For the trace, use the UID if present, else the path. */
208 /* ⚠️ TODO: also consider namespace and name? */
209 auto& uid = ds_file_group->ctf_fs_trace->cls()->uid();
210 if (uid) {
211 name << *uid;
212 } else {
213 name << ds_file_group->ctf_fs_trace->path;
214 }
215
216 /*
217 * For the stream class, use the id if present. We can omit this field
218 * otherwise, as there will only be a single stream class.
219 */
220 if (ds_file_group->dataStreamCls->id() != UINT64_C(-1)) {
221 name << " | " << ds_file_group->dataStreamCls->id();
222 }
223
224 /* For the stream, use the id if present, else, use the path. */
225 if (ds_file_group->stream_id != UINT64_C(-1)) {
226 name << " | " << ds_file_group->stream_id;
227 } else {
228 BT_ASSERT(ds_file_group->ds_file_infos.size() == 1);
229 const auto& ds_file_info = *ds_file_group->ds_file_infos[0];
230 name << " | " << ds_file_info.path;
231 }
232
233 return name.str();
234 }
235
236 static int create_one_port_for_trace(struct ctf_fs_component *ctf_fs,
237 struct ctf_fs_ds_file_group *ds_file_group,
238 const bt2::SelfSourceComponent selfSrcComp)
239 {
240 const auto port_name = ctf_fs_make_port_name(ds_file_group);
241 auto port_data = bt2s::make_unique<ctf_fs_port_data>();
242
243 BT_CPPLOGI_SPEC(ctf_fs->logger, "Creating one port named `{}`", port_name);
244
245 port_data->ctf_fs = ctf_fs;
246 port_data->ds_file_group = ds_file_group;
247
248 int ret = bt_self_component_source_add_output_port(selfSrcComp.libObjPtr(), port_name.c_str(),
249 port_data.get(), NULL);
250 if (ret) {
251 return ret;
252 }
253
254 ctf_fs->port_data.emplace_back(std::move(port_data));
255 return 0;
256 }
257
258 static int create_ports_for_trace(struct ctf_fs_component *ctf_fs,
259 struct ctf_fs_trace *ctf_fs_trace,
260 const bt2::SelfSourceComponent selfSrcComp)
261 {
262 /* Create one output port for each stream file group */
263 for (const auto& ds_file_group : ctf_fs_trace->ds_file_groups) {
264 int ret = create_one_port_for_trace(ctf_fs, ds_file_group.get(), selfSrcComp);
265 if (ret) {
266 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctf_fs->logger, "Cannot create output port.");
267 return ret;
268 }
269 }
270
271 return 0;
272 }
273
274 static bool ds_index_entries_equal(const ctf_fs_ds_index_entry& left,
275 const ctf_fs_ds_index_entry& right)
276 {
277 if (left.packetSize != right.packetSize) {
278 return false;
279 }
280
281 if (left.timestamp_begin != right.timestamp_begin) {
282 return false;
283 }
284
285 if (left.timestamp_end != right.timestamp_end) {
286 return false;
287 }
288
289 if (left.packet_seq_num != right.packet_seq_num) {
290 return false;
291 }
292
293 return true;
294 }
295
296 /*
297 * Insert `entry` into `index`, without duplication.
298 *
299 * The entry is inserted only if there isn't an identical entry already.
300 */
301
302 static void ds_index_insert_ds_index_entry_sorted(ctf_fs_ds_index& index,
303 const ctf_fs_ds_index_entry& entry)
304 {
305 /* Find the spot where to insert this index entry. */
306 auto otherEntry = index.entries.begin();
307 for (; otherEntry != index.entries.end(); ++otherEntry) {
308 if (entry.timestamp_begin_ns <= otherEntry->timestamp_begin_ns) {
309 break;
310 }
311 }
312
313 /*
314 * Insert the entry only if a duplicate doesn't already exist.
315 *
316 * There can be duplicate packets if reading multiple overlapping
317 * snapshots of the same trace. We then want the index to contain
318 * a reference to only one copy of that packet.
319 */
320 if (otherEntry == index.entries.end() || !ds_index_entries_equal(entry, *otherEntry)) {
321 index.entries.emplace(otherEntry, entry);
322 }
323 }
324
325 static void merge_ctf_fs_ds_indexes(ctf_fs_ds_index& dest, const ctf_fs_ds_index& src)
326 {
327 for (const auto& entry : src.entries) {
328 ds_index_insert_ds_index_entry_sorted(dest, entry);
329 }
330 }
331
332 static int add_ds_file_to_ds_file_group(struct ctf_fs_trace *ctf_fs_trace, const char *path,
333 const bt2c::Logger& logger)
334 {
335 auto ds_file_info = bt2s::make_unique<ctf_fs_ds_file_info>(path, logger);
336 const auto& traceCls = *ctf_fs_trace->cls();
337 ctf_fs_ds_index tempIndex;
338 ctf_fs_ds_index_entry tempIndexEntry {path, 0_bytes, ds_file_info->size};
339
340 tempIndex.entries.emplace_back(tempIndexEntry);
341
342 const auto props =
343 readPktProps(traceCls, bt2s::make_unique<fs::Medium>(tempIndex, logger), 0_bytes, logger);
344 const auto sc = props.dataStreamCls;
345
346 BT_ASSERT(sc);
347
348 bt2s::optional<unsigned long long> stream_instance_id = props.dataStreamId;
349
350 int64_t begin_ns = -1;
351 if (props.snapshots.beginDefClk) {
352 BT_ASSERT(sc->defClkCls());
353 int ret = bt_util_clock_cycles_to_ns_from_origin(
354 *props.snapshots.beginDefClk, sc->defClkCls()->freq(),
355 sc->defClkCls()->offsetFromOrigin().seconds(),
356 sc->defClkCls()->offsetFromOrigin().cycles(), &begin_ns);
357 if (ret) {
358 BT_CPPLOGE_APPEND_CAUSE_SPEC(
359 logger, "Cannot convert clock cycles to nanoseconds from origin (`{}`).", path);
360 return ret;
361 }
362 }
363
364 auto index = ctf_fs_ds_file_build_index(*ds_file_info, traceCls);
365 if (!index) {
366 BT_CPPLOGE_APPEND_CAUSE_SPEC(logger, "Failed to index CTF stream file \'{}\'", path);
367 return -1;
368 }
369
370 if (!stream_instance_id || begin_ns == -1) {
371 /*
372 * No stream instance ID or no beginning timestamp:
373 * create a unique stream file group for this stream
374 * file because, even if there's a stream instance ID,
375 * there's no timestamp to order the file within its
376 * group.
377 */
378 ctf_fs_trace->ds_file_groups.emplace_back(bt2s::make_unique<ctf_fs_ds_file_group>(
379 ctf_fs_trace, *sc, stream_instance_id ? *stream_instance_id : UINT64_C(-1),
380 std::move(*index)));
381 ctf_fs_trace->ds_file_groups.back()->insert_ds_file_info_sorted(std::move(ds_file_info));
382 return 0;
383 }
384
385 /* Find an existing stream file group with this ID */
386 ctf_fs_ds_file_group *ds_file_group = NULL;
387 for (const auto& candidate : ctf_fs_trace->ds_file_groups) {
388 if (candidate->dataStreamCls == sc && candidate->stream_id == stream_instance_id) {
389 ds_file_group = candidate.get();
390 break;
391 }
392 }
393
394 if (!ds_file_group) {
395 ctf_fs_trace->ds_file_groups.emplace_back(bt2s::make_unique<ctf_fs_ds_file_group>(
396 ctf_fs_trace, *sc, static_cast<std::uint64_t>(*stream_instance_id), std::move(*index)));
397 ds_file_group = ctf_fs_trace->ds_file_groups.back().get();
398 } else {
399 merge_ctf_fs_ds_indexes(ds_file_group->index, *index);
400 }
401
402 ds_file_group->insert_ds_file_info_sorted(std::move(ds_file_info));
403
404 return 0;
405 }
406
407 static int create_ds_file_groups(struct ctf_fs_trace *ctf_fs_trace, const bt2c::Logger& logger)
408 {
409 /* Check each file in the path directory, except specific ones */
410 GError *error = NULL;
411 const bt2c::GDirUP dir {g_dir_open(ctf_fs_trace->path.c_str(), 0, &error)};
412 if (!dir) {
413 BT_CPPLOGE_APPEND_CAUSE_SPEC(logger, "Cannot open directory `{}`: {} (code {})",
414 ctf_fs_trace->path, error->message, error->code);
415 if (error) {
416 g_error_free(error);
417 }
418 return -1;
419 }
420
421 while (const char *basename = g_dir_read_name(dir.get())) {
422 if (strcmp(basename, CTF_FS_METADATA_FILENAME) == 0) {
423 /* Ignore the metadata stream. */
424 BT_CPPLOGI_SPEC(logger, "Ignoring metadata file `{}" G_DIR_SEPARATOR_S "{}`",
425 ctf_fs_trace->path, basename);
426 continue;
427 }
428
429 if (basename[0] == '.') {
430 BT_CPPLOGI_SPEC(logger, "Ignoring hidden file `{}" G_DIR_SEPARATOR_S "{}`",
431 ctf_fs_trace->path, basename);
432 continue;
433 }
434
435 /* Create the file. */
436 ctf_fs_file file {logger};
437
438 /* Create full path string. */
439 file.path = fmt::format("{}" G_DIR_SEPARATOR_S "{}", ctf_fs_trace->path, basename);
440
441 if (!g_file_test(file.path.c_str(), G_FILE_TEST_IS_REGULAR)) {
442 BT_CPPLOGI_SPEC(logger, "Ignoring non-regular file `{}`", file.path);
443 continue;
444 }
445
446 int ret = ctf_fs_file_open(&file, "rb");
447 if (ret) {
448 BT_CPPLOGE_APPEND_CAUSE_SPEC(logger, "Cannot open stream file `{}`", file.path);
449 return ret;
450 }
451
452 if (file.size == 0) {
453 /* Skip empty stream. */
454 BT_CPPLOGI_SPEC(logger, "Ignoring empty file `{}`", file.path);
455 continue;
456 }
457
458 ret = add_ds_file_to_ds_file_group(ctf_fs_trace, file.path.c_str(), logger);
459 if (ret) {
460 BT_CPPLOGE_APPEND_CAUSE_SPEC(logger, "Cannot add stream file `{}` to stream file group",
461 file.path);
462 return ret;
463 }
464 }
465
466 return 0;
467 }
468
469 static void set_trace_name(const bt2::Trace trace, const char *name_suffix)
470 {
471 std::string name;
472
473 /*
474 * Check if we have a trace environment string value named `hostname`.
475 * If so, use it as the trace name's prefix.
476 */
477 const auto val = trace.environmentEntry("hostname");
478 if (val && val->isString()) {
479 name += val->asString().value();
480
481 if (name_suffix) {
482 name += G_DIR_SEPARATOR;
483 }
484 }
485
486 if (name_suffix) {
487 name += name_suffix;
488 }
489
490 trace.name(name);
491 }
492
493 static ctf_fs_trace::UP
494 ctf_fs_trace_create(const char *path, const char *name, const ctf::src::ClkClsCfg& clkClsCfg,
495 const bt2::OptionalBorrowedObject<bt2::SelfComponent> selfComp,
496 const bt2c::Logger& logger)
497 {
498 auto ctf_fs_trace = bt2s::make_unique<struct ctf_fs_trace>(clkClsCfg, selfComp, logger);
499 const auto metadataPath = fmt::format("{}" G_DIR_SEPARATOR_S CTF_FS_METADATA_FILENAME, path);
500
501 ctf_fs_trace->path = path;
502 ctf_fs_trace->parseMetadata(bt2c::dataFromFile(metadataPath, logger, true));
503
504 BT_ASSERT(ctf_fs_trace->cls());
505
506 if (ctf_fs_trace->cls()->libCls()) {
507 bt2::TraceClass traceCls = *ctf_fs_trace->cls()->libCls();
508 ctf_fs_trace->trace = traceCls.instantiate();
509 ctf_trace_class_configure_ir_trace(
510 *ctf_fs_trace->cls(), *ctf_fs_trace->trace,
511 bt_self_component_get_graph_mip_version(selfComp->libObjPtr()), logger);
512 set_trace_name(*ctf_fs_trace->trace, name);
513 }
514
515 int ret = create_ds_file_groups(ctf_fs_trace.get(), logger);
516 if (ret) {
517 return nullptr;
518 }
519
520 return ctf_fs_trace;
521 }
522
523 static int path_is_ctf_trace(const char *path)
524 {
525 return g_file_test(fmt::format("{}" G_DIR_SEPARATOR_S CTF_FS_METADATA_FILENAME, path).c_str(),
526 G_FILE_TEST_IS_REGULAR);
527 }
528
529 /* Helper for ctf_fs_component_create_ctf_fs_trace, to handle a single path. */
530
531 static int ctf_fs_component_create_ctf_fs_trace_one_path(
532 struct ctf_fs_component *ctf_fs, const char *path_param, const char *trace_name,
533 std::vector<ctf_fs_trace::UP>& traces,
534 const bt2::OptionalBorrowedObject<bt2::SelfComponent> selfComp)
535 {
536 bt2c::GStringUP norm_path {bt_common_normalize_path(path_param, NULL)};
537 if (!norm_path) {
538 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctf_fs->logger, "Failed to normalize path: `{}`.", path_param);
539 return -1;
540 }
541
542 int ret = path_is_ctf_trace(norm_path->str);
543 if (ret < 0) {
544 BT_CPPLOGE_APPEND_CAUSE_SPEC(
545 ctf_fs->logger, "Failed to check if path is a CTF trace: path={}", norm_path->str);
546 return ret;
547 } else if (ret == 0) {
548 BT_CPPLOGE_APPEND_CAUSE_SPEC(
549 ctf_fs->logger, "Path is not a CTF trace (does not contain a metadata file): `{}`.",
550 norm_path->str);
551 return -1;
552 }
553
554 // FIXME: Remove or ifdef for __MINGW32__
555 if (strcmp(norm_path->str, "/") == 0) {
556 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctf_fs->logger, "Opening a trace in `/` is not supported.");
557 return -1;
558 }
559
560 ctf_fs_trace::UP ctf_fs_trace = ctf_fs_trace_create(
561 norm_path->str, trace_name, ctf_fs->clkClsCfg, selfComp, ctf_fs->logger);
562 if (!ctf_fs_trace) {
563 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctf_fs->logger, "Cannot create trace for `{}`.",
564 norm_path->str);
565 return -1;
566 }
567
568 traces.emplace_back(std::move(ctf_fs_trace));
569
570 return 0;
571 }
572
573 /*
574 * Count the number of stream and event classes defined by this trace's metadata.
575 *
576 * This is used to determine which metadata is the "latest", out of multiple
577 * traces sharing the same UUID. It is assumed that amongst all these metadatas,
578 * a bigger metadata is a superset of a smaller metadata. Therefore, it is
579 * enough to just count the classes.
580 */
581
582 static unsigned int metadata_count_stream_and_event_classes(struct ctf_fs_trace *trace)
583 {
584 const TraceCls::DataStreamClsSet& dataStreamClasses = trace->cls()->dataStreamClasses();
585 unsigned int num = dataStreamClasses.size();
586
587 for (const DataStreamCls::UP& dsc : dataStreamClasses) {
588 num += dsc->eventRecordClasses().size();
589 }
590
591 return num;
592 }
593
594 /*
595 * Merge the src ds_file_group into dest. This consists of merging their
596 * ds_file_infos, making sure to keep the result sorted.
597 */
598
599 static void merge_ctf_fs_ds_file_groups(struct ctf_fs_ds_file_group *dest,
600 ctf_fs_ds_file_group::UP src)
601 {
602 for (auto& ds_file_info : src->ds_file_infos) {
603 dest->insert_ds_file_info_sorted(std::move(ds_file_info));
604 }
605
606 /* Merge both indexes. */
607 merge_ctf_fs_ds_indexes(dest->index, src->index);
608 }
609
610 /* Merge src_trace's data stream file groups into dest_trace's. */
611
612 static int merge_matching_ctf_fs_ds_file_groups(struct ctf_fs_trace *dest_trace,
613 ctf_fs_trace::UP src_trace)
614 {
615 std::vector<ctf_fs_ds_file_group::UP>& dest = dest_trace->ds_file_groups;
616 std::vector<ctf_fs_ds_file_group::UP>& src = src_trace->ds_file_groups;
617
618 /*
619 * Save the initial length of dest: we only want to check against the
620 * original elements in the inner loop.
621 */
622 size_t dest_len = dest.size();
623
624 for (auto& src_group : src) {
625 struct ctf_fs_ds_file_group *dest_group = NULL;
626
627 /* A stream instance without ID can't match a stream in the other trace. */
628 if (src_group->stream_id != -1) {
629 /* Let's search for a matching ds_file_group in the destination. */
630 for (size_t d_i = 0; d_i < dest_len; ++d_i) {
631 ctf_fs_ds_file_group *candidate_dest = dest[d_i].get();
632
633 /* Can't match a stream instance without ID. */
634 if (candidate_dest->stream_id == -1) {
635 continue;
636 }
637
638 /*
639 * If the two groups have the same stream instance id
640 * and belong to the same stream class (stream instance
641 * ids are per-stream class), they represent the same
642 * stream instance.
643 */
644 if (candidate_dest->stream_id != src_group->stream_id ||
645 candidate_dest->dataStreamCls->id() != src_group->dataStreamCls->id()) {
646 continue;
647 }
648
649 dest_group = candidate_dest;
650 break;
651 }
652 }
653
654 /*
655 * Didn't find a friend in dest to merge our src_group into?
656 * Create a new empty one. This can happen if a stream was
657 * active in the source trace chunk but not in the destination
658 * trace chunk.
659 */
660 if (!dest_group) {
661 const DataStreamCls *sc = (*dest_trace->cls())[src_group->dataStreamCls->id()];
662 BT_ASSERT(sc);
663
664 dest_trace->ds_file_groups.emplace_back(bt2s::make_unique<ctf_fs_ds_file_group>(
665 dest_trace, *sc, src_group->stream_id, ctf_fs_ds_index {}));
666 dest_group = dest_trace->ds_file_groups.back().get();
667 }
668
669 BT_ASSERT(dest_group);
670 merge_ctf_fs_ds_file_groups(dest_group, std::move(src_group));
671 }
672
673 return 0;
674 }
675
676 /*
677 * Collapse the given traces, which must all share the same UUID, in a single
678 * one.
679 *
680 * The trace with the most expansive metadata is chosen and all other traces
681 * are merged into that one. On return, the elements of `traces` are nullptr
682 * and the merged trace is placed in `out_trace`.
683 */
684
685 static int merge_ctf_fs_traces(std::vector<ctf_fs_trace::UP> traces, ctf_fs_trace::UP& out_trace)
686 {
687 BT_ASSERT(traces.size() >= 2);
688
689 unsigned int winner_count = metadata_count_stream_and_event_classes(traces[0].get());
690 ctf_fs_trace *winner = traces[0].get();
691 guint winner_i = 0;
692
693 /* Find the trace with the largest metadata. */
694 for (guint i = 1; i < traces.size(); i++) {
695 ctf_fs_trace *candidate = traces[i].get();
696 unsigned int candidate_count;
697
698 /* A bit of sanity check. */
699 /* ⚠️ TODO: also consider namespace and name */
700 BT_ASSERT(winner->cls()->uid() == candidate->cls()->uid());
701
702 candidate_count = metadata_count_stream_and_event_classes(candidate);
703
704 if (candidate_count > winner_count) {
705 winner_count = candidate_count;
706 winner = candidate;
707 winner_i = i;
708 }
709 }
710
711 /* Merge all the other traces in the winning trace. */
712 for (ctf_fs_trace::UP& trace : traces) {
713 /* Don't merge the winner into itself. */
714 if (trace.get() == winner) {
715 continue;
716 }
717
718 /* Merge trace's data stream file groups into winner's. */
719 int ret = merge_matching_ctf_fs_ds_file_groups(winner, std::move(trace));
720 if (ret) {
721 return ret;
722 }
723 }
724
725 /*
726 * Move the winner out of the array, into `*out_trace`.
727 */
728 out_trace = std::move(traces[winner_i]);
729
730 return 0;
731 }
732
733 struct ClockSnapshotAfterEventItemVisitor : public ItemVisitor
734 {
735 bool done() const
736 {
737 return _mDone;
738 }
739
740 bt2s::optional<unsigned long long> result() const
741 {
742 return _mResult;
743 }
744
745 protected:
746 bt2s::optional<unsigned long long> _mResult;
747 bool _mDone = false;
748 };
749
750 struct ClockSnapshotAfterFirstEventItemVisitor : public ClockSnapshotAfterEventItemVisitor
751 {
752 void visit(const EventRecordInfoItem& item) override
753 {
754 _mResult = item.defClkVal();
755 _mDone = true;
756 }
757 };
758
759 /*
760 * Find the timestamp of the last event of the packet, if any, otherwise
761 * find the timestamp of the beginning of the packet.
762 */
763 struct ClockSnapshotAfterLastEventItemVisitor : public ClockSnapshotAfterEventItemVisitor
764 {
765 void visit(const PktInfoItem& item) override
766 {
767 _mLastSeen = item.beginDefClkVal();
768 }
769
770 void visit(const EventRecordInfoItem& item) override
771 {
772 _mLastSeen = item.defClkVal();
773 }
774
775 void visit(const PktEndItem&) override
776 {
777 _mResult = _mLastSeen;
778 _mDone = true;
779 }
780
781 private:
782 bt2s::optional<unsigned long long> _mLastSeen;
783 };
784
785 static int decode_clock_snapshot_after_event(struct ctf_fs_trace *ctf_fs_trace,
786 const ClkCls& default_cc,
787 const ctf_fs_ds_index_entry& index_entry,
788 ClockSnapshotAfterEventItemVisitor& visitor,
789 const char *firstOrLast, const bt2c::Logger& logger,
790 uint64_t *cs, int64_t *ts_ns)
791 {
792 BT_ASSERT(ctf_fs_trace);
793 BT_ASSERT(ctf_fs_trace->cls());
794 BT_ASSERT(index_entry.path);
795
796 ctf_fs_ds_index tempIndex;
797
798 tempIndex.entries.emplace_back(index_entry);
799
800 ItemSeqIter itemSeqIter {bt2s::make_unique<fs::Medium>(tempIndex, logger), *ctf_fs_trace->cls(),
801 index_entry.offsetInFile, logger};
802 LoggingItemVisitor loggingVisitor {logger};
803
804 while (!visitor.done()) {
805 const Item *item = itemSeqIter.next();
806 BT_ASSERT(item);
807
808 if (logger.wouldLogT()) {
809 item->accept(loggingVisitor);
810 }
811
812 item->accept(visitor);
813 }
814
815 if (!visitor.result()) {
816 BT_CPPLOGE_APPEND_CAUSE_SPEC(logger, "Failed to get {} event clock snapshot.", firstOrLast);
817 return -1;
818 }
819
820 *cs = *visitor.result();
821
822 /* Convert clock snapshot to timestamp. */
823 int ret = bt_util_clock_cycles_to_ns_from_origin(*cs, default_cc.freq(),
824 default_cc.offsetFromOrigin().seconds(),
825 default_cc.offsetFromOrigin().cycles(), ts_ns);
826 if (ret) {
827 BT_CPPLOGE_APPEND_CAUSE_SPEC(logger, "Failed to convert clock snapshot to timestamp");
828 return ret;
829 }
830
831 return ret;
832 }
833
834 static int decode_packet_first_event_timestamp(struct ctf_fs_trace *ctf_fs_trace,
835 const ClkCls& default_cc,
836 const ctf_fs_ds_index_entry& index_entry,
837 const bt2c::Logger& logger, uint64_t *cs,
838 int64_t *ts_ns)
839 {
840 ClockSnapshotAfterFirstEventItemVisitor visitor {};
841
842 return decode_clock_snapshot_after_event(ctf_fs_trace, default_cc, index_entry, visitor,
843 "first", logger, cs, ts_ns);
844 }
845
846 static int decode_packet_last_event_timestamp(struct ctf_fs_trace *ctf_fs_trace,
847 const ClkCls& default_cc,
848 const ctf_fs_ds_index_entry& index_entry,
849 const bt2c::Logger& logger, uint64_t *cs,
850 int64_t *ts_ns)
851 {
852 ClockSnapshotAfterLastEventItemVisitor visitor {};
853
854 return decode_clock_snapshot_after_event(ctf_fs_trace, default_cc, index_entry, visitor, "last",
855 logger, cs, ts_ns);
856 }
857
858 /*
859 * Fix up packet index entries for lttng's "event-after-packet" bug.
860 * Some buggy lttng tracer versions may emit events with a timestamp that is
861 * larger (after) than the timestamp_end of the their packets.
862 *
863 * To fix up this erroneous data we do the following:
864 * 1. If it's not the stream file's last packet: set the packet index entry's
865 * end time to the next packet's beginning time.
866 * 2. If it's the stream file's last packet, set the packet index entry's end
867 * time to the packet's last event's time, if any, or to the packet's
868 * beginning time otherwise.
869 *
870 * Known buggy tracer versions:
871 * - before lttng-ust 2.11.0
872 * - before lttng-module 2.11.0
873 * - before lttng-module 2.10.10
874 * - before lttng-module 2.9.13
875 */
876 static int fix_index_lttng_event_after_packet_bug(struct ctf_fs_trace *trace,
877 const bt2c::Logger& logger)
878 {
879 for (const auto& ds_file_group : trace->ds_file_groups) {
880 BT_ASSERT(ds_file_group);
881 auto& index = ds_file_group->index;
882
883 BT_ASSERT(!index.entries.empty());
884
885 /*
886 * Iterate over all entries but the last one. The last one is
887 * fixed differently after.
888 */
889 for (size_t entry_i = 0; entry_i < index.entries.size() - 1; ++entry_i) {
890 auto& curr_entry = index.entries[entry_i];
891 const auto& next_entry = index.entries[entry_i + 1];
892
893 /*
894 * 1. Set the current index entry `end` timestamp to
895 * the next index entry `begin` timestamp.
896 */
897 curr_entry.timestamp_end = next_entry.timestamp_begin;
898 curr_entry.timestamp_end_ns = next_entry.timestamp_begin_ns;
899 }
900
901 /*
902 * 2. Fix the last entry by decoding the last event of the last
903 * packet.
904 */
905 auto& last_entry = index.entries.back();
906
907 BT_ASSERT(ds_file_group->dataStreamCls->defClkCls());
908 const ClkCls& default_cc = *ds_file_group->dataStreamCls->defClkCls();
909
910 /*
911 * Decode packet to read the timestamp of the last event of the
912 * entry.
913 */
914 int ret = decode_packet_last_event_timestamp(trace, default_cc, last_entry, logger,
915 &last_entry.timestamp_end,
916 &last_entry.timestamp_end_ns);
917 if (ret) {
918 BT_CPPLOGE_APPEND_CAUSE_SPEC(
919 logger,
920 "Failed to decode stream's last packet to get its last event's clock snapshot.");
921 return ret;
922 }
923 }
924
925 return 0;
926 }
927
928 /*
929 * Fix up packet index entries for barectf's "event-before-packet" bug.
930 * Some buggy barectf tracer versions may emit events with a timestamp that is
931 * less than the timestamp_begin of the their packets.
932 *
933 * To fix up this erroneous data we do the following:
934 * 1. Starting at the second index entry, set the timestamp_begin of the
935 * current entry to the timestamp of the first event of the packet.
936 * 2. Set the previous entry's timestamp_end to the timestamp_begin of the
937 * current packet.
938 *
939 * Known buggy tracer versions:
940 * - before barectf 2.3.1
941 */
942 static int fix_index_barectf_event_before_packet_bug(struct ctf_fs_trace *trace,
943 const bt2c::Logger& logger)
944 {
945 for (const auto& ds_file_group : trace->ds_file_groups) {
946 auto& index = ds_file_group->index;
947
948 BT_ASSERT(!index.entries.empty());
949
950 BT_ASSERT(ds_file_group->dataStreamCls->defClkCls());
951 const ClkCls& default_cc = *ds_file_group->dataStreamCls->defClkCls();
952
953 /*
954 * 1. Iterate over the index, starting from the second entry
955 * (index = 1).
956 */
957 for (size_t entry_i = 1; entry_i < index.entries.size(); ++entry_i) {
958 auto& prev_entry = index.entries[entry_i - 1];
959 auto& curr_entry = index.entries[entry_i];
960 /*
961 * 2. Set the current entry `begin` timestamp to the
962 * timestamp of the first event of the current packet.
963 */
964 int ret = decode_packet_first_event_timestamp(trace, default_cc, curr_entry, logger,
965 &curr_entry.timestamp_begin,
966 &curr_entry.timestamp_begin_ns);
967 if (ret) {
968 BT_CPPLOGE_APPEND_CAUSE_SPEC(logger,
969 "Failed to decode first event's clock snapshot");
970 return ret;
971 }
972
973 /*
974 * 3. Set the previous entry `end` timestamp to the
975 * timestamp of the first event of the current packet.
976 */
977 prev_entry.timestamp_end = curr_entry.timestamp_begin;
978 prev_entry.timestamp_end_ns = curr_entry.timestamp_begin_ns;
979 }
980 }
981
982 return 0;
983 }
984
985 /*
986 * When using the lttng-crash feature it's likely that the last packets of each
987 * stream have their timestamp_end set to zero. This is caused by the fact that
988 * the tracer crashed and was not able to properly close the packets.
989 *
990 * To fix up this erroneous data we do the following:
991 * For each index entry, if the entry's timestamp_end is 0 and the
992 * timestamp_begin is not 0:
993 * - If it's the stream file's last packet: set the packet index entry's end
994 * time to the packet's last event's time, if any, or to the packet's
995 * beginning time otherwise.
996 * - If it's not the stream file's last packet: set the packet index
997 * entry's end time to the next packet's beginning time.
998 *
999 * Affected versions:
1000 * - All current and future lttng-ust and lttng-modules versions.
1001 */
1002 static int fix_index_lttng_crash_quirk(struct ctf_fs_trace *trace, const bt2c::Logger& logger)
1003 {
1004 for (const auto& ds_file_group : trace->ds_file_groups) {
1005 BT_ASSERT(ds_file_group);
1006 auto& index = ds_file_group->index;
1007
1008 BT_ASSERT(ds_file_group->dataStreamCls->defClkCls());
1009 const ClkCls& default_cc = *ds_file_group->dataStreamCls->defClkCls();
1010
1011 BT_ASSERT(!index.entries.empty());
1012
1013 auto& last_entry = index.entries.back();
1014
1015 /* 1. Fix the last entry first. */
1016 if (last_entry.timestamp_end == 0 && last_entry.timestamp_begin != 0) {
1017 /*
1018 * Decode packet to read the timestamp of the
1019 * last event of the stream file.
1020 */
1021 int ret = decode_packet_last_event_timestamp(trace, default_cc, last_entry, logger,
1022 &last_entry.timestamp_end,
1023 &last_entry.timestamp_end_ns);
1024 if (ret) {
1025 BT_CPPLOGE_APPEND_CAUSE_SPEC(logger,
1026 "Failed to decode last event's clock snapshot");
1027 return ret;
1028 }
1029 }
1030
1031 /* Iterate over all entries but the last one. */
1032 for (size_t entry_idx = 0; entry_idx < index.entries.size() - 1; ++entry_idx) {
1033 auto& curr_entry = index.entries[entry_idx];
1034 const auto& next_entry = index.entries[entry_idx + 1];
1035
1036 if (curr_entry.timestamp_end == 0 && curr_entry.timestamp_begin != 0) {
1037 /*
1038 * 2. Set the current index entry `end` timestamp to
1039 * the next index entry `begin` timestamp.
1040 */
1041 curr_entry.timestamp_end = next_entry.timestamp_begin;
1042 curr_entry.timestamp_end_ns = next_entry.timestamp_begin_ns;
1043 }
1044 }
1045 }
1046
1047 return 0;
1048 }
1049
1050 /*
1051 * Extract the tracer information necessary to compare versions.
1052 * Returns 0 on success, and -1 if the extraction is not successful because the
1053 * necessary fields are absents in the trace metadata.
1054 */
1055 static int extract_tracer_info(struct ctf_fs_trace *trace, struct tracer_info *current_tracer_info)
1056 {
1057 if (!trace->cls()->env()) {
1058 return -1;
1059 }
1060
1061 bt2::ConstMapValue env = *trace->cls()->env();
1062
1063 /* Clear the current_tracer_info struct */
1064 memset(current_tracer_info, 0, sizeof(*current_tracer_info));
1065
1066 /*
1067 * To compare 2 tracer versions, at least the tracer name and it's
1068 * major version are needed. If one of these is missing, consider it an
1069 * extraction failure.
1070 */
1071 bt2::OptionalBorrowedObject<bt2::ConstValue> tracerName = env["tracer_name"];
1072 if (!tracerName || !tracerName->isString()) {
1073 return -1;
1074 }
1075
1076 /* Set tracer name. */
1077 current_tracer_info->name = tracerName->asString().value();
1078
1079 bt2::OptionalBorrowedObject<bt2::ConstValue> tracerMajor = env["tracer_major"];
1080 if (!tracerMajor || !tracerMajor->isInteger()) {
1081 return -1;
1082 }
1083
1084 /* Set major version number. */
1085 current_tracer_info->major =
1086 tracerMajor->isSignedInteger() ?
1087 tracerMajor->asSignedInteger().value() :
1088 static_cast<std::int64_t>(tracerMajor->asUnsignedInteger().value());
1089
1090 bt2::OptionalBorrowedObject<bt2::ConstValue> tracerMinor = env["tracer_minor"];
1091 if (!tracerMinor || !tracerMinor->isInteger()) {
1092 return 0;
1093 }
1094
1095 /* Set minor version number. */
1096 current_tracer_info->minor =
1097 tracerMinor->isSignedInteger() ?
1098 tracerMinor->asSignedInteger().value() :
1099 static_cast<std::int64_t>(tracerMinor->asUnsignedInteger().value());
1100
1101 /*
1102 * If `tracer_patch` doesn't exist `tracer_patchlevel` might.
1103 * For example, `lttng-modules` uses entry name `tracer_patchlevel`.
1104 */
1105 bt2::OptionalBorrowedObject<bt2::ConstValue> tracerPatch = env["tracer_patch"];
1106 if (!tracerPatch) {
1107 tracerPatch = env["tracer_patchlevel"];
1108 }
1109
1110 if (!tracerPatch || !tracerPatch->isInteger()) {
1111 return 0;
1112 }
1113
1114 /* Set patch version number. */
1115 current_tracer_info->patch =
1116 tracerPatch->isSignedInteger() ?
1117 tracerPatch->asSignedInteger().value() :
1118 static_cast<std::int64_t>(tracerPatch->asUnsignedInteger().value());
1119
1120 return 0;
1121 }
1122
1123 static bool is_tracer_affected_by_lttng_event_after_packet_bug(struct tracer_info *curr_tracer_info)
1124 {
1125 bool is_affected = false;
1126
1127 if (strcmp(curr_tracer_info->name, "lttng-ust") == 0) {
1128 if (curr_tracer_info->major < 2) {
1129 is_affected = true;
1130 } else if (curr_tracer_info->major == 2) {
1131 /* fixed in lttng-ust 2.11.0 */
1132 if (curr_tracer_info->minor < 11) {
1133 is_affected = true;
1134 }
1135 }
1136 } else if (strcmp(curr_tracer_info->name, "lttng-modules") == 0) {
1137 if (curr_tracer_info->major < 2) {
1138 is_affected = true;
1139 } else if (curr_tracer_info->major == 2) {
1140 /* fixed in lttng-modules 2.11.0 */
1141 if (curr_tracer_info->minor == 10) {
1142 /* fixed in lttng-modules 2.10.10 */
1143 if (curr_tracer_info->patch < 10) {
1144 is_affected = true;
1145 }
1146 } else if (curr_tracer_info->minor == 9) {
1147 /* fixed in lttng-modules 2.9.13 */
1148 if (curr_tracer_info->patch < 13) {
1149 is_affected = true;
1150 }
1151 } else if (curr_tracer_info->minor < 9) {
1152 is_affected = true;
1153 }
1154 }
1155 }
1156
1157 return is_affected;
1158 }
1159
1160 static bool
1161 is_tracer_affected_by_barectf_event_before_packet_bug(struct tracer_info *curr_tracer_info)
1162 {
1163 bool is_affected = false;
1164
1165 if (strcmp(curr_tracer_info->name, "barectf") == 0) {
1166 if (curr_tracer_info->major < 2) {
1167 is_affected = true;
1168 } else if (curr_tracer_info->major == 2) {
1169 if (curr_tracer_info->minor < 3) {
1170 is_affected = true;
1171 } else if (curr_tracer_info->minor == 3) {
1172 /* fixed in barectf 2.3.1 */
1173 if (curr_tracer_info->patch < 1) {
1174 is_affected = true;
1175 }
1176 }
1177 }
1178 }
1179
1180 return is_affected;
1181 }
1182
1183 static bool is_tracer_affected_by_lttng_crash_quirk(struct tracer_info *curr_tracer_info)
1184 {
1185 bool is_affected = false;
1186
1187 /* All LTTng tracer may be affected by this lttng crash quirk. */
1188 if (strcmp(curr_tracer_info->name, "lttng-ust") == 0) {
1189 is_affected = true;
1190 } else if (strcmp(curr_tracer_info->name, "lttng-modules") == 0) {
1191 is_affected = true;
1192 }
1193
1194 return is_affected;
1195 }
1196
1197 /*
1198 * Looks for trace produced by known buggy tracers and fix up the index
1199 * produced earlier.
1200 */
1201 static int fix_packet_index_tracer_bugs(ctf_fs_component *ctf_fs)
1202 {
1203 struct tracer_info current_tracer_info;
1204
1205 int ret = extract_tracer_info(ctf_fs->trace.get(), &current_tracer_info);
1206 if (ret) {
1207 /*
1208 * A trace may not have all the necessary environment
1209 * entries to do the tracer version comparison.
1210 * At least, the tracer name and major version number
1211 * are needed. Failing to extract these entries is not
1212 * an error.
1213 */
1214 BT_CPPLOGI_SPEC(
1215 ctf_fs->logger,
1216 "Cannot extract tracer information necessary to compare with buggy versions.");
1217 return 0;
1218 }
1219
1220 /* Check if the trace may be affected by old tracer bugs. */
1221 if (is_tracer_affected_by_lttng_event_after_packet_bug(&current_tracer_info)) {
1222 BT_CPPLOGI_SPEC(ctf_fs->logger,
1223 "Trace may be affected by LTTng tracer packet timestamp bug. Fixing up.");
1224 ret = fix_index_lttng_event_after_packet_bug(ctf_fs->trace.get(), ctf_fs->logger);
1225 if (ret) {
1226 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctf_fs->logger,
1227 "Failed to fix LTTng event-after-packet bug.");
1228 return ret;
1229 }
1230 ctf_fs->quirks.eventRecordDefClkValGtNextPktBeginDefClkVal = true;
1231 }
1232
1233 if (is_tracer_affected_by_barectf_event_before_packet_bug(&current_tracer_info)) {
1234 BT_CPPLOGI_SPEC(ctf_fs->logger,
1235 "Trace may be affected by barectf tracer packet timestamp bug. Fixing up.");
1236 ret = fix_index_barectf_event_before_packet_bug(ctf_fs->trace.get(), ctf_fs->logger);
1237 if (ret) {
1238 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctf_fs->logger,
1239 "Failed to fix barectf event-before-packet bug.");
1240 return ret;
1241 }
1242 ctf_fs->quirks.eventRecordDefClkValLtPktBeginDefClkVal = true;
1243 }
1244
1245 if (is_tracer_affected_by_lttng_crash_quirk(&current_tracer_info)) {
1246 ret = fix_index_lttng_crash_quirk(ctf_fs->trace.get(), ctf_fs->logger);
1247 if (ret) {
1248 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctf_fs->logger,
1249 "Failed to fix lttng-crash timestamp quirks.");
1250 return ret;
1251 }
1252 ctf_fs->quirks.pktEndDefClkValZero = true;
1253 }
1254
1255 return 0;
1256 }
1257
1258 static bool compare_ds_file_groups_by_first_path(const ctf_fs_ds_file_group::UP& ds_file_group_a,
1259 const ctf_fs_ds_file_group::UP& ds_file_group_b)
1260 {
1261 BT_ASSERT(!ds_file_group_a->ds_file_infos.empty());
1262 BT_ASSERT(!ds_file_group_b->ds_file_infos.empty());
1263
1264 const auto& first_ds_file_info_a = *ds_file_group_a->ds_file_infos[0];
1265 const auto& first_ds_file_info_b = *ds_file_group_b->ds_file_infos[0];
1266
1267 return first_ds_file_info_a.path < first_ds_file_info_b.path;
1268 }
1269
1270 int ctf_fs_component_create_ctf_fs_trace(
1271 struct ctf_fs_component *ctf_fs, const bt2::ConstArrayValue pathsValue, const char *traceName,
1272 const bt2::OptionalBorrowedObject<bt2::SelfComponent> selfComp)
1273 {
1274 std::vector<std::string> paths;
1275
1276 BT_ASSERT(!pathsValue.isEmpty());
1277
1278 /*
1279 * Create a sorted array of the paths, to make the execution of this
1280 * component deterministic.
1281 */
1282 for (const auto pathValue : pathsValue) {
1283 BT_ASSERT(pathValue.isString());
1284 paths.emplace_back(pathValue.asString().value().str());
1285 }
1286
1287 std::sort(paths.begin(), paths.end());
1288
1289 /* Create a separate ctf_fs_trace object for each path. */
1290 std::vector<ctf_fs_trace::UP> traces;
1291 for (const auto& path : paths) {
1292 int ret = ctf_fs_component_create_ctf_fs_trace_one_path(ctf_fs, path.c_str(), traceName,
1293 traces, selfComp);
1294 if (ret) {
1295 return ret;
1296 }
1297 }
1298
1299 if (traces.size() > 1) {
1300 ctf_fs_trace *first_trace = traces[0].get();
1301
1302 /*
1303 * We have more than one trace, they must all share the same
1304 * UID, verify that.
1305 */
1306 /* ⚠️ TODO: also consider namespace and name */
1307 for (const ctf_fs_trace::UP& this_trace : traces) {
1308 if (!this_trace->cls()->uid()) {
1309 BT_CPPLOGE_APPEND_CAUSE_SPEC(
1310 ctf_fs->logger,
1311 "Multiple traces given, but a trace does not have a UID: path={}",
1312 this_trace->path);
1313 return -1;
1314 }
1315
1316 auto& first_trace_uid = *first_trace->cls()->uid();
1317 auto& this_trace_uid = *this_trace->cls()->uid();
1318
1319 if (first_trace_uid != this_trace_uid) {
1320 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctf_fs->logger,
1321 "Multiple traces given, but UIDs don't match: "
1322 "first-trace-uid={}, first-trace-path={}, "
1323 "trace-uid={}, trace-path={}",
1324 first_trace_uid, first_trace->path, this_trace_uid,
1325 this_trace->path);
1326 return -1;
1327 }
1328 }
1329
1330 int ret = merge_ctf_fs_traces(std::move(traces), ctf_fs->trace);
1331 if (ret) {
1332 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctf_fs->logger,
1333 "Failed to merge traces with the same UUID.");
1334 return ret;
1335 }
1336 } else {
1337 /* Just one trace, it may or may not have a UUID, both are fine. */
1338 BT_DIAG_PUSH
1339 BT_DIAG_IGNORE_NULL_DEREFERENCE
1340 ctf_fs->trace = std::move(traces[0]);
1341 BT_DIAG_POP
1342 }
1343
1344 int ret = fix_packet_index_tracer_bugs(ctf_fs);
1345 if (ret) {
1346 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctf_fs->logger, "Failed to fix packet index tracer bugs.");
1347 return ret;
1348 }
1349
1350 /*
1351 * Sort data stream file groups by first data stream file info
1352 * path to get a deterministic order. This order influences the
1353 * order of the output ports. It also influences the order of
1354 * the automatic stream IDs if the trace's packet headers do not
1355 * contain a `stream_instance_id` field, in which case the data
1356 * stream file to stream ID association is always the same,
1357 * whatever the build and the system.
1358 *
1359 * Having a deterministic order here can help debugging and
1360 * testing.
1361 */
1362 std::sort(ctf_fs->trace->ds_file_groups.begin(), ctf_fs->trace->ds_file_groups.end(),
1363 compare_ds_file_groups_by_first_path);
1364
1365 /*
1366 * Now that indexes are not going to change anymore, compute each entry's
1367 * offset in the logical data stream.
1368 */
1369 for (ctf_fs_ds_file_group::UP& group : ctf_fs->trace->ds_file_groups) {
1370 group->index.updateOffsetsInStream();
1371 }
1372
1373 return 0;
1374 }
1375
1376 static const std::string&
1377 get_stream_instance_unique_name(struct ctf_fs_ds_file_group *ds_file_group)
1378 {
1379 /*
1380 * The first (earliest) stream file's path is used as the stream's unique
1381 * name.
1382 */
1383 BT_ASSERT(!ds_file_group->ds_file_infos.empty());
1384 return ds_file_group->ds_file_infos[0]->path;
1385 }
1386
1387 /* Create the IR stream objects for ctf_fs_trace. */
1388
1389 static void create_streams_for_trace(struct ctf_fs_trace *ctf_fs_trace)
1390 {
1391 BT_ASSERT(ctf_fs_trace->trace);
1392
1393 for (const auto& ds_file_group : ctf_fs_trace->ds_file_groups) {
1394 BT_ASSERT(ds_file_group->dataStreamCls->libCls());
1395
1396 const std::string& name = get_stream_instance_unique_name(ds_file_group.get());
1397 const auto streamCls = *ds_file_group->dataStreamCls->libCls();
1398
1399 if (ds_file_group->stream_id == UINT64_C(-1)) {
1400 /* No stream ID: use 0 */
1401 ds_file_group->stream =
1402 streamCls.instantiate(*ctf_fs_trace->trace, ctf_fs_trace->next_stream_id);
1403 ctf_fs_trace->next_stream_id++;
1404 } else {
1405 /* Specific stream ID */
1406 ds_file_group->stream =
1407 streamCls.instantiate(*ctf_fs_trace->trace, ds_file_group->stream_id);
1408 }
1409
1410 ds_file_group->stream->name(name);
1411 }
1412 }
1413
1414 static const bt_param_validation_value_descr inputs_elem_descr =
1415 bt_param_validation_value_descr::makeString();
1416
1417 static bt_param_validation_map_value_entry_descr fs_params_entries_descr[] = {
1418 {"inputs", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY,
1419 bt_param_validation_value_descr::makeArray(1, BT_PARAM_VALIDATION_INFINITE,
1420 inputs_elem_descr)},
1421 {"trace-name", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL,
1422 bt_param_validation_value_descr::makeString()},
1423 {"clock-class-offset-s", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL,
1424 bt_param_validation_value_descr::makeSignedInteger()},
1425 {"clock-class-offset-ns", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL,
1426 bt_param_validation_value_descr::makeSignedInteger()},
1427 {"force-clock-class-origin-unix-epoch", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL,
1428 bt_param_validation_value_descr::makeBool()},
1429 BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END};
1430
1431 ctf::src::fs::Parameters read_src_fs_parameters(const bt2::ConstMapValue params,
1432 const bt2c::Logger& logger)
1433 {
1434 gchar *error = NULL;
1435 bt_param_validation_status validate_value_status =
1436 bt_param_validation_validate(params.libObjPtr(), fs_params_entries_descr, &error);
1437
1438 if (validate_value_status != BT_PARAM_VALIDATION_STATUS_OK) {
1439 bt2c::GCharUP errorFreer {error};
1440 BT_CPPLOGE_APPEND_CAUSE_AND_THROW_SPEC(logger, bt2c::Error, "{}", error);
1441 }
1442
1443 ctf::src::fs::Parameters parameters {params["inputs"]->asArray()};
1444
1445 /* clock-class-offset-s parameter */
1446 if (const auto clockClassOffsetS = params["clock-class-offset-s"]) {
1447 parameters.clkClsCfg.offsetSec = clockClassOffsetS->asSignedInteger().value();
1448 }
1449
1450 /* clock-class-offset-ns parameter */
1451 if (const auto clockClassOffsetNs = params["clock-class-offset-ns"]) {
1452 parameters.clkClsCfg.offsetNanoSec = clockClassOffsetNs->asSignedInteger().value();
1453 }
1454
1455 /* force-clock-class-origin-unix-epoch parameter */
1456 if (const auto forceClockClassOriginUnixEpoch = params["force-clock-class-origin-unix-epoch"]) {
1457 parameters.clkClsCfg.forceOriginIsUnixEpoch =
1458 forceClockClassOriginUnixEpoch->asBool().value();
1459 }
1460
1461 /* trace-name parameter */
1462 if (const auto traceName = params["trace-name"]) {
1463 parameters.traceName = traceName->asString().value().str();
1464 }
1465
1466 return parameters;
1467 }
1468
1469 static ctf_fs_component::UP ctf_fs_create(const bt2::ConstMapValue params,
1470 const bt2::SelfSourceComponent selfSrcComp)
1471 {
1472 const bt2c::Logger logger {selfSrcComp, "PLUGIN/SRC.CTF.FS/COMP"};
1473 const auto parameters = read_src_fs_parameters(params, logger);
1474 auto ctf_fs = bt2s::make_unique<ctf_fs_component>(parameters.clkClsCfg, logger);
1475
1476 if (ctf_fs_component_create_ctf_fs_trace(ctf_fs.get(), parameters.inputs,
1477 parameters.traceName ? parameters.traceName->c_str() :
1478 nullptr,
1479 static_cast<bt2::SelfComponent>(selfSrcComp))) {
1480 return nullptr;
1481 }
1482
1483 create_streams_for_trace(ctf_fs->trace.get());
1484
1485 if (create_ports_for_trace(ctf_fs.get(), ctf_fs->trace.get(), selfSrcComp)) {
1486 return nullptr;
1487 }
1488
1489 return ctf_fs;
1490 }
1491
1492 bt_component_class_initialize_method_status ctf_fs_init(bt_self_component_source *self_comp_src,
1493 bt_self_component_source_configuration *,
1494 const bt_value *params, void *)
1495 {
1496 try {
1497 ctf_fs_component::UP ctf_fs =
1498 ctf_fs_create(bt2::ConstMapValue {params}, bt2::wrap(self_comp_src));
1499 if (!ctf_fs) {
1500 return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
1501 }
1502
1503 bt_self_component_set_data(bt_self_component_source_as_self_component(self_comp_src),
1504 ctf_fs.release());
1505 return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
1506 } catch (const std::bad_alloc&) {
1507 return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
1508 } catch (const bt2::Error&) {
1509 return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
1510 }
1511 }
1512
1513 bt_component_class_query_method_status ctf_fs_query(bt_self_component_class_source *comp_class_src,
1514 bt_private_query_executor *priv_query_exec,
1515 const char *object, const bt_value *params,
1516 __attribute__((unused)) void *method_data,
1517 const bt_value **result)
1518 {
1519 try {
1520 bt2c::Logger logger {bt2::SelfComponentClass {comp_class_src},
1521 bt2::PrivateQueryExecutor {priv_query_exec},
1522 "PLUGIN/SRC.CTF.FS/QUERY"};
1523 bt2::ConstMapValue paramsObj(params);
1524 bt2::Value::Shared resultObj;
1525
1526 if (strcmp(object, "metadata-info") == 0) {
1527 resultObj = metadata_info_query(paramsObj, logger);
1528 } else if (strcmp(object, "babeltrace.trace-infos") == 0) {
1529 resultObj = trace_infos_query(paramsObj, logger);
1530 } else if (!strcmp(object, "babeltrace.support-info")) {
1531 resultObj = support_info_query(paramsObj, logger);
1532 } else {
1533 BT_CPPLOGE_SPEC(logger, "Unknown query object `{}`", object);
1534 return BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_UNKNOWN_OBJECT;
1535 }
1536
1537 *result = resultObj.release().libObjPtr();
1538
1539 return BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_OK;
1540 } catch (const std::bad_alloc&) {
1541 return BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_MEMORY_ERROR;
1542 } catch (const bt2::Error&) {
1543 return BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
1544 }
1545 }
This page took 0.06145 seconds and 5 git commands to generate.