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