Replace assert() -> BT_ASSERT() and some preconditions with BT_ASSERT_PRE()
[babeltrace.git] / plugins / ctf / fs-src / fs.c
1 /*
2 * fs.c
3 *
4 * Babeltrace CTF file system Reader Component
5 *
6 * Copyright 2015-2017 Philippe Proulx <pproulx@efficios.com>
7 * Copyright 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a copy
10 * of this software and associated documentation files (the "Software"), to deal
11 * in the Software without restriction, including without limitation the rights
12 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 * copies of the Software, and to permit persons to whom the Software is
14 * furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 * SOFTWARE.
26 */
27
28 #include <babeltrace/common-internal.h>
29 #include <babeltrace/babeltrace.h>
30 #include <plugins-common.h>
31 #include <glib.h>
32 #include <babeltrace/assert-internal.h>
33 #include <inttypes.h>
34 #include <stdbool.h>
35 #include "fs.h"
36 #include "metadata.h"
37 #include "data-stream-file.h"
38 #include "file.h"
39 #include "../common/metadata/decoder.h"
40 #include "../common/notif-iter/notif-iter.h"
41 #include "../common/utils/utils.h"
42 #include "query.h"
43
44 #define BT_LOG_TAG "PLUGIN-CTF-FS-SRC"
45 #include "logging.h"
46
47 static
48 int notif_iter_data_set_current_ds_file(struct ctf_fs_notif_iter_data *notif_iter_data)
49 {
50 struct ctf_fs_ds_file_info *ds_file_info;
51 int ret = 0;
52
53 BT_ASSERT(notif_iter_data->ds_file_info_index <
54 notif_iter_data->ds_file_group->ds_file_infos->len);
55 ds_file_info = g_ptr_array_index(
56 notif_iter_data->ds_file_group->ds_file_infos,
57 notif_iter_data->ds_file_info_index);
58
59 ctf_fs_ds_file_destroy(notif_iter_data->ds_file);
60 notif_iter_data->ds_file = ctf_fs_ds_file_create(
61 notif_iter_data->ds_file_group->ctf_fs_trace,
62 notif_iter_data->notif_iter,
63 notif_iter_data->ds_file_group->stream,
64 ds_file_info->path->str);
65 if (!notif_iter_data->ds_file) {
66 ret = -1;
67 }
68
69 return ret;
70 }
71
72 static
73 void ctf_fs_notif_iter_data_destroy(
74 struct ctf_fs_notif_iter_data *notif_iter_data)
75 {
76 if (!notif_iter_data) {
77 return;
78 }
79
80 ctf_fs_ds_file_destroy(notif_iter_data->ds_file);
81
82 if (notif_iter_data->notif_iter) {
83 bt_notif_iter_destroy(notif_iter_data->notif_iter);
84 }
85
86 g_free(notif_iter_data);
87 }
88
89 struct bt_notification_iterator_next_method_return ctf_fs_iterator_next(
90 struct bt_private_connection_private_notification_iterator *iterator)
91 {
92 struct bt_notification_iterator_next_method_return next_ret;
93 struct ctf_fs_notif_iter_data *notif_iter_data =
94 bt_private_connection_private_notification_iterator_get_user_data(iterator);
95 int ret;
96
97 BT_ASSERT(notif_iter_data->ds_file);
98 next_ret = ctf_fs_ds_file_next(notif_iter_data->ds_file);
99 if (next_ret.status == BT_NOTIFICATION_ITERATOR_STATUS_END) {
100 BT_ASSERT(!next_ret.notification);
101 notif_iter_data->ds_file_info_index++;
102
103 if (notif_iter_data->ds_file_info_index ==
104 notif_iter_data->ds_file_group->ds_file_infos->len) {
105 /*
106 * No more stream files to read: we reached the
107 * real end.
108 */
109 goto end;
110 }
111
112 /*
113 * Open and start reading the next stream file within
114 * our stream file group.
115 */
116 ret = notif_iter_data_set_current_ds_file(notif_iter_data);
117 if (ret) {
118 next_ret.status = BT_NOTIFICATION_ITERATOR_STATUS_ERROR;
119 goto end;
120 }
121
122 next_ret = ctf_fs_ds_file_next(notif_iter_data->ds_file);
123
124 /*
125 * We should not get BT_NOTIFICATION_ITERATOR_STATUS_END
126 * with a brand new stream file because empty stream
127 * files are not even part of stream file groups, which
128 * means we're sure to get at least one pair of "packet
129 * begin" and "packet end" notifications in the case of
130 * a single, empty packet.
131 */
132 BT_ASSERT(next_ret.status != BT_NOTIFICATION_ITERATOR_STATUS_END);
133 }
134
135 end:
136 return next_ret;
137 }
138
139 void ctf_fs_iterator_finalize(struct bt_private_connection_private_notification_iterator *it)
140 {
141 void *notif_iter_data =
142 bt_private_connection_private_notification_iterator_get_user_data(it);
143
144 ctf_fs_notif_iter_data_destroy(notif_iter_data);
145 }
146
147 enum bt_notification_iterator_status ctf_fs_iterator_init(
148 struct bt_private_connection_private_notification_iterator *it,
149 struct bt_private_port *port)
150 {
151 struct ctf_fs_port_data *port_data;
152 struct ctf_fs_notif_iter_data *notif_iter_data = NULL;
153 enum bt_notification_iterator_status ret =
154 BT_NOTIFICATION_ITERATOR_STATUS_OK;
155 int iret;
156
157 port_data = bt_private_port_get_user_data(port);
158 if (!port_data) {
159 ret = BT_NOTIFICATION_ITERATOR_STATUS_INVALID;
160 goto error;
161 }
162
163 notif_iter_data = g_new0(struct ctf_fs_notif_iter_data, 1);
164 if (!notif_iter_data) {
165 ret = BT_NOTIFICATION_ITERATOR_STATUS_NOMEM;
166 goto error;
167 }
168
169 notif_iter_data->notif_iter = bt_notif_iter_create(
170 port_data->ds_file_group->ctf_fs_trace->metadata->trace,
171 bt_common_get_page_size() * 8,
172 ctf_fs_ds_file_medops, NULL);
173 if (!notif_iter_data->notif_iter) {
174 BT_LOGE_STR("Cannot create a CTF notification iterator.");
175 ret = BT_NOTIFICATION_ITERATOR_STATUS_NOMEM;
176 goto error;
177 }
178
179 notif_iter_data->ds_file_group = port_data->ds_file_group;
180 iret = notif_iter_data_set_current_ds_file(notif_iter_data);
181 if (iret) {
182 ret = BT_NOTIFICATION_ITERATOR_STATUS_ERROR;
183 goto error;
184 }
185
186 ret = bt_private_connection_private_notification_iterator_set_user_data(it, notif_iter_data);
187 if (ret != BT_NOTIFICATION_ITERATOR_STATUS_OK) {
188 goto error;
189 }
190
191 notif_iter_data = NULL;
192 goto end;
193
194 error:
195 (void) bt_private_connection_private_notification_iterator_set_user_data(it, NULL);
196
197 end:
198 ctf_fs_notif_iter_data_destroy(notif_iter_data);
199 return ret;
200 }
201
202 static
203 void ctf_fs_destroy(struct ctf_fs_component *ctf_fs)
204 {
205 if (!ctf_fs) {
206 return;
207 }
208
209 if (ctf_fs->traces) {
210 g_ptr_array_free(ctf_fs->traces, TRUE);
211 }
212
213 if (ctf_fs->port_data) {
214 g_ptr_array_free(ctf_fs->port_data, TRUE);
215 }
216
217 g_free(ctf_fs);
218 }
219
220 BT_HIDDEN
221 void ctf_fs_trace_destroy(struct ctf_fs_trace *ctf_fs_trace)
222 {
223 if (!ctf_fs_trace) {
224 return;
225 }
226
227 if (ctf_fs_trace->ds_file_groups) {
228 g_ptr_array_free(ctf_fs_trace->ds_file_groups, TRUE);
229 }
230
231 if (ctf_fs_trace->path) {
232 g_string_free(ctf_fs_trace->path, TRUE);
233 }
234
235 if (ctf_fs_trace->name) {
236 g_string_free(ctf_fs_trace->name, TRUE);
237 }
238
239 if (ctf_fs_trace->metadata) {
240 ctf_fs_metadata_fini(ctf_fs_trace->metadata);
241 g_free(ctf_fs_trace->metadata);
242 }
243
244 bt_put(ctf_fs_trace->cc_prio_map);
245 g_free(ctf_fs_trace);
246 }
247
248 static
249 void ctf_fs_trace_destroy_notifier(void *data)
250 {
251 struct ctf_fs_trace *trace = data;
252 ctf_fs_trace_destroy(trace);
253 }
254
255 void ctf_fs_finalize(struct bt_private_component *component)
256 {
257 void *data = bt_private_component_get_user_data(component);
258
259 ctf_fs_destroy(data);
260 }
261
262 static
263 void port_data_destroy(void *data) {
264 struct ctf_fs_port_data *port_data = data;
265
266 if (!port_data) {
267 return;
268 }
269
270 g_free(port_data);
271 }
272
273 static
274 GString *get_stream_instance_unique_name(
275 struct ctf_fs_ds_file_group *ds_file_group)
276 {
277 GString *name;
278 struct ctf_fs_ds_file_info *ds_file_info;
279
280 name = g_string_new(NULL);
281 if (!name) {
282 goto end;
283 }
284
285 /*
286 * If there's more than one stream file in the stream file
287 * group, the first (earliest) stream file's path is used as
288 * the stream's unique name.
289 */
290 BT_ASSERT(ds_file_group->ds_file_infos->len > 0);
291 ds_file_info = g_ptr_array_index(ds_file_group->ds_file_infos, 0);
292 g_string_assign(name, ds_file_info->path->str);
293
294 end:
295 return name;
296 }
297
298 static
299 int create_one_port_for_trace(struct ctf_fs_component *ctf_fs,
300 struct ctf_fs_trace *ctf_fs_trace,
301 struct ctf_fs_ds_file_group *ds_file_group)
302 {
303 int ret = 0;
304 struct ctf_fs_port_data *port_data = NULL;
305 GString *port_name = NULL;
306
307 port_name = get_stream_instance_unique_name(ds_file_group);
308 if (!port_name) {
309 goto error;
310 }
311
312 BT_LOGD("Creating one port named `%s`", port_name->str);
313
314 /* Create output port for this file */
315 port_data = g_new0(struct ctf_fs_port_data, 1);
316 if (!port_data) {
317 goto error;
318 }
319
320 port_data->ds_file_group = ds_file_group;
321 ret = bt_private_component_source_add_output_private_port(
322 ctf_fs->priv_comp, port_name->str, port_data, NULL);
323 if (ret) {
324 goto error;
325 }
326
327 g_ptr_array_add(ctf_fs->port_data, port_data);
328 port_data = NULL;
329 goto end;
330
331 error:
332 ret = -1;
333
334 end:
335 if (port_name) {
336 g_string_free(port_name, TRUE);
337 }
338
339 port_data_destroy(port_data);
340 return ret;
341 }
342
343 static
344 int create_ports_for_trace(struct ctf_fs_component *ctf_fs,
345 struct ctf_fs_trace *ctf_fs_trace)
346 {
347 int ret = 0;
348 size_t i;
349
350 /* Create one output port for each stream file group */
351 for (i = 0; i < ctf_fs_trace->ds_file_groups->len; i++) {
352 struct ctf_fs_ds_file_group *ds_file_group =
353 g_ptr_array_index(ctf_fs_trace->ds_file_groups, i);
354
355 ret = create_one_port_for_trace(ctf_fs, ctf_fs_trace,
356 ds_file_group);
357 if (ret) {
358 BT_LOGE("Cannot create output port.");
359 goto end;
360 }
361 }
362
363 end:
364 return ret;
365 }
366
367 static
368 uint64_t get_packet_header_stream_instance_id(struct ctf_fs_trace *ctf_fs_trace,
369 struct bt_field *packet_header_field)
370 {
371 struct bt_field *stream_instance_id_field = NULL;
372 uint64_t stream_instance_id = -1ULL;
373 int ret;
374
375 if (!packet_header_field) {
376 goto end;
377 }
378
379 stream_instance_id_field = bt_field_structure_get_field_by_name(
380 packet_header_field, "stream_instance_id");
381 if (!stream_instance_id_field) {
382 goto end;
383 }
384
385 ret = bt_field_unsigned_integer_get_value(stream_instance_id_field,
386 &stream_instance_id);
387 if (ret) {
388 stream_instance_id = -1ULL;
389 goto end;
390 }
391
392 end:
393 bt_put(stream_instance_id_field);
394 return stream_instance_id;
395 }
396
397 uint64_t get_packet_context_timestamp_begin_ns(
398 struct ctf_fs_trace *ctf_fs_trace,
399 struct bt_field *packet_context_field)
400 {
401 int ret;
402 struct bt_field *timestamp_begin_field = NULL;
403 struct bt_field_type *timestamp_begin_ft = NULL;
404 uint64_t timestamp_begin_raw_value = -1ULL;
405 uint64_t timestamp_begin_ns = -1ULL;
406 int64_t timestamp_begin_ns_signed;
407 struct bt_clock_class *timestamp_begin_clock_class = NULL;
408 struct bt_clock_value *clock_value = NULL;
409
410 if (!packet_context_field) {
411 goto end;
412 }
413
414 timestamp_begin_field = bt_field_structure_get_field_by_name(
415 packet_context_field, "timestamp_begin");
416 if (!timestamp_begin_field) {
417 goto end;
418 }
419
420 timestamp_begin_ft = bt_field_get_type(timestamp_begin_field);
421 BT_ASSERT(timestamp_begin_ft);
422 timestamp_begin_clock_class =
423 bt_field_type_integer_get_mapped_clock_class(timestamp_begin_ft);
424 if (!timestamp_begin_clock_class) {
425 goto end;
426 }
427
428 ret = bt_field_unsigned_integer_get_value(timestamp_begin_field,
429 &timestamp_begin_raw_value);
430 if (ret) {
431 goto end;
432 }
433
434 clock_value = bt_clock_value_create(timestamp_begin_clock_class,
435 timestamp_begin_raw_value);
436 if (!clock_value) {
437 goto end;
438 }
439
440 ret = bt_clock_value_get_value_ns_from_epoch(clock_value,
441 &timestamp_begin_ns_signed);
442 if (ret) {
443 goto end;
444 }
445
446 timestamp_begin_ns = (uint64_t) timestamp_begin_ns_signed;
447
448 end:
449 bt_put(timestamp_begin_field);
450 bt_put(timestamp_begin_ft);
451 bt_put(timestamp_begin_clock_class);
452 bt_put(clock_value);
453 return timestamp_begin_ns;
454 }
455
456 static
457 void ctf_fs_ds_file_info_destroy(struct ctf_fs_ds_file_info *ds_file_info)
458 {
459 if (!ds_file_info) {
460 return;
461 }
462
463 if (ds_file_info->path) {
464 g_string_free(ds_file_info->path, TRUE);
465 }
466
467 ctf_fs_ds_index_destroy(ds_file_info->index);
468 g_free(ds_file_info);
469 }
470
471 static
472 struct ctf_fs_ds_file_info *ctf_fs_ds_file_info_create(const char *path,
473 uint64_t begin_ns, struct ctf_fs_ds_index *index)
474 {
475 struct ctf_fs_ds_file_info *ds_file_info;
476
477 ds_file_info = g_new0(struct ctf_fs_ds_file_info, 1);
478 if (!ds_file_info) {
479 goto end;
480 }
481
482 ds_file_info->path = g_string_new(path);
483 if (!ds_file_info->path) {
484 ctf_fs_ds_file_info_destroy(ds_file_info);
485 ds_file_info = NULL;
486 goto end;
487 }
488
489 ds_file_info->begin_ns = begin_ns;
490 ds_file_info->index = index;
491 index = NULL;
492
493 end:
494 ctf_fs_ds_index_destroy(index);
495 return ds_file_info;
496 }
497
498 static
499 void ctf_fs_ds_file_group_destroy(struct ctf_fs_ds_file_group *ds_file_group)
500 {
501 if (!ds_file_group) {
502 return;
503 }
504
505 if (ds_file_group->ds_file_infos) {
506 g_ptr_array_free(ds_file_group->ds_file_infos, TRUE);
507 }
508
509 bt_put(ds_file_group->stream);
510 bt_put(ds_file_group->stream_class);
511 g_free(ds_file_group);
512 }
513
514 static
515 struct ctf_fs_ds_file_group *ctf_fs_ds_file_group_create(
516 struct ctf_fs_trace *ctf_fs_trace,
517 struct bt_stream_class *stream_class,
518 uint64_t stream_instance_id)
519 {
520 struct ctf_fs_ds_file_group *ds_file_group;
521
522 ds_file_group = g_new0(struct ctf_fs_ds_file_group, 1);
523 if (!ds_file_group) {
524 goto error;
525 }
526
527 ds_file_group->ds_file_infos = g_ptr_array_new_with_free_func(
528 (GDestroyNotify) ctf_fs_ds_file_info_destroy);
529 if (!ds_file_group->ds_file_infos) {
530 goto error;
531 }
532
533 ds_file_group->stream_id = stream_instance_id;
534 BT_ASSERT(stream_class);
535 ds_file_group->stream_class = bt_get(stream_class);
536 ds_file_group->ctf_fs_trace = ctf_fs_trace;
537 goto end;
538
539 error:
540 ctf_fs_ds_file_group_destroy(ds_file_group);
541 ds_file_group = NULL;
542
543 end:
544 return ds_file_group;
545 }
546
547 /* Replace by g_ptr_array_insert when we depend on glib >= 2.40. */
548 static
549 void array_insert(GPtrArray *array, gpointer element, size_t pos)
550 {
551 size_t original_array_len = array->len;
552
553 /* Allocate an unused element at the end of the array. */
554 g_ptr_array_add(array, NULL);
555
556 /* If we are not inserting at the end, move the elements by one. */
557 if (pos < original_array_len) {
558 memmove(&(array->pdata[pos + 1]),
559 &(array->pdata[pos]),
560 (original_array_len - pos) * sizeof(gpointer));
561 }
562
563 /* Insert the value and bump the array len */
564 array->pdata[pos] = element;
565 }
566
567 static
568 int ctf_fs_ds_file_group_add_ds_file_info(
569 struct ctf_fs_ds_file_group *ds_file_group,
570 const char *path, uint64_t begin_ns,
571 struct ctf_fs_ds_index *index)
572 {
573 struct ctf_fs_ds_file_info *ds_file_info;
574 gint i = 0;
575 int ret = 0;
576
577 /* Onwership of index is transferred. */
578 ds_file_info = ctf_fs_ds_file_info_create(path, begin_ns, index);
579 index = NULL;
580 if (!ds_file_info) {
581 goto error;
582 }
583
584 /* Find a spot to insert this one */
585 for (i = 0; i < ds_file_group->ds_file_infos->len; i++) {
586 struct ctf_fs_ds_file_info *other_ds_file_info =
587 g_ptr_array_index(ds_file_group->ds_file_infos, i);
588
589 if (begin_ns < other_ds_file_info->begin_ns) {
590 break;
591 }
592 }
593
594 array_insert(ds_file_group->ds_file_infos, ds_file_info, i);
595 ds_file_info = NULL;
596 goto end;
597
598 error:
599 ctf_fs_ds_file_info_destroy(ds_file_info);
600 ctf_fs_ds_index_destroy(index);
601 ret = -1;
602 end:
603 return ret;
604 }
605
606 static
607 int add_ds_file_to_ds_file_group(struct ctf_fs_trace *ctf_fs_trace,
608 const char *path)
609 {
610 struct bt_field *packet_header_field = NULL;
611 struct bt_field *packet_context_field = NULL;
612 struct bt_stream_class *stream_class = NULL;
613 uint64_t stream_instance_id = -1ULL;
614 uint64_t begin_ns = -1ULL;
615 struct ctf_fs_ds_file_group *ds_file_group = NULL;
616 bool add_group = false;
617 int ret;
618 size_t i;
619 struct ctf_fs_ds_file *ds_file = NULL;
620 struct ctf_fs_ds_index *index = NULL;
621 struct bt_notif_iter *notif_iter = NULL;
622
623 notif_iter = bt_notif_iter_create(ctf_fs_trace->metadata->trace,
624 bt_common_get_page_size() * 8, ctf_fs_ds_file_medops, NULL);
625 if (!notif_iter) {
626 BT_LOGE_STR("Cannot create a CTF notification iterator.");
627 goto error;
628 }
629
630 ds_file = ctf_fs_ds_file_create(ctf_fs_trace, notif_iter, NULL, path);
631 if (!ds_file) {
632 goto error;
633 }
634
635 ret = ctf_fs_ds_file_get_packet_header_context_fields(ds_file,
636 &packet_header_field, &packet_context_field);
637 if (ret) {
638 BT_LOGE("Cannot get stream file's first packet's header and context fields (`%s`).",
639 path);
640 goto error;
641 }
642
643 stream_instance_id = get_packet_header_stream_instance_id(ctf_fs_trace,
644 packet_header_field);
645 begin_ns = get_packet_context_timestamp_begin_ns(ctf_fs_trace,
646 packet_context_field);
647 stream_class = ctf_utils_stream_class_from_packet_header(
648 ctf_fs_trace->metadata->trace, packet_header_field);
649 if (!stream_class) {
650 goto error;
651 }
652
653 index = ctf_fs_ds_file_build_index(ds_file);
654 if (!index) {
655 BT_LOGW("Failed to index CTF stream file \'%s\'",
656 ds_file->file->path->str);
657 }
658
659 if (begin_ns == -1ULL) {
660 /*
661 * No beggining timestamp to sort the stream files
662 * within a stream file group, so consider that this
663 * file must be the only one within its group.
664 */
665 stream_instance_id = -1ULL;
666 }
667
668 if (stream_instance_id == -1ULL) {
669 /*
670 * No stream instance ID or no beginning timestamp:
671 * create a unique stream file group for this stream
672 * file because, even if there's a stream instance ID,
673 * there's no timestamp to order the file within its
674 * group.
675 */
676 ds_file_group = ctf_fs_ds_file_group_create(ctf_fs_trace,
677 stream_class, stream_instance_id);
678 if (!ds_file_group) {
679 goto error;
680 }
681
682 ret = ctf_fs_ds_file_group_add_ds_file_info(ds_file_group,
683 path, begin_ns, index);
684 /* Ownership of index is transferred. */
685 index = NULL;
686 if (ret) {
687 goto error;
688 }
689
690 add_group = true;
691 goto end;
692 }
693
694 BT_ASSERT(stream_instance_id != -1ULL);
695 BT_ASSERT(begin_ns != -1ULL);
696
697 /* Find an existing stream file group with this ID */
698 for (i = 0; i < ctf_fs_trace->ds_file_groups->len; i++) {
699 ds_file_group = g_ptr_array_index(
700 ctf_fs_trace->ds_file_groups, i);
701
702 if (ds_file_group->stream_class == stream_class &&
703 ds_file_group->stream_id ==
704 stream_instance_id) {
705 break;
706 }
707
708 ds_file_group = NULL;
709 }
710
711 if (!ds_file_group) {
712 ds_file_group = ctf_fs_ds_file_group_create(ctf_fs_trace,
713 stream_class, stream_instance_id);
714 if (!ds_file_group) {
715 goto error;
716 }
717
718 add_group = true;
719 }
720
721 ret = ctf_fs_ds_file_group_add_ds_file_info(ds_file_group, path,
722 begin_ns, index);
723 index = NULL;
724 if (ret) {
725 goto error;
726 }
727
728 goto end;
729
730 error:
731 ctf_fs_ds_file_group_destroy(ds_file_group);
732 ret = -1;
733
734 end:
735 if (add_group && ds_file_group) {
736 g_ptr_array_add(ctf_fs_trace->ds_file_groups, ds_file_group);
737 }
738
739 ctf_fs_ds_file_destroy(ds_file);
740
741 if (notif_iter) {
742 bt_notif_iter_destroy(notif_iter);
743 }
744
745 ctf_fs_ds_index_destroy(index);
746 bt_put(packet_header_field);
747 bt_put(packet_context_field);
748 bt_put(stream_class);
749 return ret;
750 }
751
752 static
753 int create_ds_file_groups(struct ctf_fs_trace *ctf_fs_trace)
754 {
755 int ret = 0;
756 const char *basename;
757 GError *error = NULL;
758 GDir *dir = NULL;
759 size_t i;
760
761 /* Check each file in the path directory, except specific ones */
762 dir = g_dir_open(ctf_fs_trace->path->str, 0, &error);
763 if (!dir) {
764 BT_LOGE("Cannot open directory `%s`: %s (code %d)",
765 ctf_fs_trace->path->str, error->message,
766 error->code);
767 goto error;
768 }
769
770 while ((basename = g_dir_read_name(dir))) {
771 struct ctf_fs_file *file;
772
773 if (!strcmp(basename, CTF_FS_METADATA_FILENAME)) {
774 /* Ignore the metadata stream. */
775 BT_LOGD("Ignoring metadata file `%s" G_DIR_SEPARATOR_S "%s`",
776 ctf_fs_trace->path->str, basename);
777 continue;
778 }
779
780 if (basename[0] == '.') {
781 BT_LOGD("Ignoring hidden file `%s" G_DIR_SEPARATOR_S "%s`",
782 ctf_fs_trace->path->str, basename);
783 continue;
784 }
785
786 /* Create the file. */
787 file = ctf_fs_file_create();
788 if (!file) {
789 BT_LOGE("Cannot create stream file object for file `%s" G_DIR_SEPARATOR_S "%s`",
790 ctf_fs_trace->path->str, basename);
791 goto error;
792 }
793
794 /* Create full path string. */
795 g_string_append_printf(file->path, "%s" G_DIR_SEPARATOR_S "%s",
796 ctf_fs_trace->path->str, basename);
797 if (!g_file_test(file->path->str, G_FILE_TEST_IS_REGULAR)) {
798 BT_LOGD("Ignoring non-regular file `%s`",
799 file->path->str);
800 ctf_fs_file_destroy(file);
801 file = NULL;
802 continue;
803 }
804
805 ret = ctf_fs_file_open(file, "rb");
806 if (ret) {
807 BT_LOGE("Cannot open stream file `%s`", file->path->str);
808 goto error;
809 }
810
811 if (file->size == 0) {
812 /* Skip empty stream. */
813 BT_LOGD("Ignoring empty file `%s`", file->path->str);
814 ctf_fs_file_destroy(file);
815 continue;
816 }
817
818 ret = add_ds_file_to_ds_file_group(ctf_fs_trace,
819 file->path->str);
820 if (ret) {
821 BT_LOGE("Cannot add stream file `%s` to stream file group",
822 file->path->str);
823 ctf_fs_file_destroy(file);
824 goto error;
825 }
826
827 ctf_fs_file_destroy(file);
828 }
829
830 /*
831 * At this point, DS file groupes are created, but their
832 * associated stream objects do not exist yet. This is because
833 * we need to name the created stream object with the data
834 * stream file's path. We have everything we need here to do
835 * this.
836 */
837 for (i = 0; i < ctf_fs_trace->ds_file_groups->len; i++) {
838 struct ctf_fs_ds_file_group *ds_file_group =
839 g_ptr_array_index(ctf_fs_trace->ds_file_groups, i);
840 GString *name = get_stream_instance_unique_name(ds_file_group);
841
842 if (!name) {
843 goto error;
844 }
845
846 if (ds_file_group->stream_id == -1ULL) {
847 /* No stream ID */
848 ds_file_group->stream = bt_stream_create(
849 ds_file_group->stream_class, name->str);
850 } else {
851 /* Specific stream ID */
852 ds_file_group->stream = bt_stream_create_with_id(
853 ds_file_group->stream_class, name->str,
854 ds_file_group->stream_id);
855 }
856
857 g_string_free(name, TRUE);
858
859 if (!ds_file_group->stream) {
860 BT_LOGE("Cannot create stream for DS file group: "
861 "addr=%p, stream-name=\"%s\"",
862 ds_file_group, name->str);
863 goto error;
864 }
865 }
866
867 goto end;
868
869 error:
870 ret = -1;
871
872 end:
873 if (dir) {
874 g_dir_close(dir);
875 dir = NULL;
876 }
877
878 if (error) {
879 g_error_free(error);
880 }
881
882 return ret;
883 }
884
885 static
886 int create_cc_prio_map(struct ctf_fs_trace *ctf_fs_trace)
887 {
888 int ret = 0;
889 size_t i;
890 int count;
891
892 BT_ASSERT(ctf_fs_trace);
893 ctf_fs_trace->cc_prio_map = bt_clock_class_priority_map_create();
894 if (!ctf_fs_trace->cc_prio_map) {
895 ret = -1;
896 goto end;
897 }
898
899 count = bt_trace_get_clock_class_count(
900 ctf_fs_trace->metadata->trace);
901 BT_ASSERT(count >= 0);
902
903 for (i = 0; i < count; i++) {
904 struct bt_clock_class *clock_class =
905 bt_trace_get_clock_class_by_index(
906 ctf_fs_trace->metadata->trace, i);
907
908 BT_ASSERT(clock_class);
909 ret = bt_clock_class_priority_map_add_clock_class(
910 ctf_fs_trace->cc_prio_map, clock_class, 0);
911 BT_PUT(clock_class);
912
913 if (ret) {
914 goto end;
915 }
916 }
917
918 end:
919 return ret;
920 }
921
922 BT_HIDDEN
923 struct ctf_fs_trace *ctf_fs_trace_create(const char *path, const char *name,
924 struct ctf_fs_metadata_config *metadata_config)
925 {
926 struct ctf_fs_trace *ctf_fs_trace;
927 int ret;
928
929 ctf_fs_trace = g_new0(struct ctf_fs_trace, 1);
930 if (!ctf_fs_trace) {
931 goto end;
932 }
933
934 ctf_fs_trace->path = g_string_new(path);
935 if (!ctf_fs_trace->path) {
936 goto error;
937 }
938
939 ctf_fs_trace->name = g_string_new(name);
940 if (!ctf_fs_trace->name) {
941 goto error;
942 }
943
944 ctf_fs_trace->metadata = g_new0(struct ctf_fs_metadata, 1);
945 if (!ctf_fs_trace->metadata) {
946 goto error;
947 }
948
949 ctf_fs_trace->ds_file_groups = g_ptr_array_new_with_free_func(
950 (GDestroyNotify) ctf_fs_ds_file_group_destroy);
951 if (!ctf_fs_trace->ds_file_groups) {
952 goto error;
953 }
954
955 ret = ctf_fs_metadata_set_trace(ctf_fs_trace, metadata_config);
956 if (ret) {
957 goto error;
958 }
959
960 ret = create_ds_file_groups(ctf_fs_trace);
961 if (ret) {
962 goto error;
963 }
964
965 ret = create_cc_prio_map(ctf_fs_trace);
966 if (ret) {
967 goto error;
968 }
969
970 /*
971 * create_ds_file_groups() created all the streams that this
972 * trace needs. There won't be any more. Therefore it is safe to
973 * make this trace static.
974 */
975 (void) bt_trace_set_is_static(ctf_fs_trace->metadata->trace);
976
977 goto end;
978
979 error:
980 ctf_fs_trace_destroy(ctf_fs_trace);
981 ctf_fs_trace = NULL;
982 end:
983 return ctf_fs_trace;
984 }
985
986 static
987 int path_is_ctf_trace(const char *path)
988 {
989 GString *metadata_path = g_string_new(NULL);
990 int ret = 0;
991
992 if (!metadata_path) {
993 ret = -1;
994 goto end;
995 }
996
997 g_string_printf(metadata_path, "%s" G_DIR_SEPARATOR_S "%s", path, CTF_FS_METADATA_FILENAME);
998
999 if (g_file_test(metadata_path->str, G_FILE_TEST_IS_REGULAR)) {
1000 ret = 1;
1001 goto end;
1002 }
1003
1004 end:
1005 g_string_free(metadata_path, TRUE);
1006 return ret;
1007 }
1008
1009 static
1010 int add_trace_path(GList **trace_paths, const char *path)
1011 {
1012 GString *norm_path = NULL;
1013 int ret = 0;
1014
1015 norm_path = bt_common_normalize_path(path, NULL);
1016 if (!norm_path) {
1017 BT_LOGE("Failed to normalize path `%s`.", path);
1018 ret = -1;
1019 goto end;
1020 }
1021
1022 // FIXME: Remove or ifdef for __MINGW32__
1023 if (strcmp(norm_path->str, "/") == 0) {
1024 BT_LOGE("Opening a trace in `/` is not supported.");
1025 ret = -1;
1026 goto end;
1027 }
1028
1029 *trace_paths = g_list_prepend(*trace_paths, norm_path);
1030 BT_ASSERT(*trace_paths);
1031 norm_path = NULL;
1032
1033 end:
1034 if (norm_path) {
1035 g_string_free(norm_path, TRUE);
1036 }
1037
1038 return ret;
1039 }
1040
1041 BT_HIDDEN
1042 int ctf_fs_find_traces(GList **trace_paths, const char *start_path)
1043 {
1044 int ret;
1045 GError *error = NULL;
1046 GDir *dir = NULL;
1047 const char *basename = NULL;
1048
1049 /* Check if the starting path is a CTF trace itself */
1050 ret = path_is_ctf_trace(start_path);
1051 if (ret < 0) {
1052 goto end;
1053 }
1054
1055 if (ret) {
1056 /*
1057 * Stop recursion: a CTF trace cannot contain another
1058 * CTF trace.
1059 */
1060 ret = add_trace_path(trace_paths, start_path);
1061 goto end;
1062 }
1063
1064 /* Look for subdirectories */
1065 if (!g_file_test(start_path, G_FILE_TEST_IS_DIR)) {
1066 /* Starting path is not a directory: end of recursion */
1067 goto end;
1068 }
1069
1070 dir = g_dir_open(start_path, 0, &error);
1071 if (!dir) {
1072 if (error->code == G_FILE_ERROR_ACCES) {
1073 BT_LOGD("Cannot open directory `%s`: %s (code %d): continuing",
1074 start_path, error->message, error->code);
1075 goto end;
1076 }
1077
1078 BT_LOGE("Cannot open directory `%s`: %s (code %d)",
1079 start_path, error->message, error->code);
1080 ret = -1;
1081 goto end;
1082 }
1083
1084 while ((basename = g_dir_read_name(dir))) {
1085 GString *sub_path = g_string_new(NULL);
1086
1087 if (!sub_path) {
1088 ret = -1;
1089 goto end;
1090 }
1091
1092 g_string_printf(sub_path, "%s" G_DIR_SEPARATOR_S "%s", start_path, basename);
1093 ret = ctf_fs_find_traces(trace_paths, sub_path->str);
1094 g_string_free(sub_path, TRUE);
1095 if (ret) {
1096 goto end;
1097 }
1098 }
1099
1100 end:
1101 if (dir) {
1102 g_dir_close(dir);
1103 }
1104
1105 if (error) {
1106 g_error_free(error);
1107 }
1108
1109 return ret;
1110 }
1111
1112 BT_HIDDEN
1113 GList *ctf_fs_create_trace_names(GList *trace_paths, const char *base_path) {
1114 GList *trace_names = NULL;
1115 GList *node;
1116 const char *last_sep;
1117 size_t base_dist;
1118
1119 /*
1120 * At this point we know that all the trace paths are
1121 * normalized, and so is the base path. This means that
1122 * they are absolute and they don't end with a separator.
1123 * We can simply find the location of the last separator
1124 * in the base path, which gives us the name of the actual
1125 * directory to look into, and use this location as the
1126 * start of each trace name within each trace path.
1127 *
1128 * For example:
1129 *
1130 * Base path: /home/user/my-traces/some-trace
1131 * Trace paths:
1132 * - /home/user/my-traces/some-trace/host1/trace1
1133 * - /home/user/my-traces/some-trace/host1/trace2
1134 * - /home/user/my-traces/some-trace/host2/trace
1135 * - /home/user/my-traces/some-trace/other-trace
1136 *
1137 * In this case the trace names are:
1138 *
1139 * - some-trace/host1/trace1
1140 * - some-trace/host1/trace2
1141 * - some-trace/host2/trace
1142 * - some-trace/other-trace
1143 */
1144 last_sep = strrchr(base_path, G_DIR_SEPARATOR);
1145
1146 /* We know there's at least one separator */
1147 BT_ASSERT(last_sep);
1148
1149 /* Distance to base */
1150 base_dist = last_sep - base_path + 1;
1151
1152 /* Create the trace names */
1153 for (node = trace_paths; node; node = g_list_next(node)) {
1154 GString *trace_name = g_string_new(NULL);
1155 GString *trace_path = node->data;
1156
1157 BT_ASSERT(trace_name);
1158 g_string_assign(trace_name, &trace_path->str[base_dist]);
1159 trace_names = g_list_append(trace_names, trace_name);
1160 }
1161
1162 return trace_names;
1163 }
1164
1165 static
1166 int create_ctf_fs_traces(struct ctf_fs_component *ctf_fs,
1167 const char *path_param)
1168 {
1169 struct ctf_fs_trace *ctf_fs_trace = NULL;
1170 int ret = 0;
1171 GString *norm_path = NULL;
1172 GList *trace_paths = NULL;
1173 GList *trace_names = NULL;
1174 GList *tp_node;
1175 GList *tn_node;
1176
1177 norm_path = bt_common_normalize_path(path_param, NULL);
1178 if (!norm_path) {
1179 BT_LOGE("Failed to normalize path: `%s`.",
1180 path_param);
1181 goto error;
1182 }
1183
1184 ret = ctf_fs_find_traces(&trace_paths, norm_path->str);
1185 if (ret) {
1186 goto error;
1187 }
1188
1189 if (!trace_paths) {
1190 BT_LOGE("No CTF traces recursively found in `%s`.",
1191 path_param);
1192 goto error;
1193 }
1194
1195 trace_names = ctf_fs_create_trace_names(trace_paths, norm_path->str);
1196 if (!trace_names) {
1197 BT_LOGE("Cannot create trace names from trace paths.");
1198 goto error;
1199 }
1200
1201 for (tp_node = trace_paths, tn_node = trace_names; tp_node;
1202 tp_node = g_list_next(tp_node),
1203 tn_node = g_list_next(tn_node)) {
1204 GString *trace_path = tp_node->data;
1205 GString *trace_name = tn_node->data;
1206
1207 ctf_fs_trace = ctf_fs_trace_create(trace_path->str,
1208 trace_name->str, &ctf_fs->metadata_config);
1209 if (!ctf_fs_trace) {
1210 BT_LOGE("Cannot create trace for `%s`.",
1211 trace_path->str);
1212 goto error;
1213 }
1214
1215 ret = create_ports_for_trace(ctf_fs, ctf_fs_trace);
1216 if (ret) {
1217 goto error;
1218 }
1219
1220 g_ptr_array_add(ctf_fs->traces, ctf_fs_trace);
1221 ctf_fs_trace = NULL;
1222 }
1223
1224 goto end;
1225
1226 error:
1227 ret = -1;
1228 ctf_fs_trace_destroy(ctf_fs_trace);
1229
1230 end:
1231 for (tp_node = trace_paths; tp_node; tp_node = g_list_next(tp_node)) {
1232 if (tp_node->data) {
1233 g_string_free(tp_node->data, TRUE);
1234 }
1235 }
1236
1237 for (tn_node = trace_names; tn_node; tn_node = g_list_next(tn_node)) {
1238 if (tn_node->data) {
1239 g_string_free(tn_node->data, TRUE);
1240 }
1241 }
1242
1243 if (trace_paths) {
1244 g_list_free(trace_paths);
1245 }
1246
1247 if (trace_names) {
1248 g_list_free(trace_names);
1249 }
1250
1251 if (norm_path) {
1252 g_string_free(norm_path, TRUE);
1253 }
1254
1255 return ret;
1256 }
1257
1258 static
1259 struct ctf_fs_component *ctf_fs_create(struct bt_private_component *priv_comp,
1260 struct bt_value *params)
1261 {
1262 struct ctf_fs_component *ctf_fs;
1263 struct bt_value *value = NULL;
1264 const char *path_param;
1265 enum bt_component_status ret;
1266 enum bt_value_status value_ret;
1267
1268 ctf_fs = g_new0(struct ctf_fs_component, 1);
1269 if (!ctf_fs) {
1270 goto end;
1271 }
1272
1273 ret = bt_private_component_set_user_data(priv_comp, ctf_fs);
1274 BT_ASSERT(ret == BT_COMPONENT_STATUS_OK);
1275
1276 /*
1277 * We don't need to get a new reference here because as long as
1278 * our private ctf_fs_component object exists, the containing
1279 * private component should also exist.
1280 */
1281 ctf_fs->priv_comp = priv_comp;
1282 value = bt_value_map_get(params, "path");
1283 if (value && !bt_value_is_string(value)) {
1284 goto error;
1285 }
1286
1287 value_ret = bt_value_string_get(value, &path_param);
1288 BT_ASSERT(value_ret == BT_VALUE_STATUS_OK);
1289 BT_PUT(value);
1290 value = bt_value_map_get(params, "clock-class-offset-s");
1291 if (value) {
1292 if (!bt_value_is_integer(value)) {
1293 BT_LOGE("clock-class-offset-s should be an integer");
1294 goto error;
1295 }
1296 value_ret = bt_value_integer_get(value,
1297 &ctf_fs->metadata_config.clock_class_offset_s);
1298 BT_ASSERT(value_ret == BT_VALUE_STATUS_OK);
1299 BT_PUT(value);
1300 }
1301
1302 value = bt_value_map_get(params, "clock-class-offset-ns");
1303 if (value) {
1304 if (!bt_value_is_integer(value)) {
1305 BT_LOGE("clock-class-offset-ns should be an integer");
1306 goto error;
1307 }
1308 value_ret = bt_value_integer_get(value,
1309 &ctf_fs->metadata_config.clock_class_offset_ns);
1310 BT_ASSERT(value_ret == BT_VALUE_STATUS_OK);
1311 BT_PUT(value);
1312 }
1313
1314 ctf_fs->port_data = g_ptr_array_new_with_free_func(port_data_destroy);
1315 if (!ctf_fs->port_data) {
1316 goto error;
1317 }
1318
1319 ctf_fs->traces = g_ptr_array_new_with_free_func(
1320 ctf_fs_trace_destroy_notifier);
1321 if (!ctf_fs->traces) {
1322 goto error;
1323 }
1324
1325 if (create_ctf_fs_traces(ctf_fs, path_param)) {
1326 goto error;
1327 }
1328
1329 goto end;
1330
1331 error:
1332 ctf_fs_destroy(ctf_fs);
1333 ctf_fs = NULL;
1334 ret = bt_private_component_set_user_data(priv_comp, NULL);
1335 BT_ASSERT(ret == BT_COMPONENT_STATUS_OK);
1336
1337 end:
1338 bt_put(value);
1339 return ctf_fs;
1340 }
1341
1342 BT_HIDDEN
1343 enum bt_component_status ctf_fs_init(struct bt_private_component *priv_comp,
1344 struct bt_value *params, UNUSED_VAR void *init_method_data)
1345 {
1346 struct ctf_fs_component *ctf_fs;
1347 enum bt_component_status ret = BT_COMPONENT_STATUS_OK;
1348
1349 ctf_fs = ctf_fs_create(priv_comp, params);
1350 if (!ctf_fs) {
1351 ret = BT_COMPONENT_STATUS_ERROR;
1352 }
1353
1354 return ret;
1355 }
1356
1357 BT_HIDDEN
1358 struct bt_component_class_query_method_return ctf_fs_query(
1359 struct bt_component_class *comp_class,
1360 struct bt_query_executor *query_exec,
1361 const char *object, struct bt_value *params)
1362 {
1363 struct bt_component_class_query_method_return ret = {
1364 .result = NULL,
1365 .status = BT_QUERY_STATUS_OK,
1366 };
1367
1368 if (!strcmp(object, "metadata-info")) {
1369 ret = metadata_info_query(comp_class, params);
1370 } else if (!strcmp(object, "trace-info")) {
1371 ret = trace_info_query(comp_class, params);
1372 } else {
1373 BT_LOGE("Unknown query object `%s`", object);
1374 ret.status = BT_QUERY_STATUS_INVALID_OBJECT;
1375 goto end;
1376 }
1377 end:
1378 return ret;
1379 }
This page took 0.061687 seconds and 5 git commands to generate.