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