Remove stdbool.h includes from 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 #include <glib.h>
8 #include <stdio.h>
9
10 #include <babeltrace2/babeltrace.h>
11
12 #define BT_COMP_LOG_SELF_COMP (trace->fs_sink->self_comp)
13 #define BT_LOG_OUTPUT_LEVEL (trace->log_level)
14 #define BT_LOG_TAG "PLUGIN/SINK.CTF.FS/TRACE"
15 #include "logging/comp-logging.h"
16
17 #include "common/assert.h"
18 #include "ctfser/ctfser.h"
19
20 #include "fs-sink-stream.hpp"
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"
25
26 /*
27 * Sanitizes `path` so as to:
28 *
29 * * Replace `.` subdirectories with `_`.
30 * * Replace `..` subdirectories with `__`.
31 * * Remove trailing slashes.
32 */
33 static GString *sanitize_trace_path(const char *path)
34 {
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;
96 }
97
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
103 static GString *make_unique_trace_path(const char *path)
104 {
105 GString *unique_path;
106 unsigned int suffix = 0;
107
108 unique_path = g_string_new(path);
109
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 }
114
115 return unique_path;
116 }
117
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
127 /*
128 * Validate that the input string `datetime` is an ISO8601-compliant string (the
129 * format used by LTTng in the metadata).
130 */
131
132 static int lttng_validate_datetime(const struct fs_sink_trace *trace, const char *datetime)
133 {
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;
149
150 end:
151 return ret;
152 }
153
154 #pragma GCC diagnostic pop
155
156 static int append_lttng_trace_path_ust_uid(const struct fs_sink_trace *trace, GString *path,
157 const bt_trace *tc)
158 {
159 const bt_value *v;
160 int ret;
161
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 }
167
168 g_string_append_printf(path, G_DIR_SEPARATOR_S "%" PRId64, bt_value_integer_signed_get(v));
169
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 }
175
176 g_string_append_printf(path, G_DIR_SEPARATOR_S "%" PRIu64 "-bit",
177 bt_value_integer_signed_get(v));
178
179 ret = 0;
180 goto end;
181
182 error:
183 ret = -1;
184
185 end:
186 return ret;
187 }
188
189 static int append_lttng_trace_path_ust_pid(const struct fs_sink_trace *trace, GString *path,
190 const bt_trace *tc)
191 {
192 const bt_value *v;
193 const char *datetime;
194 int ret;
195
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 }
201
202 g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", bt_value_string_get(v));
203
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 }
209
210 g_string_append_printf(path, "-%" PRId64, bt_value_integer_signed_get(v));
211
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 }
217
218 datetime = bt_value_string_get(v);
219
220 if (lttng_validate_datetime(trace, datetime)) {
221 goto error;
222 }
223
224 g_string_append_printf(path, "-%s", datetime);
225
226 ret = 0;
227 goto end;
228
229 error:
230 ret = -1;
231
232 end:
233 return ret;
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 */
240 static GString *make_lttng_trace_path_rel(const struct fs_sink_trace *trace)
241 {
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;
362
363 error:
364 if (path) {
365 g_string_free(path, TRUE);
366 path = NULL;
367 }
368
369 end:
370 return path;
371 }
372
373 /* Build the relative output path for `trace`. */
374
375 static GString *make_trace_path_rel(const struct fs_sink_trace *trace)
376 {
377 GString *path = NULL;
378 const char *trace_name;
379
380 BT_ASSERT(!trace->fs_sink->assume_single_trace);
381
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 }
387
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 }
394
395 /* Otherwise, use "trace". */
396 path = g_string_new("trace");
397
398 end:
399 return path;
400 }
401
402 /*
403 * Compute the trace output path for `trace`, rooted at `output_base_directory`.
404 */
405
406 static GString *make_trace_path(const struct fs_sink_trace *trace,
407 const char *output_base_directory)
408 {
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 }
441
442 end:
443 if (rel_path) {
444 g_string_free(rel_path, TRUE);
445 }
446
447 if (rel_path_san) {
448 g_string_free(rel_path_san, TRUE);
449 }
450
451 if (full_path) {
452 g_string_free(full_path, TRUE);
453 }
454
455 return unique_full_path;
456 }
457
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 struct fs_sink_trace *fs_sink_trace_create(struct fs_sink_comp *fs_sink, const bt_trace *ir_trace)
551 {
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;
592
593 error:
594 fs_sink_trace_destroy(trace);
595 trace = NULL;
596
597 end:
598 return trace;
599 }
This page took 0.05201 seconds and 4 git commands to generate.