lib: strictly type function return status enumerations
[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 "plugins/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 * Validate that the input string `datetime` is an ISO8601-compliant string (the
138 * format used by LTTng in the metadata).
139 */
140
141 static
142 int lttng_validate_datetime(const struct fs_sink_trace *trace,
143 const char *datetime)
144 {
145 GTimeVal tv;
146 int ret = -1;
147
148 /*
149 * We are using g_time_val_from_iso8601, as the safer/more modern
150 * alternative, g_date_time_new_from_iso8601, is only available in
151 * glib >= 2.56, and this is sufficient for our use case of validating
152 * the format.
153 */
154 if (!g_time_val_from_iso8601(datetime, &tv)) {
155 BT_COMP_LOGI("Couldn't parse datetime as ISO 8601: date=\"%s\"", datetime);
156 goto end;
157 }
158
159 ret = 0;
160
161 end:
162 return ret;
163 }
164
165 static
166 int append_lttng_trace_path_ust_uid(const struct fs_sink_trace *trace,
167 GString *path, const bt_trace_class *tc)
168 {
169 const bt_value *v;
170 int ret;
171
172 v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "tracer_buffering_id");
173 if (!v || !bt_value_is_signed_integer(v)) {
174 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_buffering_id\"");
175 goto error;
176 }
177
178 g_string_append_printf(path, G_DIR_SEPARATOR_S "%" PRId64,
179 bt_value_signed_integer_get(v));
180
181 v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "isa_length");
182 if (!v || !bt_value_is_signed_integer(v)) {
183 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"isa_length\"");
184 goto error;
185 }
186
187 g_string_append_printf(path, G_DIR_SEPARATOR_S "%" PRIu64 "-bit",
188 bt_value_signed_integer_get(v));
189
190 ret = 0;
191 goto end;
192
193 error:
194 ret = -1;
195
196 end:
197 return ret;
198 }
199
200 static
201 int append_lttng_trace_path_ust_pid(const struct fs_sink_trace *trace,
202 GString *path, const bt_trace_class *tc)
203 {
204 const bt_value *v;
205 const char *datetime;
206 int ret;
207
208 v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "procname");
209 if (!v || !bt_value_is_string(v)) {
210 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"procname\"");
211 goto error;
212 }
213
214 g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", bt_value_string_get(v));
215
216 v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "vpid");
217 if (!v || !bt_value_is_signed_integer(v)) {
218 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"vpid\"");
219 goto error;
220 }
221
222 g_string_append_printf(path, "-%" PRId64, bt_value_signed_integer_get(v));
223
224 v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "vpid_datetime");
225 if (!v || !bt_value_is_string(v)) {
226 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"vpid_datetime\"");
227 goto error;
228 }
229
230 datetime = bt_value_string_get(v);
231
232 if (lttng_validate_datetime(trace, datetime)) {
233 goto error;
234 }
235
236 g_string_append_printf(path, "-%s", datetime);
237
238 ret = 0;
239 goto end;
240
241 error:
242 ret = -1;
243
244 end:
245 return ret;
246 }
247
248 /*
249 * Try to build a trace path based on environment values put in the trace
250 * environment by the LTTng tracer, starting with version 2.11.
251 */
252 static
253 GString *make_lttng_trace_path_rel(const struct fs_sink_trace *trace)
254 {
255 const bt_trace_class *tc;
256 const bt_value *v;
257 const char *tracer_name, *domain, *datetime;
258 int64_t tracer_major, tracer_minor;
259 GString *path;
260
261 path = g_string_new(NULL);
262 if (!path) {
263 goto error;
264 }
265
266 tc = bt_trace_borrow_class_const(trace->ir_trace);
267
268 v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "tracer_name");
269 if (!v || !bt_value_is_string(v)) {
270 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_name\"");
271 goto error;
272 }
273
274 tracer_name = bt_value_string_get(v);
275
276 if (!g_str_equal(tracer_name, "lttng-ust")
277 && !g_str_equal(tracer_name, "lttng-modules")) {
278 BT_COMP_LOGI("Unrecognized tracer name: name=\"%s\"", tracer_name);
279 goto error;
280 }
281
282 v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "tracer_major");
283 if (!v || !bt_value_is_signed_integer(v)) {
284 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_major\"");
285 goto error;
286 }
287
288 tracer_major = bt_value_signed_integer_get(v);
289
290 v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "tracer_minor");
291 if (!v || !bt_value_is_signed_integer(v)) {
292 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_minor\"");
293 goto error;
294 }
295
296 tracer_minor = bt_value_signed_integer_get(v);
297
298 if (!(tracer_major >= 3 || (tracer_major == 2 && tracer_minor >= 11))) {
299 BT_COMP_LOGI("Unsupported LTTng version for automatic trace path: major=%" PRId64 ", minor=%" PRId64,
300 tracer_major, tracer_minor);
301 goto error;
302 }
303
304 v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "hostname");
305 if (!v || !bt_value_is_string(v)) {
306 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_hostname\"");
307 goto error;
308 }
309
310 g_string_assign(path, bt_value_string_get(v));
311
312 v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "trace_name");
313 if (!v || !bt_value_is_string(v)) {
314 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"trace_name\"");
315 goto error;
316 }
317
318 g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", bt_value_string_get(v));
319
320 v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "trace_creation_datetime");
321 if (!v || !bt_value_is_string(v)) {
322 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"trace_creation_datetime\"");
323 goto error;
324 }
325
326 datetime = bt_value_string_get(v);
327
328 if (lttng_validate_datetime(trace, datetime)) {
329 goto error;
330 }
331
332 g_string_append_printf(path, "-%s", datetime);
333
334 v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "domain");
335 if (!v || !bt_value_is_string(v)) {
336 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"domain\"");
337 goto error;
338 }
339
340 domain = bt_value_string_get(v);
341 g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", domain);
342
343 if (g_str_equal(domain, "ust")) {
344 const char *tracer_buffering_scheme;
345
346 v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "tracer_buffering_scheme");
347 if (!v || !bt_value_is_string(v)) {
348 BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_buffering_scheme\"");
349 goto error;
350 }
351
352 tracer_buffering_scheme = bt_value_string_get(v);
353 g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", tracer_buffering_scheme);
354
355 if (g_str_equal(tracer_buffering_scheme, "uid")) {
356 if (append_lttng_trace_path_ust_uid(trace, path, tc)) {
357 goto error;
358 }
359 } else if (g_str_equal(tracer_buffering_scheme, "pid")){
360 if (append_lttng_trace_path_ust_pid(trace, path, tc)) {
361 goto error;
362 }
363 } else {
364 /* Unknown buffering scheme. */
365 BT_COMP_LOGI("Unknown buffering scheme: tracer_buffering_scheme=\"%s\"", tracer_buffering_scheme);
366 goto error;
367 }
368 } else if (!g_str_equal(domain, "kernel")) {
369 /* Unknown domain. */
370 BT_COMP_LOGI("Unknown domain: domain=\"%s\"", domain);
371 goto error;
372 }
373
374 goto end;
375
376 error:
377 if (path) {
378 g_string_free(path, TRUE);
379 path = NULL;
380 }
381
382 end:
383 return path;
384 }
385
386 /* Build the relative output path for `trace`. */
387
388 static
389 GString *make_trace_path_rel(const struct fs_sink_trace *trace)
390 {
391 GString *path = NULL;
392
393 if (trace->fs_sink->assume_single_trace) {
394 /* Use output directory directly */
395 path = g_string_new("");
396 goto end;
397 }
398
399 /* First, try to build a path using environment fields written by LTTng. */
400 path = make_lttng_trace_path_rel(trace);
401 if (path) {
402 goto end;
403 }
404
405 /* Otherwise, use the trace name, if available. */
406 const char *trace_name = bt_trace_get_name(trace->ir_trace);
407 if (trace_name) {
408 path = g_string_new(trace_name);
409 goto end;
410 }
411
412 /* Otherwise, use "trace". */
413 path = g_string_new("trace");
414
415 end:
416 return path;
417 }
418
419 /*
420 * Compute the trace output path for `trace`, rooted at `output_base_directory`.
421 */
422
423 static
424 GString *make_trace_path(const struct fs_sink_trace *trace, const char *output_base_directory)
425 {
426 GString *rel_path = NULL;
427 GString *rel_path_san = NULL;
428 GString *full_path = NULL;
429 GString *unique_full_path = NULL;
430
431 rel_path = make_trace_path_rel(trace);
432 if (!rel_path) {
433 goto end;
434 }
435
436 rel_path_san = sanitize_trace_path(rel_path->str);
437 if (!rel_path_san) {
438 goto end;
439 }
440
441 full_path = g_string_new(NULL);
442 if (!full_path) {
443 goto end;
444 }
445
446 g_string_printf(full_path, "%s" G_DIR_SEPARATOR_S "%s",
447 output_base_directory, rel_path_san->str);
448
449 unique_full_path = make_unique_trace_path(full_path->str);
450
451 end:
452 if (rel_path) {
453 g_string_free(rel_path, TRUE);
454 }
455
456 if (rel_path_san) {
457 g_string_free(rel_path_san, TRUE);
458 }
459
460 if (full_path) {
461 g_string_free(full_path, TRUE);
462 }
463
464 return unique_full_path;
465 }
466
467 BT_HIDDEN
468 void fs_sink_trace_destroy(struct fs_sink_trace *trace)
469 {
470 GString *tsdl = NULL;
471 FILE *fh = NULL;
472 size_t len;
473
474 if (!trace) {
475 goto end;
476 }
477
478 if (trace->ir_trace_destruction_listener_id != UINT64_C(-1)) {
479 /*
480 * Remove the destruction listener, otherwise it could
481 * be called in the future, and its private data is this
482 * CTF FS sink trace object which won't exist anymore.
483 */
484 (void) bt_trace_remove_destruction_listener(trace->ir_trace,
485 trace->ir_trace_destruction_listener_id);
486 trace->ir_trace_destruction_listener_id = UINT64_C(-1);
487 }
488
489 if (trace->streams) {
490 g_hash_table_destroy(trace->streams);
491 trace->streams = NULL;
492 }
493
494 tsdl = g_string_new(NULL);
495 BT_ASSERT(tsdl);
496 translate_trace_class_ctf_ir_to_tsdl(trace->tc, tsdl);
497
498 BT_ASSERT(trace->metadata_path);
499 fh = fopen(trace->metadata_path->str, "wb");
500 if (!fh) {
501 BT_COMP_LOGF_ERRNO("In trace destruction listener: "
502 "cannot open metadata file for writing: ",
503 ": path=\"%s\"", trace->metadata_path->str);
504 abort();
505 }
506
507 len = fwrite(tsdl->str, sizeof(*tsdl->str), tsdl->len, fh);
508 if (len != tsdl->len) {
509 BT_COMP_LOGF_ERRNO("In trace destruction listener: "
510 "cannot write metadata file: ",
511 ": path=\"%s\"", trace->metadata_path->str);
512 abort();
513 }
514
515 if (!trace->fs_sink->quiet) {
516 printf("Created CTF trace `%s`.\n", trace->path->str);
517 }
518
519 if (trace->path) {
520 g_string_free(trace->path, TRUE);
521 trace->path = NULL;
522 }
523
524 g_string_free(trace->metadata_path, TRUE);
525 trace->metadata_path = NULL;
526
527 fs_sink_ctf_trace_class_destroy(trace->tc);
528 trace->tc = NULL;
529 g_free(trace);
530
531 end:
532 if (fh) {
533 int ret = fclose(fh);
534
535 if (ret != 0) {
536 BT_COMP_LOGW_ERRNO("In trace destruction listener: "
537 "cannot close metadata file: ",
538 ": path=\"%s\"", trace->metadata_path->str);
539 }
540 }
541
542 if (tsdl) {
543 g_string_free(tsdl, TRUE);
544 }
545
546 return;
547 }
548
549 static
550 void ir_trace_destruction_listener(const bt_trace *ir_trace, void *data)
551 {
552 struct fs_sink_trace *trace = data;
553
554 /*
555 * Prevent bt_trace_remove_destruction_listener() from being
556 * called in fs_sink_trace_destroy(), which is called by
557 * g_hash_table_remove() below.
558 */
559 trace->ir_trace_destruction_listener_id = UINT64_C(-1);
560 g_hash_table_remove(trace->fs_sink->traces, ir_trace);
561 }
562
563 BT_HIDDEN
564 struct fs_sink_trace *fs_sink_trace_create(struct fs_sink_comp *fs_sink,
565 const bt_trace *ir_trace)
566 {
567 int ret;
568 struct fs_sink_trace *trace = g_new0(struct fs_sink_trace, 1);
569 bt_trace_add_listener_status trace_status;
570
571 if (!trace) {
572 goto end;
573 }
574
575 trace->log_level = fs_sink->log_level;
576 trace->fs_sink = fs_sink;
577 trace->ir_trace = ir_trace;
578 trace->ir_trace_destruction_listener_id = UINT64_C(-1);
579 trace->tc = translate_trace_class_trace_ir_to_ctf_ir(
580 fs_sink, bt_trace_borrow_class_const(ir_trace));
581 if (!trace->tc) {
582 goto error;
583 }
584
585 trace->path = make_trace_path(trace, fs_sink->output_dir_path->str);
586 BT_ASSERT(trace->path);
587 ret = g_mkdir_with_parents(trace->path->str, 0755);
588 if (ret) {
589 BT_COMP_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
610 error:
611 fs_sink_trace_destroy(trace);
612 trace = NULL;
613
614 end:
615 return trace;
616 }
This page took 0.042364 seconds and 4 git commands to generate.