Add bt_common_abort() and use it instead of abort() directly
[babeltrace.git] / src / plugins / ctf / fs-sink / fs-sink-trace.c
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_COMP_LOG_SELF_COMP (trace->fs_sink->self_comp)
24 #define BT_LOG_OUTPUT_LEVEL (trace->log_level)
25 #define BT_LOG_TAG "PLUGIN/SINK.CTF.FS/TRACE"
26 #include "logging/comp-logging.h"
27
28 #include <babeltrace2/babeltrace.h>
29 #include <stdio.h>
30 #include <stdbool.h>
31 #include <glib.h>
32 #include "common/assert.h"
33 #include "ctfser/ctfser.h"
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 */
48 static
49 GString *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
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
120 static
121 GString *make_unique_trace_path(const char *path)
122 {
123 GString *unique_path;
124 unsigned int suffix = 0;
125
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
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
145 /*
146 * Validate that the input string `datetime` is an ISO8601-compliant string (the
147 * format used by LTTng in the metadata).
148 */
149
150 static
151 int lttng_validate_datetime(const struct fs_sink_trace *trace,
152 const char *datetime)
153 {
154 GTimeVal tv;
155 int ret = -1;
156
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)) {
164 BT_COMP_LOGI("Couldn't parse datetime as ISO 8601: date=\"%s\"", datetime);
165 goto end;
166 }
167
168 ret = 0;
169
170 end:
171 return ret;
172 }
173
174 #pragma GCC diagnostic pop
175
176 static
177 int append_lttng_trace_path_ust_uid(const struct fs_sink_trace *trace,
178 GString *path, const bt_trace *tc)
179 {
180 const bt_value *v;
181 int ret;
182
183 v = bt_trace_borrow_environment_entry_value_by_name_const(tc, "tracer_buffering_id");
184 if (!v || !bt_value_is_signed_integer(v)) {
185 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_buffering_id\"");
186 goto error;
187 }
188
189 g_string_append_printf(path, G_DIR_SEPARATOR_S "%" PRId64,
190 bt_value_integer_signed_get(v));
191
192 v = bt_trace_borrow_environment_entry_value_by_name_const(tc, "architecture_bit_width");
193 if (!v || !bt_value_is_signed_integer(v)) {
194 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"architecture_bit_width\"");
195 goto error;
196 }
197
198 g_string_append_printf(path, G_DIR_SEPARATOR_S "%" PRIu64 "-bit",
199 bt_value_integer_signed_get(v));
200
201 ret = 0;
202 goto end;
203
204 error:
205 ret = -1;
206
207 end:
208 return ret;
209 }
210
211 static
212 int append_lttng_trace_path_ust_pid(const struct fs_sink_trace *trace,
213 GString *path, const bt_trace *tc)
214 {
215 const bt_value *v;
216 const char *datetime;
217 int ret;
218
219 v = bt_trace_borrow_environment_entry_value_by_name_const(tc, "procname");
220 if (!v || !bt_value_is_string(v)) {
221 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"procname\"");
222 goto error;
223 }
224
225 g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", bt_value_string_get(v));
226
227 v = bt_trace_borrow_environment_entry_value_by_name_const(tc, "vpid");
228 if (!v || !bt_value_is_signed_integer(v)) {
229 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"vpid\"");
230 goto error;
231 }
232
233 g_string_append_printf(path, "-%" PRId64, bt_value_integer_signed_get(v));
234
235 v = bt_trace_borrow_environment_entry_value_by_name_const(tc, "vpid_datetime");
236 if (!v || !bt_value_is_string(v)) {
237 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"vpid_datetime\"");
238 goto error;
239 }
240
241 datetime = bt_value_string_get(v);
242
243 if (lttng_validate_datetime(trace, datetime)) {
244 goto error;
245 }
246
247 g_string_append_printf(path, "-%s", datetime);
248
249 ret = 0;
250 goto end;
251
252 error:
253 ret = -1;
254
255 end:
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 */
263 static
264 GString *make_lttng_trace_path_rel(const struct fs_sink_trace *trace)
265 {
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
276 v = bt_trace_borrow_environment_entry_value_by_name_const(
277 trace->ir_trace, "tracer_name");
278 if (!v || !bt_value_is_string(v)) {
279 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_name\"");
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")) {
287 BT_COMP_LOGI("Unrecognized tracer name: name=\"%s\"", tracer_name);
288 goto error;
289 }
290
291 v = bt_trace_borrow_environment_entry_value_by_name_const(
292 trace->ir_trace, "tracer_major");
293 if (!v || !bt_value_is_signed_integer(v)) {
294 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_major\"");
295 goto error;
296 }
297
298 tracer_major = bt_value_integer_signed_get(v);
299
300 v = bt_trace_borrow_environment_entry_value_by_name_const(
301 trace->ir_trace, "tracer_minor");
302 if (!v || !bt_value_is_signed_integer(v)) {
303 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_minor\"");
304 goto error;
305 }
306
307 tracer_minor = bt_value_integer_signed_get(v);
308
309 if (!(tracer_major >= 3 || (tracer_major == 2 && tracer_minor >= 11))) {
310 BT_COMP_LOGI("Unsupported LTTng version for automatic trace path: major=%" PRId64 ", minor=%" PRId64,
311 tracer_major, tracer_minor);
312 goto error;
313 }
314
315 v = bt_trace_borrow_environment_entry_value_by_name_const(
316 trace->ir_trace, "hostname");
317 if (!v || !bt_value_is_string(v)) {
318 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_hostname\"");
319 goto error;
320 }
321
322 g_string_assign(path, bt_value_string_get(v));
323
324 v = bt_trace_borrow_environment_entry_value_by_name_const(
325 trace->ir_trace, "trace_name");
326 if (!v || !bt_value_is_string(v)) {
327 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"trace_name\"");
328 goto error;
329 }
330
331 g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", bt_value_string_get(v));
332
333 v = bt_trace_borrow_environment_entry_value_by_name_const(
334 trace->ir_trace, "trace_creation_datetime");
335 if (!v || !bt_value_is_string(v)) {
336 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"trace_creation_datetime\"");
337 goto error;
338 }
339
340 datetime = bt_value_string_get(v);
341
342 if (lttng_validate_datetime(trace, datetime)) {
343 goto error;
344 }
345
346 g_string_append_printf(path, "-%s", datetime);
347
348 v = bt_trace_borrow_environment_entry_value_by_name_const(
349 trace->ir_trace, "domain");
350 if (!v || !bt_value_is_string(v)) {
351 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"domain\"");
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
361 v = bt_trace_borrow_environment_entry_value_by_name_const(
362 trace->ir_trace, "tracer_buffering_scheme");
363 if (!v || !bt_value_is_string(v)) {
364 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_buffering_scheme\"");
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")) {
372 if (append_lttng_trace_path_ust_uid(trace, path,
373 trace->ir_trace)) {
374 goto error;
375 }
376 } else if (g_str_equal(tracer_buffering_scheme, "pid")){
377 if (append_lttng_trace_path_ust_pid(trace, path,
378 trace->ir_trace)) {
379 goto error;
380 }
381 } else {
382 /* Unknown buffering scheme. */
383 BT_COMP_LOGI("Unknown buffering scheme: tracer_buffering_scheme=\"%s\"", tracer_buffering_scheme);
384 goto error;
385 }
386 } else if (!g_str_equal(domain, "kernel")) {
387 /* Unknown domain. */
388 BT_COMP_LOGI("Unknown domain: domain=\"%s\"", domain);
389 goto error;
390 }
391
392 goto end;
393
394 error:
395 if (path) {
396 g_string_free(path, TRUE);
397 path = NULL;
398 }
399
400 end:
401 return path;
402 }
403
404 /* Build the relative output path for `trace`. */
405
406 static
407 GString *make_trace_path_rel(const struct fs_sink_trace *trace)
408 {
409 GString *path = NULL;
410 const char *trace_name;
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. */
425 trace_name = bt_trace_get_name(trace->ir_trace);
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
434 end:
435 return path;
436 }
437
438 /*
439 * Compute the trace output path for `trace`, rooted at `output_base_directory`.
440 */
441
442 static
443 GString *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
470 end:
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
486 BT_HIDDEN
487 void 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);
515 translate_trace_ctf_ir_to_tsdl(trace->trace, tsdl);
516
517 BT_ASSERT(trace->metadata_path);
518 fh = fopen(trace->metadata_path->str, "wb");
519 if (!fh) {
520 BT_COMP_LOGF_ERRNO("In trace destruction listener: "
521 "cannot open metadata file for writing",
522 ": path=\"%s\"", trace->metadata_path->str);
523 bt_common_abort();
524 }
525
526 len = fwrite(tsdl->str, sizeof(*tsdl->str), tsdl->len, fh);
527 if (len != tsdl->len) {
528 BT_COMP_LOGF_ERRNO("In trace destruction listener: "
529 "cannot write metadata file",
530 ": path=\"%s\"", trace->metadata_path->str);
531 bt_common_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
543 if (fh) {
544 int ret = fclose(fh);
545
546 if (ret != 0) {
547 BT_COMP_LOGW_ERRNO("In trace destruction listener: "
548 "cannot close metadata file",
549 ": path=\"%s\"", trace->metadata_path->str);
550 }
551 }
552
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
560 g_string_free(tsdl, TRUE);
561
562 end:
563 return;
564 }
565
566 static
567 void 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
580 BT_HIDDEN
581 struct 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);
586 bt_trace_add_listener_status trace_status;
587
588 if (!trace) {
589 goto end;
590 }
591
592 trace->log_level = fs_sink->log_level;
593 trace->fs_sink = fs_sink;
594 trace->ir_trace = ir_trace;
595 trace->ir_trace_destruction_listener_id = UINT64_C(-1);
596 trace->trace = translate_trace_trace_ir_to_ctf_ir(fs_sink, ir_trace);
597 if (!trace->trace) {
598 goto error;
599 }
600
601 trace->path = make_trace_path(trace, fs_sink->output_dir_path->str);
602 BT_ASSERT(trace->path);
603 ret = g_mkdir_with_parents(trace->path->str, 0755);
604 if (ret) {
605 BT_COMP_LOGE_ERRNO("Cannot create directories for trace directory",
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
626 error:
627 fs_sink_trace_destroy(trace);
628 trace = NULL;
629
630 end:
631 return trace;
632 }
This page took 0.054082 seconds and 4 git commands to generate.