fs-sink-trace.c: lttng_validate_datetime(): ignore deprecated decl.
[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
411 if (trace->fs_sink->assume_single_trace) {
412 /* Use output directory directly */
413 path = g_string_new("");
414 goto end;
415 }
416
417 /* First, try to build a path using environment fields written by LTTng. */
418 path = make_lttng_trace_path_rel(trace);
419 if (path) {
420 goto end;
421 }
422
423 /* Otherwise, use the trace name, if available. */
424 const char *trace_name = bt_trace_get_name(trace->ir_trace);
425 if (trace_name) {
426 path = g_string_new(trace_name);
427 goto end;
428 }
429
430 /* Otherwise, use "trace". */
431 path = g_string_new("trace");
432
433 end:
434 return path;
435 }
436
437 /*
438 * Compute the trace output path for `trace`, rooted at `output_base_directory`.
439 */
440
441 static
442 GString *make_trace_path(const struct fs_sink_trace *trace, const char *output_base_directory)
443 {
444 GString *rel_path = NULL;
445 GString *rel_path_san = NULL;
446 GString *full_path = NULL;
447 GString *unique_full_path = NULL;
448
449 rel_path = make_trace_path_rel(trace);
450 if (!rel_path) {
451 goto end;
452 }
453
454 rel_path_san = sanitize_trace_path(rel_path->str);
455 if (!rel_path_san) {
456 goto end;
457 }
458
459 full_path = g_string_new(NULL);
460 if (!full_path) {
461 goto end;
462 }
463
464 g_string_printf(full_path, "%s" G_DIR_SEPARATOR_S "%s",
465 output_base_directory, rel_path_san->str);
466
467 unique_full_path = make_unique_trace_path(full_path->str);
468
469 end:
470 if (rel_path) {
471 g_string_free(rel_path, TRUE);
472 }
473
474 if (rel_path_san) {
475 g_string_free(rel_path_san, TRUE);
476 }
477
478 if (full_path) {
479 g_string_free(full_path, TRUE);
480 }
481
482 return unique_full_path;
483 }
484
485 BT_HIDDEN
486 void fs_sink_trace_destroy(struct fs_sink_trace *trace)
487 {
488 GString *tsdl = NULL;
489 FILE *fh = NULL;
490 size_t len;
491
492 if (!trace) {
493 goto end;
494 }
495
496 if (trace->ir_trace_destruction_listener_id != UINT64_C(-1)) {
497 /*
498 * Remove the destruction listener, otherwise it could
499 * be called in the future, and its private data is this
500 * CTF FS sink trace object which won't exist anymore.
501 */
502 (void) bt_trace_remove_destruction_listener(trace->ir_trace,
503 trace->ir_trace_destruction_listener_id);
504 trace->ir_trace_destruction_listener_id = UINT64_C(-1);
505 }
506
507 if (trace->streams) {
508 g_hash_table_destroy(trace->streams);
509 trace->streams = NULL;
510 }
511
512 tsdl = g_string_new(NULL);
513 BT_ASSERT(tsdl);
514 translate_trace_ctf_ir_to_tsdl(trace->trace, tsdl);
515
516 BT_ASSERT(trace->metadata_path);
517 fh = fopen(trace->metadata_path->str, "wb");
518 if (!fh) {
519 BT_COMP_LOGF_ERRNO("In trace destruction listener: "
520 "cannot open metadata file for writing",
521 ": path=\"%s\"", trace->metadata_path->str);
522 abort();
523 }
524
525 len = fwrite(tsdl->str, sizeof(*tsdl->str), tsdl->len, fh);
526 if (len != tsdl->len) {
527 BT_COMP_LOGF_ERRNO("In trace destruction listener: "
528 "cannot write metadata file",
529 ": path=\"%s\"", trace->metadata_path->str);
530 abort();
531 }
532
533 if (!trace->fs_sink->quiet) {
534 printf("Created CTF trace `%s`.\n", trace->path->str);
535 }
536
537 if (trace->path) {
538 g_string_free(trace->path, TRUE);
539 trace->path = NULL;
540 }
541
542 if (fh) {
543 int ret = fclose(fh);
544
545 if (ret != 0) {
546 BT_COMP_LOGW_ERRNO("In trace destruction listener: "
547 "cannot close metadata file",
548 ": path=\"%s\"", trace->metadata_path->str);
549 }
550 }
551
552 g_string_free(trace->metadata_path, TRUE);
553 trace->metadata_path = NULL;
554
555 fs_sink_ctf_trace_destroy(trace->trace);
556 trace->trace = NULL;
557 g_free(trace);
558
559 g_string_free(tsdl, TRUE);
560
561 end:
562 return;
563 }
564
565 static
566 void ir_trace_destruction_listener(const bt_trace *ir_trace, void *data)
567 {
568 struct fs_sink_trace *trace = data;
569
570 /*
571 * Prevent bt_trace_remove_destruction_listener() from being
572 * called in fs_sink_trace_destroy(), which is called by
573 * g_hash_table_remove() below.
574 */
575 trace->ir_trace_destruction_listener_id = UINT64_C(-1);
576 g_hash_table_remove(trace->fs_sink->traces, ir_trace);
577 }
578
579 BT_HIDDEN
580 struct fs_sink_trace *fs_sink_trace_create(struct fs_sink_comp *fs_sink,
581 const bt_trace *ir_trace)
582 {
583 int ret;
584 struct fs_sink_trace *trace = g_new0(struct fs_sink_trace, 1);
585 bt_trace_add_listener_status trace_status;
586
587 if (!trace) {
588 goto end;
589 }
590
591 trace->log_level = fs_sink->log_level;
592 trace->fs_sink = fs_sink;
593 trace->ir_trace = ir_trace;
594 trace->ir_trace_destruction_listener_id = UINT64_C(-1);
595 trace->trace = translate_trace_trace_ir_to_ctf_ir(fs_sink, ir_trace);
596 if (!trace->trace) {
597 goto error;
598 }
599
600 trace->path = make_trace_path(trace, fs_sink->output_dir_path->str);
601 BT_ASSERT(trace->path);
602 ret = g_mkdir_with_parents(trace->path->str, 0755);
603 if (ret) {
604 BT_COMP_LOGE_ERRNO("Cannot create directories for trace directory",
605 ": path=\"%s\"", trace->path->str);
606 goto error;
607 }
608
609 trace->metadata_path = g_string_new(trace->path->str);
610 BT_ASSERT(trace->metadata_path);
611 g_string_append(trace->metadata_path, "/metadata");
612 trace->streams = g_hash_table_new_full(g_direct_hash, g_direct_equal,
613 NULL, (GDestroyNotify) fs_sink_stream_destroy);
614 BT_ASSERT(trace->streams);
615 trace_status = bt_trace_add_destruction_listener(ir_trace,
616 ir_trace_destruction_listener, trace,
617 &trace->ir_trace_destruction_listener_id);
618 if (trace_status) {
619 goto error;
620 }
621
622 g_hash_table_insert(fs_sink->traces, (gpointer) ir_trace, trace);
623 goto end;
624
625 error:
626 fs_sink_trace_destroy(trace);
627 trace = NULL;
628
629 end:
630 return trace;
631 }
This page took 0.047309 seconds and 5 git commands to generate.