ctf.fs source: add trace file rotation (stream instance ID) support
[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 goto end;
934
935 error:
936 ctf_fs_trace_destroy(ctf_fs_trace);
937 ctf_fs_trace = NULL;
938 end:
939 return ctf_fs_trace;
940 }
941
942 static
943 int path_is_ctf_trace(const char *path)
944 {
945 GString *metadata_path = g_string_new(NULL);
946 int ret = 0;
947
948 if (!metadata_path) {
949 ret = -1;
950 goto end;
951 }
952
953 g_string_printf(metadata_path, "%s/%s", path, CTF_FS_METADATA_FILENAME);
954
955 if (g_file_test(metadata_path->str, G_FILE_TEST_IS_REGULAR)) {
956 ret = 1;
957 goto end;
958 }
959
960 end:
961 g_string_free(metadata_path, TRUE);
962 return ret;
963 }
964
965 static
966 int add_trace_path(struct ctf_fs_component *ctf_fs, GList **trace_paths,
967 const char *path)
968 {
969 GString *path_str = g_string_new(NULL);
970 int ret = 0;
971 char *rp = NULL;
972
973 if (!path_str) {
974 ret = -1;
975 goto end;
976 }
977
978 /*
979 * Find the real path so that we don't have relative components
980 * in the trace name. This also squashes consecutive slashes and
981 * removes any slash at the end.
982 */
983 rp = realpath(path, NULL);
984 if (!rp) {
985 PERR("realpath() failed: %s (%d)\n", strerror(errno), errno);
986 ret = -1;
987 goto end;
988 }
989
990 if (strcmp(rp, "/") == 0) {
991 PERR("Opening a trace in `/` is not supported.\n");
992 ret = -1;
993 goto end;
994 }
995
996 g_string_assign(path_str, rp);
997 *trace_paths = g_list_prepend(*trace_paths, path_str);
998 assert(*trace_paths);
999
1000 end:
1001 free(rp);
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 * Do not even recurse: a CTF trace cannot contain
1023 * another 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) {
1079 GList *trace_names = NULL;
1080 size_t chars_to_strip = 0;
1081 size_t at = 0;
1082 GList *node;
1083 bool done = false;
1084
1085 /*
1086 * Find the number of characters to strip from the beginning,
1087 * that is, the longest prefix until a common slash (also
1088 * stripped).
1089 */
1090 while (true) {
1091 gchar common_ch = '\0';
1092
1093 for (node = trace_paths; node; node = g_list_next(node)) {
1094 GString *gstr = node->data;
1095 gchar this_ch = gstr->str[at];
1096
1097 if (this_ch == '\0') {
1098 done = true;
1099 break;
1100 }
1101
1102 if (common_ch == '\0') {
1103 /*
1104 * Establish the expected common
1105 * character at this position.
1106 */
1107 common_ch = this_ch;
1108 continue;
1109 }
1110
1111 if (this_ch != common_ch) {
1112 done = true;
1113 break;
1114 }
1115 }
1116
1117 if (done) {
1118 break;
1119 }
1120
1121 if (common_ch == '/') {
1122 /*
1123 * Common character is a slash: safe to include
1124 * this slash in the number of characters to
1125 * strip because the paths are guaranteed not to
1126 * end with slash.
1127 */
1128 chars_to_strip = at + 1;
1129 }
1130
1131 at++;
1132 }
1133
1134 /* Create the trace names */
1135 for (node = trace_paths; node; node = g_list_next(node)) {
1136 GString *trace_name = g_string_new(NULL);
1137 GString *trace_path = node->data;
1138
1139 g_string_assign(trace_name, &trace_path->str[chars_to_strip]);
1140 trace_names = g_list_append(trace_names, trace_name);
1141 }
1142
1143 return trace_names;
1144 }
1145
1146 static
1147 int create_ctf_fs_traces(struct ctf_fs_component *ctf_fs,
1148 const char *path_param)
1149 {
1150 struct ctf_fs_trace *ctf_fs_trace = NULL;
1151 int ret = 0;
1152 GList *trace_paths = NULL;
1153 GList *trace_names = NULL;
1154 GList *tp_node;
1155 GList *tn_node;
1156
1157 ret = find_ctf_traces(ctf_fs, &trace_paths, path_param);
1158 if (ret) {
1159 goto error;
1160 }
1161
1162 if (!trace_paths) {
1163 PERR("No CTF traces recursively found in `%s`.\n",
1164 path_param);
1165 goto error;
1166 }
1167
1168 trace_names = create_trace_names(trace_paths);
1169 if (!trace_names) {
1170 PERR("Cannot create trace names from trace paths.\n");
1171 goto error;
1172 }
1173
1174 for (tp_node = trace_paths, tn_node = trace_names; tp_node;
1175 tp_node = g_list_next(tp_node),
1176 tn_node = g_list_next(tn_node)) {
1177 GString *trace_path = tp_node->data;
1178 GString *trace_name = tn_node->data;
1179
1180 ctf_fs_trace = ctf_fs_trace_create(ctf_fs, trace_path->str,
1181 trace_name->str);
1182 if (!ctf_fs_trace) {
1183 PERR("Cannot create trace for `%s`.\n",
1184 trace_path->str);
1185 goto error;
1186 }
1187
1188 g_ptr_array_add(ctf_fs->traces, ctf_fs_trace);
1189 ctf_fs_trace = NULL;
1190 }
1191
1192 goto end;
1193
1194 error:
1195 ret = -1;
1196 ctf_fs_trace_destroy(ctf_fs_trace);
1197
1198 end:
1199 for (tp_node = trace_paths; tp_node; tp_node = g_list_next(tp_node)) {
1200 if (tp_node->data) {
1201 g_string_free(tp_node->data, TRUE);
1202 }
1203 }
1204
1205 for (tn_node = trace_names; tn_node; tn_node = g_list_next(tn_node)) {
1206 if (tn_node->data) {
1207 g_string_free(tn_node->data, TRUE);
1208 }
1209 }
1210
1211 if (trace_paths) {
1212 g_list_free(trace_paths);
1213 }
1214
1215 if (trace_names) {
1216 g_list_free(trace_names);
1217 }
1218
1219 return ret;
1220 }
1221
1222 static
1223 struct ctf_fs_component *ctf_fs_create(struct bt_private_component *priv_comp,
1224 struct bt_value *params)
1225 {
1226 struct ctf_fs_component *ctf_fs;
1227 struct bt_value *value = NULL;
1228 const char *path_param;
1229 int ret;
1230
1231 ctf_fs = g_new0(struct ctf_fs_component, 1);
1232 if (!ctf_fs) {
1233 goto end;
1234 }
1235
1236 ret = bt_private_component_set_user_data(priv_comp, ctf_fs);
1237 assert(ret == 0);
1238
1239 /*
1240 * We don't need to get a new reference here because as long as
1241 * our private ctf_fs_component object exists, the containing
1242 * private component should also exist.
1243 */
1244 ctf_fs->priv_comp = priv_comp;
1245 value = bt_value_map_get(params, "path");
1246 if (!bt_value_is_string(value)) {
1247 goto error;
1248 }
1249
1250 ret = bt_value_string_get(value, &path_param);
1251 assert(ret == 0);
1252 BT_PUT(value);
1253 value = bt_value_map_get(params, "offset-s");
1254 if (value) {
1255 int64_t offset;
1256
1257 if (!bt_value_is_integer(value)) {
1258 fprintf(stderr,
1259 "offset-s should be an integer\n");
1260 goto error;
1261 }
1262 ret = bt_value_integer_get(value, &offset);
1263 assert(ret == 0);
1264 ctf_fs->options.clock_offset = offset;
1265 BT_PUT(value);
1266 }
1267
1268 value = bt_value_map_get(params, "offset-ns");
1269 if (value) {
1270 int64_t offset;
1271
1272 if (!bt_value_is_integer(value)) {
1273 fprintf(stderr,
1274 "offset-ns should be an integer\n");
1275 goto error;
1276 }
1277 ret = bt_value_integer_get(value, &offset);
1278 assert(ret == 0);
1279 ctf_fs->options.clock_offset_ns = offset;
1280 BT_PUT(value);
1281 }
1282
1283 ctf_fs->error_fp = stderr;
1284 ctf_fs->page_size = (size_t) getpagesize();
1285 ctf_fs->port_data = g_ptr_array_new_with_free_func(port_data_destroy);
1286 if (!ctf_fs->port_data) {
1287 goto error;
1288 }
1289
1290 ctf_fs->traces = g_ptr_array_new_with_free_func(ctf_fs_trace_destroy);
1291 if (!ctf_fs->traces) {
1292 goto error;
1293 }
1294
1295 ret = create_ctf_fs_traces(ctf_fs, path_param);
1296 if (ret) {
1297 goto error;
1298 }
1299
1300 goto end;
1301
1302 error:
1303 ctf_fs_destroy(ctf_fs);
1304 ctf_fs = NULL;
1305 ret = bt_private_component_set_user_data(priv_comp, NULL);
1306 assert(ret == 0);
1307
1308 end:
1309 bt_put(value);
1310 return ctf_fs;
1311 }
1312
1313 BT_HIDDEN
1314 enum bt_component_status ctf_fs_init(struct bt_private_component *priv_comp,
1315 struct bt_value *params, UNUSED_VAR void *init_method_data)
1316 {
1317 struct ctf_fs_component *ctf_fs;
1318 enum bt_component_status ret = BT_COMPONENT_STATUS_OK;
1319
1320 ctf_fs_debug = g_strcmp0(getenv("CTF_FS_DEBUG"), "1") == 0;
1321 ctf_fs = ctf_fs_create(priv_comp, params);
1322 if (!ctf_fs) {
1323 ret = BT_COMPONENT_STATUS_ERROR;
1324 }
1325
1326 return ret;
1327 }
1328
1329 BT_HIDDEN
1330 struct bt_value *ctf_fs_query(struct bt_component_class *comp_class,
1331 const char *object, struct bt_value *params)
1332 {
1333 struct bt_value *results = NULL;
1334 struct bt_value *path_value = NULL;
1335 char *metadata_text = NULL;
1336 FILE *metadata_fp = NULL;
1337 GString *g_metadata_text = NULL;
1338
1339 if (strcmp(object, "metadata-info") == 0) {
1340 int ret;
1341 int bo;
1342 const char *path;
1343 bool is_packetized;
1344
1345 results = bt_value_map_create();
1346 if (!results) {
1347 goto error;
1348 }
1349
1350 if (!bt_value_is_map(params)) {
1351 fprintf(stderr,
1352 "Query parameters is not a map value object\n");
1353 goto error;
1354 }
1355
1356 path_value = bt_value_map_get(params, "path");
1357 ret = bt_value_string_get(path_value, &path);
1358 if (ret) {
1359 fprintf(stderr,
1360 "Cannot get `path` string parameter\n");
1361 goto error;
1362 }
1363
1364 assert(path);
1365 metadata_fp = ctf_fs_metadata_open_file(path);
1366 if (!metadata_fp) {
1367 fprintf(stderr,
1368 "Cannot open trace at path `%s`\n", path);
1369 goto error;
1370 }
1371
1372 is_packetized = ctf_metadata_decoder_is_packetized(metadata_fp,
1373 &bo);
1374
1375 if (is_packetized) {
1376 ret = ctf_metadata_decoder_packetized_file_stream_to_buf(
1377 metadata_fp, &metadata_text, bo);
1378 if (ret) {
1379 fprintf(stderr,
1380 "Cannot decode packetized metadata file\n");
1381 goto error;
1382 }
1383 } else {
1384 long filesize;
1385
1386 fseek(metadata_fp, 0, SEEK_END);
1387 filesize = ftell(metadata_fp);
1388 rewind(metadata_fp);
1389 metadata_text = malloc(filesize + 1);
1390 if (!metadata_text) {
1391 fprintf(stderr,
1392 "Cannot allocate buffer for metadata text\n");
1393 goto error;
1394 }
1395
1396 if (fread(metadata_text, filesize, 1, metadata_fp) !=
1397 1) {
1398 fprintf(stderr,
1399 "Cannot read metadata file\n");
1400 goto error;
1401 }
1402
1403 metadata_text[filesize] = '\0';
1404 }
1405
1406 g_metadata_text = g_string_new(NULL);
1407 if (!g_metadata_text) {
1408 goto error;
1409 }
1410
1411 if (strncmp(metadata_text, METADATA_TEXT_SIG,
1412 sizeof(METADATA_TEXT_SIG) - 1) != 0) {
1413 g_string_assign(g_metadata_text, METADATA_TEXT_SIG);
1414 g_string_append(g_metadata_text, " */\n\n");
1415 }
1416
1417 g_string_append(g_metadata_text, metadata_text);
1418
1419 ret = bt_value_map_insert_string(results, "text",
1420 g_metadata_text->str);
1421 if (ret) {
1422 fprintf(stderr, "Cannot insert metadata text into results\n");
1423 goto error;
1424 }
1425
1426 ret = bt_value_map_insert_bool(results, "is-packetized",
1427 is_packetized);
1428 if (ret) {
1429 fprintf(stderr, "Cannot insert is packetized into results\n");
1430 goto error;
1431 }
1432 } else {
1433 fprintf(stderr, "Unknown query object `%s`\n", object);
1434 goto error;
1435 }
1436
1437 goto end;
1438
1439 error:
1440 BT_PUT(results);
1441
1442 end:
1443 bt_put(path_value);
1444 free(metadata_text);
1445
1446 if (g_metadata_text) {
1447 g_string_free(g_metadata_text, TRUE);
1448 }
1449
1450 if (metadata_fp) {
1451 fclose(metadata_fp);
1452 }
1453 return results;
1454 }
This page took 0.06221 seconds and 5 git commands to generate.