Port: Add bt_common_get_page_size
[babeltrace.git] / plugins / ctf / fs-src / fs.c
1 /*
2 * fs.c
3 *
4 * Babeltrace CTF file system Reader Component
5 *
6 * Copyright 2015-2017 Philippe Proulx <pproulx@efficios.com>
7 * Copyright 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a copy
10 * of this software and associated documentation files (the "Software"), to deal
11 * in the Software without restriction, including without limitation the rights
12 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 * copies of the Software, and to permit persons to whom the Software is
14 * furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 * SOFTWARE.
26 */
27
28 #include <babeltrace/common-internal.h>
29 #include <babeltrace/ctf-ir/packet.h>
30 #include <babeltrace/ctf-ir/clock-class.h>
31 #include <babeltrace/ctf-ir/stream.h>
32 #include <babeltrace/ctf-ir/fields.h>
33 #include <babeltrace/graph/private-port.h>
34 #include <babeltrace/graph/private-component.h>
35 #include <babeltrace/graph/private-component-source.h>
36 #include <babeltrace/graph/private-notification-iterator.h>
37 #include <babeltrace/graph/component.h>
38 #include <babeltrace/graph/notification-iterator.h>
39 #include <babeltrace/graph/clock-class-priority-map.h>
40 #include <plugins-common.h>
41 #include <glib.h>
42 #include <assert.h>
43 #include <inttypes.h>
44 #include <stdbool.h>
45 #include "fs.h"
46 #include "metadata.h"
47 #include "data-stream-file.h"
48 #include "file.h"
49 #include "../common/metadata/decoder.h"
50
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 *norm_path = NULL;
977 int ret = 0;
978
979 norm_path = bt_common_normalize_path(path, NULL);
980 if (!norm_path) {
981 PERR("Failed to normalize path `%s`.\n", path);
982 ret = -1;
983 goto end;
984 }
985
986 if (strcmp(norm_path->str, "/") == 0) {
987 PERR("Opening a trace in `/` is not supported.\n");
988 ret = -1;
989 goto end;
990 }
991
992 *trace_paths = g_list_prepend(*trace_paths, norm_path);
993 assert(*trace_paths);
994 norm_path = NULL;
995
996 end:
997 if (norm_path) {
998 g_string_free(norm_path, TRUE);
999 }
1000
1001 return ret;
1002 }
1003
1004 static
1005 int find_ctf_traces(struct ctf_fs_component *ctf_fs,
1006 GList **trace_paths, const char *start_path)
1007 {
1008 int ret;
1009 GError *error = NULL;
1010 GDir *dir = NULL;
1011 const char *basename = NULL;
1012
1013 /* Check if the starting path is a CTF trace itself */
1014 ret = path_is_ctf_trace(start_path);
1015 if (ret < 0) {
1016 goto end;
1017 }
1018
1019 if (ret) {
1020 /*
1021 * Stop recursion: a CTF trace cannot contain another
1022 * CTF trace.
1023 */
1024 ret = add_trace_path(ctf_fs, trace_paths, start_path);
1025 goto end;
1026 }
1027
1028 /* Look for subdirectories */
1029 if (!g_file_test(start_path, G_FILE_TEST_IS_DIR)) {
1030 /* Starting path is not a directory: end of recursion */
1031 goto end;
1032 }
1033
1034 dir = g_dir_open(start_path, 0, &error);
1035 if (!dir) {
1036 if (error->code == G_FILE_ERROR_ACCES) {
1037 PDBG("Cannot open directory `%s`: %s (code %d): continuing\n",
1038 start_path, error->message, error->code);
1039 goto end;
1040 }
1041
1042 PERR("Cannot open directory `%s`: %s (code %d)\n",
1043 start_path, error->message, error->code);
1044 ret = -1;
1045 goto end;
1046 }
1047
1048 while ((basename = g_dir_read_name(dir))) {
1049 GString *sub_path = g_string_new(NULL);
1050
1051 if (!sub_path) {
1052 ret = -1;
1053 goto end;
1054 }
1055
1056 g_string_printf(sub_path, "%s/%s", start_path, basename);
1057 ret = find_ctf_traces(ctf_fs, trace_paths, sub_path->str);
1058 g_string_free(sub_path, TRUE);
1059 if (ret) {
1060 goto end;
1061 }
1062 }
1063
1064 end:
1065 if (dir) {
1066 g_dir_close(dir);
1067 }
1068
1069 if (error) {
1070 g_error_free(error);
1071 }
1072
1073 return ret;
1074 }
1075
1076 static
1077 GList *create_trace_names(GList *trace_paths, const char *base_path) {
1078 GList *trace_names = NULL;
1079 GList *node;
1080 const char *last_sep;
1081 size_t base_dist;
1082
1083 /*
1084 * At this point we know that all the trace paths are
1085 * normalized, and so is the base path. This means that
1086 * they are absolute and they don't end with a separator.
1087 * We can simply find the location of the last separator
1088 * in the base path, which gives us the name of the actual
1089 * directory to look into, and use this location as the
1090 * start of each trace name within each trace path.
1091 *
1092 * For example:
1093 *
1094 * Base path: /home/user/my-traces/some-trace
1095 * Trace paths:
1096 * - /home/user/my-traces/some-trace/host1/trace1
1097 * - /home/user/my-traces/some-trace/host1/trace2
1098 * - /home/user/my-traces/some-trace/host2/trace
1099 * - /home/user/my-traces/some-trace/other-trace
1100 *
1101 * In this case the trace names are:
1102 *
1103 * - some-trace/host1/trace1
1104 * - some-trace/host1/trace2
1105 * - some-trace/host2/trace
1106 * - some-trace/other-trace
1107 */
1108 last_sep = strrchr(base_path, G_DIR_SEPARATOR);
1109
1110 /* We know there's at least one separator */
1111 assert(last_sep);
1112
1113 /* Distance to base */
1114 base_dist = last_sep - base_path + 1;
1115
1116 /* Create the trace names */
1117 for (node = trace_paths; node; node = g_list_next(node)) {
1118 GString *trace_name = g_string_new(NULL);
1119 GString *trace_path = node->data;
1120
1121 assert(trace_name);
1122 g_string_assign(trace_name, &trace_path->str[base_dist]);
1123 trace_names = g_list_append(trace_names, trace_name);
1124 }
1125
1126 return trace_names;
1127 }
1128
1129 static
1130 int create_ctf_fs_traces(struct ctf_fs_component *ctf_fs,
1131 const char *path_param)
1132 {
1133 struct ctf_fs_trace *ctf_fs_trace = NULL;
1134 int ret = 0;
1135 GString *norm_path = NULL;
1136 GList *trace_paths = NULL;
1137 GList *trace_names = NULL;
1138 GList *tp_node;
1139 GList *tn_node;
1140
1141 norm_path = bt_common_normalize_path(path_param, NULL);
1142 if (!norm_path) {
1143 PERR("Failed to normalize path: `%s`.\n",
1144 path_param);
1145 goto error;
1146 }
1147
1148 ret = find_ctf_traces(ctf_fs, &trace_paths, norm_path->str);
1149 if (ret) {
1150 goto error;
1151 }
1152
1153 if (!trace_paths) {
1154 PERR("No CTF traces recursively found in `%s`.\n",
1155 path_param);
1156 goto error;
1157 }
1158
1159 trace_names = create_trace_names(trace_paths, norm_path->str);
1160 if (!trace_names) {
1161 PERR("Cannot create trace names from trace paths.\n");
1162 goto error;
1163 }
1164
1165 for (tp_node = trace_paths, tn_node = trace_names; tp_node;
1166 tp_node = g_list_next(tp_node),
1167 tn_node = g_list_next(tn_node)) {
1168 GString *trace_path = tp_node->data;
1169 GString *trace_name = tn_node->data;
1170
1171 ctf_fs_trace = ctf_fs_trace_create(ctf_fs, trace_path->str,
1172 trace_name->str);
1173 if (!ctf_fs_trace) {
1174 PERR("Cannot create trace for `%s`.\n",
1175 trace_path->str);
1176 goto error;
1177 }
1178
1179 g_ptr_array_add(ctf_fs->traces, ctf_fs_trace);
1180 ctf_fs_trace = NULL;
1181 }
1182
1183 goto end;
1184
1185 error:
1186 ret = -1;
1187 ctf_fs_trace_destroy(ctf_fs_trace);
1188
1189 end:
1190 for (tp_node = trace_paths; tp_node; tp_node = g_list_next(tp_node)) {
1191 if (tp_node->data) {
1192 g_string_free(tp_node->data, TRUE);
1193 }
1194 }
1195
1196 for (tn_node = trace_names; tn_node; tn_node = g_list_next(tn_node)) {
1197 if (tn_node->data) {
1198 g_string_free(tn_node->data, TRUE);
1199 }
1200 }
1201
1202 if (trace_paths) {
1203 g_list_free(trace_paths);
1204 }
1205
1206 if (trace_names) {
1207 g_list_free(trace_names);
1208 }
1209
1210 if (norm_path) {
1211 g_string_free(norm_path, TRUE);
1212 }
1213
1214 return ret;
1215 }
1216
1217 static
1218 struct ctf_fs_component *ctf_fs_create(struct bt_private_component *priv_comp,
1219 struct bt_value *params)
1220 {
1221 struct ctf_fs_component *ctf_fs;
1222 struct bt_value *value = NULL;
1223 const char *path_param;
1224 int ret;
1225
1226 ctf_fs = g_new0(struct ctf_fs_component, 1);
1227 if (!ctf_fs) {
1228 goto end;
1229 }
1230
1231 ret = bt_private_component_set_user_data(priv_comp, ctf_fs);
1232 assert(ret == 0);
1233
1234 /*
1235 * We don't need to get a new reference here because as long as
1236 * our private ctf_fs_component object exists, the containing
1237 * private component should also exist.
1238 */
1239 ctf_fs->priv_comp = priv_comp;
1240 value = bt_value_map_get(params, "path");
1241 if (!bt_value_is_string(value)) {
1242 goto error;
1243 }
1244
1245 ret = bt_value_string_get(value, &path_param);
1246 assert(ret == 0);
1247 BT_PUT(value);
1248 value = bt_value_map_get(params, "offset-s");
1249 if (value) {
1250 int64_t offset;
1251
1252 if (!bt_value_is_integer(value)) {
1253 fprintf(stderr,
1254 "offset-s should be an integer\n");
1255 goto error;
1256 }
1257 ret = bt_value_integer_get(value, &offset);
1258 assert(ret == 0);
1259 ctf_fs->options.clock_offset = offset;
1260 BT_PUT(value);
1261 }
1262
1263 value = bt_value_map_get(params, "offset-ns");
1264 if (value) {
1265 int64_t offset;
1266
1267 if (!bt_value_is_integer(value)) {
1268 fprintf(stderr,
1269 "offset-ns should be an integer\n");
1270 goto error;
1271 }
1272 ret = bt_value_integer_get(value, &offset);
1273 assert(ret == 0);
1274 ctf_fs->options.clock_offset_ns = offset;
1275 BT_PUT(value);
1276 }
1277
1278 ctf_fs->error_fp = stderr;
1279 ctf_fs->page_size = bt_common_get_page_size();
1280 ctf_fs->port_data = g_ptr_array_new_with_free_func(port_data_destroy);
1281 if (!ctf_fs->port_data) {
1282 goto error;
1283 }
1284
1285 ctf_fs->traces = g_ptr_array_new_with_free_func(ctf_fs_trace_destroy);
1286 if (!ctf_fs->traces) {
1287 goto error;
1288 }
1289
1290 ret = create_ctf_fs_traces(ctf_fs, path_param);
1291 if (ret) {
1292 goto error;
1293 }
1294
1295 goto end;
1296
1297 error:
1298 ctf_fs_destroy(ctf_fs);
1299 ctf_fs = NULL;
1300 ret = bt_private_component_set_user_data(priv_comp, NULL);
1301 assert(ret == 0);
1302
1303 end:
1304 bt_put(value);
1305 return ctf_fs;
1306 }
1307
1308 BT_HIDDEN
1309 enum bt_component_status ctf_fs_init(struct bt_private_component *priv_comp,
1310 struct bt_value *params, UNUSED_VAR void *init_method_data)
1311 {
1312 struct ctf_fs_component *ctf_fs;
1313 enum bt_component_status ret = BT_COMPONENT_STATUS_OK;
1314
1315 ctf_fs_debug = g_strcmp0(getenv("CTF_FS_DEBUG"), "1") == 0;
1316 ctf_fs = ctf_fs_create(priv_comp, params);
1317 if (!ctf_fs) {
1318 ret = BT_COMPONENT_STATUS_ERROR;
1319 }
1320
1321 return ret;
1322 }
1323
1324 BT_HIDDEN
1325 struct bt_value *ctf_fs_query(struct bt_component_class *comp_class,
1326 const char *object, struct bt_value *params)
1327 {
1328 struct bt_value *results = NULL;
1329 struct bt_value *path_value = NULL;
1330 char *metadata_text = NULL;
1331 FILE *metadata_fp = NULL;
1332 GString *g_metadata_text = NULL;
1333
1334 if (strcmp(object, "metadata-info") == 0) {
1335 int ret;
1336 int bo;
1337 const char *path;
1338 bool is_packetized;
1339
1340 results = bt_value_map_create();
1341 if (!results) {
1342 goto error;
1343 }
1344
1345 if (!bt_value_is_map(params)) {
1346 fprintf(stderr,
1347 "Query parameters is not a map value object\n");
1348 goto error;
1349 }
1350
1351 path_value = bt_value_map_get(params, "path");
1352 ret = bt_value_string_get(path_value, &path);
1353 if (ret) {
1354 fprintf(stderr,
1355 "Cannot get `path` string parameter\n");
1356 goto error;
1357 }
1358
1359 assert(path);
1360 metadata_fp = ctf_fs_metadata_open_file(path);
1361 if (!metadata_fp) {
1362 fprintf(stderr,
1363 "Cannot open trace at path `%s`\n", path);
1364 goto error;
1365 }
1366
1367 is_packetized = ctf_metadata_decoder_is_packetized(metadata_fp,
1368 &bo);
1369
1370 if (is_packetized) {
1371 ret = ctf_metadata_decoder_packetized_file_stream_to_buf(
1372 metadata_fp, &metadata_text, bo);
1373 if (ret) {
1374 fprintf(stderr,
1375 "Cannot decode packetized metadata file\n");
1376 goto error;
1377 }
1378 } else {
1379 long filesize;
1380
1381 fseek(metadata_fp, 0, SEEK_END);
1382 filesize = ftell(metadata_fp);
1383 rewind(metadata_fp);
1384 metadata_text = malloc(filesize + 1);
1385 if (!metadata_text) {
1386 fprintf(stderr,
1387 "Cannot allocate buffer for metadata text\n");
1388 goto error;
1389 }
1390
1391 if (fread(metadata_text, filesize, 1, metadata_fp) !=
1392 1) {
1393 fprintf(stderr,
1394 "Cannot read metadata file\n");
1395 goto error;
1396 }
1397
1398 metadata_text[filesize] = '\0';
1399 }
1400
1401 g_metadata_text = g_string_new(NULL);
1402 if (!g_metadata_text) {
1403 goto error;
1404 }
1405
1406 if (strncmp(metadata_text, METADATA_TEXT_SIG,
1407 sizeof(METADATA_TEXT_SIG) - 1) != 0) {
1408 g_string_assign(g_metadata_text, METADATA_TEXT_SIG);
1409 g_string_append(g_metadata_text, " */\n\n");
1410 }
1411
1412 g_string_append(g_metadata_text, metadata_text);
1413
1414 ret = bt_value_map_insert_string(results, "text",
1415 g_metadata_text->str);
1416 if (ret) {
1417 fprintf(stderr, "Cannot insert metadata text into results\n");
1418 goto error;
1419 }
1420
1421 ret = bt_value_map_insert_bool(results, "is-packetized",
1422 is_packetized);
1423 if (ret) {
1424 fprintf(stderr, "Cannot insert is packetized into results\n");
1425 goto error;
1426 }
1427 } else {
1428 fprintf(stderr, "Unknown query object `%s`\n", object);
1429 goto error;
1430 }
1431
1432 goto end;
1433
1434 error:
1435 BT_PUT(results);
1436
1437 end:
1438 bt_put(path_value);
1439 free(metadata_text);
1440
1441 if (g_metadata_text) {
1442 g_string_free(g_metadata_text, TRUE);
1443 }
1444
1445 if (metadata_fp) {
1446 fclose(metadata_fp);
1447 }
1448 return results;
1449 }
This page took 0.060529 seconds and 5 git commands to generate.