d9a5fb63eff71363bb57cd53e3beaf79ae8693e5
[deliverable/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 #define BT_CLOG_CFG logCfg
11 #define BT_LOG_TAG "PLUGIN/SRC.CTF.FS"
12
13 #include "common/common.h"
14 #include <babeltrace2/babeltrace.h>
15 #include "common/uuid.h"
16 #include <glib.h>
17 #include "common/assert.h"
18 #include <inttypes.h>
19 #include <stdbool.h>
20 #include "fs.hpp"
21 #include "metadata.hpp"
22 #include "data-stream-file.hpp"
23 #include "file.hpp"
24 #include "../common/src/metadata/tsdl/decoder.hpp"
25 #include "../common/src/metadata/tsdl/ctf-meta-configure-ir-trace.hpp"
26 #include "../common/src/msg-iter/msg-iter.hpp"
27 #include "query.hpp"
28 #include "plugins/common/param-validation/param-validation.h"
29 #include "cpp-common/cfg-logging.hpp"
30 #include "cpp-common/cfg-logging-error-reporting.hpp"
31 #include "cpp-common/exc.hpp"
32 #include "cpp-common/make-unique.hpp"
33 #include <vector>
34 #include <sstream>
35
36 struct tracer_info
37 {
38 const char *name;
39 int64_t major;
40 int64_t minor;
41 int64_t patch;
42 };
43
44 static bt_message_iterator_class_next_method_status
45 ctf_fs_iterator_next_one(struct ctf_fs_msg_iter_data *msg_iter_data, const bt_message **out_msg)
46 {
47 bt_message_iterator_class_next_method_status status;
48 enum ctf_msg_iter_status msg_iter_status;
49 const bt2_common::LogCfg& logCfg = msg_iter_data->logCfg;
50
51 msg_iter_status = ctf_msg_iter_get_next_message(msg_iter_data->msg_iter.get(), out_msg);
52
53 switch (msg_iter_status) {
54 case CTF_MSG_ITER_STATUS_OK:
55 /* Cool, message has been written to *out_msg. */
56 status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK;
57 break;
58
59 case CTF_MSG_ITER_STATUS_EOF:
60 status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_END;
61 break;
62
63 case CTF_MSG_ITER_STATUS_AGAIN:
64 /*
65 * Should not make it this far as this is
66 * medium-specific; there is nothing for the user to do
67 * and it should have been handled upstream.
68 */
69 bt_common_abort();
70
71 case CTF_MSG_ITER_STATUS_ERROR:
72 BT_CLOGE_APPEND_CAUSE("Failed to get next message from CTF message iterator.");
73 status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR;
74 break;
75
76 case CTF_MSG_ITER_STATUS_MEMORY_ERROR:
77 BT_CLOGE_APPEND_CAUSE("Failed to get next message from CTF message iterator.");
78 status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_MEMORY_ERROR;
79 break;
80
81 default:
82 bt_common_abort();
83 }
84
85 return status;
86 }
87
88 BT_HIDDEN
89 bt_message_iterator_class_next_method_status
90 ctf_fs_iterator_next(bt_self_message_iterator *iterator, bt_message_array_const msgs,
91 uint64_t capacity, uint64_t *count)
92 {
93 struct ctf_fs_msg_iter_data *msg_iter_data =
94 (struct ctf_fs_msg_iter_data *) bt_self_message_iterator_get_data(iterator);
95 const bt2_common::LogCfg& logCfg = msg_iter_data->logCfg;
96
97 try {
98 uint64_t i = 0;
99
100 if (G_UNLIKELY(msg_iter_data->next_saved_error)) {
101 /*
102 * Last time we were called, we hit an error but had some
103 * messages to deliver, so we stashed the error here. Return
104 * it now.
105 */
106 BT_CURRENT_THREAD_MOVE_ERROR_AND_RESET(msg_iter_data->next_saved_error);
107 return msg_iter_data->next_saved_status;
108 }
109
110 bt_message_iterator_class_next_method_status status;
111
112 do {
113 status = ctf_fs_iterator_next_one(msg_iter_data, &msgs[i]);
114 if (status == BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK) {
115 i++;
116 }
117 } while (i < capacity && status == BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK);
118
119 if (i > 0) {
120 /*
121 * Even if ctf_fs_iterator_next_one() returned something
122 * else than BT_MESSAGE_ITERATOR_NEXT_METHOD_STATUS_OK, we
123 * accumulated message objects in the output
124 * message array, so we need to return
125 * BT_MESSAGE_ITERATOR_NEXT_METHOD_STATUS_OK so that they are
126 * transfered to downstream. This other status occurs
127 * again the next time muxer_msg_iter_do_next() is
128 * called, possibly without any accumulated
129 * message, in which case we'll return it.
130 */
131 if (status < 0) {
132 /*
133 * Save this error for the next _next call. Assume that
134 * this component always appends error causes when
135 * returning an error status code, which will cause the
136 * current thread error to be non-NULL.
137 */
138 msg_iter_data->next_saved_error = bt_current_thread_take_error();
139 BT_ASSERT(msg_iter_data->next_saved_error);
140 msg_iter_data->next_saved_status = status;
141 }
142
143 *count = i;
144 status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK;
145 }
146
147 return status;
148 } catch (const std::bad_alloc&) {
149 return BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_MEMORY_ERROR;
150 } catch (const bt2_common::Error&) {
151 BT_CLOGE_APPEND_CAUSE("Failed to fetch next messages");
152 return BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR;
153 }
154 }
155
156 BT_HIDDEN
157 bt_message_iterator_class_seek_beginning_method_status
158 ctf_fs_iterator_seek_beginning(bt_self_message_iterator *it)
159 {
160 struct ctf_fs_msg_iter_data *msg_iter_data =
161 (struct ctf_fs_msg_iter_data *) bt_self_message_iterator_get_data(it);
162
163 BT_ASSERT(msg_iter_data);
164
165 const bt2_common::LogCfg& logCfg = msg_iter_data->logCfg;
166
167 try {
168 ctf_msg_iter_reset(msg_iter_data->msg_iter.get());
169 ctf_fs_ds_group_medops_data_reset(msg_iter_data->msg_iter_medops_data.get());
170
171 return BT_MESSAGE_ITERATOR_CLASS_SEEK_BEGINNING_METHOD_STATUS_OK;
172 } catch (const std::bad_alloc&) {
173 return BT_MESSAGE_ITERATOR_CLASS_SEEK_BEGINNING_METHOD_STATUS_MEMORY_ERROR;
174 } catch (const bt2_common::Error&) {
175 BT_CLOGE_APPEND_CAUSE("Failed to seek beginning");
176 return BT_MESSAGE_ITERATOR_CLASS_SEEK_BEGINNING_METHOD_STATUS_ERROR;
177 }
178 }
179
180 BT_HIDDEN
181 void ctf_fs_iterator_finalize(bt_self_message_iterator *it)
182 {
183 ctf_fs_msg_iter_data::UP {
184 ((struct ctf_fs_msg_iter_data *) bt_self_message_iterator_get_data(it))};
185 }
186
187 static bt_message_iterator_class_initialize_method_status
188 ctf_msg_iter_medium_status_to_msg_iter_initialize_status(enum ctf_msg_iter_medium_status status)
189 {
190 switch (status) {
191 case CTF_MSG_ITER_MEDIUM_STATUS_EOF:
192 case CTF_MSG_ITER_MEDIUM_STATUS_AGAIN:
193 case CTF_MSG_ITER_MEDIUM_STATUS_ERROR:
194 return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
195 case CTF_MSG_ITER_MEDIUM_STATUS_MEMORY_ERROR:
196 return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
197 case CTF_MSG_ITER_MEDIUM_STATUS_OK:
198 return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_OK;
199 }
200
201 bt_common_abort();
202 }
203
204 BT_HIDDEN
205 bt_message_iterator_class_initialize_method_status
206 ctf_fs_iterator_init(bt_self_message_iterator *self_msg_iter,
207 bt_self_message_iterator_configuration *config,
208 bt_self_component_port_output *self_port)
209 {
210 bt_self_component_port *self_comp_port =
211 bt_self_component_port_output_as_self_component_port(self_port);
212 ctf_fs_port_data *port_data =
213 (ctf_fs_port_data *) bt_self_component_port_get_data(self_comp_port);
214 BT_ASSERT(port_data);
215
216 bt2_common::LogCfg logCfg {port_data->ctf_fs->logCfg.logLevel(), *self_msg_iter};
217
218 try {
219 enum ctf_msg_iter_medium_status medium_status;
220
221 ctf_fs_msg_iter_data::UP msg_iter_data =
222 bt2_common::makeUnique<ctf_fs_msg_iter_data>(logCfg);
223
224 msg_iter_data->self_msg_iter = self_msg_iter;
225 msg_iter_data->ds_file_group = port_data->ds_file_group;
226
227 medium_status =
228 ctf_fs_ds_group_medops_data_create(msg_iter_data->ds_file_group, self_msg_iter, logCfg,
229 msg_iter_data->msg_iter_medops_data);
230 BT_ASSERT(medium_status == CTF_MSG_ITER_MEDIUM_STATUS_OK ||
231 medium_status == CTF_MSG_ITER_MEDIUM_STATUS_ERROR ||
232 medium_status == CTF_MSG_ITER_MEDIUM_STATUS_MEMORY_ERROR);
233 if (medium_status != CTF_MSG_ITER_MEDIUM_STATUS_OK) {
234 BT_CLOGE_APPEND_CAUSE("Failed to create ctf_fs_ds_group_medops");
235 return ctf_msg_iter_medium_status_to_msg_iter_initialize_status(medium_status);
236 }
237
238 msg_iter_data->msg_iter = ctf_msg_iter_create(
239 msg_iter_data->ds_file_group->ctf_fs_trace->metadata->tc,
240 bt_common_get_page_size(logCfg.logLevel()) * 8, ctf_fs_ds_group_medops,
241 msg_iter_data->msg_iter_medops_data.get(), self_msg_iter, logCfg);
242 if (!msg_iter_data->msg_iter) {
243 BT_CLOGE_APPEND_CAUSE("Cannot create a CTF message iterator.");
244 return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
245 }
246
247 /*
248 * This iterator can seek forward if its stream class has a default
249 * clock class.
250 */
251 if (msg_iter_data->ds_file_group->sc->default_clock_class) {
252 bt_self_message_iterator_configuration_set_can_seek_forward(config, true);
253 }
254
255 bt_self_message_iterator_set_data(self_msg_iter, msg_iter_data.release());
256
257 return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_OK;
258 } catch (const std::bad_alloc&) {
259 return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
260 } catch (const bt2_common::Error&) {
261 BT_CLOGE_APPEND_CAUSE("Failed to initialize iterator");
262 return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
263 }
264 }
265
266 void ctf_fs_finalize(bt_self_component_source *component)
267 {
268 ctf_fs_component::UP {(ctf_fs_component *) bt_self_component_get_data(
269 bt_self_component_source_as_self_component(component))};
270 }
271
272 std::string ctf_fs_make_port_name(struct ctf_fs_ds_file_group *ds_file_group)
273 {
274 std::stringstream name;
275
276 /*
277 * The unique port name is generated by concatenating unique identifiers
278 * for:
279 *
280 * - the trace
281 * - the stream class
282 * - the stream
283 */
284
285 /* For the trace, use the uuid if present, else the path. */
286 if (ds_file_group->ctf_fs_trace->metadata->tc->is_uuid_set) {
287 char uuid_str[BT_UUID_STR_LEN + 1];
288
289 bt_uuid_to_str(ds_file_group->ctf_fs_trace->metadata->tc->uuid, uuid_str);
290 name << uuid_str;
291 } else {
292 name << ds_file_group->ctf_fs_trace->path;
293 }
294
295 /*
296 * For the stream class, use the id if present. We can omit this field
297 * otherwise, as there will only be a single stream class.
298 */
299 if (ds_file_group->sc->id != UINT64_C(-1)) {
300 name << " | " << ds_file_group->sc->id;
301 }
302
303 /* For the stream, use the id if present, else, use the path. */
304 if (ds_file_group->stream_id != UINT64_C(-1)) {
305 name << " | " << ds_file_group->stream_id;
306 } else {
307 BT_ASSERT(ds_file_group->ds_file_infos.size() == 1);
308 const ctf_fs_ds_file_info& ds_file_info = *ds_file_group->ds_file_infos[0];
309 name << " | " << ds_file_info.path;
310 }
311
312 return name.str();
313 }
314
315 static int create_one_port_for_trace(struct ctf_fs_component *ctf_fs,
316 struct ctf_fs_trace *ctf_fs_trace,
317 struct ctf_fs_ds_file_group *ds_file_group,
318 bt_self_component_source *self_comp_src)
319 {
320 const bt2_common::LogCfg& logCfg = ctf_fs->logCfg;
321
322 std::string port_name = ctf_fs_make_port_name(ds_file_group);
323 BT_CLOGI("Creating one port named `%s`", port_name.c_str());
324
325 /* Create output port for this file */
326 ctf_fs_port_data::UP port_data = bt2_common::makeUnique<ctf_fs_port_data>();
327 port_data->ctf_fs = ctf_fs;
328 port_data->ds_file_group = ds_file_group;
329 int ret = bt_self_component_source_add_output_port(self_comp_src, port_name.c_str(),
330 port_data.get(), NULL);
331 if (ret) {
332 return ret;
333 }
334
335 ctf_fs->port_data.emplace_back(std::move(port_data));
336 return 0;
337 }
338
339 static int create_ports_for_trace(struct ctf_fs_component *ctf_fs,
340 struct ctf_fs_trace *ctf_fs_trace,
341 bt_self_component_source *self_comp_src)
342 {
343 const bt2_common::LogCfg& logCfg = ctf_fs->logCfg;
344
345 /* Create one output port for each stream file group */
346 for (ctf_fs_ds_file_group::UP& ds_file_group : ctf_fs_trace->ds_file_groups) {
347 int ret =
348 create_one_port_for_trace(ctf_fs, ctf_fs_trace, ds_file_group.get(), self_comp_src);
349 if (ret) {
350 BT_CLOGE_APPEND_CAUSE("Cannot create output port.");
351 return ret;
352 }
353 }
354
355 return 0;
356 }
357
358 /*
359 * Insert ds_file_info in ds_file_group's list of ds_file_infos at the right
360 * place to keep it sorted.
361 */
362
363 static void ds_file_group_insert_ds_file_info_sorted(struct ctf_fs_ds_file_group *ds_file_group,
364 ctf_fs_ds_file_info::UP ds_file_info)
365 {
366 /* Find the spot where to insert this ds_file_info. */
367 auto it = ds_file_group->ds_file_infos.begin();
368
369 for (; it != ds_file_group->ds_file_infos.end(); ++it) {
370 const ctf_fs_ds_file_info& other_ds_file_info = **it;
371
372 if (ds_file_info->begin_ns < other_ds_file_info.begin_ns) {
373 break;
374 }
375 }
376
377 ds_file_group->ds_file_infos.insert(it, std::move(ds_file_info));
378 }
379
380 static bool ds_index_entries_equal(const struct ctf_fs_ds_index_entry *left,
381 const struct ctf_fs_ds_index_entry *right)
382 {
383 if (left->packetSize != right->packetSize) {
384 return false;
385 }
386
387 if (left->timestamp_begin != right->timestamp_begin) {
388 return false;
389 }
390
391 if (left->timestamp_end != right->timestamp_end) {
392 return false;
393 }
394
395 if (left->packet_seq_num != right->packet_seq_num) {
396 return false;
397 }
398
399 return true;
400 }
401
402 /*
403 * Insert `entry` into `index`, without duplication.
404 *
405 * The entry is inserted only if there isn't an identical entry already.
406 *
407 * In any case, the ownership of `entry` is transferred to this function. So if
408 * the entry is not inserted, it is freed.
409 */
410
411 static void ds_index_insert_ds_index_entry_sorted(struct ctf_fs_ds_index *index,
412 ctf_fs_ds_index_entry::UP entry)
413 {
414 /* Find the spot where to insert this index entry. */
415 auto otherEntry = index->entries.begin();
416 for (; otherEntry != index->entries.end(); ++otherEntry) {
417 if (entry->timestamp_begin_ns <= (*otherEntry)->timestamp_begin_ns) {
418 break;
419 }
420 }
421
422 /*
423 * Insert the entry only if a duplicate doesn't already exist.
424 *
425 * There can be duplicate packets if reading multiple overlapping
426 * snapshots of the same trace. We then want the index to contain
427 * a reference to only one copy of that packet.
428 */
429 if (otherEntry == index->entries.end() ||
430 !ds_index_entries_equal(entry.get(), otherEntry->get())) {
431 index->entries.insert(otherEntry, std::move(entry));
432 }
433 }
434
435 static void merge_ctf_fs_ds_indexes(struct ctf_fs_ds_index *dest, ctf_fs_ds_index::UP src)
436 {
437 for (ctf_fs_ds_index_entry::UP& entry : src->entries) {
438 ds_index_insert_ds_index_entry_sorted(dest, std::move(entry));
439 }
440 }
441
442 static int add_ds_file_to_ds_file_group(struct ctf_fs_trace *ctf_fs_trace, const char *path)
443 {
444 int64_t stream_instance_id = -1;
445 int64_t begin_ns = -1;
446 struct ctf_fs_ds_file_group *ds_file_group = NULL;
447 ctf_fs_ds_file_group::UP new_ds_file_group;
448 ctf_fs_ds_file_info::UP ds_file_info;
449 ctf_fs_ds_index::UP index;
450 ctf_msg_iter_up msg_iter;
451 struct ctf_stream_class *sc = NULL;
452 struct ctf_msg_iter_packet_properties props;
453 const bt2_common::LogCfg& logCfg = ctf_fs_trace->logCfg;
454
455 /*
456 * Create a temporary ds_file to read some properties about the data
457 * stream file.
458 */
459 ctf_fs_ds_file::UP ds_file = ctf_fs_ds_file_create(ctf_fs_trace, nonstd::nullopt, path, logCfg);
460 if (!ds_file) {
461 return -1;
462 }
463
464 /* Create a temporary iterator to read the ds_file. */
465 msg_iter = ctf_msg_iter_create(ctf_fs_trace->metadata->tc,
466 bt_common_get_page_size(logCfg.logLevel()) * 8,
467 ctf_fs_ds_file_medops, ds_file.get(), nullptr, logCfg);
468 if (!msg_iter) {
469 BT_CLOGE_STR("Cannot create a CTF message iterator.");
470 return -1;
471 }
472
473 ctf_msg_iter_set_dry_run(msg_iter.get(), true);
474
475 int ret = ctf_msg_iter_get_packet_properties(msg_iter.get(), &props);
476 if (ret) {
477 BT_CLOGE_APPEND_CAUSE(
478 "Cannot get stream file's first packet's header and context fields (`%s`).", path);
479 return ret;
480 }
481
482 sc = ctf_trace_class_borrow_stream_class_by_id(ds_file->metadata->tc, props.stream_class_id);
483 BT_ASSERT(sc);
484 stream_instance_id = props.data_stream_id;
485
486 if (props.snapshots.beginning_clock != UINT64_C(-1)) {
487 BT_ASSERT(sc->default_clock_class);
488 ret = bt_util_clock_cycles_to_ns_from_origin(
489 props.snapshots.beginning_clock, sc->default_clock_class->frequency,
490 sc->default_clock_class->offset_seconds, sc->default_clock_class->offset_cycles,
491 &begin_ns);
492 if (ret) {
493 BT_CLOGE_APPEND_CAUSE("Cannot convert clock cycles to nanoseconds from origin (`%s`).",
494 path);
495 return ret;
496 }
497 }
498
499 ds_file_info = ctf_fs_ds_file_info_create(path, begin_ns);
500 if (!ds_file_info) {
501 return -1;
502 }
503
504 index = ctf_fs_ds_file_build_index(ds_file.get(), ds_file_info.get(), msg_iter.get());
505 if (!index) {
506 BT_CLOGE_APPEND_CAUSE("Failed to index CTF stream file \'%s\'",
507 ds_file->file->path.c_str());
508 return -1;
509 }
510
511 if (begin_ns == -1) {
512 /*
513 * No beginning timestamp to sort the stream files
514 * within a stream file group, so consider that this
515 * file must be the only one within its group.
516 */
517 stream_instance_id = -1;
518 }
519
520 if (stream_instance_id == -1) {
521 /*
522 * No stream instance ID or no beginning timestamp:
523 * create a unique stream file group for this stream
524 * file because, even if there's a stream instance ID,
525 * there's no timestamp to order the file within its
526 * group.
527 */
528 new_ds_file_group =
529 ctf_fs_ds_file_group_create(ctf_fs_trace, sc, UINT64_C(-1), std::move(index));
530
531 if (!new_ds_file_group) {
532 return -1;
533 }
534
535 ds_file_group_insert_ds_file_info_sorted(new_ds_file_group.get(), std::move(ds_file_info));
536 ctf_fs_trace->ds_file_groups.emplace_back(std::move(new_ds_file_group));
537 return 0;
538 }
539
540 BT_ASSERT(stream_instance_id != -1);
541 BT_ASSERT(begin_ns != -1);
542
543 /* Find an existing stream file group with this ID */
544 for (ctf_fs_ds_file_group::UP& candidate : ctf_fs_trace->ds_file_groups) {
545 if (candidate->sc == sc && candidate->stream_id == stream_instance_id) {
546 ds_file_group = candidate.get();
547 break;
548 }
549 }
550
551 if (!ds_file_group) {
552 new_ds_file_group =
553 ctf_fs_ds_file_group_create(ctf_fs_trace, sc, stream_instance_id, std::move(index));
554 if (!new_ds_file_group) {
555 return -1;
556 }
557
558 ds_file_group = new_ds_file_group.get();
559 ctf_fs_trace->ds_file_groups.emplace_back(std::move(new_ds_file_group));
560 } else {
561 merge_ctf_fs_ds_indexes(ds_file_group->index.get(), std::move(index));
562 }
563
564 ds_file_group_insert_ds_file_info_sorted(ds_file_group, std::move(ds_file_info));
565
566 return 0;
567 }
568
569 static int create_ds_file_groups(struct ctf_fs_trace *ctf_fs_trace)
570 {
571 const char *basename;
572 GError *error = NULL;
573 const bt2_common::LogCfg& logCfg = ctf_fs_trace->logCfg;
574
575 /* Check each file in the path directory, except specific ones */
576 bt2_common::GDirUP dir {g_dir_open(ctf_fs_trace->path.c_str(), 0, &error)};
577 if (!dir) {
578 BT_CLOGE_APPEND_CAUSE("Cannot open directory `%s`: %s (code %d)",
579 ctf_fs_trace->path.c_str(), error->message, error->code);
580 if (error) {
581 g_error_free(error);
582 }
583 return -1;
584 }
585
586 while ((basename = g_dir_read_name(dir.get()))) {
587 if (strcmp(basename, CTF_FS_METADATA_FILENAME) == 0) {
588 /* Ignore the metadata stream. */
589 BT_CLOGI("Ignoring metadata file `%s" G_DIR_SEPARATOR_S "%s`",
590 ctf_fs_trace->path.c_str(), basename);
591 continue;
592 }
593
594 if (basename[0] == '.') {
595 BT_CLOGI("Ignoring hidden file `%s" G_DIR_SEPARATOR_S "%s`", ctf_fs_trace->path.c_str(),
596 basename);
597 continue;
598 }
599
600 /* Create the file. */
601 ctf_fs_file::UP file = bt2_common::makeUnique<ctf_fs_file>(logCfg);
602
603 /* Create full path string. */
604 file->path = ctf_fs_trace->path;
605 file->path += G_DIR_SEPARATOR;
606 file->path += basename;
607
608 if (!g_file_test(file->path.c_str(), G_FILE_TEST_IS_REGULAR)) {
609 BT_CLOGI("Ignoring non-regular file `%s`", file->path.c_str());
610 continue;
611 }
612
613 int ret = ctf_fs_file_open(file.get(), "rb");
614 if (ret) {
615 BT_CLOGE_APPEND_CAUSE("Cannot open stream file `%s`", file->path.c_str());
616 return ret;
617 }
618
619 if (file->size == 0) {
620 /* Skip empty stream. */
621 BT_CLOGI("Ignoring empty file `%s`", file->path.c_str());
622 continue;
623 }
624
625 ret = add_ds_file_to_ds_file_group(ctf_fs_trace, file->path.c_str());
626 if (ret) {
627 BT_CLOGE_APPEND_CAUSE("Cannot add stream file `%s` to stream file group",
628 file->path.c_str());
629 return ret;
630 }
631 }
632
633 return 0;
634 }
635
636 static int set_trace_name(bt_trace *trace, const char *name_suffix,
637 const bt2_common::LogCfg& logCfg)
638 {
639 const bt_value *val;
640 std::string name;
641
642 /*
643 * Check if we have a trace environment string value named `hostname`.
644 * If so, use it as the trace name's prefix.
645 */
646 val = bt_trace_borrow_environment_entry_value_by_name_const(trace, "hostname");
647 if (val && bt_value_is_string(val)) {
648 name += bt_value_string_get(val);
649
650 if (name_suffix) {
651 name += G_DIR_SEPARATOR;
652 }
653 }
654
655 if (name_suffix) {
656 name += name_suffix;
657 }
658
659 return bt_trace_set_name(trace, name.c_str());
660 }
661
662 static ctf_fs_trace::UP ctf_fs_trace_create(const char *path, const char *name,
663 ctf::src::ClkClsCfg clkClsCfg,
664 bt_self_component *selfComp,
665 const bt2_common::LogCfg& logCfg)
666 {
667 ctf_fs_trace::UP ctf_fs_trace = bt2_common::makeUnique<struct ctf_fs_trace>(logCfg);
668 ctf_fs_trace->path = path;
669 ctf_fs_trace->metadata = bt2_common::makeUnique<ctf_fs_metadata>();
670
671 int ret = ctf_fs_metadata_set_trace_class(ctf_fs_trace.get(), clkClsCfg, selfComp, logCfg);
672 if (ret) {
673 return nullptr;
674 }
675
676 if (ctf_fs_trace->metadata->trace_class) {
677 bt_trace *trace = bt_trace_create((*ctf_fs_trace->metadata->trace_class)->libObjPtr());
678 if (!trace) {
679 return nullptr;
680 }
681 ctf_fs_trace->trace = bt2::Trace::Shared::createWithoutRef(trace);
682 }
683
684 if (ctf_fs_trace->trace) {
685 ret = ctf_trace_class_configure_ir_trace(ctf_fs_trace->metadata->tc,
686 (*ctf_fs_trace->trace)->libObjPtr());
687 if (ret) {
688 return nullptr;
689 }
690
691 ret = set_trace_name((*ctf_fs_trace->trace)->libObjPtr(), name, logCfg);
692 if (ret) {
693 return nullptr;
694 }
695 }
696
697 ret = create_ds_file_groups(ctf_fs_trace.get());
698 if (ret) {
699 return nullptr;
700 }
701
702 return ctf_fs_trace;
703 }
704
705 static int path_is_ctf_trace(const char *path)
706 {
707 std::string metadata_path = path;
708 metadata_path += G_DIR_SEPARATOR;
709 metadata_path += CTF_FS_METADATA_FILENAME;
710
711 return g_file_test(metadata_path.c_str(), G_FILE_TEST_IS_REGULAR);
712 }
713
714 /* Helper for ctf_fs_component_create_ctf_fs_trace, to handle a single path. */
715
716 static int ctf_fs_component_create_ctf_fs_trace_one_path(struct ctf_fs_component *ctf_fs,
717 const char *path_param,
718 const char *trace_name,
719 std::vector<ctf_fs_trace::UP>& traces,
720 bt_self_component *selfComp)
721 {
722 ctf_fs_trace::UP ctf_fs_trace;
723 int ret;
724 const bt2_common::LogCfg& logCfg = ctf_fs->logCfg;
725
726 bt2_common::GStringUP norm_path {bt_common_normalize_path(path_param, NULL)};
727 if (!norm_path) {
728 BT_CLOGE_APPEND_CAUSE("Failed to normalize path: `%s`.", path_param);
729 return -1;
730 }
731
732 ret = path_is_ctf_trace(norm_path->str);
733 if (ret < 0) {
734 BT_CLOGE_APPEND_CAUSE("Failed to check if path is a CTF trace: path=%s", norm_path->str);
735 return ret;
736 } else if (ret == 0) {
737 BT_CLOGE_APPEND_CAUSE("Path is not a CTF trace (does not contain a metadata file): `%s`.",
738 norm_path->str);
739 return -1;
740 }
741
742 // FIXME: Remove or ifdef for __MINGW32__
743 if (strcmp(norm_path->str, "/") == 0) {
744 BT_CLOGE_APPEND_CAUSE("Opening a trace in `/` is not supported.");
745 return -1;
746 }
747
748 ctf_fs_trace =
749 ctf_fs_trace_create(norm_path->str, trace_name, ctf_fs->clkClsCfg, selfComp, logCfg);
750 if (!ctf_fs_trace) {
751 BT_CLOGE_APPEND_CAUSE("Cannot create trace for `%s`.", norm_path->str);
752 return -1;
753 }
754
755 traces.emplace_back(std::move(ctf_fs_trace));
756
757 return 0;
758 }
759
760 /*
761 * Count the number of stream and event classes defined by this trace's metadata.
762 *
763 * This is used to determine which metadata is the "latest", out of multiple
764 * traces sharing the same UUID. It is assumed that amongst all these metadatas,
765 * a bigger metadata is a superset of a smaller metadata. Therefore, it is
766 * enough to just count the classes.
767 */
768
769 static unsigned int metadata_count_stream_and_event_classes(struct ctf_fs_trace *trace)
770 {
771 unsigned int num = trace->metadata->tc->stream_classes->len;
772 guint i;
773
774 for (i = 0; i < trace->metadata->tc->stream_classes->len; i++) {
775 struct ctf_stream_class *sc =
776 (struct ctf_stream_class *) trace->metadata->tc->stream_classes->pdata[i];
777 num += sc->event_classes->len;
778 }
779
780 return num;
781 }
782
783 /*
784 * Merge the src ds_file_group into dest. This consists of merging their
785 * ds_file_infos, making sure to keep the result sorted.
786 */
787
788 static void merge_ctf_fs_ds_file_groups(struct ctf_fs_ds_file_group *dest,
789 ctf_fs_ds_file_group::UP src)
790 {
791 for (ctf_fs_ds_file_info::UP& ds_file_info : src->ds_file_infos) {
792 ds_file_group_insert_ds_file_info_sorted(dest, std::move(ds_file_info));
793 }
794
795 /* Merge both indexes. */
796 merge_ctf_fs_ds_indexes(dest->index.get(), std::move(src->index));
797 }
798
799 /* Merge src_trace's data stream file groups into dest_trace's. */
800
801 static int merge_matching_ctf_fs_ds_file_groups(struct ctf_fs_trace *dest_trace,
802 ctf_fs_trace::UP src_trace)
803 {
804 std::vector<ctf_fs_ds_file_group::UP>& dest = dest_trace->ds_file_groups;
805 std::vector<ctf_fs_ds_file_group::UP>& src = src_trace->ds_file_groups;
806
807 /*
808 * Save the initial length of dest: we only want to check against the
809 * original elements in the inner loop.
810 */
811 size_t dest_len = dest.size();
812
813 for (ctf_fs_ds_file_group::UP& src_group : src) {
814 struct ctf_fs_ds_file_group *dest_group = NULL;
815
816 /* A stream instance without ID can't match a stream in the other trace. */
817 if (src_group->stream_id != -1) {
818 /* Let's search for a matching ds_file_group in the destination. */
819 for (size_t d_i = 0; d_i < dest_len; ++d_i) {
820 ctf_fs_ds_file_group *candidate_dest = dest[d_i].get();
821
822 /* Can't match a stream instance without ID. */
823 if (candidate_dest->stream_id == -1) {
824 continue;
825 }
826
827 /*
828 * If the two groups have the same stream instance id
829 * and belong to the same stream class (stream instance
830 * ids are per-stream class), they represent the same
831 * stream instance.
832 */
833 if (candidate_dest->stream_id != src_group->stream_id ||
834 candidate_dest->sc->id != src_group->sc->id) {
835 continue;
836 }
837
838 dest_group = candidate_dest;
839 break;
840 }
841 }
842
843 /*
844 * Didn't find a friend in dest to merge our src_group into?
845 * Create a new empty one. This can happen if a stream was
846 * active in the source trace chunk but not in the destination
847 * trace chunk.
848 */
849 if (!dest_group) {
850 struct ctf_stream_class *sc;
851
852 sc = ctf_trace_class_borrow_stream_class_by_id(dest_trace->metadata->tc,
853 src_group->sc->id);
854 BT_ASSERT(sc);
855
856 ctf_fs_ds_index::UP index = bt2_common::makeUnique<ctf_fs_ds_index>();
857 ctf_fs_ds_file_group::UP new_dest_group =
858 ctf_fs_ds_file_group_create(dest_trace, sc, src_group->stream_id, std::move(index));
859 /* Ownership of index is transferred. */
860 index = NULL;
861 if (!new_dest_group) {
862 return -1;
863 }
864
865 dest_group = new_dest_group.get();
866 dest_trace->ds_file_groups.emplace_back(std::move(new_dest_group));
867 }
868
869 BT_ASSERT(dest_group);
870 merge_ctf_fs_ds_file_groups(dest_group, std::move(src_group));
871 }
872
873 return 0;
874 }
875
876 /*
877 * Collapse the given traces, which must all share the same UUID, in a single
878 * one.
879 *
880 * The trace with the most expansive metadata is chosen and all other traces
881 * are merged into that one. On return, the elements of `traces` are nullptr
882 * and the merged trace is placed in `out_trace`.
883 */
884
885 static int merge_ctf_fs_traces(std::vector<ctf_fs_trace::UP> traces, ctf_fs_trace::UP& out_trace)
886 {
887 unsigned int winner_count;
888 struct ctf_fs_trace *winner;
889 guint i, winner_i;
890
891 BT_ASSERT(traces.size() >= 2);
892
893 winner_count = metadata_count_stream_and_event_classes(traces[0].get());
894 winner = traces[0].get();
895 winner_i = 0;
896
897 /* Find the trace with the largest metadata. */
898 for (i = 1; i < traces.size(); i++) {
899 ctf_fs_trace *candidate = traces[i].get();
900 unsigned int candidate_count;
901
902 /* A bit of sanity check. */
903 BT_ASSERT(bt_uuid_compare(winner->metadata->tc->uuid, candidate->metadata->tc->uuid) == 0);
904
905 candidate_count = metadata_count_stream_and_event_classes(candidate);
906
907 if (candidate_count > winner_count) {
908 winner_count = candidate_count;
909 winner = candidate;
910 winner_i = i;
911 }
912 }
913
914 /* Merge all the other traces in the winning trace. */
915 for (ctf_fs_trace::UP& trace : traces) {
916 /* Don't merge the winner into itself. */
917 if (trace.get() == winner) {
918 continue;
919 }
920
921 /* Merge trace's data stream file groups into winner's. */
922 int ret = merge_matching_ctf_fs_ds_file_groups(winner, std::move(trace));
923 if (ret) {
924 return ret;
925 }
926 }
927
928 /*
929 * Move the winner out of the array, into `*out_trace`.
930 */
931 out_trace = std::move(traces[winner_i]);
932
933 return 0;
934 }
935
936 enum target_event
937 {
938 FIRST_EVENT,
939 LAST_EVENT,
940 };
941
942 static int decode_clock_snapshot_after_event(struct ctf_fs_trace *ctf_fs_trace,
943 struct ctf_clock_class *default_cc,
944 struct ctf_fs_ds_index_entry *index_entry,
945 enum target_event target_event, uint64_t *cs,
946 int64_t *ts_ns)
947 {
948 enum ctf_msg_iter_status iter_status = CTF_MSG_ITER_STATUS_OK;
949 ctf_msg_iter_up msg_iter;
950 const bt2_common::LogCfg& logCfg = ctf_fs_trace->logCfg;
951
952 BT_ASSERT(ctf_fs_trace);
953 BT_ASSERT(index_entry);
954 BT_ASSERT(index_entry->path);
955
956 ctf_fs_ds_file::UP ds_file =
957 ctf_fs_ds_file_create(ctf_fs_trace, nonstd::nullopt, index_entry->path, logCfg);
958 if (!ds_file) {
959 BT_CLOGE_APPEND_CAUSE("Failed to create a ctf_fs_ds_file");
960 return -1;
961 }
962
963 BT_ASSERT(ctf_fs_trace->metadata);
964 BT_ASSERT(ctf_fs_trace->metadata->tc);
965
966 msg_iter = ctf_msg_iter_create(ctf_fs_trace->metadata->tc,
967 bt_common_get_page_size(logCfg.logLevel()) * 8,
968
969 ctf_fs_ds_file_medops, ds_file.get(), NULL, logCfg);
970 if (!msg_iter) {
971 /* ctf_msg_iter_create() logs errors. */
972 return -1;
973 }
974
975 /*
976 * Turn on dry run mode to prevent the creation and usage of Babeltrace
977 * library objects (bt_field, bt_message_*, etc.).
978 */
979 ctf_msg_iter_set_dry_run(msg_iter.get(), true);
980
981 /* Seek to the beginning of the target packet. */
982 iter_status = ctf_msg_iter_seek(msg_iter.get(), index_entry->offset.bytes());
983 if (iter_status) {
984 /* ctf_msg_iter_seek() logs errors. */
985 return -1;
986 }
987
988 switch (target_event) {
989 case FIRST_EVENT:
990 /*
991 * Start to decode the packet until we reach the end of
992 * the first event. To extract the first event's clock
993 * snapshot.
994 */
995 iter_status = ctf_msg_iter_curr_packet_first_event_clock_snapshot(msg_iter.get(), cs);
996 break;
997 case LAST_EVENT:
998 /* Decode the packet to extract the last event's clock snapshot. */
999 iter_status = ctf_msg_iter_curr_packet_last_event_clock_snapshot(msg_iter.get(), cs);
1000 break;
1001 default:
1002 bt_common_abort();
1003 }
1004 if (iter_status) {
1005 return -1;
1006 }
1007
1008 /* Convert clock snapshot to timestamp. */
1009 int ret = bt_util_clock_cycles_to_ns_from_origin(
1010 *cs, default_cc->frequency, default_cc->offset_seconds, default_cc->offset_cycles, ts_ns);
1011 if (ret) {
1012 BT_CLOGE_APPEND_CAUSE("Failed to convert clock snapshot to timestamp");
1013 return ret;
1014 }
1015
1016 return 0;
1017 }
1018
1019 static int decode_packet_first_event_timestamp(struct ctf_fs_trace *ctf_fs_trace,
1020 struct ctf_clock_class *default_cc,
1021 struct ctf_fs_ds_index_entry *index_entry,
1022 uint64_t *cs, int64_t *ts_ns)
1023 {
1024 return decode_clock_snapshot_after_event(ctf_fs_trace, default_cc, index_entry, FIRST_EVENT, cs,
1025 ts_ns);
1026 }
1027
1028 static int decode_packet_last_event_timestamp(struct ctf_fs_trace *ctf_fs_trace,
1029 struct ctf_clock_class *default_cc,
1030 struct ctf_fs_ds_index_entry *index_entry,
1031 uint64_t *cs, int64_t *ts_ns)
1032 {
1033 return decode_clock_snapshot_after_event(ctf_fs_trace, default_cc, index_entry, LAST_EVENT, cs,
1034 ts_ns);
1035 }
1036
1037 /*
1038 * Fix up packet index entries for lttng's "event-after-packet" bug.
1039 * Some buggy lttng tracer versions may emit events with a timestamp that is
1040 * larger (after) than the timestamp_end of the their packets.
1041 *
1042 * To fix up this erroneous data we do the following:
1043 * 1. If it's not the stream file's last packet: set the packet index entry's
1044 * end time to the next packet's beginning time.
1045 * 2. If it's the stream file's last packet, set the packet index entry's end
1046 * time to the packet's last event's time, if any, or to the packet's
1047 * beginning time otherwise.
1048 *
1049 * Known buggy tracer versions:
1050 * - before lttng-ust 2.11.0
1051 * - before lttng-module 2.11.0
1052 * - before lttng-module 2.10.10
1053 * - before lttng-module 2.9.13
1054 */
1055 static int fix_index_lttng_event_after_packet_bug(struct ctf_fs_trace *trace)
1056 {
1057 const bt2_common::LogCfg& logCfg = trace->logCfg;
1058
1059 for (ctf_fs_ds_file_group::UP& ds_file_group : trace->ds_file_groups) {
1060 struct ctf_clock_class *default_cc;
1061 struct ctf_fs_ds_index *index;
1062
1063 BT_ASSERT(ds_file_group);
1064 index = ds_file_group->index.get();
1065
1066 BT_ASSERT(index);
1067 BT_ASSERT(!index->entries.empty());
1068
1069 /*
1070 * Iterate over all entries but the last one. The last one is
1071 * fixed differently after.
1072 */
1073 for (size_t entry_i = 0; entry_i < index->entries.size() - 1; ++entry_i) {
1074 ctf_fs_ds_index_entry *curr_entry = index->entries[entry_i].get();
1075 ctf_fs_ds_index_entry *next_entry = index->entries[entry_i + 1].get();
1076
1077 /*
1078 * 1. Set the current index entry `end` timestamp to
1079 * the next index entry `begin` timestamp.
1080 */
1081 curr_entry->timestamp_end = next_entry->timestamp_begin;
1082 curr_entry->timestamp_end_ns = next_entry->timestamp_begin_ns;
1083 }
1084
1085 /*
1086 * 2. Fix the last entry by decoding the last event of the last
1087 * packet.
1088 */
1089 ctf_fs_ds_index_entry *last_entry = index->entries.back().get();
1090 BT_ASSERT(last_entry);
1091
1092 BT_ASSERT(ds_file_group->sc->default_clock_class);
1093 default_cc = ds_file_group->sc->default_clock_class;
1094
1095 /*
1096 * Decode packet to read the timestamp of the last event of the
1097 * entry.
1098 */
1099 int ret = decode_packet_last_event_timestamp(trace, default_cc, last_entry,
1100 &last_entry->timestamp_end,
1101 &last_entry->timestamp_end_ns);
1102 if (ret) {
1103 BT_CLOGE_APPEND_CAUSE(
1104 "Failed to decode stream's last packet to get its last event's clock snapshot.");
1105 return ret;
1106 }
1107 }
1108
1109 return 0;
1110 }
1111
1112 /*
1113 * Fix up packet index entries for barectf's "event-before-packet" bug.
1114 * Some buggy barectf tracer versions may emit events with a timestamp that is
1115 * less than the timestamp_begin of the their packets.
1116 *
1117 * To fix up this erroneous data we do the following:
1118 * 1. Starting at the second index entry, set the timestamp_begin of the
1119 * current entry to the timestamp of the first event of the packet.
1120 * 2. Set the previous entry's timestamp_end to the timestamp_begin of the
1121 * current packet.
1122 *
1123 * Known buggy tracer versions:
1124 * - before barectf 2.3.1
1125 */
1126 static int fix_index_barectf_event_before_packet_bug(struct ctf_fs_trace *trace)
1127 {
1128 const bt2_common::LogCfg& logCfg = trace->logCfg;
1129
1130 for (ctf_fs_ds_file_group::UP& ds_file_group : trace->ds_file_groups) {
1131 struct ctf_clock_class *default_cc;
1132 struct ctf_fs_ds_index *index = ds_file_group->index.get();
1133
1134 BT_ASSERT(index);
1135 BT_ASSERT(!index->entries.empty());
1136
1137 BT_ASSERT(ds_file_group->sc->default_clock_class);
1138 default_cc = ds_file_group->sc->default_clock_class;
1139
1140 /*
1141 * 1. Iterate over the index, starting from the second entry
1142 * (index = 1).
1143 */
1144 for (size_t entry_i = 1; entry_i < index->entries.size(); ++entry_i) {
1145 ctf_fs_ds_index_entry *prev_entry = index->entries[entry_i - 1].get();
1146 ctf_fs_ds_index_entry *curr_entry = index->entries[entry_i].get();
1147 /*
1148 * 2. Set the current entry `begin` timestamp to the
1149 * timestamp of the first event of the current packet.
1150 */
1151 int ret = decode_packet_first_event_timestamp(trace, default_cc, curr_entry,
1152 &curr_entry->timestamp_begin,
1153 &curr_entry->timestamp_begin_ns);
1154 if (ret) {
1155 BT_CLOGE_APPEND_CAUSE("Failed to decode first event's clock snapshot");
1156 return ret;
1157 }
1158
1159 /*
1160 * 3. Set the previous entry `end` timestamp to the
1161 * timestamp of the first event of the current packet.
1162 */
1163 prev_entry->timestamp_end = curr_entry->timestamp_begin;
1164 prev_entry->timestamp_end_ns = curr_entry->timestamp_begin_ns;
1165 }
1166 }
1167
1168 return 0;
1169 }
1170
1171 /*
1172 * When using the lttng-crash feature it's likely that the last packets of each
1173 * stream have their timestamp_end set to zero. This is caused by the fact that
1174 * the tracer crashed and was not able to properly close the packets.
1175 *
1176 * To fix up this erroneous data we do the following:
1177 * For each index entry, if the entry's timestamp_end is 0 and the
1178 * timestamp_begin is not 0:
1179 * - If it's the stream file's last packet: set the packet index entry's end
1180 * time to the packet's last event's time, if any, or to the packet's
1181 * beginning time otherwise.
1182 * - If it's not the stream file's last packet: set the packet index
1183 * entry's end time to the next packet's beginning time.
1184 *
1185 * Affected versions:
1186 * - All current and future lttng-ust and lttng-modules versions.
1187 */
1188 static int fix_index_lttng_crash_quirk(struct ctf_fs_trace *trace)
1189 {
1190 const bt2_common::LogCfg& logCfg = trace->logCfg;
1191
1192 for (ctf_fs_ds_file_group::UP& ds_file_group : trace->ds_file_groups) {
1193 struct ctf_clock_class *default_cc;
1194 struct ctf_fs_ds_index *index;
1195
1196 BT_ASSERT(ds_file_group);
1197 index = ds_file_group->index.get();
1198
1199 BT_ASSERT(ds_file_group->sc->default_clock_class);
1200 default_cc = ds_file_group->sc->default_clock_class;
1201
1202 BT_ASSERT(index);
1203 BT_ASSERT(!index->entries.empty());
1204
1205 ctf_fs_ds_index_entry *last_entry = index->entries.back().get();
1206 BT_ASSERT(last_entry);
1207
1208 /* 1. Fix the last entry first. */
1209 if (last_entry->timestamp_end == 0 && last_entry->timestamp_begin != 0) {
1210 /*
1211 * Decode packet to read the timestamp of the
1212 * last event of the stream file.
1213 */
1214 int ret = decode_packet_last_event_timestamp(trace, default_cc, last_entry,
1215 &last_entry->timestamp_end,
1216 &last_entry->timestamp_end_ns);
1217 if (ret) {
1218 BT_CLOGE_APPEND_CAUSE("Failed to decode last event's clock snapshot");
1219 return ret;
1220 }
1221 }
1222
1223 /* Iterate over all entries but the last one. */
1224 for (size_t entry_idx = 0; entry_idx < index->entries.size() - 1; ++entry_idx) {
1225 ctf_fs_ds_index_entry *curr_entry = index->entries[entry_idx].get();
1226 ctf_fs_ds_index_entry *next_entry = index->entries[entry_idx + 1].get();
1227
1228 if (curr_entry->timestamp_end == 0 && curr_entry->timestamp_begin != 0) {
1229 /*
1230 * 2. Set the current index entry `end` timestamp to
1231 * the next index entry `begin` timestamp.
1232 */
1233 curr_entry->timestamp_end = next_entry->timestamp_begin;
1234 curr_entry->timestamp_end_ns = next_entry->timestamp_begin_ns;
1235 }
1236 }
1237 }
1238
1239 return 0;
1240 }
1241
1242 /*
1243 * Extract the tracer information necessary to compare versions.
1244 * Returns 0 on success, and -1 if the extraction is not successful because the
1245 * necessary fields are absents in the trace metadata.
1246 */
1247 static int extract_tracer_info(struct ctf_fs_trace *trace, struct tracer_info *current_tracer_info)
1248 {
1249 struct ctf_trace_class_env_entry *entry;
1250
1251 /* Clear the current_tracer_info struct */
1252 memset(current_tracer_info, 0, sizeof(*current_tracer_info));
1253
1254 /*
1255 * To compare 2 tracer versions, at least the tracer name and it's
1256 * major version are needed. If one of these is missing, consider it an
1257 * extraction failure.
1258 */
1259 entry = ctf_trace_class_borrow_env_entry_by_name(trace->metadata->tc, "tracer_name");
1260 if (!entry || entry->type != CTF_TRACE_CLASS_ENV_ENTRY_TYPE_STR) {
1261 return -1;
1262 }
1263
1264 /* Set tracer name. */
1265 current_tracer_info->name = entry->value.str->str;
1266
1267 entry = ctf_trace_class_borrow_env_entry_by_name(trace->metadata->tc, "tracer_major");
1268 if (!entry || entry->type != CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT) {
1269 return -1;
1270 }
1271
1272 /* Set major version number. */
1273 current_tracer_info->major = entry->value.i;
1274
1275 entry = ctf_trace_class_borrow_env_entry_by_name(trace->metadata->tc, "tracer_minor");
1276 if (!entry || entry->type != CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT) {
1277 return 0;
1278 }
1279
1280 /* Set minor version number. */
1281 current_tracer_info->minor = entry->value.i;
1282
1283 entry = ctf_trace_class_borrow_env_entry_by_name(trace->metadata->tc, "tracer_patch");
1284 if (!entry) {
1285 /*
1286 * If `tracer_patch` doesn't exist `tracer_patchlevel` might.
1287 * For example, `lttng-modules` uses entry name
1288 * `tracer_patchlevel`.
1289 */
1290 entry = ctf_trace_class_borrow_env_entry_by_name(trace->metadata->tc, "tracer_patchlevel");
1291 }
1292
1293 if (!entry || entry->type != CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT) {
1294 return 0;
1295 }
1296
1297 /* Set patch version number. */
1298 current_tracer_info->patch = entry->value.i;
1299
1300 return 0;
1301 }
1302
1303 static bool is_tracer_affected_by_lttng_event_after_packet_bug(struct tracer_info *curr_tracer_info)
1304 {
1305 bool is_affected = false;
1306
1307 if (strcmp(curr_tracer_info->name, "lttng-ust") == 0) {
1308 if (curr_tracer_info->major < 2) {
1309 is_affected = true;
1310 } else if (curr_tracer_info->major == 2) {
1311 /* fixed in lttng-ust 2.11.0 */
1312 if (curr_tracer_info->minor < 11) {
1313 is_affected = true;
1314 }
1315 }
1316 } else if (strcmp(curr_tracer_info->name, "lttng-modules") == 0) {
1317 if (curr_tracer_info->major < 2) {
1318 is_affected = true;
1319 } else if (curr_tracer_info->major == 2) {
1320 /* fixed in lttng-modules 2.11.0 */
1321 if (curr_tracer_info->minor == 10) {
1322 /* fixed in lttng-modules 2.10.10 */
1323 if (curr_tracer_info->patch < 10) {
1324 is_affected = true;
1325 }
1326 } else if (curr_tracer_info->minor == 9) {
1327 /* fixed in lttng-modules 2.9.13 */
1328 if (curr_tracer_info->patch < 13) {
1329 is_affected = true;
1330 }
1331 } else if (curr_tracer_info->minor < 9) {
1332 is_affected = true;
1333 }
1334 }
1335 }
1336
1337 return is_affected;
1338 }
1339
1340 static bool
1341 is_tracer_affected_by_barectf_event_before_packet_bug(struct tracer_info *curr_tracer_info)
1342 {
1343 bool is_affected = false;
1344
1345 if (strcmp(curr_tracer_info->name, "barectf") == 0) {
1346 if (curr_tracer_info->major < 2) {
1347 is_affected = true;
1348 } else if (curr_tracer_info->major == 2) {
1349 if (curr_tracer_info->minor < 3) {
1350 is_affected = true;
1351 } else if (curr_tracer_info->minor == 3) {
1352 /* fixed in barectf 2.3.1 */
1353 if (curr_tracer_info->patch < 1) {
1354 is_affected = true;
1355 }
1356 }
1357 }
1358 }
1359
1360 return is_affected;
1361 }
1362
1363 static bool is_tracer_affected_by_lttng_crash_quirk(struct tracer_info *curr_tracer_info)
1364 {
1365 bool is_affected = false;
1366
1367 /* All LTTng tracer may be affected by this lttng crash quirk. */
1368 if (strcmp(curr_tracer_info->name, "lttng-ust") == 0) {
1369 is_affected = true;
1370 } else if (strcmp(curr_tracer_info->name, "lttng-modules") == 0) {
1371 is_affected = true;
1372 }
1373
1374 return is_affected;
1375 }
1376
1377 /*
1378 * Looks for trace produced by known buggy tracers and fix up the index
1379 * produced earlier.
1380 */
1381 static int fix_packet_index_tracer_bugs(struct ctf_fs_component *ctf_fs)
1382 {
1383 struct tracer_info current_tracer_info;
1384 const bt2_common::LogCfg& logCfg = ctf_fs->logCfg;
1385
1386 int ret = extract_tracer_info(ctf_fs->trace.get(), &current_tracer_info);
1387 if (ret) {
1388 /*
1389 * A trace may not have all the necessary environment
1390 * entries to do the tracer version comparison.
1391 * At least, the tracer name and major version number
1392 * are needed. Failing to extract these entries is not
1393 * an error.
1394 */
1395 BT_LOGI_STR("Cannot extract tracer information necessary to compare with buggy versions.");
1396 return 0;
1397 }
1398
1399 /* Check if the trace may be affected by old tracer bugs. */
1400 if (is_tracer_affected_by_lttng_event_after_packet_bug(&current_tracer_info)) {
1401 BT_LOGI_STR("Trace may be affected by LTTng tracer packet timestamp bug. Fixing up.");
1402 ret = fix_index_lttng_event_after_packet_bug(ctf_fs->trace.get());
1403 if (ret) {
1404 BT_CLOGE_APPEND_CAUSE("Failed to fix LTTng event-after-packet bug.");
1405 return ret;
1406 }
1407 ctf_fs->trace->metadata->tc->quirks.lttng_event_after_packet = true;
1408 }
1409
1410 if (is_tracer_affected_by_barectf_event_before_packet_bug(&current_tracer_info)) {
1411 BT_LOGI_STR("Trace may be affected by barectf tracer packet timestamp bug. Fixing up.");
1412 ret = fix_index_barectf_event_before_packet_bug(ctf_fs->trace.get());
1413 if (ret) {
1414 BT_CLOGE_APPEND_CAUSE("Failed to fix barectf event-before-packet bug.");
1415 return ret;
1416 }
1417 ctf_fs->trace->metadata->tc->quirks.barectf_event_before_packet = true;
1418 }
1419
1420 if (is_tracer_affected_by_lttng_crash_quirk(&current_tracer_info)) {
1421 ret = fix_index_lttng_crash_quirk(ctf_fs->trace.get());
1422 if (ret) {
1423 BT_CLOGE_APPEND_CAUSE("Failed to fix lttng-crash timestamp quirks.");
1424 return ret;
1425 }
1426 ctf_fs->trace->metadata->tc->quirks.lttng_crash = true;
1427 }
1428
1429 return 0;
1430 }
1431
1432 static bool compare_ds_file_groups_by_first_path(const ctf_fs_ds_file_group::UP& ds_file_group_a,
1433 const ctf_fs_ds_file_group::UP& ds_file_group_b)
1434 {
1435 BT_ASSERT(!ds_file_group_a->ds_file_infos.empty());
1436 BT_ASSERT(!ds_file_group_b->ds_file_infos.empty());
1437
1438 const ctf_fs_ds_file_info& first_ds_file_info_a = *ds_file_group_a->ds_file_infos[0];
1439 const ctf_fs_ds_file_info& first_ds_file_info_b = *ds_file_group_b->ds_file_infos[0];
1440
1441 return first_ds_file_info_a.path < first_ds_file_info_b.path;
1442 }
1443
1444 int ctf_fs_component_create_ctf_fs_trace(struct ctf_fs_component *ctf_fs,
1445 const bt_value *paths_value,
1446 const bt_value *trace_name_value,
1447 bt_self_component *selfComp)
1448 {
1449 const bt2_common::LogCfg& logCfg = ctf_fs->logCfg;
1450 std::vector<std::string> paths;
1451 std::vector<ctf_fs_trace::UP> traces;
1452 const char *trace_name;
1453
1454 BT_ASSERT(bt_value_get_type(paths_value) == BT_VALUE_TYPE_ARRAY);
1455 BT_ASSERT(!bt_value_array_is_empty(paths_value));
1456
1457 trace_name = trace_name_value ? bt_value_string_get(trace_name_value) : NULL;
1458
1459 /*
1460 * Create a sorted array of the paths, to make the execution of this
1461 * component deterministic.
1462 */
1463 for (std::uint64_t i = 0; i < bt_value_array_get_length(paths_value); i++) {
1464 const bt_value *path_value = bt_value_array_borrow_element_by_index_const(paths_value, i);
1465 const char *input = bt_value_string_get(path_value);
1466 paths.emplace_back(input);
1467 }
1468
1469 std::sort(paths.begin(), paths.end());
1470
1471 /* Create a separate ctf_fs_trace object for each path. */
1472 for (const std::string& path : paths) {
1473 int ret = ctf_fs_component_create_ctf_fs_trace_one_path(ctf_fs, path.c_str(), trace_name,
1474 traces, selfComp);
1475 if (ret) {
1476 return ret;
1477 }
1478 }
1479
1480 if (traces.size() > 1) {
1481 ctf_fs_trace *first_trace = traces[0].get();
1482 const uint8_t *first_trace_uuid = first_trace->metadata->tc->uuid;
1483
1484 /*
1485 * We have more than one trace, they must all share the same
1486 * UUID, verify that.
1487 */
1488 for (size_t i = 0; i < traces.size(); i++) {
1489 ctf_fs_trace *this_trace = traces[i].get();
1490 const uint8_t *this_trace_uuid = this_trace->metadata->tc->uuid;
1491
1492 if (!this_trace->metadata->tc->is_uuid_set) {
1493 BT_CLOGE_APPEND_CAUSE(
1494 "Multiple traces given, but a trace does not have a UUID: path=%s",
1495 this_trace->path.c_str());
1496 return -1;
1497 }
1498
1499 if (bt_uuid_compare(first_trace_uuid, this_trace_uuid) != 0) {
1500 char first_trace_uuid_str[BT_UUID_STR_LEN + 1];
1501 char this_trace_uuid_str[BT_UUID_STR_LEN + 1];
1502
1503 bt_uuid_to_str(first_trace_uuid, first_trace_uuid_str);
1504 bt_uuid_to_str(this_trace_uuid, this_trace_uuid_str);
1505
1506 BT_CLOGE_APPEND_CAUSE("Multiple traces given, but UUIDs don't match: "
1507 "first-trace-uuid=%s, first-trace-path=%s, "
1508 "trace-uuid=%s, trace-path=%s",
1509 first_trace_uuid_str, first_trace->path.c_str(),
1510 this_trace_uuid_str, this_trace->path.c_str());
1511 return -1;
1512 }
1513 }
1514
1515 int ret = merge_ctf_fs_traces(std::move(traces), ctf_fs->trace);
1516 if (ret) {
1517 BT_CLOGE_APPEND_CAUSE("Failed to merge traces with the same UUID.");
1518 return ret;
1519 }
1520 } else {
1521 /* Just one trace, it may or may not have a UUID, both are fine. */
1522 ctf_fs->trace = std::move(traces[0]);
1523 }
1524
1525 int ret = fix_packet_index_tracer_bugs(ctf_fs);
1526 if (ret) {
1527 BT_CLOGE_APPEND_CAUSE("Failed to fix packet index tracer bugs.");
1528 return ret;
1529 }
1530
1531 /*
1532 * Sort data stream file groups by first data stream file info
1533 * path to get a deterministic order. This order influences the
1534 * order of the output ports. It also influences the order of
1535 * the automatic stream IDs if the trace's packet headers do not
1536 * contain a `stream_instance_id` field, in which case the data
1537 * stream file to stream ID association is always the same,
1538 * whatever the build and the system.
1539 *
1540 * Having a deterministic order here can help debugging and
1541 * testing.
1542 */
1543 std::sort(ctf_fs->trace->ds_file_groups.begin(), ctf_fs->trace->ds_file_groups.end(),
1544 compare_ds_file_groups_by_first_path);
1545
1546 return 0;
1547 }
1548
1549 static const std::string&
1550 get_stream_instance_unique_name(struct ctf_fs_ds_file_group *ds_file_group)
1551 {
1552 /*
1553 * The first (earliest) stream file's path is used as the stream's unique
1554 * name.
1555 */
1556 BT_ASSERT(!ds_file_group->ds_file_infos.empty());
1557 ctf_fs_ds_file_info *ds_file_info = ds_file_group->ds_file_infos[0].get();
1558 return ds_file_info->path;
1559 }
1560
1561 /* Create the IR stream objects for ctf_fs_trace. */
1562
1563 static int create_streams_for_trace(struct ctf_fs_trace *ctf_fs_trace)
1564 {
1565 const bt2_common::LogCfg& logCfg = ctf_fs_trace->logCfg;
1566
1567 for (ctf_fs_ds_file_group::UP& ds_file_group : ctf_fs_trace->ds_file_groups) {
1568 const std::string& name = get_stream_instance_unique_name(ds_file_group.get());
1569
1570 BT_ASSERT(ds_file_group->sc->ir_sc);
1571 BT_ASSERT(ctf_fs_trace->trace);
1572
1573 bt_stream *stream;
1574
1575 if (ds_file_group->stream_id == UINT64_C(-1)) {
1576 /* No stream ID: use 0 */
1577 stream = bt_stream_create_with_id(ds_file_group->sc->ir_sc,
1578 (*ctf_fs_trace->trace)->libObjPtr(),
1579 ctf_fs_trace->next_stream_id);
1580 ctf_fs_trace->next_stream_id++;
1581 } else {
1582 /* Specific stream ID */
1583 stream = bt_stream_create_with_id(ds_file_group->sc->ir_sc,
1584 (*ctf_fs_trace->trace)->libObjPtr(),
1585 (uint64_t) ds_file_group->stream_id);
1586 }
1587
1588 if (!stream) {
1589 BT_CLOGE_APPEND_CAUSE("Cannot create stream for DS file group: "
1590 "addr=%p, stream-name=\"%s\"",
1591 ds_file_group.get(), name.c_str());
1592 return -1;
1593 }
1594
1595 ds_file_group->stream = bt2::Stream::Shared::createWithoutRef(stream);
1596
1597 int ret = bt_stream_set_name((*ds_file_group->stream)->libObjPtr(), name.c_str());
1598 if (ret) {
1599 BT_CLOGE_APPEND_CAUSE("Cannot set stream's name: "
1600 "addr=%p, stream-name=\"%s\"",
1601 (*ds_file_group->stream)->libObjPtr(), name.c_str());
1602 return ret;
1603 }
1604 }
1605
1606 return 0;
1607 }
1608
1609 static const bt_param_validation_value_descr inputs_elem_descr =
1610 bt_param_validation_value_descr::makeString();
1611
1612 static bt_param_validation_map_value_entry_descr fs_params_entries_descr[] = {
1613 {"inputs", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY,
1614 bt_param_validation_value_descr::makeArray(1, BT_PARAM_VALIDATION_INFINITE,
1615 inputs_elem_descr)},
1616 {"trace-name", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL,
1617 bt_param_validation_value_descr::makeString()},
1618 {"clock-class-offset-s", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL,
1619 bt_param_validation_value_descr::makeSignedInteger()},
1620 {"clock-class-offset-ns", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL,
1621 bt_param_validation_value_descr::makeSignedInteger()},
1622 {"force-clock-class-origin-unix-epoch", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL,
1623 bt_param_validation_value_descr::makeBool()},
1624 BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END};
1625
1626 bool read_src_fs_parameters(const bt_value *params, const bt_value **inputs,
1627 const bt_value **trace_name, struct ctf_fs_component *ctf_fs)
1628 {
1629 const bt_value *value;
1630 enum bt_param_validation_status validate_value_status;
1631 gchar *error = NULL;
1632 const bt2_common::LogCfg& logCfg = ctf_fs->logCfg;
1633
1634 validate_value_status = bt_param_validation_validate(params, fs_params_entries_descr, &error);
1635 if (validate_value_status != BT_PARAM_VALIDATION_STATUS_OK) {
1636 BT_CLOGE_APPEND_CAUSE("%s", error);
1637 g_free(error);
1638 return false;
1639 }
1640
1641 /* inputs parameter */
1642 *inputs = bt_value_map_borrow_entry_value_const(params, "inputs");
1643
1644 /* clock-class-offset-s parameter */
1645 value = bt_value_map_borrow_entry_value_const(params, "clock-class-offset-s");
1646 if (value) {
1647 ctf_fs->clkClsCfg.offsetSec = bt_value_integer_signed_get(value);
1648 }
1649
1650 /* clock-class-offset-ns parameter */
1651 value = bt_value_map_borrow_entry_value_const(params, "clock-class-offset-ns");
1652 if (value) {
1653 ctf_fs->clkClsCfg.offsetNanoSec = bt_value_integer_signed_get(value);
1654 }
1655
1656 /* force-clock-class-origin-unix-epoch parameter */
1657 value = bt_value_map_borrow_entry_value_const(params, "force-clock-class-origin-unix-epoch");
1658 if (value) {
1659 ctf_fs->clkClsCfg.forceOriginIsUnixEpoch = bt_value_bool_get(value);
1660 }
1661
1662 /* trace-name parameter */
1663 *trace_name = bt_value_map_borrow_entry_value_const(params, "trace-name");
1664
1665 return true;
1666 }
1667
1668 static ctf_fs_component::UP ctf_fs_create(const bt_value *params,
1669 bt_self_component_source *self_comp_src,
1670 const bt2_common::LogCfg& logCfg)
1671 {
1672 const bt_value *inputs_value;
1673 const bt_value *trace_name_value;
1674 bt_self_component *self_comp = bt_self_component_source_as_self_component(self_comp_src);
1675
1676 ctf_fs_component::UP ctf_fs = bt2_common::makeUnique<ctf_fs_component>(logCfg);
1677
1678 if (!read_src_fs_parameters(params, &inputs_value, &trace_name_value, ctf_fs.get())) {
1679 return nullptr;
1680 }
1681
1682 if (ctf_fs_component_create_ctf_fs_trace(ctf_fs.get(), inputs_value, trace_name_value,
1683 self_comp)) {
1684 return nullptr;
1685 }
1686
1687 if (create_streams_for_trace(ctf_fs->trace.get())) {
1688 return nullptr;
1689 }
1690
1691 if (create_ports_for_trace(ctf_fs.get(), ctf_fs->trace.get(), self_comp_src)) {
1692 return nullptr;
1693 }
1694
1695 return ctf_fs;
1696 }
1697
1698 BT_HIDDEN
1699 bt_component_class_initialize_method_status
1700 ctf_fs_init(bt_self_component_source *self_comp_src, bt_self_component_source_configuration *config,
1701 const bt_value *params, __attribute__((unused)) void *init_method_data)
1702 {
1703 bt_self_component *selfComp = bt_self_component_source_as_self_component(self_comp_src);
1704 const bt_component *comp = bt_self_component_as_component(selfComp);
1705 bt_logging_level logLevel = bt_component_get_logging_level(comp);
1706 bt2_common::LogCfg logCfg(logLevel, *selfComp);
1707
1708 try {
1709 ctf_fs_component::UP ctf_fs = ctf_fs_create(params, self_comp_src, logCfg);
1710 if (!ctf_fs) {
1711 return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
1712 }
1713
1714 bt_self_component_set_data(selfComp, ctf_fs.release());
1715
1716 return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
1717 } catch (const std::bad_alloc&) {
1718 return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
1719 } catch (const bt2_common::Error&) {
1720 BT_CLOGE_APPEND_CAUSE("Failed to initialize component");
1721 return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
1722 }
1723 }
1724
1725 BT_HIDDEN
1726 bt_component_class_query_method_status ctf_fs_query(bt_self_component_class_source *comp_class_src,
1727 bt_private_query_executor *priv_query_exec,
1728 const char *object, const bt_value *params,
1729 __attribute__((unused)) void *method_data,
1730 const bt_value **result)
1731 {
1732 const bt_query_executor *query_exec =
1733 bt_private_query_executor_as_query_executor_const(priv_query_exec);
1734 bt_logging_level log_level = bt_query_executor_get_logging_level(query_exec);
1735 bt_self_component_class *comp_class =
1736 bt_self_component_class_source_as_self_component_class(comp_class_src);
1737 bt2_common::LogCfg logCfg(log_level, *comp_class);
1738
1739 try {
1740 bt2::ConstMapValue paramsObj(params);
1741 nonstd::optional<bt2::Value::Shared> resultObj;
1742
1743 if (strcmp(object, "metadata-info") == 0) {
1744 resultObj = metadata_info_query(paramsObj, logCfg);
1745 } else if (strcmp(object, "babeltrace.trace-infos") == 0) {
1746 resultObj = trace_infos_query(paramsObj, logCfg);
1747 } else if (!strcmp(object, "babeltrace.support-info")) {
1748 resultObj = support_info_query(paramsObj, logCfg);
1749 } else {
1750 BT_LOGE("Unknown query object `%s`", object);
1751 return BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_UNKNOWN_OBJECT;
1752 }
1753
1754 *result = resultObj->release().libObjPtr();
1755
1756 return BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_OK;
1757 } catch (const std::bad_alloc&) {
1758 return BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_MEMORY_ERROR;
1759 } catch (const bt2_common::Error&) {
1760 BT_CLOGE_APPEND_CAUSE("Failed to execute query: object=%s", object);
1761 return BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
1762 }
1763 }
This page took 0.064188 seconds and 4 git commands to generate.