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