+ const guint dest_len = dest->len;
+
+ for (s_i = 0; s_i < src->len; s_i++) {
+ struct ctf_fs_ds_file_group *src_group = g_ptr_array_index(src, s_i);
+ struct ctf_fs_ds_file_group *dest_group = NULL;
+
+ /* A stream instance without ID can't match a stream in the other trace. */
+ if (src_group->stream_id != -1) {
+ guint d_i;
+
+ /* Let's search for a matching ds_file_group in the destination. */
+ for (d_i = 0; d_i < dest_len; d_i++) {
+ struct ctf_fs_ds_file_group *candidate_dest = g_ptr_array_index(dest, d_i);
+
+ /* Can't match a stream instance without ID. */
+ if (candidate_dest->stream_id == -1) {
+ continue;
+ }
+
+ /*
+ * If the two groups have the same stream instance id
+ * and belong to the same stream class (stream instance
+ * ids are per-stream class), they represent the same
+ * stream instance.
+ */
+ if (candidate_dest->stream_id != src_group->stream_id ||
+ candidate_dest->sc->id != src_group->sc->id) {
+ continue;
+ }
+
+ dest_group = candidate_dest;
+ break;
+ }
+ }
+
+ /*
+ * Didn't find a friend in dest to merge our src_group into?
+ * Create a new empty one.
+ */
+ if (!dest_group) {
+ struct ctf_stream_class *sc;
+
+ sc = ctf_trace_class_borrow_stream_class_by_id(
+ dest_trace->metadata->tc, src_group->sc->id);
+ BT_ASSERT(sc);
+
+ dest_group = ctf_fs_ds_file_group_create(dest_trace, sc,
+ src_group->stream_id);
+ if (!dest_group) {
+ ret = -1;
+ goto end;
+ }
+
+ g_ptr_array_add(dest_trace->ds_file_groups, dest_group);
+ }
+
+ BT_ASSERT(dest_group);
+ merge_ctf_fs_ds_file_groups(dest_group, src_group);
+ }
+
+end:
+ return ret;
+}
+
+/*
+ * Collapse the given traces, which must all share the same UUID, in a single
+ * one.
+ *
+ * The trace with the most expansive metadata is chosen and all other traces
+ * are merged into that one. The array slots of all the traces that get merged
+ * in the chosen one are set to NULL, so only the slot of the chosen trace
+ * remains non-NULL.
+ */
+
+static
+int merge_ctf_fs_traces(struct ctf_fs_trace **traces, unsigned int num_traces)
+{
+ unsigned int winner_count;
+ struct ctf_fs_trace *winner;
+ guint i;
+ int ret = 0;
+ char uuid_str[BABELTRACE_UUID_STR_LEN];
+
+ BT_ASSERT(num_traces >= 2);
+
+ winner_count = metadata_count_stream_and_event_classes(traces[0]);
+ winner = traces[0];
+
+ /* Find the trace with the largest metadata. */
+ for (i = 1; i < num_traces; i++) {
+ struct ctf_fs_trace *candidate;
+ unsigned int candidate_count;
+
+ candidate = traces[i];
+
+ /* A bit of sanity check. */
+ BT_ASSERT(bt_uuid_compare(winner->metadata->tc->uuid, candidate->metadata->tc->uuid) == 0);
+
+ candidate_count = metadata_count_stream_and_event_classes(candidate);
+
+ if (candidate_count > winner_count) {
+ winner_count = candidate_count;
+ winner = candidate;
+ }
+ }
+
+ /* Merge all the other traces in the winning trace. */
+ for (i = 0; i < num_traces; i++) {
+ struct ctf_fs_trace *trace = traces[i];
+
+ /* Don't merge the winner into itself. */
+ if (trace == winner) {
+ continue;
+ }
+
+ /* Merge trace's data stream file groups into winner's. */
+ ret = merge_matching_ctf_fs_ds_file_groups(winner, trace);
+ if (ret) {
+ goto end;
+ }
+
+ /* Free the trace that got merged into winner, clear the slot in the array. */
+ ctf_fs_trace_destroy(trace);
+ traces[i] = NULL;
+ }
+
+ /* Use the string representation of the UUID as the trace name. */
+ bt_uuid_unparse(winner->metadata->tc->uuid, uuid_str);
+ g_string_printf(winner->name, "%s", uuid_str);
+
+end:
+ return ret;
+}
+
+/*
+ * Merge all traces of `ctf_fs` that share the same UUID in a single trace.
+ * Traces with no UUID are not merged.
+ */
+
+static
+int merge_traces_with_same_uuid(struct ctf_fs_component *ctf_fs)
+{
+ GPtrArray *traces = ctf_fs->traces;
+ guint range_start_idx = 0;
+ unsigned int num_traces = 0;
+ guint i;
+ int ret = 0;
+
+ /* Sort the traces by uuid, then collapse traces with the same uuid in a single one. */
+ g_ptr_array_sort(traces, sort_traces_by_uuid);
+
+ /* Find ranges of consecutive traces that share the same UUID. */
+ while (range_start_idx < traces->len) {
+ guint range_len;
+ struct ctf_fs_trace *range_start_trace = g_ptr_array_index(traces, range_start_idx);
+
+ /* Exclusive end of range. */
+ guint range_end_exc_idx = range_start_idx + 1;
+
+ while (range_end_exc_idx < traces->len) {
+ struct ctf_fs_trace *this_trace = g_ptr_array_index(traces, range_end_exc_idx);
+
+ if (!range_start_trace->metadata->tc->is_uuid_set ||
+ (bt_uuid_compare(range_start_trace->metadata->tc->uuid, this_trace->metadata->tc->uuid) != 0)) {
+ break;
+ }
+
+ range_end_exc_idx++;
+ }
+
+ /* If we have two or more traces with matching UUIDs, merge them. */
+ range_len = range_end_exc_idx - range_start_idx;
+ if (range_len > 1) {
+ struct ctf_fs_trace **range_start = (struct ctf_fs_trace **) &traces->pdata[range_start_idx];
+ ret = merge_ctf_fs_traces(range_start, range_len);
+ if (ret) {
+ goto end;
+ }
+ }
+
+ num_traces++;
+ range_start_idx = range_end_exc_idx;
+ }
+
+ /* Clear any NULL slot (traces that got merged in another one) in the array. */
+ for (i = 0; i < traces->len;) {
+ if (g_ptr_array_index(traces, i) == NULL) {
+ g_ptr_array_remove_index_fast(traces, i);
+ } else {
+ i++;
+ }
+ }
+
+ BT_ASSERT(num_traces == traces->len);
+
+end:
+ return ret;
+}
+
+int ctf_fs_component_create_ctf_fs_traces(bt_self_component_source *self_comp,
+ struct ctf_fs_component *ctf_fs,
+ const bt_value *paths_value)
+{
+ int ret = 0;
+ uint64_t i;
+
+ for (i = 0; i < bt_value_array_get_size(paths_value); i++) {
+ const bt_value *path_value = bt_value_array_borrow_element_by_index_const(paths_value, i);
+ const char *path = bt_value_string_get(path_value);
+
+ ret = ctf_fs_component_create_ctf_fs_traces_one_root(self_comp, ctf_fs, path);
+ if (ret) {
+ goto end;
+ }
+ }
+
+ ret = merge_traces_with_same_uuid(ctf_fs);
+
+end:
+ return ret;
+}
+
+static
+GString *get_stream_instance_unique_name(
+ struct ctf_fs_ds_file_group *ds_file_group)
+{
+ GString *name;
+ struct ctf_fs_ds_file_info *ds_file_info;
+
+ name = g_string_new(NULL);
+ if (!name) {
+ goto end;
+ }
+
+ /*
+ * If there's more than one stream file in the stream file
+ * group, the first (earliest) stream file's path is used as
+ * the stream's unique name.
+ */
+ BT_ASSERT(ds_file_group->ds_file_infos->len > 0);
+ ds_file_info = g_ptr_array_index(ds_file_group->ds_file_infos, 0);
+ g_string_assign(name, ds_file_info->path->str);
+
+end:
+ return name;
+}
+
+/* Create the IR stream objects for ctf_fs_trace. */
+
+static
+int create_streams_for_trace(struct ctf_fs_trace *ctf_fs_trace)
+{
+ int ret;
+ GString *name = NULL;
+ guint i;
+
+ for (i = 0; i < ctf_fs_trace->ds_file_groups->len; i++) {
+ struct ctf_fs_ds_file_group *ds_file_group =
+ g_ptr_array_index(ctf_fs_trace->ds_file_groups, i);
+ name = get_stream_instance_unique_name(ds_file_group);
+
+ if (!name) {
+ goto error;
+ }
+
+ if (ds_file_group->sc->ir_sc) {
+ BT_ASSERT(ctf_fs_trace->trace);
+
+ if (ds_file_group->stream_id == UINT64_C(-1)) {
+ /* No stream ID: use 0 */
+ ds_file_group->stream = bt_stream_create_with_id(
+ ds_file_group->sc->ir_sc,
+ ctf_fs_trace->trace,
+ ctf_fs_trace->next_stream_id);
+ ctf_fs_trace->next_stream_id++;
+ } else {
+ /* Specific stream ID */
+ ds_file_group->stream = bt_stream_create_with_id(
+ ds_file_group->sc->ir_sc,
+ ctf_fs_trace->trace,
+ (uint64_t) ds_file_group->stream_id);
+ }
+ } else {
+ ds_file_group->stream = NULL;
+ }
+
+ if (!ds_file_group->stream) {
+ BT_LOGE("Cannot create stream for DS file group: "
+ "addr=%p, stream-name=\"%s\"",
+ ds_file_group, name->str);
+ goto error;
+ }
+
+ ret = bt_stream_set_name(ds_file_group->stream,
+ name->str);
+ if (ret) {
+ BT_LOGE("Cannot set stream's name: "
+ "addr=%p, stream-name=\"%s\"",
+ ds_file_group->stream, name->str);
+ goto error;
+ }
+
+ g_string_free(name, TRUE);
+ name = NULL;
+ }
+
+ ret = 0;
+ goto end;
+
+error:
+ ret = -1;
+
+end:
+
+ if (name) {
+ g_string_free(name, TRUE);
+ }
+ return ret;
+}
+
+/*
+ * Validate the "paths" parameter passed to this component. It must be
+ * present, and it must be an array of strings.
+ */
+
+static
+bool validate_paths_parameter(const bt_value *paths)
+{
+ bool ret;
+ bt_value_type type;
+ uint64_t i;
+
+ if (!paths) {
+ BT_LOGE("missing \"paths\" parameter");
+ goto error;
+ }
+
+ type = bt_value_get_type(paths);
+ if (type != BT_VALUE_TYPE_ARRAY) {
+ BT_LOGE("`paths` parameter: expecting array value: type=%s",
+ bt_common_value_type_string(type));