Fix: sink.ctf.fs: remove spurious directory level when using assume-single-trace
[babeltrace.git] / src / plugins / ctf / fs-sink / fs-sink-trace.c
CommitLineData
15fe47e0 1/*
0235b0db 2 * SPDX-License-Identifier: MIT
15fe47e0 3 *
0235b0db 4 * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
15fe47e0
PP
5 */
6
aa1a7452 7#define BT_COMP_LOG_SELF_COMP (trace->fs_sink->self_comp)
ffa3b2b3 8#define BT_LOG_OUTPUT_LEVEL (trace->log_level)
350ad6c1 9#define BT_LOG_TAG "PLUGIN/SINK.CTF.FS/TRACE"
d9c39b0a 10#include "logging/comp-logging.h"
15fe47e0 11
3fadfbc0 12#include <babeltrace2/babeltrace.h>
15fe47e0
PP
13#include <stdio.h>
14#include <stdbool.h>
15#include <glib.h>
578e048b
MJ
16#include "common/assert.h"
17#include "ctfser/ctfser.h"
15fe47e0
PP
18
19#include "translate-trace-ir-to-ctf-ir.h"
20#include "translate-ctf-ir-to-tsdl.h"
21#include "fs-sink.h"
22#include "fs-sink-trace.h"
23#include "fs-sink-stream.h"
24
25/*
26 * Sanitizes `path` so as to:
27 *
28 * * Replace `.` subdirectories with `_`.
29 * * Replace `..` subdirectories with `__`.
30 * * Remove trailing slashes.
31 */
32static
33GString *sanitize_trace_path(const char *path)
34{
35 GString *san_path = g_string_new(NULL);
36 const char *ch = path;
37 bool dir_start = true;
38
39 BT_ASSERT(san_path);
40 BT_ASSERT(path);
41
42 while (*ch != '\0') {
43 switch (*ch) {
44 case '/':
45 /* Start of directory */
46 dir_start = true;
47 g_string_append_c(san_path, *ch);
48 ch++;
49 continue;
50 case '.':
51 if (dir_start) {
52 switch (ch[1]) {
53 case '\0':
54 case '/':
55 /* `.` -> `_` */
56 g_string_append_c(san_path, '_');
57 ch++;
58 continue;
59 case '.':
60 switch (ch[2]) {
61 case '\0':
62 case '/':
63 /* `..` -> `__` */
64 g_string_append(san_path, "__");
65 ch += 2;
66 continue;
67 default:
68 break;
69 }
70 default:
71 break;
72 }
73 }
74 default:
75 break;
76 }
77
78 /* Not a special character */
79 g_string_append_c(san_path, *ch);
80 ch++;
81 dir_start = false;
82 }
83
84 /* Remove trailing slashes */
85 while (san_path->len > 0 &&
86 san_path->str[san_path->len - 1] == '/') {
87 /* Remove trailing slash */
88 g_string_set_size(san_path, san_path->len - 1);
89 }
90
91 if (san_path->len == 0) {
92 /* Looks like there's nothing left: just use `trace` */
93 g_string_assign(san_path, "trace");
94 }
95
96 return san_path;
97}
98
be38c486
SM
99/*
100 * Find a path based on `path` that doesn't exist yet. First, try `path`
101 * itself, then try with incrementing suffixes.
102 */
103
15fe47e0 104static
be38c486 105GString *make_unique_trace_path(const char *path)
15fe47e0 106{
be38c486 107 GString *unique_path;
15fe47e0
PP
108 unsigned int suffix = 0;
109
be38c486
SM
110 unique_path = g_string_new(path);
111
112 while (g_file_test(unique_path->str, G_FILE_TEST_EXISTS)) {
113 g_string_printf(unique_path, "%s-%u", path, suffix);
114 suffix++;
115 }
116
117 return unique_path;
118}
119
f2817f2e
PP
120/*
121 * Disable `deprecated-declarations` warnings for
122 * lttng_validate_datetime() because we're using `GTimeVal` and
123 * g_time_val_from_iso8601() which are deprecated since GLib 2.56
124 * (Babeltrace supports older versions too).
125 */
126#pragma GCC diagnostic push
127#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
128
be38c486
SM
129/*
130 * Validate that the input string `datetime` is an ISO8601-compliant string (the
131 * format used by LTTng in the metadata).
132 */
133
134static
ffa3b2b3
PP
135int lttng_validate_datetime(const struct fs_sink_trace *trace,
136 const char *datetime)
be38c486 137{
5ec1bd5c 138 GTimeVal tv;
be38c486
SM
139 int ret = -1;
140
5ec1bd5c
SM
141 /*
142 * We are using g_time_val_from_iso8601, as the safer/more modern
143 * alternative, g_date_time_new_from_iso8601, is only available in
144 * glib >= 2.56, and this is sufficient for our use case of validating
145 * the format.
146 */
147 if (!g_time_val_from_iso8601(datetime, &tv)) {
aa1a7452 148 BT_COMP_LOGI("Couldn't parse datetime as ISO 8601: date=\"%s\"", datetime);
be38c486
SM
149 goto end;
150 }
15fe47e0 151
be38c486
SM
152 ret = 0;
153
154end:
be38c486
SM
155 return ret;
156}
157
f2817f2e
PP
158#pragma GCC diagnostic pop
159
be38c486 160static
ffa3b2b3 161int append_lttng_trace_path_ust_uid(const struct fs_sink_trace *trace,
335a2da5 162 GString *path, const bt_trace *tc)
be38c486
SM
163{
164 const bt_value *v;
165 int ret;
166
335a2da5 167 v = bt_trace_borrow_environment_entry_value_by_name_const(tc, "tracer_buffering_id");
fdd3a2da 168 if (!v || !bt_value_is_signed_integer(v)) {
aa1a7452 169 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_buffering_id\"");
be38c486
SM
170 goto error;
171 }
172
fdd3a2da 173 g_string_append_printf(path, G_DIR_SEPARATOR_S "%" PRId64,
9c08c816 174 bt_value_integer_signed_get(v));
be38c486 175
4a543d06 176 v = bt_trace_borrow_environment_entry_value_by_name_const(tc, "architecture_bit_width");
fdd3a2da 177 if (!v || !bt_value_is_signed_integer(v)) {
4a543d06 178 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"architecture_bit_width\"");
be38c486
SM
179 goto error;
180 }
181
fdd3a2da 182 g_string_append_printf(path, G_DIR_SEPARATOR_S "%" PRIu64 "-bit",
9c08c816 183 bt_value_integer_signed_get(v));
be38c486
SM
184
185 ret = 0;
186 goto end;
187
188error:
189 ret = -1;
190
15fe47e0 191end:
be38c486
SM
192 return ret;
193}
194
195static
ffa3b2b3 196int append_lttng_trace_path_ust_pid(const struct fs_sink_trace *trace,
335a2da5 197 GString *path, const bt_trace *tc)
be38c486
SM
198{
199 const bt_value *v;
200 const char *datetime;
201 int ret;
202
335a2da5 203 v = bt_trace_borrow_environment_entry_value_by_name_const(tc, "procname");
be38c486 204 if (!v || !bt_value_is_string(v)) {
aa1a7452 205 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"procname\"");
be38c486
SM
206 goto error;
207 }
208
209 g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", bt_value_string_get(v));
210
335a2da5 211 v = bt_trace_borrow_environment_entry_value_by_name_const(tc, "vpid");
fdd3a2da 212 if (!v || !bt_value_is_signed_integer(v)) {
aa1a7452 213 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"vpid\"");
be38c486
SM
214 goto error;
215 }
216
9c08c816 217 g_string_append_printf(path, "-%" PRId64, bt_value_integer_signed_get(v));
be38c486 218
335a2da5 219 v = bt_trace_borrow_environment_entry_value_by_name_const(tc, "vpid_datetime");
be38c486 220 if (!v || !bt_value_is_string(v)) {
aa1a7452 221 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"vpid_datetime\"");
be38c486
SM
222 goto error;
223 }
224
225 datetime = bt_value_string_get(v);
226
ffa3b2b3 227 if (lttng_validate_datetime(trace, datetime)) {
be38c486
SM
228 goto error;
229 }
230
231 g_string_append_printf(path, "-%s", datetime);
232
233 ret = 0;
234 goto end;
235
236error:
237 ret = -1;
238
239end:
240 return ret;
241}
242
243/*
244 * Try to build a trace path based on environment values put in the trace
245 * environment by the LTTng tracer, starting with version 2.11.
246 */
247static
248GString *make_lttng_trace_path_rel(const struct fs_sink_trace *trace)
249{
be38c486
SM
250 const bt_value *v;
251 const char *tracer_name, *domain, *datetime;
252 int64_t tracer_major, tracer_minor;
253 GString *path;
254
255 path = g_string_new(NULL);
256 if (!path) {
257 goto error;
258 }
259
335a2da5
PP
260 v = bt_trace_borrow_environment_entry_value_by_name_const(
261 trace->ir_trace, "tracer_name");
be38c486 262 if (!v || !bt_value_is_string(v)) {
aa1a7452 263 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_name\"");
be38c486
SM
264 goto error;
265 }
266
267 tracer_name = bt_value_string_get(v);
268
269 if (!g_str_equal(tracer_name, "lttng-ust")
270 && !g_str_equal(tracer_name, "lttng-modules")) {
aa1a7452 271 BT_COMP_LOGI("Unrecognized tracer name: name=\"%s\"", tracer_name);
be38c486 272 goto error;
15fe47e0
PP
273 }
274
335a2da5
PP
275 v = bt_trace_borrow_environment_entry_value_by_name_const(
276 trace->ir_trace, "tracer_major");
fdd3a2da 277 if (!v || !bt_value_is_signed_integer(v)) {
aa1a7452 278 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_major\"");
be38c486
SM
279 goto error;
280 }
281
9c08c816 282 tracer_major = bt_value_integer_signed_get(v);
be38c486 283
335a2da5
PP
284 v = bt_trace_borrow_environment_entry_value_by_name_const(
285 trace->ir_trace, "tracer_minor");
fdd3a2da 286 if (!v || !bt_value_is_signed_integer(v)) {
aa1a7452 287 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_minor\"");
be38c486
SM
288 goto error;
289 }
290
9c08c816 291 tracer_minor = bt_value_integer_signed_get(v);
be38c486
SM
292
293 if (!(tracer_major >= 3 || (tracer_major == 2 && tracer_minor >= 11))) {
aa1a7452 294 BT_COMP_LOGI("Unsupported LTTng version for automatic trace path: major=%" PRId64 ", minor=%" PRId64,
be38c486
SM
295 tracer_major, tracer_minor);
296 goto error;
297 }
298
335a2da5
PP
299 v = bt_trace_borrow_environment_entry_value_by_name_const(
300 trace->ir_trace, "hostname");
be38c486 301 if (!v || !bt_value_is_string(v)) {
aa1a7452 302 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_hostname\"");
be38c486
SM
303 goto error;
304 }
305
306 g_string_assign(path, bt_value_string_get(v));
307
335a2da5
PP
308 v = bt_trace_borrow_environment_entry_value_by_name_const(
309 trace->ir_trace, "trace_name");
be38c486 310 if (!v || !bt_value_is_string(v)) {
aa1a7452 311 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"trace_name\"");
be38c486
SM
312 goto error;
313 }
314
315 g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", bt_value_string_get(v));
316
335a2da5
PP
317 v = bt_trace_borrow_environment_entry_value_by_name_const(
318 trace->ir_trace, "trace_creation_datetime");
be38c486 319 if (!v || !bt_value_is_string(v)) {
aa1a7452 320 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"trace_creation_datetime\"");
be38c486
SM
321 goto error;
322 }
323
324 datetime = bt_value_string_get(v);
325
ffa3b2b3 326 if (lttng_validate_datetime(trace, datetime)) {
be38c486
SM
327 goto error;
328 }
329
330 g_string_append_printf(path, "-%s", datetime);
331
335a2da5
PP
332 v = bt_trace_borrow_environment_entry_value_by_name_const(
333 trace->ir_trace, "domain");
be38c486 334 if (!v || !bt_value_is_string(v)) {
aa1a7452 335 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"domain\"");
be38c486
SM
336 goto error;
337 }
338
339 domain = bt_value_string_get(v);
340 g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", domain);
341
342 if (g_str_equal(domain, "ust")) {
343 const char *tracer_buffering_scheme;
344
335a2da5
PP
345 v = bt_trace_borrow_environment_entry_value_by_name_const(
346 trace->ir_trace, "tracer_buffering_scheme");
be38c486 347 if (!v || !bt_value_is_string(v)) {
aa1a7452 348 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_buffering_scheme\"");
be38c486
SM
349 goto error;
350 }
351
352 tracer_buffering_scheme = bt_value_string_get(v);
353 g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", tracer_buffering_scheme);
354
355 if (g_str_equal(tracer_buffering_scheme, "uid")) {
335a2da5
PP
356 if (append_lttng_trace_path_ust_uid(trace, path,
357 trace->ir_trace)) {
be38c486
SM
358 goto error;
359 }
360 } else if (g_str_equal(tracer_buffering_scheme, "pid")){
335a2da5
PP
361 if (append_lttng_trace_path_ust_pid(trace, path,
362 trace->ir_trace)) {
be38c486
SM
363 goto error;
364 }
365 } else {
366 /* Unknown buffering scheme. */
aa1a7452 367 BT_COMP_LOGI("Unknown buffering scheme: tracer_buffering_scheme=\"%s\"", tracer_buffering_scheme);
be38c486
SM
368 goto error;
369 }
370 } else if (!g_str_equal(domain, "kernel")) {
371 /* Unknown domain. */
aa1a7452 372 BT_COMP_LOGI("Unknown domain: domain=\"%s\"", domain);
be38c486
SM
373 goto error;
374 }
375
376 goto end;
377
378error:
379 if (path) {
380 g_string_free(path, TRUE);
381 path = NULL;
382 }
383
384end:
385 return path;
386}
387
388/* Build the relative output path for `trace`. */
389
390static
391GString *make_trace_path_rel(const struct fs_sink_trace *trace)
392{
393 GString *path = NULL;
96741e7f 394 const char *trace_name;
be38c486 395
72d458a3 396 BT_ASSERT(!trace->fs_sink->assume_single_trace);
be38c486
SM
397
398 /* First, try to build a path using environment fields written by LTTng. */
399 path = make_lttng_trace_path_rel(trace);
400 if (path) {
401 goto end;
402 }
403
404 /* Otherwise, use the trace name, if available. */
96741e7f 405 trace_name = bt_trace_get_name(trace->ir_trace);
be38c486
SM
406 if (trace_name) {
407 path = g_string_new(trace_name);
408 goto end;
409 }
410
411 /* Otherwise, use "trace". */
412 path = g_string_new("trace");
413
414end:
15fe47e0
PP
415 return path;
416}
417
be38c486
SM
418/*
419 * Compute the trace output path for `trace`, rooted at `output_base_directory`.
420 */
421
422static
423GString *make_trace_path(const struct fs_sink_trace *trace, const char *output_base_directory)
424{
425 GString *rel_path = NULL;
426 GString *rel_path_san = NULL;
427 GString *full_path = NULL;
428 GString *unique_full_path = NULL;
429
72d458a3
SM
430 if (trace->fs_sink->assume_single_trace) {
431 /* Use output directory directly */
432 unique_full_path = g_string_new(output_base_directory);
433 if (!unique_full_path) {
434 goto end;
435 }
436 } else {
437 rel_path = make_trace_path_rel(trace);
438 if (!rel_path) {
439 goto end;
440 }
be38c486 441
72d458a3
SM
442 rel_path_san = sanitize_trace_path(rel_path->str);
443 if (!rel_path_san) {
444 goto end;
445 }
be38c486 446
72d458a3
SM
447 full_path = g_string_new(NULL);
448 if (!full_path) {
449 goto end;
450 }
be38c486 451
72d458a3
SM
452 g_string_printf(full_path, "%s" G_DIR_SEPARATOR_S "%s",
453 output_base_directory, rel_path_san->str);
be38c486 454
72d458a3
SM
455 unique_full_path = make_unique_trace_path(full_path->str);
456 }
be38c486
SM
457
458end:
459 if (rel_path) {
460 g_string_free(rel_path, TRUE);
461 }
462
463 if (rel_path_san) {
464 g_string_free(rel_path_san, TRUE);
465 }
466
467 if (full_path) {
468 g_string_free(full_path, TRUE);
469 }
470
471 return unique_full_path;
472}
473
15fe47e0
PP
474BT_HIDDEN
475void fs_sink_trace_destroy(struct fs_sink_trace *trace)
476{
477 GString *tsdl = NULL;
478 FILE *fh = NULL;
479 size_t len;
480
481 if (!trace) {
482 goto end;
483 }
484
485 if (trace->ir_trace_destruction_listener_id != UINT64_C(-1)) {
486 /*
487 * Remove the destruction listener, otherwise it could
488 * be called in the future, and its private data is this
489 * CTF FS sink trace object which won't exist anymore.
490 */
491 (void) bt_trace_remove_destruction_listener(trace->ir_trace,
492 trace->ir_trace_destruction_listener_id);
493 trace->ir_trace_destruction_listener_id = UINT64_C(-1);
494 }
495
496 if (trace->streams) {
497 g_hash_table_destroy(trace->streams);
498 trace->streams = NULL;
499 }
500
501 tsdl = g_string_new(NULL);
502 BT_ASSERT(tsdl);
335a2da5 503 translate_trace_ctf_ir_to_tsdl(trace->trace, tsdl);
20078f92
FD
504
505 BT_ASSERT(trace->metadata_path);
15fe47e0
PP
506 fh = fopen(trace->metadata_path->str, "wb");
507 if (!fh) {
aa1a7452 508 BT_COMP_LOGF_ERRNO("In trace destruction listener: "
c6eb0200 509 "cannot open metadata file for writing",
15fe47e0 510 ": path=\"%s\"", trace->metadata_path->str);
498e7994 511 bt_common_abort();
15fe47e0
PP
512 }
513
514 len = fwrite(tsdl->str, sizeof(*tsdl->str), tsdl->len, fh);
515 if (len != tsdl->len) {
aa1a7452 516 BT_COMP_LOGF_ERRNO("In trace destruction listener: "
c6eb0200 517 "cannot write metadata file",
15fe47e0 518 ": path=\"%s\"", trace->metadata_path->str);
498e7994 519 bt_common_abort();
15fe47e0
PP
520 }
521
522 if (!trace->fs_sink->quiet) {
523 printf("Created CTF trace `%s`.\n", trace->path->str);
524 }
525
526 if (trace->path) {
527 g_string_free(trace->path, TRUE);
528 trace->path = NULL;
529 }
530
15fe47e0
PP
531 if (fh) {
532 int ret = fclose(fh);
533
534 if (ret != 0) {
aa1a7452 535 BT_COMP_LOGW_ERRNO("In trace destruction listener: "
c6eb0200 536 "cannot close metadata file",
15fe47e0
PP
537 ": path=\"%s\"", trace->metadata_path->str);
538 }
539 }
540
3e83e4f2
FD
541 g_string_free(trace->metadata_path, TRUE);
542 trace->metadata_path = NULL;
543
544 fs_sink_ctf_trace_destroy(trace->trace);
545 trace->trace = NULL;
546 g_free(trace);
547
57c48dcf 548 g_string_free(tsdl, TRUE);
15fe47e0 549
3e83e4f2 550end:
15fe47e0
PP
551 return;
552}
553
554static
555void ir_trace_destruction_listener(const bt_trace *ir_trace, void *data)
556{
557 struct fs_sink_trace *trace = data;
558
559 /*
560 * Prevent bt_trace_remove_destruction_listener() from being
561 * called in fs_sink_trace_destroy(), which is called by
562 * g_hash_table_remove() below.
563 */
564 trace->ir_trace_destruction_listener_id = UINT64_C(-1);
565 g_hash_table_remove(trace->fs_sink->traces, ir_trace);
566}
567
568BT_HIDDEN
569struct fs_sink_trace *fs_sink_trace_create(struct fs_sink_comp *fs_sink,
570 const bt_trace *ir_trace)
571{
572 int ret;
573 struct fs_sink_trace *trace = g_new0(struct fs_sink_trace, 1);
d24d5663 574 bt_trace_add_listener_status trace_status;
15fe47e0
PP
575
576 if (!trace) {
577 goto end;
578 }
579
ffa3b2b3 580 trace->log_level = fs_sink->log_level;
15fe47e0
PP
581 trace->fs_sink = fs_sink;
582 trace->ir_trace = ir_trace;
583 trace->ir_trace_destruction_listener_id = UINT64_C(-1);
335a2da5
PP
584 trace->trace = translate_trace_trace_ir_to_ctf_ir(fs_sink, ir_trace);
585 if (!trace->trace) {
15fe47e0
PP
586 goto error;
587 }
588
be38c486 589 trace->path = make_trace_path(trace, fs_sink->output_dir_path->str);
15fe47e0
PP
590 BT_ASSERT(trace->path);
591 ret = g_mkdir_with_parents(trace->path->str, 0755);
592 if (ret) {
aa1a7452 593 BT_COMP_LOGE_ERRNO("Cannot create directories for trace directory",
15fe47e0
PP
594 ": path=\"%s\"", trace->path->str);
595 goto error;
596 }
597
598 trace->metadata_path = g_string_new(trace->path->str);
599 BT_ASSERT(trace->metadata_path);
600 g_string_append(trace->metadata_path, "/metadata");
601 trace->streams = g_hash_table_new_full(g_direct_hash, g_direct_equal,
602 NULL, (GDestroyNotify) fs_sink_stream_destroy);
603 BT_ASSERT(trace->streams);
604 trace_status = bt_trace_add_destruction_listener(ir_trace,
605 ir_trace_destruction_listener, trace,
606 &trace->ir_trace_destruction_listener_id);
607 if (trace_status) {
608 goto error;
609 }
610
611 g_hash_table_insert(fs_sink->traces, (gpointer) ir_trace, trace);
612 goto end;
613
614error:
615 fs_sink_trace_destroy(trace);
616 trace = NULL;
617
618end:
619 return trace;
620}
This page took 0.08475 seconds and 4 git commands to generate.