2 * SPDX-License-Identifier: MIT
4 * Copyright 2015-2017 Philippe Proulx <pproulx@efficios.com>
5 * Copyright 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
7 * Babeltrace CTF file system Reader Component
14 #include <babeltrace2/babeltrace.h>
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"
25 #include "plugins/common/param-validation/param-validation.h"
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"
34 #include "metadata.hpp"
37 using namespace bt2c::literals::datalen
;
38 using namespace ctf::src
;
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
)
53 struct ctf_fs_msg_iter_data
*msg_iter_data
=
54 (struct ctf_fs_msg_iter_data
*) bt_self_message_iterator_get_data(iterator
);
57 if (G_UNLIKELY(msg_iter_data
->next_saved_error
)) {
59 * Last time we were called, we hit an error but had some
60 * messages to deliver, so we stashed the error here. Return
63 BT_CURRENT_THREAD_MOVE_ERROR_AND_RESET(msg_iter_data
->next_saved_error
);
64 return msg_iter_data
->next_saved_status
;
67 bt_message_iterator_class_next_method_status status
=
68 BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK
;
72 bt2::ConstMessage::Shared msg
= msg_iter_data
->msgIter
->next();
74 msgs
[i
] = msg
.release().libObjPtr();
77 status
= BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_END
;
79 } catch (const bt2::Error
&) {
80 status
= BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR
;
82 } catch (const std::bad_alloc
&) {
83 status
= BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_MEMORY_ERROR
;
86 } while (i
< capacity
&& status
== BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK
);
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.
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.
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
;
113 status
= BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK
;
119 static void instantiateMsgIter(ctf_fs_msg_iter_data
*msg_iter_data
)
121 ctf_fs_ds_file_group
*ds_file_group
= msg_iter_data
->port_data
->ds_file_group
;
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
);
130 bt_message_iterator_class_seek_beginning_method_status
131 ctf_fs_iterator_seek_beginning(bt_self_message_iterator
*it
)
134 struct ctf_fs_msg_iter_data
*msg_iter_data
=
135 (struct ctf_fs_msg_iter_data
*) bt_self_message_iterator_get_data(it
);
137 BT_ASSERT(msg_iter_data
);
139 instantiateMsgIter(msg_iter_data
);
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
;
149 void ctf_fs_iterator_finalize(bt_self_message_iterator
*it
)
151 ctf_fs_msg_iter_data::UP
{
152 static_cast<ctf_fs_msg_iter_data
*>(bt_self_message_iterator_get_data(it
))};
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
)
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
);
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
;
168 instantiateMsgIter(msg_iter_data
.get());
171 * This iterator can seek forward if its stream class has a default
174 if (msg_iter_data
->port_data
->ds_file_group
->dataStreamCls
->defClkCls()) {
175 bt_self_message_iterator_configuration_set_can_seek_forward(config
, true);
178 bt_self_message_iterator_set_data(self_msg_iter
, msg_iter_data
.release());
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
;
188 void ctf_fs_finalize(bt_self_component_source
*component
)
190 ctf_fs_component::UP
{static_cast<ctf_fs_component
*>(
191 bt_self_component_get_data(bt_self_component_source_as_self_component(component
)))};
194 std::string
ctf_fs_make_port_name(ctf_fs_ds_file_group
*ds_file_group
)
196 std::stringstream name
;
199 * The unique port name is generated by concatenating unique identifiers
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();
213 name
<< ds_file_group
->ctf_fs_trace
->path
;
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.
220 if (ds_file_group
->dataStreamCls
->id() != UINT64_C(-1)) {
221 name
<< " | " << ds_file_group
->dataStreamCls
->id();
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
;
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
;
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
)
240 const auto port_name
= ctf_fs_make_port_name(ds_file_group
);
241 auto port_data
= bt2s::make_unique
<ctf_fs_port_data
>();
243 BT_CPPLOGI_SPEC(ctf_fs
->logger
, "Creating one port named `{}`", port_name
);
245 port_data
->ctf_fs
= ctf_fs
;
246 port_data
->ds_file_group
= ds_file_group
;
248 int ret
= bt_self_component_source_add_output_port(selfSrcComp
.libObjPtr(), port_name
.c_str(),
249 port_data
.get(), NULL
);
254 ctf_fs
->port_data
.emplace_back(std::move(port_data
));
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
)
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
);
266 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctf_fs
->logger
, "Cannot create output port.");
274 static bool ds_index_entries_equal(const ctf_fs_ds_index_entry
& left
,
275 const ctf_fs_ds_index_entry
& right
)
277 if (left
.packetSize
!= right
.packetSize
) {
281 if (left
.timestamp_begin
!= right
.timestamp_begin
) {
285 if (left
.timestamp_end
!= right
.timestamp_end
) {
289 if (left
.packet_seq_num
!= right
.packet_seq_num
) {
297 * Insert `entry` into `index`, without duplication.
299 * The entry is inserted only if there isn't an identical entry already.
302 static void ds_index_insert_ds_index_entry_sorted(ctf_fs_ds_index
& index
,
303 const ctf_fs_ds_index_entry
& entry
)
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
) {
314 * Insert the entry only if a duplicate doesn't already exist.
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.
320 if (otherEntry
== index
.entries
.end() || !ds_index_entries_equal(entry
, *otherEntry
)) {
321 index
.entries
.emplace(otherEntry
, entry
);
325 static void merge_ctf_fs_ds_indexes(ctf_fs_ds_index
& dest
, const ctf_fs_ds_index
& src
)
327 for (const auto& entry
: src
.entries
) {
328 ds_index_insert_ds_index_entry_sorted(dest
, entry
);
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
)
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
};
340 tempIndex
.entries
.emplace_back(tempIndexEntry
);
343 readPktProps(traceCls
, bt2s::make_unique
<fs::Medium
>(tempIndex
, logger
), 0_bytes
, logger
);
344 const auto sc
= props
.dataStreamCls
;
348 bt2s::optional
<unsigned long long> stream_instance_id
= props
.dataStreamId
;
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
);
358 BT_CPPLOGE_APPEND_CAUSE_SPEC(
359 logger
, "Cannot convert clock cycles to nanoseconds from origin (`{}`).", path
);
364 auto index
= ctf_fs_ds_file_build_index(*ds_file_info
, traceCls
);
366 BT_CPPLOGE_APPEND_CAUSE_SPEC(logger
, "Failed to index CTF stream file \'{}\'", path
);
370 if (!stream_instance_id
|| begin_ns
== -1) {
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
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),
381 ctf_fs_trace
->ds_file_groups
.back()->insert_ds_file_info_sorted(std::move(ds_file_info
));
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();
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();
399 merge_ctf_fs_ds_indexes(ds_file_group
->index
, *index
);
402 ds_file_group
->insert_ds_file_info_sorted(std::move(ds_file_info
));
407 static int create_ds_file_groups(struct ctf_fs_trace
*ctf_fs_trace
, const bt2c::Logger
& logger
)
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
)};
413 BT_CPPLOGE_APPEND_CAUSE_SPEC(logger
, "Cannot open directory `{}`: {} (code {})",
414 ctf_fs_trace
->path
, error
->message
, error
->code
);
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
);
429 if (basename
[0] == '.') {
430 BT_CPPLOGI_SPEC(logger
, "Ignoring hidden file `{}" G_DIR_SEPARATOR_S
"{}`",
431 ctf_fs_trace
->path
, basename
);
435 /* Create the file. */
436 ctf_fs_file file
{logger
};
438 /* Create full path string. */
439 file
.path
= fmt::format("{}" G_DIR_SEPARATOR_S
"{}", ctf_fs_trace
->path
, basename
);
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
);
446 int ret
= ctf_fs_file_open(&file
, "rb");
448 BT_CPPLOGE_APPEND_CAUSE_SPEC(logger
, "Cannot open stream file `{}`", file
.path
);
452 if (file
.size
== 0) {
453 /* Skip empty stream. */
454 BT_CPPLOGI_SPEC(logger
, "Ignoring empty file `{}`", file
.path
);
458 ret
= add_ds_file_to_ds_file_group(ctf_fs_trace
, file
.path
.c_str(), logger
);
460 BT_CPPLOGE_APPEND_CAUSE_SPEC(logger
, "Cannot add stream file `{}` to stream file group",
469 static void set_trace_name(const bt2::Trace trace
, const char *name_suffix
)
474 * Check if we have a trace environment string value named `hostname`.
475 * If so, use it as the trace name's prefix.
477 const auto val
= trace
.environmentEntry("hostname");
478 if (val
&& val
->isString()) {
479 name
+= val
->asString().value();
482 name
+= G_DIR_SEPARATOR
;
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
)
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
);
501 ctf_fs_trace
->path
= path
;
502 ctf_fs_trace
->parseMetadata(bt2c::dataFromFile(metadataPath
, logger
, true));
504 BT_ASSERT(ctf_fs_trace
->cls());
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
);
515 int ret
= create_ds_file_groups(ctf_fs_trace
.get(), logger
);
523 static int path_is_ctf_trace(const char *path
)
525 return g_file_test(fmt::format("{}" G_DIR_SEPARATOR_S CTF_FS_METADATA_FILENAME
, path
).c_str(),
526 G_FILE_TEST_IS_REGULAR
);
529 /* Helper for ctf_fs_component_create_ctf_fs_trace, to handle a single path. */
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
)
536 bt2c::GStringUP norm_path
{bt_common_normalize_path(path_param
, NULL
)};
538 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctf_fs
->logger
, "Failed to normalize path: `{}`.", path_param
);
542 int ret
= path_is_ctf_trace(norm_path
->str
);
544 BT_CPPLOGE_APPEND_CAUSE_SPEC(
545 ctf_fs
->logger
, "Failed to check if path is a CTF trace: path={}", norm_path
->str
);
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): `{}`.",
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.");
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
);
563 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctf_fs
->logger
, "Cannot create trace for `{}`.",
568 traces
.emplace_back(std::move(ctf_fs_trace
));
574 * Count the number of stream and event classes defined by this trace's metadata.
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.
582 static unsigned int metadata_count_stream_and_event_classes(struct ctf_fs_trace
*trace
)
584 const TraceCls::DataStreamClsSet
& dataStreamClasses
= trace
->cls()->dataStreamClasses();
585 unsigned int num
= dataStreamClasses
.size();
587 for (const DataStreamCls::UP
& dsc
: dataStreamClasses
) {
588 num
+= dsc
->eventRecordClasses().size();
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.
599 static void merge_ctf_fs_ds_file_groups(struct ctf_fs_ds_file_group
*dest
,
600 ctf_fs_ds_file_group::UP src
)
602 for (auto& ds_file_info
: src
->ds_file_infos
) {
603 dest
->insert_ds_file_info_sorted(std::move(ds_file_info
));
606 /* Merge both indexes. */
607 merge_ctf_fs_ds_indexes(dest
->index
, src
->index
);
610 /* Merge src_trace's data stream file groups into dest_trace's. */
612 static int merge_matching_ctf_fs_ds_file_groups(struct ctf_fs_trace
*dest_trace
,
613 ctf_fs_trace::UP src_trace
)
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
;
619 * Save the initial length of dest: we only want to check against the
620 * original elements in the inner loop.
622 size_t dest_len
= dest
.size();
624 for (auto& src_group
: src
) {
625 struct ctf_fs_ds_file_group
*dest_group
= NULL
;
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();
633 /* Can't match a stream instance without ID. */
634 if (candidate_dest
->stream_id
== -1) {
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
644 if (candidate_dest
->stream_id
!= src_group
->stream_id
||
645 candidate_dest
->dataStreamCls
->id() != src_group
->dataStreamCls
->id()) {
649 dest_group
= candidate_dest
;
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
661 const DataStreamCls
*sc
= (*dest_trace
->cls())[src_group
->dataStreamCls
->id()];
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();
669 BT_ASSERT(dest_group
);
670 merge_ctf_fs_ds_file_groups(dest_group
, std::move(src_group
));
677 * Collapse the given traces, which must all share the same UUID, in a single
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`.
685 static int merge_ctf_fs_traces(std::vector
<ctf_fs_trace::UP
> traces
, ctf_fs_trace::UP
& out_trace
)
687 BT_ASSERT(traces
.size() >= 2);
689 unsigned int winner_count
= metadata_count_stream_and_event_classes(traces
[0].get());
690 ctf_fs_trace
*winner
= traces
[0].get();
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
;
698 /* A bit of sanity check. */
699 /* ⚠️ TODO: also consider namespace and name */
700 BT_ASSERT(winner
->cls()->uid() == candidate
->cls()->uid());
702 candidate_count
= metadata_count_stream_and_event_classes(candidate
);
704 if (candidate_count
> winner_count
) {
705 winner_count
= candidate_count
;
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
) {
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
));
726 * Move the winner out of the array, into `*out_trace`.
728 out_trace
= std::move(traces
[winner_i
]);
733 struct ClockSnapshotAfterEventItemVisitor
: public ItemVisitor
740 bt2s::optional
<unsigned long long> result() const
746 bt2s::optional
<unsigned long long> _mResult
;
750 struct ClockSnapshotAfterFirstEventItemVisitor
: public ClockSnapshotAfterEventItemVisitor
752 void visit(const EventRecordInfoItem
& item
) override
754 _mResult
= item
.defClkVal();
760 * Find the timestamp of the last event of the packet, if any, otherwise
761 * find the timestamp of the beginning of the packet.
763 struct ClockSnapshotAfterLastEventItemVisitor
: public ClockSnapshotAfterEventItemVisitor
765 void visit(const PktInfoItem
& item
) override
767 _mLastSeen
= item
.beginDefClkVal();
770 void visit(const EventRecordInfoItem
& item
) override
772 _mLastSeen
= item
.defClkVal();
775 void visit(const PktEndItem
&) override
777 _mResult
= _mLastSeen
;
782 bt2s::optional
<unsigned long long> _mLastSeen
;
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
)
792 BT_ASSERT(ctf_fs_trace
);
793 BT_ASSERT(ctf_fs_trace
->cls());
794 BT_ASSERT(index_entry
.path
);
796 ctf_fs_ds_index tempIndex
;
798 tempIndex
.entries
.emplace_back(index_entry
);
800 ItemSeqIter itemSeqIter
{bt2s::make_unique
<fs::Medium
>(tempIndex
, logger
), *ctf_fs_trace
->cls(),
801 index_entry
.offsetInFile
, logger
};
802 LoggingItemVisitor loggingVisitor
{logger
};
804 while (!visitor
.done()) {
805 const Item
*item
= itemSeqIter
.next();
808 if (logger
.wouldLogT()) {
809 item
->accept(loggingVisitor
);
812 item
->accept(visitor
);
815 if (!visitor
.result()) {
816 BT_CPPLOGE_APPEND_CAUSE_SPEC(logger
, "Failed to get {} event clock snapshot.", firstOrLast
);
820 *cs
= *visitor
.result();
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
);
827 BT_CPPLOGE_APPEND_CAUSE_SPEC(logger
, "Failed to convert clock snapshot to timestamp");
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
,
840 ClockSnapshotAfterFirstEventItemVisitor visitor
{};
842 return decode_clock_snapshot_after_event(ctf_fs_trace
, default_cc
, index_entry
, visitor
,
843 "first", logger
, cs
, ts_ns
);
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
,
852 ClockSnapshotAfterLastEventItemVisitor visitor
{};
854 return decode_clock_snapshot_after_event(ctf_fs_trace
, default_cc
, index_entry
, visitor
, "last",
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.
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.
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
876 static int fix_index_lttng_event_after_packet_bug(struct ctf_fs_trace
*trace
,
877 const bt2c::Logger
& logger
)
879 for (const auto& ds_file_group
: trace
->ds_file_groups
) {
880 BT_ASSERT(ds_file_group
);
881 auto& index
= ds_file_group
->index
;
883 BT_ASSERT(!index
.entries
.empty());
886 * Iterate over all entries but the last one. The last one is
887 * fixed differently after.
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];
894 * 1. Set the current index entry `end` timestamp to
895 * the next index entry `begin` timestamp.
897 curr_entry
.timestamp_end
= next_entry
.timestamp_begin
;
898 curr_entry
.timestamp_end_ns
= next_entry
.timestamp_begin_ns
;
902 * 2. Fix the last entry by decoding the last event of the last
905 auto& last_entry
= index
.entries
.back();
907 BT_ASSERT(ds_file_group
->dataStreamCls
->defClkCls());
908 const ClkCls
& default_cc
= *ds_file_group
->dataStreamCls
->defClkCls();
911 * Decode packet to read the timestamp of the last event of the
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
);
918 BT_CPPLOGE_APPEND_CAUSE_SPEC(
920 "Failed to decode stream's last packet to get its last event's clock snapshot.");
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.
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
939 * Known buggy tracer versions:
940 * - before barectf 2.3.1
942 static int fix_index_barectf_event_before_packet_bug(struct ctf_fs_trace
*trace
,
943 const bt2c::Logger
& logger
)
945 for (const auto& ds_file_group
: trace
->ds_file_groups
) {
946 auto& index
= ds_file_group
->index
;
948 BT_ASSERT(!index
.entries
.empty());
950 BT_ASSERT(ds_file_group
->dataStreamCls
->defClkCls());
951 const ClkCls
& default_cc
= *ds_file_group
->dataStreamCls
->defClkCls();
954 * 1. Iterate over the index, starting from the second entry
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
];
961 * 2. Set the current entry `begin` timestamp to the
962 * timestamp of the first event of the current packet.
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
);
968 BT_CPPLOGE_APPEND_CAUSE_SPEC(logger
,
969 "Failed to decode first event's clock snapshot");
974 * 3. Set the previous entry `end` timestamp to the
975 * timestamp of the first event of the current packet.
977 prev_entry
.timestamp_end
= curr_entry
.timestamp_begin
;
978 prev_entry
.timestamp_end_ns
= curr_entry
.timestamp_begin_ns
;
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.
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.
1000 * - All current and future lttng-ust and lttng-modules versions.
1002 static int fix_index_lttng_crash_quirk(struct ctf_fs_trace
*trace
, const bt2c::Logger
& logger
)
1004 for (const auto& ds_file_group
: trace
->ds_file_groups
) {
1005 BT_ASSERT(ds_file_group
);
1006 auto& index
= ds_file_group
->index
;
1008 BT_ASSERT(ds_file_group
->dataStreamCls
->defClkCls());
1009 const ClkCls
& default_cc
= *ds_file_group
->dataStreamCls
->defClkCls();
1011 BT_ASSERT(!index
.entries
.empty());
1013 auto& last_entry
= index
.entries
.back();
1015 /* 1. Fix the last entry first. */
1016 if (last_entry
.timestamp_end
== 0 && last_entry
.timestamp_begin
!= 0) {
1018 * Decode packet to read the timestamp of the
1019 * last event of the stream file.
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
);
1025 BT_CPPLOGE_APPEND_CAUSE_SPEC(logger
,
1026 "Failed to decode last event's clock snapshot");
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];
1036 if (curr_entry
.timestamp_end
== 0 && curr_entry
.timestamp_begin
!= 0) {
1038 * 2. Set the current index entry `end` timestamp to
1039 * the next index entry `begin` timestamp.
1041 curr_entry
.timestamp_end
= next_entry
.timestamp_begin
;
1042 curr_entry
.timestamp_end_ns
= next_entry
.timestamp_begin_ns
;
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.
1055 static int extract_tracer_info(struct ctf_fs_trace
*trace
, struct tracer_info
*current_tracer_info
)
1057 if (!trace
->cls()->env()) {
1061 bt2::ConstMapValue env
= *trace
->cls()->env();
1063 /* Clear the current_tracer_info struct */
1064 memset(current_tracer_info
, 0, sizeof(*current_tracer_info
));
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.
1071 bt2::OptionalBorrowedObject
<bt2::ConstValue
> tracerName
= env
["tracer_name"];
1072 if (!tracerName
|| !tracerName
->isString()) {
1076 /* Set tracer name. */
1077 current_tracer_info
->name
= tracerName
->asString().value();
1079 bt2::OptionalBorrowedObject
<bt2::ConstValue
> tracerMajor
= env
["tracer_major"];
1080 if (!tracerMajor
|| !tracerMajor
->isInteger()) {
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());
1090 bt2::OptionalBorrowedObject
<bt2::ConstValue
> tracerMinor
= env
["tracer_minor"];
1091 if (!tracerMinor
|| !tracerMinor
->isInteger()) {
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());
1102 * If `tracer_patch` doesn't exist `tracer_patchlevel` might.
1103 * For example, `lttng-modules` uses entry name `tracer_patchlevel`.
1105 bt2::OptionalBorrowedObject
<bt2::ConstValue
> tracerPatch
= env
["tracer_patch"];
1107 tracerPatch
= env
["tracer_patchlevel"];
1110 if (!tracerPatch
|| !tracerPatch
->isInteger()) {
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());
1123 static bool is_tracer_affected_by_lttng_event_after_packet_bug(struct tracer_info
*curr_tracer_info
)
1125 bool is_affected
= false;
1127 if (strcmp(curr_tracer_info
->name
, "lttng-ust") == 0) {
1128 if (curr_tracer_info
->major
< 2) {
1130 } else if (curr_tracer_info
->major
== 2) {
1131 /* fixed in lttng-ust 2.11.0 */
1132 if (curr_tracer_info
->minor
< 11) {
1136 } else if (strcmp(curr_tracer_info
->name
, "lttng-modules") == 0) {
1137 if (curr_tracer_info
->major
< 2) {
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) {
1146 } else if (curr_tracer_info
->minor
== 9) {
1147 /* fixed in lttng-modules 2.9.13 */
1148 if (curr_tracer_info
->patch
< 13) {
1151 } else if (curr_tracer_info
->minor
< 9) {
1161 is_tracer_affected_by_barectf_event_before_packet_bug(struct tracer_info
*curr_tracer_info
)
1163 bool is_affected
= false;
1165 if (strcmp(curr_tracer_info
->name
, "barectf") == 0) {
1166 if (curr_tracer_info
->major
< 2) {
1168 } else if (curr_tracer_info
->major
== 2) {
1169 if (curr_tracer_info
->minor
< 3) {
1171 } else if (curr_tracer_info
->minor
== 3) {
1172 /* fixed in barectf 2.3.1 */
1173 if (curr_tracer_info
->patch
< 1) {
1183 static bool is_tracer_affected_by_lttng_crash_quirk(struct tracer_info
*curr_tracer_info
)
1185 bool is_affected
= false;
1187 /* All LTTng tracer may be affected by this lttng crash quirk. */
1188 if (strcmp(curr_tracer_info
->name
, "lttng-ust") == 0) {
1190 } else if (strcmp(curr_tracer_info
->name
, "lttng-modules") == 0) {
1198 * Looks for trace produced by known buggy tracers and fix up the index
1201 static int fix_packet_index_tracer_bugs(ctf_fs_component
*ctf_fs
)
1203 struct tracer_info current_tracer_info
;
1205 int ret
= extract_tracer_info(ctf_fs
->trace
.get(), ¤t_tracer_info
);
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
1216 "Cannot extract tracer information necessary to compare with buggy versions.");
1220 /* Check if the trace may be affected by old tracer bugs. */
1221 if (is_tracer_affected_by_lttng_event_after_packet_bug(¤t_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
);
1226 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctf_fs
->logger
,
1227 "Failed to fix LTTng event-after-packet bug.");
1230 ctf_fs
->quirks
.eventRecordDefClkValGtNextPktBeginDefClkVal
= true;
1233 if (is_tracer_affected_by_barectf_event_before_packet_bug(¤t_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
);
1238 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctf_fs
->logger
,
1239 "Failed to fix barectf event-before-packet bug.");
1242 ctf_fs
->quirks
.eventRecordDefClkValLtPktBeginDefClkVal
= true;
1245 if (is_tracer_affected_by_lttng_crash_quirk(¤t_tracer_info
)) {
1246 ret
= fix_index_lttng_crash_quirk(ctf_fs
->trace
.get(), ctf_fs
->logger
);
1248 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctf_fs
->logger
,
1249 "Failed to fix lttng-crash timestamp quirks.");
1252 ctf_fs
->quirks
.pktEndDefClkValZero
= true;
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
)
1261 BT_ASSERT(!ds_file_group_a
->ds_file_infos
.empty());
1262 BT_ASSERT(!ds_file_group_b
->ds_file_infos
.empty());
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];
1267 return first_ds_file_info_a
.path
< first_ds_file_info_b
.path
;
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
)
1274 std::vector
<std::string
> paths
;
1276 BT_ASSERT(!pathsValue
.isEmpty());
1279 * Create a sorted array of the paths, to make the execution of this
1280 * component deterministic.
1282 for (const auto pathValue
: pathsValue
) {
1283 BT_ASSERT(pathValue
.isString());
1284 paths
.emplace_back(pathValue
.asString().value().str());
1287 std::sort(paths
.begin(), paths
.end());
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
,
1299 if (traces
.size() > 1) {
1300 ctf_fs_trace
*first_trace
= traces
[0].get();
1303 * We have more than one trace, they must all share the same
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(
1311 "Multiple traces given, but a trace does not have a UID: path={}",
1316 auto& first_trace_uid
= *first_trace
->cls()->uid();
1317 auto& this_trace_uid
= *this_trace
->cls()->uid();
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
,
1330 int ret
= merge_ctf_fs_traces(std::move(traces
), ctf_fs
->trace
);
1332 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctf_fs
->logger
,
1333 "Failed to merge traces with the same UUID.");
1337 /* Just one trace, it may or may not have a UUID, both are fine. */
1339 BT_DIAG_IGNORE_NULL_DEREFERENCE
1340 ctf_fs
->trace
= std::move(traces
[0]);
1344 int ret
= fix_packet_index_tracer_bugs(ctf_fs
);
1346 BT_CPPLOGE_APPEND_CAUSE_SPEC(ctf_fs
->logger
, "Failed to fix packet index tracer bugs.");
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.
1359 * Having a deterministic order here can help debugging and
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
);
1366 * Now that indexes are not going to change anymore, compute each entry's
1367 * offset in the logical data stream.
1369 for (ctf_fs_ds_file_group::UP
& group
: ctf_fs
->trace
->ds_file_groups
) {
1370 group
->index
.updateOffsetsInStream();
1376 static const std::string
&
1377 get_stream_instance_unique_name(struct ctf_fs_ds_file_group
*ds_file_group
)
1380 * The first (earliest) stream file's path is used as the stream's unique
1383 BT_ASSERT(!ds_file_group
->ds_file_infos
.empty());
1384 return ds_file_group
->ds_file_infos
[0]->path
;
1387 /* Create the IR stream objects for ctf_fs_trace. */
1389 static void create_streams_for_trace(struct ctf_fs_trace
*ctf_fs_trace
)
1391 BT_ASSERT(ctf_fs_trace
->trace
);
1393 for (const auto& ds_file_group
: ctf_fs_trace
->ds_file_groups
) {
1394 BT_ASSERT(ds_file_group
->dataStreamCls
->libCls());
1396 const std::string
& name
= get_stream_instance_unique_name(ds_file_group
.get());
1397 const auto streamCls
= *ds_file_group
->dataStreamCls
->libCls();
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
++;
1405 /* Specific stream ID */
1406 ds_file_group
->stream
=
1407 streamCls
.instantiate(*ctf_fs_trace
->trace
, ds_file_group
->stream_id
);
1410 ds_file_group
->stream
->name(name
);
1414 static const bt_param_validation_value_descr inputs_elem_descr
=
1415 bt_param_validation_value_descr::makeString();
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
};
1431 ctf::src::fs::Parameters
read_src_fs_parameters(const bt2::ConstMapValue params
,
1432 const bt2c::Logger
& logger
)
1434 gchar
*error
= NULL
;
1435 bt_param_validation_status validate_value_status
=
1436 bt_param_validation_validate(params
.libObjPtr(), fs_params_entries_descr
, &error
);
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
);
1443 ctf::src::fs::Parameters parameters
{params
["inputs"]->asArray()};
1445 /* clock-class-offset-s parameter */
1446 if (const auto clockClassOffsetS
= params
["clock-class-offset-s"]) {
1447 parameters
.clkClsCfg
.offsetSec
= clockClassOffsetS
->asSignedInteger().value();
1450 /* clock-class-offset-ns parameter */
1451 if (const auto clockClassOffsetNs
= params
["clock-class-offset-ns"]) {
1452 parameters
.clkClsCfg
.offsetNanoSec
= clockClassOffsetNs
->asSignedInteger().value();
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();
1461 /* trace-name parameter */
1462 if (const auto traceName
= params
["trace-name"]) {
1463 parameters
.traceName
= traceName
->asString().value().str();
1469 static ctf_fs_component::UP
ctf_fs_create(const bt2::ConstMapValue params
,
1470 const bt2::SelfSourceComponent selfSrcComp
)
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
);
1476 if (ctf_fs_component_create_ctf_fs_trace(ctf_fs
.get(), parameters
.inputs
,
1477 parameters
.traceName
? parameters
.traceName
->c_str() :
1479 static_cast<bt2::SelfComponent
>(selfSrcComp
))) {
1483 create_streams_for_trace(ctf_fs
->trace
.get());
1485 if (create_ports_for_trace(ctf_fs
.get(), ctf_fs
->trace
.get(), selfSrcComp
)) {
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 *)
1497 ctf_fs_component::UP ctf_fs
=
1498 ctf_fs_create(bt2::ConstMapValue
{params
}, bt2::wrap(self_comp_src
));
1500 return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR
;
1503 bt_self_component_set_data(bt_self_component_source_as_self_component(self_comp_src
),
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
;
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
)
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
;
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
);
1533 BT_CPPLOGE_SPEC(logger
, "Unknown query object `{}`", object
);
1534 return BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_UNKNOWN_OBJECT
;
1537 *result
= resultObj
.release().libObjPtr();
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
;