Adapt `sink.ctf.fs` to current API
[babeltrace.git] / plugins / ctf / fs-sink / fs-sink-trace.c
diff --git a/plugins/ctf/fs-sink/fs-sink-trace.c b/plugins/ctf/fs-sink/fs-sink-trace.c
new file mode 100644 (file)
index 0000000..1eb87b5
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#define BT_LOG_TAG "PLUGIN-CTF-FS-SINK-TRACE"
+#include "logging.h"
+
+#include <babeltrace/babeltrace.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <glib.h>
+#include <babeltrace/assert-internal.h>
+#include <babeltrace/ctfser-internal.h>
+
+#include "translate-trace-ir-to-ctf-ir.h"
+#include "translate-ctf-ir-to-tsdl.h"
+#include "fs-sink.h"
+#include "fs-sink-trace.h"
+#include "fs-sink-stream.h"
+
+/*
+ * Sanitizes `path` so as to:
+ *
+ * * Replace `.` subdirectories with `_`.
+ * * Replace `..` subdirectories with `__`.
+ * * Remove trailing slashes.
+ */
+static
+GString *sanitize_trace_path(const char *path)
+{
+       GString *san_path = g_string_new(NULL);
+       const char *ch = path;
+       bool dir_start = true;
+
+       BT_ASSERT(san_path);
+       BT_ASSERT(path);
+
+       while (*ch != '\0') {
+               switch (*ch) {
+               case '/':
+                       /* Start of directory */
+                       dir_start = true;
+                       g_string_append_c(san_path, *ch);
+                       ch++;
+                       continue;
+               case '.':
+                       if (dir_start) {
+                               switch (ch[1]) {
+                               case '\0':
+                               case '/':
+                                       /* `.` -> `_` */
+                                       g_string_append_c(san_path, '_');
+                                       ch++;
+                                       continue;
+                               case '.':
+                                       switch (ch[2]) {
+                                       case '\0':
+                                       case '/':
+                                               /* `..` -> `__` */
+                                               g_string_append(san_path, "__");
+                                               ch += 2;
+                                               continue;
+                                       default:
+                                               break;
+                                       }
+                               default:
+                                       break;
+                               }
+                       }
+               default:
+                       break;
+               }
+
+               /* Not a special character */
+               g_string_append_c(san_path, *ch);
+               ch++;
+               dir_start = false;
+       }
+
+       /* Remove trailing slashes */
+       while (san_path->len > 0 &&
+                       san_path->str[san_path->len - 1] == '/') {
+               /* Remove trailing slash */
+               g_string_set_size(san_path, san_path->len - 1);
+       }
+
+       if (san_path->len == 0) {
+               /* Looks like there's nothing left: just use `trace` */
+               g_string_assign(san_path, "trace");
+       }
+
+       return san_path;
+}
+
+static
+GString *make_unique_trace_path(struct fs_sink_comp *fs_sink,
+               const char *output_dir_path, const char *base)
+{
+       GString *path = g_string_new(output_dir_path);
+       GString *san_base = NULL;
+       unsigned int suffix = 0;
+
+       if (fs_sink->assume_single_trace) {
+               /* Use output directory directly */
+               goto end;
+       }
+
+       san_base = sanitize_trace_path(base);
+       g_string_append_printf(path, "/%s", san_base->str);
+
+       while (g_file_test(path->str, G_FILE_TEST_IS_DIR)) {
+               g_string_assign(path, output_dir_path);
+               g_string_append_printf(path, "/%s%u", san_base->str, suffix);
+               suffix++;
+       }
+
+end:
+       if (san_base) {
+               g_string_free(san_base, TRUE);
+       }
+
+       return path;
+}
+
+BT_HIDDEN
+void fs_sink_trace_destroy(struct fs_sink_trace *trace)
+{
+       GString *tsdl = NULL;
+       FILE *fh = NULL;
+       size_t len;
+
+       if (!trace) {
+               goto end;
+       }
+
+       if (trace->ir_trace_destruction_listener_id != UINT64_C(-1)) {
+               /*
+                * Remove the destruction listener, otherwise it could
+                * be called in the future, and its private data is this
+                * CTF FS sink trace object which won't exist anymore.
+                */
+               (void) bt_trace_remove_destruction_listener(trace->ir_trace,
+                       trace->ir_trace_destruction_listener_id);
+               trace->ir_trace_destruction_listener_id = UINT64_C(-1);
+       }
+
+       if (trace->streams) {
+               g_hash_table_destroy(trace->streams);
+               trace->streams = NULL;
+       }
+
+       tsdl = g_string_new(NULL);
+       BT_ASSERT(tsdl);
+       translate_trace_class_ctf_ir_to_tsdl(trace->tc, tsdl);
+       fh = fopen(trace->metadata_path->str, "wb");
+       if (!fh) {
+               BT_LOGF_ERRNO("In trace destruction listener: "
+                       "cannot open metadata file for writing: ",
+                       ": path=\"%s\"", trace->metadata_path->str);
+               abort();
+       }
+
+       len = fwrite(tsdl->str, sizeof(*tsdl->str), tsdl->len, fh);
+       if (len != tsdl->len) {
+               BT_LOGF_ERRNO("In trace destruction listener: "
+                       "cannot write metadata file: ",
+                       ": path=\"%s\"", trace->metadata_path->str);
+               abort();
+       }
+
+       if (!trace->fs_sink->quiet) {
+               printf("Created CTF trace `%s`.\n", trace->path->str);
+       }
+
+       if (trace->path) {
+               g_string_free(trace->path, TRUE);
+               trace->path = NULL;
+       }
+
+       if (trace->metadata_path) {
+               g_string_free(trace->metadata_path, TRUE);
+               trace->metadata_path = NULL;
+       }
+
+       fs_sink_ctf_trace_class_destroy(trace->tc);
+       trace->tc = NULL;
+       g_free(trace);
+
+end:
+       if (fh) {
+               int ret = fclose(fh);
+
+               if (ret != 0) {
+                       BT_LOGW_ERRNO("In trace destruction listener: "
+                               "cannot close metadata file: ",
+                               ": path=\"%s\"", trace->metadata_path->str);
+               }
+       }
+
+       if (tsdl) {
+               g_string_free(tsdl, TRUE);
+       }
+
+       return;
+}
+
+static
+void ir_trace_destruction_listener(const bt_trace *ir_trace, void *data)
+{
+       struct fs_sink_trace *trace = data;
+
+       /*
+        * Prevent bt_trace_remove_destruction_listener() from being
+        * called in fs_sink_trace_destroy(), which is called by
+        * g_hash_table_remove() below.
+        */
+       trace->ir_trace_destruction_listener_id = UINT64_C(-1);
+       g_hash_table_remove(trace->fs_sink->traces, ir_trace);
+}
+
+BT_HIDDEN
+struct fs_sink_trace *fs_sink_trace_create(struct fs_sink_comp *fs_sink,
+               const bt_trace *ir_trace)
+{
+       int ret;
+       struct fs_sink_trace *trace = g_new0(struct fs_sink_trace, 1);
+       const char *trace_name = bt_trace_get_name(ir_trace);
+       bt_trace_status trace_status;
+
+       if (!trace) {
+               goto end;
+       }
+
+       if (!trace_name) {
+               trace_name = "trace";
+       }
+
+       trace->fs_sink = fs_sink;
+       trace->ir_trace = ir_trace;
+       trace->ir_trace_destruction_listener_id = UINT64_C(-1);
+       trace->tc = translate_trace_class_trace_ir_to_ctf_ir(
+               bt_trace_borrow_class_const(ir_trace));
+       if (!trace->tc) {
+               goto error;
+       }
+
+       trace->path = make_unique_trace_path(fs_sink,
+               fs_sink->output_dir_path->str, trace_name);
+       BT_ASSERT(trace->path);
+       ret = g_mkdir_with_parents(trace->path->str, 0755);
+       if (ret) {
+               BT_LOGE_ERRNO("Cannot create directories for trace directory",
+                       ": path=\"%s\"", trace->path->str);
+               goto error;
+       }
+
+       trace->metadata_path = g_string_new(trace->path->str);
+       BT_ASSERT(trace->metadata_path);
+       g_string_append(trace->metadata_path, "/metadata");
+       trace->streams = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+               NULL, (GDestroyNotify) fs_sink_stream_destroy);
+       BT_ASSERT(trace->streams);
+       trace_status = bt_trace_add_destruction_listener(ir_trace,
+               ir_trace_destruction_listener, trace,
+               &trace->ir_trace_destruction_listener_id);
+       if (trace_status) {
+               goto error;
+       }
+
+       g_hash_table_insert(fs_sink->traces, (gpointer) ir_trace, trace);
+       goto end;
+
+error:
+       fs_sink_trace_destroy(trace);
+       trace = NULL;
+
+end:
+       return trace;
+}
This page took 0.029221 seconds and 4 git commands to generate.