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