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