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