Remove stdbool.h includes from 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 7#include <glib.h>
c802cacb
SM
8#include <stdio.h>
9
10#include <babeltrace2/babeltrace.h>
11
aa1a7452 12#define BT_COMP_LOG_SELF_COMP (trace->fs_sink->self_comp)
4164020e
SM
13#define BT_LOG_OUTPUT_LEVEL (trace->log_level)
14#define BT_LOG_TAG "PLUGIN/SINK.CTF.FS/TRACE"
d9c39b0a 15#include "logging/comp-logging.h"
15fe47e0 16
578e048b
MJ
17#include "common/assert.h"
18#include "ctfser/ctfser.h"
15fe47e0 19
087cd0f5 20#include "fs-sink-stream.hpp"
c802cacb
SM
21#include "fs-sink-trace.hpp"
22#include "fs-sink.hpp"
23#include "translate-ctf-ir-to-tsdl.hpp"
24#include "translate-trace-ir-to-ctf-ir.hpp"
15fe47e0
PP
25
26/*
27 * Sanitizes `path` so as to:
28 *
29 * * Replace `.` subdirectories with `_`.
30 * * Replace `..` subdirectories with `__`.
31 * * Remove trailing slashes.
32 */
4164020e 33static GString *sanitize_trace_path(const char *path)
15fe47e0 34{
4164020e
SM
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 && san_path->str[san_path->len - 1] == '/') {
86 /* Remove trailing slash */
87 g_string_set_size(san_path, san_path->len - 1);
88 }
89
90 if (san_path->len == 0) {
91 /* Looks like there's nothing left: just use `trace` */
92 g_string_assign(san_path, "trace");
93 }
94
95 return san_path;
15fe47e0
PP
96}
97
be38c486
SM
98/*
99 * Find a path based on `path` that doesn't exist yet. First, try `path`
100 * itself, then try with incrementing suffixes.
101 */
102
4164020e 103static GString *make_unique_trace_path(const char *path)
15fe47e0 104{
4164020e
SM
105 GString *unique_path;
106 unsigned int suffix = 0;
15fe47e0 107
4164020e 108 unique_path = g_string_new(path);
be38c486 109
4164020e
SM
110 while (g_file_test(unique_path->str, G_FILE_TEST_EXISTS)) {
111 g_string_printf(unique_path, "%s-%u", path, suffix);
112 suffix++;
113 }
be38c486 114
4164020e 115 return unique_path;
be38c486
SM
116}
117
f2817f2e
PP
118/*
119 * Disable `deprecated-declarations` warnings for
120 * lttng_validate_datetime() because we're using `GTimeVal` and
121 * g_time_val_from_iso8601() which are deprecated since GLib 2.56
122 * (Babeltrace supports older versions too).
123 */
124#pragma GCC diagnostic push
125#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
126
be38c486
SM
127/*
128 * Validate that the input string `datetime` is an ISO8601-compliant string (the
129 * format used by LTTng in the metadata).
130 */
131
4164020e 132static int lttng_validate_datetime(const struct fs_sink_trace *trace, const char *datetime)
be38c486 133{
4164020e
SM
134 GTimeVal tv;
135 int ret = -1;
136
137 /*
138 * We are using g_time_val_from_iso8601, as the safer/more modern
139 * alternative, g_date_time_new_from_iso8601, is only available in
140 * glib >= 2.56, and this is sufficient for our use case of validating
141 * the format.
142 */
143 if (!g_time_val_from_iso8601(datetime, &tv)) {
144 BT_COMP_LOGI("Couldn't parse datetime as ISO 8601: date=\"%s\"", datetime);
145 goto end;
146 }
147
148 ret = 0;
be38c486
SM
149
150end:
4164020e 151 return ret;
be38c486
SM
152}
153
f2817f2e
PP
154#pragma GCC diagnostic pop
155
4164020e
SM
156static int append_lttng_trace_path_ust_uid(const struct fs_sink_trace *trace, GString *path,
157 const bt_trace *tc)
be38c486 158{
4164020e
SM
159 const bt_value *v;
160 int ret;
be38c486 161
4164020e
SM
162 v = bt_trace_borrow_environment_entry_value_by_name_const(tc, "tracer_buffering_id");
163 if (!v || !bt_value_is_signed_integer(v)) {
164 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_buffering_id\"");
165 goto error;
166 }
be38c486 167
4164020e 168 g_string_append_printf(path, G_DIR_SEPARATOR_S "%" PRId64, bt_value_integer_signed_get(v));
be38c486 169
4164020e
SM
170 v = bt_trace_borrow_environment_entry_value_by_name_const(tc, "architecture_bit_width");
171 if (!v || !bt_value_is_signed_integer(v)) {
172 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"architecture_bit_width\"");
173 goto error;
174 }
be38c486 175
4164020e
SM
176 g_string_append_printf(path, G_DIR_SEPARATOR_S "%" PRIu64 "-bit",
177 bt_value_integer_signed_get(v));
be38c486 178
4164020e
SM
179 ret = 0;
180 goto end;
be38c486
SM
181
182error:
4164020e 183 ret = -1;
be38c486 184
15fe47e0 185end:
4164020e 186 return ret;
be38c486
SM
187}
188
4164020e
SM
189static int append_lttng_trace_path_ust_pid(const struct fs_sink_trace *trace, GString *path,
190 const bt_trace *tc)
be38c486 191{
4164020e
SM
192 const bt_value *v;
193 const char *datetime;
194 int ret;
be38c486 195
4164020e
SM
196 v = bt_trace_borrow_environment_entry_value_by_name_const(tc, "procname");
197 if (!v || !bt_value_is_string(v)) {
198 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"procname\"");
199 goto error;
200 }
be38c486 201
4164020e 202 g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", bt_value_string_get(v));
be38c486 203
4164020e
SM
204 v = bt_trace_borrow_environment_entry_value_by_name_const(tc, "vpid");
205 if (!v || !bt_value_is_signed_integer(v)) {
206 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"vpid\"");
207 goto error;
208 }
be38c486 209
4164020e 210 g_string_append_printf(path, "-%" PRId64, bt_value_integer_signed_get(v));
be38c486 211
4164020e
SM
212 v = bt_trace_borrow_environment_entry_value_by_name_const(tc, "vpid_datetime");
213 if (!v || !bt_value_is_string(v)) {
214 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"vpid_datetime\"");
215 goto error;
216 }
be38c486 217
4164020e 218 datetime = bt_value_string_get(v);
be38c486 219
4164020e
SM
220 if (lttng_validate_datetime(trace, datetime)) {
221 goto error;
222 }
be38c486 223
4164020e 224 g_string_append_printf(path, "-%s", datetime);
be38c486 225
4164020e
SM
226 ret = 0;
227 goto end;
be38c486
SM
228
229error:
4164020e 230 ret = -1;
be38c486
SM
231
232end:
4164020e 233 return ret;
be38c486
SM
234}
235
236/*
237 * Try to build a trace path based on environment values put in the trace
238 * environment by the LTTng tracer, starting with version 2.11.
239 */
4164020e 240static GString *make_lttng_trace_path_rel(const struct fs_sink_trace *trace)
be38c486 241{
4164020e
SM
242 const bt_value *v;
243 const char *tracer_name, *domain, *datetime;
244 int64_t tracer_major, tracer_minor;
245 GString *path;
246
247 path = g_string_new(NULL);
248 if (!path) {
249 goto error;
250 }
251
252 v = bt_trace_borrow_environment_entry_value_by_name_const(trace->ir_trace, "tracer_name");
253 if (!v || !bt_value_is_string(v)) {
254 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_name\"");
255 goto error;
256 }
257
258 tracer_name = bt_value_string_get(v);
259
260 if (!g_str_equal(tracer_name, "lttng-ust") && !g_str_equal(tracer_name, "lttng-modules")) {
261 BT_COMP_LOGI("Unrecognized tracer name: name=\"%s\"", tracer_name);
262 goto error;
263 }
264
265 v = bt_trace_borrow_environment_entry_value_by_name_const(trace->ir_trace, "tracer_major");
266 if (!v || !bt_value_is_signed_integer(v)) {
267 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_major\"");
268 goto error;
269 }
270
271 tracer_major = bt_value_integer_signed_get(v);
272
273 v = bt_trace_borrow_environment_entry_value_by_name_const(trace->ir_trace, "tracer_minor");
274 if (!v || !bt_value_is_signed_integer(v)) {
275 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_minor\"");
276 goto error;
277 }
278
279 tracer_minor = bt_value_integer_signed_get(v);
280
281 if (!(tracer_major >= 3 || (tracer_major == 2 && tracer_minor >= 11))) {
282 BT_COMP_LOGI("Unsupported LTTng version for automatic trace path: major=%" PRId64
283 ", minor=%" PRId64,
284 tracer_major, tracer_minor);
285 goto error;
286 }
287
288 v = bt_trace_borrow_environment_entry_value_by_name_const(trace->ir_trace, "hostname");
289 if (!v || !bt_value_is_string(v)) {
290 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_hostname\"");
291 goto error;
292 }
293
294 g_string_assign(path, bt_value_string_get(v));
295
296 v = bt_trace_borrow_environment_entry_value_by_name_const(trace->ir_trace, "trace_name");
297 if (!v || !bt_value_is_string(v)) {
298 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"trace_name\"");
299 goto error;
300 }
301
302 g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", bt_value_string_get(v));
303
304 v = bt_trace_borrow_environment_entry_value_by_name_const(trace->ir_trace,
305 "trace_creation_datetime");
306 if (!v || !bt_value_is_string(v)) {
307 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"trace_creation_datetime\"");
308 goto error;
309 }
310
311 datetime = bt_value_string_get(v);
312
313 if (lttng_validate_datetime(trace, datetime)) {
314 goto error;
315 }
316
317 g_string_append_printf(path, "-%s", datetime);
318
319 v = bt_trace_borrow_environment_entry_value_by_name_const(trace->ir_trace, "domain");
320 if (!v || !bt_value_is_string(v)) {
321 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"domain\"");
322 goto error;
323 }
324
325 domain = bt_value_string_get(v);
326 g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", domain);
327
328 if (g_str_equal(domain, "ust")) {
329 const char *tracer_buffering_scheme;
330
331 v = bt_trace_borrow_environment_entry_value_by_name_const(trace->ir_trace,
332 "tracer_buffering_scheme");
333 if (!v || !bt_value_is_string(v)) {
334 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_buffering_scheme\"");
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. */
351 BT_COMP_LOGI("Unknown buffering scheme: tracer_buffering_scheme=\"%s\"",
352 tracer_buffering_scheme);
353 goto error;
354 }
355 } else if (!g_str_equal(domain, "kernel")) {
356 /* Unknown domain. */
357 BT_COMP_LOGI("Unknown domain: domain=\"%s\"", domain);
358 goto error;
359 }
360
361 goto end;
be38c486
SM
362
363error:
4164020e
SM
364 if (path) {
365 g_string_free(path, TRUE);
366 path = NULL;
367 }
be38c486
SM
368
369end:
4164020e 370 return path;
be38c486
SM
371}
372
373/* Build the relative output path for `trace`. */
374
4164020e 375static GString *make_trace_path_rel(const struct fs_sink_trace *trace)
be38c486 376{
4164020e
SM
377 GString *path = NULL;
378 const char *trace_name;
be38c486 379
4164020e 380 BT_ASSERT(!trace->fs_sink->assume_single_trace);
be38c486 381
4164020e
SM
382 /* First, try to build a path using environment fields written by LTTng. */
383 path = make_lttng_trace_path_rel(trace);
384 if (path) {
385 goto end;
386 }
be38c486 387
4164020e
SM
388 /* Otherwise, use the trace name, if available. */
389 trace_name = bt_trace_get_name(trace->ir_trace);
390 if (trace_name) {
391 path = g_string_new(trace_name);
392 goto end;
393 }
be38c486 394
4164020e
SM
395 /* Otherwise, use "trace". */
396 path = g_string_new("trace");
be38c486
SM
397
398end:
4164020e 399 return path;
15fe47e0
PP
400}
401
be38c486
SM
402/*
403 * Compute the trace output path for `trace`, rooted at `output_base_directory`.
404 */
405
4164020e
SM
406static GString *make_trace_path(const struct fs_sink_trace *trace,
407 const char *output_base_directory)
be38c486 408{
4164020e
SM
409 GString *rel_path = NULL;
410 GString *rel_path_san = NULL;
411 GString *full_path = NULL;
412 GString *unique_full_path = NULL;
413
414 if (trace->fs_sink->assume_single_trace) {
415 /* Use output directory directly */
416 unique_full_path = g_string_new(output_base_directory);
417 if (!unique_full_path) {
418 goto end;
419 }
420 } else {
421 rel_path = make_trace_path_rel(trace);
422 if (!rel_path) {
423 goto end;
424 }
425
426 rel_path_san = sanitize_trace_path(rel_path->str);
427 if (!rel_path_san) {
428 goto end;
429 }
430
431 full_path = g_string_new(NULL);
432 if (!full_path) {
433 goto end;
434 }
435
436 g_string_printf(full_path, "%s" G_DIR_SEPARATOR_S "%s", output_base_directory,
437 rel_path_san->str);
438
439 unique_full_path = make_unique_trace_path(full_path->str);
440 }
be38c486
SM
441
442end:
4164020e
SM
443 if (rel_path) {
444 g_string_free(rel_path, TRUE);
445 }
be38c486 446
4164020e
SM
447 if (rel_path_san) {
448 g_string_free(rel_path_san, TRUE);
449 }
be38c486 450
4164020e
SM
451 if (full_path) {
452 g_string_free(full_path, TRUE);
453 }
be38c486 454
4164020e 455 return unique_full_path;
be38c486
SM
456}
457
15fe47e0
PP
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
4164020e 550struct fs_sink_trace *fs_sink_trace_create(struct fs_sink_comp *fs_sink, const bt_trace *ir_trace)
15fe47e0 551{
4164020e
SM
552 int ret;
553 struct fs_sink_trace *trace = g_new0(struct fs_sink_trace, 1);
554 bt_trace_add_listener_status trace_status;
555
556 if (!trace) {
557 goto end;
558 }
559
560 trace->log_level = fs_sink->log_level;
561 trace->fs_sink = fs_sink;
562 trace->ir_trace = ir_trace;
563 trace->ir_trace_destruction_listener_id = UINT64_C(-1);
564 trace->trace = translate_trace_trace_ir_to_ctf_ir(fs_sink, ir_trace);
565 if (!trace->trace) {
566 goto error;
567 }
568
569 trace->path = make_trace_path(trace, fs_sink->output_dir_path->str);
570 BT_ASSERT(trace->path);
571 ret = g_mkdir_with_parents(trace->path->str, 0755);
572 if (ret) {
573 BT_COMP_LOGE_ERRNO("Cannot create directories for trace directory", ": path=\"%s\"",
574 trace->path->str);
575 goto error;
576 }
577
578 trace->metadata_path = g_string_new(trace->path->str);
579 BT_ASSERT(trace->metadata_path);
580 g_string_append(trace->metadata_path, "/metadata");
581 trace->streams = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
582 (GDestroyNotify) fs_sink_stream_destroy);
583 BT_ASSERT(trace->streams);
584 trace_status = bt_trace_add_destruction_listener(ir_trace, ir_trace_destruction_listener, trace,
585 &trace->ir_trace_destruction_listener_id);
586 if (trace_status) {
587 goto error;
588 }
589
590 g_hash_table_insert(fs_sink->traces, (gpointer) ir_trace, trace);
591 goto end;
15fe47e0
PP
592
593error:
4164020e
SM
594 fs_sink_trace_destroy(trace);
595 trace = NULL;
15fe47e0
PP
596
597end:
4164020e 598 return trace;
15fe47e0 599}
This page took 0.096846 seconds and 4 git commands to generate.