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