Print timestamps in text plug-in
[babeltrace.git] / plugins / ctf / fs / fs.c
CommitLineData
7a278c8e 1/*
ea0b4b9e 2 * fs.c
7a278c8e 3 *
ea0b4b9e 4 * Babeltrace CTF file system Reader Component
7a278c8e 5 *
f3bc2010 6 * Copyright 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
7a278c8e
JG
7 *
8 * Author: Jérémie Galarneau <jeremie.galarneau@efficios.com>
9 *
10 * Permission is hereby granted, free of charge, to any person obtaining a copy
11 * of this software and associated documentation files (the "Software"), to deal
12 * in the Software without restriction, including without limitation the rights
13 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 * copies of the Software, and to permit persons to whom the Software is
15 * furnished to do so, subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be included in
18 * all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 * SOFTWARE.
27 */
28
ea0b4b9e 29#include <babeltrace/plugin/plugin-system.h>
5b29e799 30#include <babeltrace/ctf-ir/packet.h>
760051fa 31#include <babeltrace/plugin/notification/iterator.h>
5b29e799
JG
32#include <babeltrace/plugin/notification/stream.h>
33#include <babeltrace/plugin/notification/event.h>
34#include <babeltrace/plugin/notification/packet.h>
35#include <babeltrace/plugin/notification/heap.h>
ea0b4b9e
JG
36#include <glib.h>
37#include <assert.h>
56a1cced
JG
38#include <unistd.h>
39#include "fs.h"
413bc2c4
JG
40#include "metadata.h"
41#include "data-stream.h"
e7a4393b
JG
42#include "file.h"
43
44#define PRINT_ERR_STREAM ctf_fs->error_fp
45#define PRINT_PREFIX "ctf-fs"
46#include "print.h"
ea0b4b9e
JG
47
48static bool ctf_fs_debug;
49
50static
760051fa
JG
51struct bt_notification *ctf_fs_iterator_get(
52 struct bt_notification_iterator *iterator)
ea0b4b9e 53{
5b29e799
JG
54 struct ctf_fs_iterator *ctf_it =
55 bt_notification_iterator_get_private_data(iterator);
d01e0f33 56
5b29e799 57 return bt_get(ctf_it->current_notification);
ea0b4b9e
JG
58}
59
60static
5b29e799
JG
61enum bt_notification_iterator_status ctf_fs_iterator_get_next_notification(
62 struct ctf_fs_iterator *it,
63 struct ctf_fs_stream *stream,
64 struct bt_notification **notification)
ea0b4b9e 65{
5b29e799 66 enum bt_ctf_notif_iter_status status;
d01e0f33 67 enum bt_notification_iterator_status ret;
d01e0f33 68
5b29e799
JG
69 if (stream->end_reached) {
70 status = BT_CTF_NOTIF_ITER_STATUS_EOF;
d01e0f33
JG
71 goto end;
72 }
73
5b29e799
JG
74 status = bt_ctf_notif_iter_get_next_notification(stream->notif_iter,
75 notification);
76 if (status != BT_CTF_NOTIF_ITER_STATUS_OK &&
77 status != BT_CTF_NOTIF_ITER_STATUS_EOF) {
d01e0f33
JG
78 goto end;
79 }
80
5b29e799
JG
81 /* Should be handled in bt_ctf_notif_iter_get_next_notification. */
82 if (status == BT_CTF_NOTIF_ITER_STATUS_EOF) {
83 *notification = bt_notification_stream_end_create(
84 stream->stream);
85 if (!*notification) {
86 status = BT_CTF_NOTIF_ITER_STATUS_ERROR;
87 }
88 status = BT_CTF_NOTIF_ITER_STATUS_OK;
89 stream->end_reached = true;
90 }
d01e0f33 91end:
5b29e799
JG
92 switch (status) {
93 case BT_CTF_NOTIF_ITER_STATUS_EOF:
94 ret = BT_NOTIFICATION_ITERATOR_STATUS_END;
95 break;
96 case BT_CTF_NOTIF_ITER_STATUS_OK:
97 ret = BT_NOTIFICATION_ITERATOR_STATUS_OK;
98 break;
99 case BT_CTF_NOTIF_ITER_STATUS_AGAIN:
100 /*
101 * Should not make it this far as this is medium-specific;
102 * there is nothing for the user to do and it should have been
103 * handled upstream.
104 */
105 assert(0);
106 case BT_CTF_NOTIF_ITER_STATUS_INVAL:
107 /* No argument provided by the user, so don't return INVAL. */
108 case BT_CTF_NOTIF_ITER_STATUS_ERROR:
109 default:
110 ret = BT_NOTIFICATION_ITERATOR_STATUS_ERROR;
111 break;
112 }
043e2020 113 return ret;
ea0b4b9e 114}
bfd20a42 115
5b29e799
JG
116/*
117 * Remove me. This is a temporary work-around due to our inhability to use
118 * libbabeltrace-ctf from libbabeltrace-plugin.
119 */
760051fa 120static
5b29e799
JG
121struct bt_ctf_stream *internal_bt_notification_get_stream(
122 struct bt_notification *notification)
760051fa 123{
5b29e799
JG
124 struct bt_ctf_stream *stream = NULL;
125
126 assert(notification);
127 switch (bt_notification_get_type(notification)) {
128 case BT_NOTIFICATION_TYPE_EVENT:
129 {
130 struct bt_ctf_event *event;
131
132 event = bt_notification_event_get_event(notification);
133 stream = bt_ctf_event_get_stream(event);
134 bt_put(event);
135 break;
136 }
137 case BT_NOTIFICATION_TYPE_PACKET_START:
138 {
139 struct bt_ctf_packet *packet;
140
141 packet = bt_notification_packet_start_get_packet(notification);
142 stream = bt_ctf_packet_get_stream(packet);
143 bt_put(packet);
144 break;
145 }
146 case BT_NOTIFICATION_TYPE_PACKET_END:
147 {
148 struct bt_ctf_packet *packet;
149
150 packet = bt_notification_packet_end_get_packet(notification);
151 stream = bt_ctf_packet_get_stream(packet);
152 bt_put(packet);
153 break;
154 }
155 case BT_NOTIFICATION_TYPE_STREAM_END:
156 stream = bt_notification_stream_end_get_stream(notification);
157 break;
158 default:
159 goto end;
160 }
161end:
162 return stream;
760051fa
JG
163}
164
165static
5b29e799 166enum bt_notification_iterator_status populate_heap(struct ctf_fs_iterator *it)
760051fa 167{
5b29e799
JG
168 size_t i, pending_streams_count = it->pending_streams->len;
169 enum bt_notification_iterator_status ret =
170 BT_NOTIFICATION_ITERATOR_STATUS_OK;
171
172 /* Insert one stream-associated notification for each stream. */
173 for (i = 0; i < pending_streams_count; i++) {
174 struct bt_notification *notification;
175 struct ctf_fs_stream *fs_stream;
176 struct bt_ctf_stream *stream;
177 size_t pending_stream_index = pending_streams_count - 1 - i;
178
179 fs_stream = g_ptr_array_index(it->pending_streams,
180 pending_stream_index);
181
182 do {
183 int heap_ret;
184
185 ret = ctf_fs_iterator_get_next_notification(
186 it, fs_stream, &notification);
187 if (ret && ret != BT_NOTIFICATION_ITERATOR_STATUS_END) {
188 printf_debug("Failed to populate heap at stream %zu\n",
189 pending_stream_index);
190 goto end;
191 }
192
193 stream = internal_bt_notification_get_stream(
194 notification);
195 if (stream) {
196 gboolean inserted;
197
198 /*
199 * Associate pending ctf_fs_stream to
200 * bt_ctf_stream. Ownership of stream
201 * is passed to the stream ht.
202 */
203 inserted = g_hash_table_insert(it->stream_ht,
204 stream, fs_stream);
205 if (!inserted) {
206 ret = BT_NOTIFICATION_ITERATOR_STATUS_NOMEM;
207 printf_debug("Failed to associate fs stream to ctf stream\n");
208 goto end;
209 }
210 }
211
212 heap_ret = bt_notification_heap_insert(
213 it->pending_notifications,
214 notification);
215 bt_put(notification);
216 if (heap_ret) {
217 ret = BT_NOTIFICATION_ITERATOR_STATUS_NOMEM;
218 printf_debug("Failed to insert notification in heap\n");
219 goto end;
220 }
221 } while (!stream && ret != BT_NOTIFICATION_ITERATOR_STATUS_END);
222 /*
223 * Set NULL so the destruction callback registered with the
224 * array is not invoked on the stream (its ownership was
225 * transferred to the streams hashtable).
226 */
227 g_ptr_array_index(it->pending_streams,
228 pending_stream_index) = NULL;
229 g_ptr_array_remove_index(it->pending_streams,
230 pending_stream_index);
231 }
760051fa 232
5b29e799
JG
233 g_ptr_array_free(it->pending_streams, TRUE);
234 it->pending_streams = NULL;
235end:
236 return ret;
760051fa
JG
237}
238
239static
5b29e799
JG
240enum bt_notification_iterator_status ctf_fs_iterator_next(
241 struct bt_notification_iterator *iterator)
4c1456f0 242{
5b29e799 243 int heap_ret;
a11bd504 244 struct bt_ctf_stream *stream = NULL;
5b29e799
JG
245 struct ctf_fs_stream *fs_stream;
246 struct bt_notification *notification;
247 struct bt_notification *next_stream_notification;
248 enum bt_notification_iterator_status ret =
249 BT_NOTIFICATION_ITERATOR_STATUS_OK;
250 struct ctf_fs_iterator *ctf_it =
251 bt_notification_iterator_get_private_data(iterator);
252
253 notification = bt_notification_heap_pop(ctf_it->pending_notifications);
254 if (!notification && !ctf_it->pending_streams) {
255 ret = BT_NOTIFICATION_ITERATOR_STATUS_END;
760051fa
JG
256 goto end;
257 }
258
5b29e799
JG
259 if (!notification && ctf_it->pending_streams) {
260 /*
261 * Insert at one notification per stream in the heap and pop
262 * one.
263 */
264 ret = populate_heap(ctf_it);
265 if (ret) {
266 goto end;
267 }
268
269 notification = bt_notification_heap_pop(
270 ctf_it->pending_notifications);
271 if (!notification) {
272 ret = BT_NOTIFICATION_ITERATOR_STATUS_END;
273 goto end;
274 }
760051fa
JG
275 }
276
5b29e799
JG
277 /* notification is set from here. */
278
279 stream = internal_bt_notification_get_stream(notification);
280 if (!stream) {
281 /*
282 * The current notification is not associated to a particular
283 * stream, there is no need to insert a new notification from
284 * a stream in the heap.
285 */
286 goto end;
760051fa
JG
287 }
288
5b29e799
JG
289 fs_stream = g_hash_table_lookup(ctf_it->stream_ht, stream);
290 if (!fs_stream) {
291 /* We have reached this stream's end. */
292 goto end;
760051fa
JG
293 }
294
5b29e799
JG
295 ret = ctf_fs_iterator_get_next_notification(ctf_it, fs_stream,
296 &next_stream_notification);
297 if ((ret && ret != BT_NOTIFICATION_ITERATOR_STATUS_END)) {
298 heap_ret = bt_notification_heap_insert(
299 ctf_it->pending_notifications, notification);
300
301 assert(!next_stream_notification);
302 if (heap_ret) {
303 /*
304 * We're dropping the most recent notification, but at
305 * this point, something is seriously wrong...
306 */
307 ret = BT_NOTIFICATION_ITERATOR_STATUS_NOMEM;
308 }
309 BT_PUT(notification);
310 goto end;
311 }
312
313 if (ret == BT_NOTIFICATION_ITERATOR_STATUS_END) {
314 gboolean success;
315
316 /* Remove stream. */
317 success = g_hash_table_remove(ctf_it->stream_ht, stream);
318 assert(success);
319 ret = BT_NOTIFICATION_ITERATOR_STATUS_OK;
320 } else {
321 heap_ret = bt_notification_heap_insert(ctf_it->pending_notifications,
322 next_stream_notification);
323 BT_PUT(next_stream_notification);
324 if (heap_ret) {
325 /*
326 * We're dropping the most recent notification...
327 */
328 ret = BT_NOTIFICATION_ITERATOR_STATUS_NOMEM;
329 }
760051fa 330 }
5b29e799
JG
331
332 /*
333 * Ensure that the stream is removed from both pending_streams and
334 * the streams hashtable on reception of the "end of stream"
335 * notification.
336 */
760051fa 337end:
5b29e799 338 BT_MOVE(ctf_it->current_notification, notification);
33bf10b7 339 bt_put(stream);
760051fa
JG
340 return ret;
341}
342
760051fa 343static
5b29e799 344void ctf_fs_iterator_destroy_data(struct ctf_fs_iterator *ctf_it)
760051fa 345{
5b29e799
JG
346 bt_put(ctf_it->current_notification);
347 bt_put(ctf_it->pending_notifications);
348 if (ctf_it->pending_streams) {
349 g_ptr_array_free(ctf_it->pending_streams, TRUE);
56a1cced 350 }
5b29e799
JG
351 if (ctf_it->stream_ht) {
352 g_hash_table_destroy(ctf_it->stream_ht);
c14d7e26 353 }
5b29e799 354 g_free(ctf_it);
760051fa
JG
355}
356
357static
5b29e799 358void ctf_fs_iterator_destroy(struct bt_notification_iterator *it)
760051fa 359{
5b29e799 360 void *data = bt_notification_iterator_get_private_data(it);
760051fa 361
5b29e799 362 ctf_fs_iterator_destroy_data(data);
4c1456f0
JG
363}
364
e7a4393b 365static
5b29e799
JG
366bool compare_notifications(struct bt_notification *a, struct bt_notification *b,
367 void *unused)
368{
369 return a < b;
370}
371
372static
373void stream_destroy(void *stream)
374{
375 ctf_fs_stream_destroy((struct ctf_fs_stream *) stream);
376}
377
378static
379int open_trace_streams(struct ctf_fs_component *ctf_fs,
380 struct ctf_fs_iterator *ctf_it)
e7a4393b
JG
381{
382 int ret = 0;
383 const char *name;
384 GError *error = NULL;
385 GDir *dir = g_dir_open(ctf_fs->trace_path->str, 0, &error);
386
387 if (!dir) {
388 PERR("Cannot open directory \"%s\": %s (code %d)\n",
389 ctf_fs->trace_path->str, error->message,
390 error->code);
391 goto error;
392 }
393
394 while ((name = g_dir_read_name(dir))) {
395 struct ctf_fs_file *file = NULL;
396 struct ctf_fs_stream *stream = NULL;
397
398 if (!strcmp(name, CTF_FS_METADATA_FILENAME)) {
399 /* Ignore the metadata stream. */
400 PDBG("Ignoring metadata file \"%s\"\n",
401 name);
402 continue;
403 }
404
405 if (name[0] == '.') {
406 PDBG("Ignoring hidden file \"%s\"\n",
407 name);
408 continue;
409 }
410
411 /* Create the file. */
412 file = ctf_fs_file_create(ctf_fs);
413 if (!file) {
414 PERR("Cannot create stream file object\n");
415 goto error;
416 }
417
418 /* Create full path string. */
419 g_string_append_printf(file->path, "%s/%s",
420 ctf_fs->trace_path->str, name);
421 if (!g_file_test(file->path->str, G_FILE_TEST_IS_REGULAR)) {
422 PDBG("Ignoring non-regular file \"%s\"\n", name);
423 ctf_fs_file_destroy(file);
424 continue;
425 }
426
427 /* Open the file. */
428 if (ctf_fs_file_open(ctf_fs, file, "rb")) {
429 ctf_fs_file_destroy(file);
430 goto error;
431 }
432
433 /* Create a private stream; file ownership is passed to it. */
434 stream = ctf_fs_stream_create(ctf_fs, file);
435 if (!stream) {
436 ctf_fs_file_destroy(file);
437 goto error;
438 }
439
5b29e799 440 g_ptr_array_add(ctf_it->pending_streams, stream);
e7a4393b
JG
441 }
442
443 goto end;
444error:
445 ret = -1;
446end:
447 if (dir) {
448 g_dir_close(dir);
449 dir = NULL;
450 }
451 if (error) {
452 g_error_free(error);
453 }
454 return ret;
455}
456
457static
5b29e799
JG
458enum bt_component_status ctf_fs_iterator_init(struct bt_component *source,
459 struct bt_notification_iterator *it)
e7a4393b 460{
5b29e799
JG
461 struct ctf_fs_iterator *ctf_it;
462 struct ctf_fs_component *ctf_fs;
463 enum bt_component_status ret = BT_COMPONENT_STATUS_OK;
464
465 assert(source && it);
466
467 ctf_fs = bt_component_get_private_data(source);
468 if (!ctf_fs) {
469 ret = BT_COMPONENT_STATUS_INVALID;
470 goto end;
471 }
472
473 ctf_it = g_new0(struct ctf_fs_iterator, 1);
474 if (!ctf_it) {
475 ret = BT_COMPONENT_STATUS_NOMEM;
476 goto end;
477 }
478
479 ctf_it->stream_ht = g_hash_table_new_full(g_direct_hash,
480 g_direct_equal, bt_put, stream_destroy);
481 if (!ctf_it->stream_ht) {
482 goto error;
483 }
484 ctf_it->pending_streams = g_ptr_array_new_with_free_func(
485 stream_destroy);
486 if (!ctf_it->pending_streams) {
487 goto error;
488 }
489 ctf_it->pending_notifications = bt_notification_heap_create(
490 compare_notifications, NULL);
491 if (!ctf_it->pending_notifications) {
492 goto error;
493 }
494
495 ret = open_trace_streams(ctf_fs, ctf_it);
496 if (ret) {
497 goto error;
498 }
499
500 ret = bt_notification_iterator_set_get_cb(it, ctf_fs_iterator_get);
501 if (ret) {
502 goto error;
503 }
504
505 ret = bt_notification_iterator_set_next_cb(it, ctf_fs_iterator_next);
506 if (ret) {
507 goto error;
508 }
509
510 ret = bt_notification_iterator_set_destroy_cb(it,
511 ctf_fs_iterator_destroy);
512 if (ret) {
513 goto error;
514 }
515
516 ret = bt_notification_iterator_set_private_data(it, ctf_it);
517 if (ret) {
518 goto error;
519 }
520end:
521 return ret;
522error:
523 (void) bt_notification_iterator_set_private_data(it, NULL);
524 ctf_fs_iterator_destroy_data(ctf_it);
525 goto end;
526}
527
528static
529void ctf_fs_destroy_data(struct ctf_fs_component *ctf_fs)
530{
531 if (ctf_fs->trace_path) {
532 g_string_free(ctf_fs->trace_path, TRUE);
533 }
534 if (ctf_fs->metadata) {
535 ctf_fs_metadata_fini(ctf_fs->metadata);
536 g_free(ctf_fs->metadata);
537 }
538 g_free(ctf_fs);
539}
540
541static
542void ctf_fs_destroy(struct bt_component *component)
543{
544 void *data = bt_component_get_private_data(component);
545
546 ctf_fs_destroy_data(data);
e7a4393b
JG
547}
548
56a1cced
JG
549static
550struct ctf_fs_component *ctf_fs_create(struct bt_value *params)
551{
552 struct ctf_fs_component *ctf_fs;
1ef09eb5 553 struct bt_value *value = NULL;
56a1cced
JG
554 const char *path;
555 enum bt_value_status ret;
556
557 ctf_fs = g_new0(struct ctf_fs_component, 1);
558 if (!ctf_fs) {
559 goto end;
560 }
561
562 /* FIXME: should probably look for a source URI */
563 value = bt_value_map_get(params, "path");
564 if (!value || bt_value_is_null(value) || !bt_value_is_string(value)) {
565 goto error;
566 }
567
568 ret = bt_value_string_get(value, &path);
569 if (ret != BT_VALUE_STATUS_OK) {
570 goto error;
571 }
572
573 ctf_fs->trace_path = g_string_new(path);
574 if (!ctf_fs->trace_path) {
575 goto error;
576 }
56a1cced
JG
577 ctf_fs->error_fp = stderr;
578 ctf_fs->page_size = (size_t) getpagesize();
e7a4393b
JG
579
580 // FIXME: check error.
5b29e799
JG
581 ctf_fs->metadata = g_new0(struct ctf_fs_metadata, 1);
582 if (!ctf_fs->metadata) {
e7a4393b
JG
583 goto error;
584 }
5b29e799 585 ctf_fs_metadata_set_trace(ctf_fs);
1ef09eb5
JG
586 goto end;
587
56a1cced
JG
588error:
589 ctf_fs_destroy_data(ctf_fs);
e7a4393b 590 ctf_fs = NULL;
1ef09eb5
JG
591end:
592 BT_PUT(value);
56a1cced
JG
593 return ctf_fs;
594}
595
ea0b4b9e
JG
596BT_HIDDEN
597enum bt_component_status ctf_fs_init(struct bt_component *source,
5c80adeb 598 struct bt_value *params)
ea0b4b9e
JG
599{
600 struct ctf_fs_component *ctf_fs;
601 enum bt_component_status ret = BT_COMPONENT_STATUS_OK;
602
603 assert(source);
604 ctf_fs_debug = g_strcmp0(getenv("CTF_FS_DEBUG"), "1") == 0;
605 ctf_fs = ctf_fs_create(params);
606 if (!ctf_fs) {
607 ret = BT_COMPONENT_STATUS_NOMEM;
608 goto end;
609 }
4c1456f0 610
ea0b4b9e
JG
611 ret = bt_component_set_destroy_cb(source, ctf_fs_destroy);
612 if (ret != BT_COMPONENT_STATUS_OK) {
613 goto error;
614 }
615
616 ret = bt_component_set_private_data(source, ctf_fs);
617 if (ret != BT_COMPONENT_STATUS_OK) {
618 goto error;
619 }
620
621 ret = bt_component_source_set_iterator_init_cb(source,
622 ctf_fs_iterator_init);
623 if (ret != BT_COMPONENT_STATUS_OK) {
624 goto error;
625 }
626end:
627 return ret;
628error:
629 (void) bt_component_set_private_data(source, NULL);
760051fa 630 ctf_fs_destroy_data(ctf_fs);
ea0b4b9e
JG
631 return ret;
632}
This page took 0.049972 seconds and 4 git commands to generate.