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