ctf.fs: split streams, one per port
[babeltrace.git] / plugins / ctf / fs / fs.c
1 /*
2 * fs.c
3 *
4 * Babeltrace CTF file system Reader Component
5 *
6 * Copyright 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
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
29 #include <babeltrace/ctf-ir/packet.h>
30 #include <babeltrace/ctf-ir/clock-class.h>
31 #include <babeltrace/graph/private-port.h>
32 #include <babeltrace/graph/private-component.h>
33 #include <babeltrace/graph/private-component-source.h>
34 #include <babeltrace/graph/private-notification-iterator.h>
35 #include <babeltrace/graph/component.h>
36 #include <babeltrace/graph/notification-iterator.h>
37 #include <plugins-common.h>
38 #include <glib.h>
39 #include <assert.h>
40 #include <unistd.h>
41 #include "fs.h"
42 #include "metadata.h"
43 #include "data-stream.h"
44 #include "file.h"
45
46 #define PRINT_ERR_STREAM ctf_fs->error_fp
47 #define PRINT_PREFIX "ctf-fs"
48 #include "print.h"
49 #define METADATA_TEXT_SIG "/* CTF 1.8"
50
51 BT_HIDDEN
52 bool ctf_fs_debug;
53
54 struct bt_notification_iterator_next_return ctf_fs_iterator_next(
55 struct bt_private_notification_iterator *iterator)
56 {
57 struct ctf_fs_stream *fs_stream =
58 bt_private_notification_iterator_get_user_data(iterator);
59
60 return ctf_fs_stream_next(fs_stream);
61 }
62
63 void ctf_fs_iterator_finalize(struct bt_private_notification_iterator *it)
64 {
65 void *ctf_fs_stream =
66 bt_private_notification_iterator_get_user_data(it);
67
68 ctf_fs_stream_destroy(ctf_fs_stream);
69 }
70
71 enum bt_notification_iterator_status ctf_fs_iterator_init(
72 struct bt_private_notification_iterator *it,
73 struct bt_private_port *port)
74 {
75 struct ctf_fs_stream *stream = NULL;
76 struct ctf_fs_component *ctf_fs;
77 struct ctf_fs_port_data *port_data;
78 struct bt_private_component *priv_comp =
79 bt_private_notification_iterator_get_private_component(it);
80 enum bt_notification_iterator_status ret =
81 BT_NOTIFICATION_ITERATOR_STATUS_OK;
82
83 assert(priv_comp);
84
85 ctf_fs = bt_private_component_get_user_data(priv_comp);
86 if (!ctf_fs) {
87 ret = BT_NOTIFICATION_ITERATOR_STATUS_INVAL;
88 goto error;
89 }
90
91 port_data = bt_private_port_get_user_data(port);
92 if (!port_data) {
93 ret = BT_NOTIFICATION_ITERATOR_STATUS_INVAL;
94 goto error;
95 }
96
97 stream = ctf_fs_stream_create(ctf_fs, port_data->path->str);
98 if (!stream) {
99 goto error;
100 }
101
102 ret = bt_private_notification_iterator_set_user_data(it, stream);
103 if (ret) {
104 goto error;
105 }
106
107 stream = NULL;
108 goto end;
109
110 error:
111 (void) bt_private_notification_iterator_set_user_data(it, NULL);
112
113 if (ret == BT_NOTIFICATION_ITERATOR_STATUS_OK) {
114 ret = BT_NOTIFICATION_ITERATOR_STATUS_ERROR;
115 }
116
117 end:
118 ctf_fs_stream_destroy(stream);
119 bt_put(priv_comp);
120 return ret;
121 }
122
123 static
124 void ctf_fs_destroy_data(struct ctf_fs_component *ctf_fs)
125 {
126 if (!ctf_fs) {
127 return;
128 }
129
130 if (ctf_fs->trace_path) {
131 g_string_free(ctf_fs->trace_path, TRUE);
132 }
133
134 if (ctf_fs->port_data) {
135 g_ptr_array_free(ctf_fs->port_data, TRUE);
136 }
137
138 if (ctf_fs->metadata) {
139 ctf_fs_metadata_fini(ctf_fs->metadata);
140 g_free(ctf_fs->metadata);
141 }
142
143 g_free(ctf_fs);
144 }
145
146 void ctf_fs_finalize(struct bt_private_component *component)
147 {
148 void *data = bt_private_component_get_user_data(component);
149
150 ctf_fs_destroy_data(data);
151 }
152
153 static
154 void port_data_destroy(void *data) {
155 struct ctf_fs_port_data *port_data = data;
156
157 if (!port_data) {
158 return;
159 }
160
161 if (port_data->path) {
162 g_string_free(port_data->path, TRUE);
163 }
164
165 g_free(port_data);
166 }
167
168 static
169 int create_one_port(struct ctf_fs_component *ctf_fs,
170 const char *stream_basename, const char *stream_path)
171 {
172 int ret = 0;
173 struct bt_private_port *port = NULL;
174 struct ctf_fs_port_data *port_data = NULL;
175 GString *port_name = NULL;
176
177 port_name = g_string_new(NULL);
178 if (!port_name) {
179 goto error;
180 }
181
182 /* Assign the name for the new output port */
183 g_string_assign(port_name, "");
184 g_string_printf(port_name, "trace0-stream-%s", stream_basename);
185 PDBG("Creating one port named `%s` associated with path `%s`\n",
186 port_name->str, stream_path);
187
188 /* Create output port for this file */
189 port = bt_private_component_source_add_output_private_port(
190 ctf_fs->priv_comp, port_name->str);
191 if (!port) {
192 goto error;
193 }
194
195 port_data = g_new0(struct ctf_fs_port_data, 1);
196 if (!port_data) {
197 goto error;
198 }
199
200 port_data->path = g_string_new(stream_path);
201 if (!port_data->path) {
202 goto error;
203 }
204
205 ret = bt_private_port_set_user_data(port, port_data);
206 if (ret) {
207 goto error;
208 }
209
210 g_ptr_array_add(ctf_fs->port_data, port_data);
211 port_data = NULL;
212 goto end;
213
214 error:
215 ret = -1;
216
217 end:
218 if (port_name) {
219 g_string_free(port_name, TRUE);
220 }
221
222 bt_put(port);
223 port_data_destroy(port_data);
224 return ret;
225 }
226
227 static
228 int create_ports(struct ctf_fs_component *ctf_fs)
229 {
230 int ret = 0;
231 const char *basename;
232 GError *error = NULL;
233 GDir *dir = NULL;
234 struct bt_private_port *def_port;
235 struct ctf_fs_file *file = NULL;
236
237 /* Remove default port if needed */
238 def_port = bt_private_component_source_get_default_output_private_port(
239 ctf_fs->priv_comp);
240 if (def_port) {
241 bt_private_port_remove_from_component(def_port);
242 bt_put(def_port);
243 }
244
245 /* Create one output port for each stream file */
246 dir = g_dir_open(ctf_fs->trace_path->str, 0, &error);
247 if (!dir) {
248 PERR("Cannot open directory `%s`: %s (code %d)\n",
249 ctf_fs->trace_path->str, error->message,
250 error->code);
251 goto error;
252 }
253
254 while ((basename = g_dir_read_name(dir))) {
255 if (!strcmp(basename, CTF_FS_METADATA_FILENAME)) {
256 /* Ignore the metadata stream. */
257 PDBG("Ignoring metadata file `%s`\n", basename);
258 continue;
259 }
260
261 if (basename[0] == '.') {
262 PDBG("Ignoring hidden file `%s`\n", basename);
263 continue;
264 }
265
266 /* Create the file. */
267 file = ctf_fs_file_create(ctf_fs);
268 if (!file) {
269 PERR("Cannot create stream file object for file `%s`\n",
270 basename);
271 goto error;
272 }
273
274 /* Create full path string. */
275 g_string_append_printf(file->path, "%s/%s",
276 ctf_fs->trace_path->str, basename);
277 if (!g_file_test(file->path->str, G_FILE_TEST_IS_REGULAR)) {
278 PDBG("Ignoring non-regular file `%s`\n", basename);
279 ctf_fs_file_destroy(file);
280 file = NULL;
281 continue;
282 }
283
284 ret = ctf_fs_file_open(ctf_fs, file, "rb");
285 if (ret) {
286 PERR("Cannot open stream file `%s`\n", basename);
287 goto error;
288 }
289
290 if (file->size == 0) {
291 /* Skip empty stream. */
292 PDBG("Ignoring empty file `%s`\n", basename);
293 ctf_fs_file_destroy(file);
294 file = NULL;
295 continue;
296 }
297
298 ret = create_one_port(ctf_fs, basename, file->path->str);
299 if (ret) {
300 PERR("Cannot create output port for file `%s`\n",
301 basename);
302 goto error;
303 }
304
305 ctf_fs_file_destroy(file);
306 file = NULL;
307 }
308
309 goto end;
310
311 error:
312 ret = -1;
313
314 end:
315 if (dir) {
316 g_dir_close(dir);
317 dir = NULL;
318 }
319
320 if (error) {
321 g_error_free(error);
322 }
323
324 ctf_fs_file_destroy(file);
325 return ret;
326 }
327
328 static
329 struct ctf_fs_component *ctf_fs_create(struct bt_private_component *priv_comp,
330 struct bt_value *params)
331 {
332 struct ctf_fs_component *ctf_fs;
333 struct bt_value *value = NULL;
334 const char *path;
335 int ret;
336
337 ctf_fs = g_new0(struct ctf_fs_component, 1);
338 if (!ctf_fs) {
339 goto end;
340 }
341
342 /*
343 * We don't need to get a new reference here because as long as
344 * our private ctf_fs_component object exists, the containing
345 * private component should also exist.
346 */
347 ctf_fs->priv_comp = priv_comp;
348
349 /* FIXME: should probably look for a source URI */
350 value = bt_value_map_get(params, "path");
351 if (!value || bt_value_is_null(value) || !bt_value_is_string(value)) {
352 goto error;
353 }
354
355 ret = bt_value_string_get(value, &path);
356 if (ret) {
357 goto error;
358 }
359
360 ctf_fs->port_data = g_ptr_array_new_with_free_func(port_data_destroy);
361 if (!ctf_fs->port_data) {
362 goto error;
363 }
364
365 ctf_fs->trace_path = g_string_new(path);
366 if (!ctf_fs->trace_path) {
367 goto error;
368 }
369 ctf_fs->error_fp = stderr;
370 ctf_fs->page_size = (size_t) getpagesize();
371
372 // FIXME: check error.
373 ctf_fs->metadata = g_new0(struct ctf_fs_metadata, 1);
374 if (!ctf_fs->metadata) {
375 goto error;
376 }
377
378 ret = ctf_fs_metadata_set_trace(ctf_fs);
379 if (ret) {
380 goto error;
381 }
382
383 ret = create_ports(ctf_fs);
384 if (ret) {
385 goto error;
386 }
387
388 goto end;
389
390 error:
391 ctf_fs_destroy_data(ctf_fs);
392 ctf_fs = NULL;
393 end:
394 BT_PUT(value);
395 return ctf_fs;
396 }
397
398 BT_HIDDEN
399 enum bt_component_status ctf_fs_init(struct bt_private_component *priv_comp,
400 struct bt_value *params, UNUSED_VAR void *init_method_data)
401 {
402 struct ctf_fs_component *ctf_fs;
403 enum bt_component_status ret = BT_COMPONENT_STATUS_OK;
404
405 assert(priv_comp);
406 ctf_fs_debug = g_strcmp0(getenv("CTF_FS_DEBUG"), "1") == 0;
407 ctf_fs = ctf_fs_create(priv_comp, params);
408 if (!ctf_fs) {
409 ret = BT_COMPONENT_STATUS_NOMEM;
410 goto end;
411 }
412
413 ret = bt_private_component_set_user_data(priv_comp, ctf_fs);
414 if (ret != BT_COMPONENT_STATUS_OK) {
415 goto error;
416 }
417 end:
418 return ret;
419 error:
420 (void) bt_private_component_set_user_data(priv_comp, NULL);
421 ctf_fs_destroy_data(ctf_fs);
422 return ret;
423 }
424
425 BT_HIDDEN
426 struct bt_value *ctf_fs_query(struct bt_component_class *comp_class,
427 const char *object, struct bt_value *params)
428 {
429 struct bt_value *results = NULL;
430 struct bt_value *path_value = NULL;
431 char *metadata_text = NULL;
432 FILE *metadata_fp = NULL;
433 GString *g_metadata_text = NULL;
434
435 if (strcmp(object, "metadata-info") == 0) {
436 int ret;
437 int bo;
438 const char *path;
439 bool is_packetized;
440
441 results = bt_value_map_create();
442 if (!results) {
443 goto error;
444 }
445
446 if (!bt_value_is_map(params)) {
447 fprintf(stderr,
448 "Query parameters is not a map value object\n");
449 goto error;
450 }
451
452 path_value = bt_value_map_get(params, "path");
453 ret = bt_value_string_get(path_value, &path);
454 if (ret) {
455 fprintf(stderr,
456 "Cannot get `path` string parameter\n");
457 goto error;
458 }
459
460 assert(path);
461 metadata_fp = ctf_fs_metadata_open_file(path);
462 if (!metadata_fp) {
463 fprintf(stderr,
464 "Cannot open trace at path `%s`\n", path);
465 goto error;
466 }
467
468 is_packetized = ctf_metadata_is_packetized(metadata_fp, &bo);
469
470 if (is_packetized) {
471 ret = ctf_metadata_packetized_file_to_buf(NULL,
472 metadata_fp, (uint8_t **) &metadata_text, bo);
473 if (ret) {
474 fprintf(stderr,
475 "Cannot decode packetized metadata file\n");
476 goto error;
477 }
478 } else {
479 long filesize;
480
481 fseek(metadata_fp, 0, SEEK_END);
482 filesize = ftell(metadata_fp);
483 rewind(metadata_fp);
484 metadata_text = malloc(filesize + 1);
485 if (!metadata_text) {
486 fprintf(stderr,
487 "Cannot allocate buffer for metadata text\n");
488 goto error;
489 }
490
491 if (fread(metadata_text, filesize, 1, metadata_fp) !=
492 1) {
493 fprintf(stderr,
494 "Cannot read metadata file\n");
495 goto error;
496 }
497
498 metadata_text[filesize] = '\0';
499 }
500
501 g_metadata_text = g_string_new(NULL);
502 if (!g_metadata_text) {
503 goto error;
504 }
505
506 if (strncmp(metadata_text, METADATA_TEXT_SIG,
507 sizeof(METADATA_TEXT_SIG) - 1) != 0) {
508 g_string_assign(g_metadata_text, METADATA_TEXT_SIG);
509 g_string_append(g_metadata_text, " */\n\n");
510 }
511
512 g_string_append(g_metadata_text, metadata_text);
513
514 ret = bt_value_map_insert_string(results, "text",
515 g_metadata_text->str);
516 if (ret) {
517 fprintf(stderr, "Cannot insert metadata text into results\n");
518 goto error;
519 }
520
521 ret = bt_value_map_insert_bool(results, "is-packetized",
522 is_packetized);
523 if (ret) {
524 fprintf(stderr, "Cannot insert is packetized into results\n");
525 goto error;
526 }
527 } else {
528 fprintf(stderr, "Unknown query object `%s`\n", object);
529 goto error;
530 }
531
532 goto end;
533
534 error:
535 BT_PUT(results);
536
537 end:
538 bt_put(path_value);
539 free(metadata_text);
540
541 if (g_metadata_text) {
542 g_string_free(g_metadata_text, TRUE);
543 }
544
545 if (metadata_fp) {
546 fclose(metadata_fp);
547 }
548 return results;
549 }
This page took 0.042102 seconds and 4 git commands to generate.