Re-format new C++ files
[babeltrace.git] / src / plugins / ctf / fs-sink / fs-sink-trace.cpp
1 /*
2 * SPDX-License-Identifier: MIT
3 *
4 * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
5 */
6
7 #define BT_COMP_LOG_SELF_COMP (trace->fs_sink->self_comp)
8 #define BT_LOG_OUTPUT_LEVEL (trace->log_level)
9 #define BT_LOG_TAG "PLUGIN/SINK.CTF.FS/TRACE"
10 #include "logging/comp-logging.h"
11
12 #include <babeltrace2/babeltrace.h>
13 #include <stdio.h>
14 #include <stdbool.h>
15 #include <glib.h>
16 #include "common/assert.h"
17 #include "ctfser/ctfser.h"
18
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"
24
25 /*
26 * Sanitizes `path` so as to:
27 *
28 * * Replace `.` subdirectories with `_`.
29 * * Replace `..` subdirectories with `__`.
30 * * Remove trailing slashes.
31 */
32 static GString *sanitize_trace_path(const char *path)
33 {
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;
95 }
96
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
102 static GString *make_unique_trace_path(const char *path)
103 {
104 GString *unique_path;
105 unsigned int suffix = 0;
106
107 unique_path = g_string_new(path);
108
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 }
113
114 return unique_path;
115 }
116
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
126 /*
127 * Validate that the input string `datetime` is an ISO8601-compliant string (the
128 * format used by LTTng in the metadata).
129 */
130
131 static int lttng_validate_datetime(const struct fs_sink_trace *trace, const char *datetime)
132 {
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;
148
149 end:
150 return ret;
151 }
152
153 #pragma GCC diagnostic pop
154
155 static int append_lttng_trace_path_ust_uid(const struct fs_sink_trace *trace, GString *path,
156 const bt_trace *tc)
157 {
158 const bt_value *v;
159 int ret;
160
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 }
166
167 g_string_append_printf(path, G_DIR_SEPARATOR_S "%" PRId64, bt_value_integer_signed_get(v));
168
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 }
174
175 g_string_append_printf(path, G_DIR_SEPARATOR_S "%" PRIu64 "-bit",
176 bt_value_integer_signed_get(v));
177
178 ret = 0;
179 goto end;
180
181 error:
182 ret = -1;
183
184 end:
185 return ret;
186 }
187
188 static int append_lttng_trace_path_ust_pid(const struct fs_sink_trace *trace, GString *path,
189 const bt_trace *tc)
190 {
191 const bt_value *v;
192 const char *datetime;
193 int ret;
194
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 }
200
201 g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", bt_value_string_get(v));
202
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 }
208
209 g_string_append_printf(path, "-%" PRId64, bt_value_integer_signed_get(v));
210
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 }
216
217 datetime = bt_value_string_get(v);
218
219 if (lttng_validate_datetime(trace, datetime)) {
220 goto error;
221 }
222
223 g_string_append_printf(path, "-%s", datetime);
224
225 ret = 0;
226 goto end;
227
228 error:
229 ret = -1;
230
231 end:
232 return ret;
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 */
239 static GString *make_lttng_trace_path_rel(const struct fs_sink_trace *trace)
240 {
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;
361
362 error:
363 if (path) {
364 g_string_free(path, TRUE);
365 path = NULL;
366 }
367
368 end:
369 return path;
370 }
371
372 /* Build the relative output path for `trace`. */
373
374 static GString *make_trace_path_rel(const struct fs_sink_trace *trace)
375 {
376 GString *path = NULL;
377 const char *trace_name;
378
379 BT_ASSERT(!trace->fs_sink->assume_single_trace);
380
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 }
386
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 }
393
394 /* Otherwise, use "trace". */
395 path = g_string_new("trace");
396
397 end:
398 return path;
399 }
400
401 /*
402 * Compute the trace output path for `trace`, rooted at `output_base_directory`.
403 */
404
405 static GString *make_trace_path(const struct fs_sink_trace *trace,
406 const char *output_base_directory)
407 {
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 }
440
441 end:
442 if (rel_path) {
443 g_string_free(rel_path, TRUE);
444 }
445
446 if (rel_path_san) {
447 g_string_free(rel_path_san, TRUE);
448 }
449
450 if (full_path) {
451 g_string_free(full_path, TRUE);
452 }
453
454 return unique_full_path;
455 }
456
457 BT_HIDDEN
458 void fs_sink_trace_destroy(struct fs_sink_trace *trace)
459 {
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);
532
533 end:
534 return;
535 }
536
537 static void ir_trace_destruction_listener(const bt_trace *ir_trace, void *data)
538 {
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);
548 }
549
550 BT_HIDDEN
551 struct fs_sink_trace *fs_sink_trace_create(struct fs_sink_comp *fs_sink, const bt_trace *ir_trace)
552 {
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;
593
594 error:
595 fs_sink_trace_destroy(trace);
596 trace = NULL;
597
598 end:
599 return trace;
600 }
This page took 0.041028 seconds and 4 git commands to generate.