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