Sort includes in 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
c802cacb
SM
7#include <glib.h>
8#include <stdbool.h>
9#include <stdio.h>
10
11#include <babeltrace2/babeltrace.h>
12
aa1a7452 13#define BT_COMP_LOG_SELF_COMP (trace->fs_sink->self_comp)
4164020e
SM
14#define BT_LOG_OUTPUT_LEVEL (trace->log_level)
15#define BT_LOG_TAG "PLUGIN/SINK.CTF.FS/TRACE"
d9c39b0a 16#include "logging/comp-logging.h"
15fe47e0 17
578e048b
MJ
18#include "common/assert.h"
19#include "ctfser/ctfser.h"
15fe47e0 20
087cd0f5 21#include "fs-sink-stream.hpp"
c802cacb
SM
22#include "fs-sink-trace.hpp"
23#include "fs-sink.hpp"
24#include "translate-ctf-ir-to-tsdl.hpp"
25#include "translate-trace-ir-to-ctf-ir.hpp"
15fe47e0
PP
26
27/*
28 * Sanitizes `path` so as to:
29 *
30 * * Replace `.` subdirectories with `_`.
31 * * Replace `..` subdirectories with `__`.
32 * * Remove trailing slashes.
33 */
4164020e 34static GString *sanitize_trace_path(const char *path)
15fe47e0 35{
4164020e
SM
36 GString *san_path = g_string_new(NULL);
37 const char *ch = path;
38 bool dir_start = true;
39
40 BT_ASSERT(san_path);
41 BT_ASSERT(path);
42
43 while (*ch != '\0') {
44 switch (*ch) {
45 case '/':
46 /* Start of directory */
47 dir_start = true;
48 g_string_append_c(san_path, *ch);
49 ch++;
50 continue;
51 case '.':
52 if (dir_start) {
53 switch (ch[1]) {
54 case '\0':
55 case '/':
56 /* `.` -> `_` */
57 g_string_append_c(san_path, '_');
58 ch++;
59 continue;
60 case '.':
61 switch (ch[2]) {
62 case '\0':
63 case '/':
64 /* `..` -> `__` */
65 g_string_append(san_path, "__");
66 ch += 2;
67 continue;
68 default:
69 break;
70 }
71 default:
72 break;
73 }
74 }
75 default:
76 break;
77 }
78
79 /* Not a special character */
80 g_string_append_c(san_path, *ch);
81 ch++;
82 dir_start = false;
83 }
84
85 /* Remove trailing slashes */
86 while (san_path->len > 0 && 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;
15fe47e0
PP
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
4164020e 104static GString *make_unique_trace_path(const char *path)
15fe47e0 105{
4164020e
SM
106 GString *unique_path;
107 unsigned int suffix = 0;
15fe47e0 108
4164020e 109 unique_path = g_string_new(path);
be38c486 110
4164020e
SM
111 while (g_file_test(unique_path->str, G_FILE_TEST_EXISTS)) {
112 g_string_printf(unique_path, "%s-%u", path, suffix);
113 suffix++;
114 }
be38c486 115
4164020e 116 return unique_path;
be38c486
SM
117}
118
f2817f2e
PP
119/*
120 * Disable `deprecated-declarations` warnings for
121 * lttng_validate_datetime() because we're using `GTimeVal` and
122 * g_time_val_from_iso8601() which are deprecated since GLib 2.56
123 * (Babeltrace supports older versions too).
124 */
125#pragma GCC diagnostic push
126#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
127
be38c486
SM
128/*
129 * Validate that the input string `datetime` is an ISO8601-compliant string (the
130 * format used by LTTng in the metadata).
131 */
132
4164020e 133static int lttng_validate_datetime(const struct fs_sink_trace *trace, const char *datetime)
be38c486 134{
4164020e
SM
135 GTimeVal tv;
136 int ret = -1;
137
138 /*
139 * We are using g_time_val_from_iso8601, as the safer/more modern
140 * alternative, g_date_time_new_from_iso8601, is only available in
141 * glib >= 2.56, and this is sufficient for our use case of validating
142 * the format.
143 */
144 if (!g_time_val_from_iso8601(datetime, &tv)) {
145 BT_COMP_LOGI("Couldn't parse datetime as ISO 8601: date=\"%s\"", datetime);
146 goto end;
147 }
148
149 ret = 0;
be38c486
SM
150
151end:
4164020e 152 return ret;
be38c486
SM
153}
154
f2817f2e
PP
155#pragma GCC diagnostic pop
156
4164020e
SM
157static int append_lttng_trace_path_ust_uid(const struct fs_sink_trace *trace, GString *path,
158 const bt_trace *tc)
be38c486 159{
4164020e
SM
160 const bt_value *v;
161 int ret;
be38c486 162
4164020e
SM
163 v = bt_trace_borrow_environment_entry_value_by_name_const(tc, "tracer_buffering_id");
164 if (!v || !bt_value_is_signed_integer(v)) {
165 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_buffering_id\"");
166 goto error;
167 }
be38c486 168
4164020e 169 g_string_append_printf(path, G_DIR_SEPARATOR_S "%" PRId64, bt_value_integer_signed_get(v));
be38c486 170
4164020e
SM
171 v = bt_trace_borrow_environment_entry_value_by_name_const(tc, "architecture_bit_width");
172 if (!v || !bt_value_is_signed_integer(v)) {
173 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"architecture_bit_width\"");
174 goto error;
175 }
be38c486 176
4164020e
SM
177 g_string_append_printf(path, G_DIR_SEPARATOR_S "%" PRIu64 "-bit",
178 bt_value_integer_signed_get(v));
be38c486 179
4164020e
SM
180 ret = 0;
181 goto end;
be38c486
SM
182
183error:
4164020e 184 ret = -1;
be38c486 185
15fe47e0 186end:
4164020e 187 return ret;
be38c486
SM
188}
189
4164020e
SM
190static int append_lttng_trace_path_ust_pid(const struct fs_sink_trace *trace, GString *path,
191 const bt_trace *tc)
be38c486 192{
4164020e
SM
193 const bt_value *v;
194 const char *datetime;
195 int ret;
be38c486 196
4164020e
SM
197 v = bt_trace_borrow_environment_entry_value_by_name_const(tc, "procname");
198 if (!v || !bt_value_is_string(v)) {
199 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"procname\"");
200 goto error;
201 }
be38c486 202
4164020e 203 g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", bt_value_string_get(v));
be38c486 204
4164020e
SM
205 v = bt_trace_borrow_environment_entry_value_by_name_const(tc, "vpid");
206 if (!v || !bt_value_is_signed_integer(v)) {
207 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"vpid\"");
208 goto error;
209 }
be38c486 210
4164020e 211 g_string_append_printf(path, "-%" PRId64, bt_value_integer_signed_get(v));
be38c486 212
4164020e
SM
213 v = bt_trace_borrow_environment_entry_value_by_name_const(tc, "vpid_datetime");
214 if (!v || !bt_value_is_string(v)) {
215 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"vpid_datetime\"");
216 goto error;
217 }
be38c486 218
4164020e 219 datetime = bt_value_string_get(v);
be38c486 220
4164020e
SM
221 if (lttng_validate_datetime(trace, datetime)) {
222 goto error;
223 }
be38c486 224
4164020e 225 g_string_append_printf(path, "-%s", datetime);
be38c486 226
4164020e
SM
227 ret = 0;
228 goto end;
be38c486
SM
229
230error:
4164020e 231 ret = -1;
be38c486
SM
232
233end:
4164020e 234 return ret;
be38c486
SM
235}
236
237/*
238 * Try to build a trace path based on environment values put in the trace
239 * environment by the LTTng tracer, starting with version 2.11.
240 */
4164020e 241static GString *make_lttng_trace_path_rel(const struct fs_sink_trace *trace)
be38c486 242{
4164020e
SM
243 const bt_value *v;
244 const char *tracer_name, *domain, *datetime;
245 int64_t tracer_major, tracer_minor;
246 GString *path;
247
248 path = g_string_new(NULL);
249 if (!path) {
250 goto error;
251 }
252
253 v = bt_trace_borrow_environment_entry_value_by_name_const(trace->ir_trace, "tracer_name");
254 if (!v || !bt_value_is_string(v)) {
255 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_name\"");
256 goto error;
257 }
258
259 tracer_name = bt_value_string_get(v);
260
261 if (!g_str_equal(tracer_name, "lttng-ust") && !g_str_equal(tracer_name, "lttng-modules")) {
262 BT_COMP_LOGI("Unrecognized tracer name: name=\"%s\"", tracer_name);
263 goto error;
264 }
265
266 v = bt_trace_borrow_environment_entry_value_by_name_const(trace->ir_trace, "tracer_major");
267 if (!v || !bt_value_is_signed_integer(v)) {
268 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_major\"");
269 goto error;
270 }
271
272 tracer_major = bt_value_integer_signed_get(v);
273
274 v = bt_trace_borrow_environment_entry_value_by_name_const(trace->ir_trace, "tracer_minor");
275 if (!v || !bt_value_is_signed_integer(v)) {
276 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_minor\"");
277 goto error;
278 }
279
280 tracer_minor = bt_value_integer_signed_get(v);
281
282 if (!(tracer_major >= 3 || (tracer_major == 2 && tracer_minor >= 11))) {
283 BT_COMP_LOGI("Unsupported LTTng version for automatic trace path: major=%" PRId64
284 ", minor=%" PRId64,
285 tracer_major, tracer_minor);
286 goto error;
287 }
288
289 v = bt_trace_borrow_environment_entry_value_by_name_const(trace->ir_trace, "hostname");
290 if (!v || !bt_value_is_string(v)) {
291 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_hostname\"");
292 goto error;
293 }
294
295 g_string_assign(path, bt_value_string_get(v));
296
297 v = bt_trace_borrow_environment_entry_value_by_name_const(trace->ir_trace, "trace_name");
298 if (!v || !bt_value_is_string(v)) {
299 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"trace_name\"");
300 goto error;
301 }
302
303 g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", bt_value_string_get(v));
304
305 v = bt_trace_borrow_environment_entry_value_by_name_const(trace->ir_trace,
306 "trace_creation_datetime");
307 if (!v || !bt_value_is_string(v)) {
308 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"trace_creation_datetime\"");
309 goto error;
310 }
311
312 datetime = bt_value_string_get(v);
313
314 if (lttng_validate_datetime(trace, datetime)) {
315 goto error;
316 }
317
318 g_string_append_printf(path, "-%s", datetime);
319
320 v = bt_trace_borrow_environment_entry_value_by_name_const(trace->ir_trace, "domain");
321 if (!v || !bt_value_is_string(v)) {
322 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"domain\"");
323 goto error;
324 }
325
326 domain = bt_value_string_get(v);
327 g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", domain);
328
329 if (g_str_equal(domain, "ust")) {
330 const char *tracer_buffering_scheme;
331
332 v = bt_trace_borrow_environment_entry_value_by_name_const(trace->ir_trace,
333 "tracer_buffering_scheme");
334 if (!v || !bt_value_is_string(v)) {
335 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_buffering_scheme\"");
336 goto error;
337 }
338
339 tracer_buffering_scheme = bt_value_string_get(v);
340 g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", tracer_buffering_scheme);
341
342 if (g_str_equal(tracer_buffering_scheme, "uid")) {
343 if (append_lttng_trace_path_ust_uid(trace, path, trace->ir_trace)) {
344 goto error;
345 }
346 } else if (g_str_equal(tracer_buffering_scheme, "pid")) {
347 if (append_lttng_trace_path_ust_pid(trace, path, trace->ir_trace)) {
348 goto error;
349 }
350 } else {
351 /* Unknown buffering scheme. */
352 BT_COMP_LOGI("Unknown buffering scheme: tracer_buffering_scheme=\"%s\"",
353 tracer_buffering_scheme);
354 goto error;
355 }
356 } else if (!g_str_equal(domain, "kernel")) {
357 /* Unknown domain. */
358 BT_COMP_LOGI("Unknown domain: domain=\"%s\"", domain);
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) {
492 BT_COMP_LOGF_ERRNO("In trace destruction listener: "
493 "cannot open metadata file for writing",
494 ": path=\"%s\"", trace->metadata_path->str);
495 bt_common_abort();
496 }
497
498 len = fwrite(tsdl->str, sizeof(*tsdl->str), tsdl->len, fh);
499 if (len != tsdl->len) {
500 BT_COMP_LOGF_ERRNO("In trace destruction listener: "
501 "cannot write metadata file",
502 ": path=\"%s\"", trace->metadata_path->str);
503 bt_common_abort();
504 }
505
506 if (!trace->fs_sink->quiet) {
507 printf("Created CTF trace `%s`.\n", trace->path->str);
508 }
509
510 if (trace->path) {
511 g_string_free(trace->path, TRUE);
512 trace->path = NULL;
513 }
514
515 if (fh) {
516 int ret = fclose(fh);
517
518 if (ret != 0) {
519 BT_COMP_LOGW_ERRNO("In trace destruction listener: "
520 "cannot close metadata file",
521 ": path=\"%s\"", trace->metadata_path->str);
522 }
523 }
524
525 g_string_free(trace->metadata_path, TRUE);
526 trace->metadata_path = NULL;
527
528 fs_sink_ctf_trace_destroy(trace->trace);
529 trace->trace = NULL;
530 g_free(trace);
531
532 g_string_free(tsdl, TRUE);
15fe47e0 533
3e83e4f2 534end:
4164020e 535 return;
15fe47e0
PP
536}
537
4164020e 538static void ir_trace_destruction_listener(const bt_trace *ir_trace, void *data)
15fe47e0 539{
4164020e
SM
540 struct fs_sink_trace *trace = (fs_sink_trace *) data;
541
542 /*
543 * Prevent bt_trace_remove_destruction_listener() from being
544 * called in fs_sink_trace_destroy(), which is called by
545 * g_hash_table_remove() below.
546 */
547 trace->ir_trace_destruction_listener_id = UINT64_C(-1);
548 g_hash_table_remove(trace->fs_sink->traces, ir_trace);
15fe47e0
PP
549}
550
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.099275 seconds and 4 git commands to generate.