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