ctf: Use environment fields provided by LTTng to make output trace path
authorSimon Marchi <simon.marchi@efficios.com>
Tue, 23 Apr 2019 13:32:58 +0000 (09:32 -0400)
committerJérémie Galarneau <jeremie.galarneau@efficios.com>
Fri, 3 May 2019 22:19:39 +0000 (18:19 -0400)
Starting with version 2.11, the LTTng tracer includes in the trace
environment all the information necessary to rebuild the output path
that it itself uses when outputting traces.  This lets us recreate an
output path similar to what LTTng does when outputting ctf traces (with
the exception that we also add a level with the hostname, which LTTng
doesn't).

If the trace does not come from LTTng, or comes from LTTng < 2.11, we
keep using the same strategy: use a trace path based on the trace name,
which is itself based on the original directory.

This feature will especially be useful in conjunction with session
rotation: since the input trace is actually composed of multiple
traces/chunks, the trace name would otherwise be based on one particular
of these chunks.  With the environment values, we can recreate a path
that describes correctly the trace as a whole.  The output path is then
similar to what one would have been output by LTTng, if one had not used
the session rotation feature in the first place.

Change-Id: I03c0569869561ec11873b808c47d30196889cb36
Signed-off-by: Simon Marchi <simon.marchi@efficios.com>
Reviewed-on: https://review.gerrithub.io/c/eepp/babeltrace/+/451942
Reviewed-by: Philippe Proulx <eeppeliteloop@gmail.com>
Tested-by: Philippe Proulx <eeppeliteloop@gmail.com>
plugins/ctf/fs-sink/fs-sink-trace.c

index 1eb87b5e1ed62a8729799c5ce4e697ecc6067c02..375fff970996cdd5a5da14c5243b5c46a067e8f2 100644 (file)
@@ -110,36 +110,365 @@ GString *sanitize_trace_path(const char *path)
        return san_path;
 }
 
+/*
+ * Find a path based on `path` that doesn't exist yet.  First, try `path`
+ * itself, then try with incrementing suffixes.
+ */
+
 static
-GString *make_unique_trace_path(struct fs_sink_comp *fs_sink,
-               const char *output_dir_path, const char *base)
+GString *make_unique_trace_path(const char *path)
 {
-       GString *path = g_string_new(output_dir_path);
-       GString *san_base = NULL;
+       GString *unique_path;
        unsigned int suffix = 0;
 
-       if (fs_sink->assume_single_trace) {
-               /* Use output directory directly */
+       unique_path = g_string_new(path);
+
+       while (g_file_test(unique_path->str, G_FILE_TEST_EXISTS)) {
+               g_string_printf(unique_path, "%s-%u", path, suffix);
+               suffix++;
+       }
+
+       return unique_path;
+}
+
+/*
+ * Validate that the input string `datetime` is an ISO8601-compliant string (the
+ * format used by LTTng in the metadata).
+ */
+
+static
+int lttng_validate_datetime(const char *datetime)
+{
+       GTimeZone *default_tz;
+       GDateTime *gdatetime = NULL;
+       int ret = -1;
+
+       default_tz = g_time_zone_new_utc();
+       if (!default_tz) {
+               BT_LOGD("Failed to allocate timezone");
                goto end;
        }
 
-       san_base = sanitize_trace_path(base);
-       g_string_append_printf(path, "/%s", san_base->str);
+       gdatetime = g_date_time_new_from_iso8601(datetime, default_tz);
+       if (!gdatetime) {
+               BT_LOGD("Couldn't parse datetime as iso8601: date=\"%s\"", datetime);
+               goto end;
+       }
 
-       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++;
+       ret = 0;
+
+end:
+       if (default_tz) {
+               g_time_zone_unref(default_tz);
+               default_tz = NULL;
+       }
+
+       if (gdatetime) {
+               g_date_time_unref(gdatetime);
+               gdatetime = NULL;
        }
 
+       return ret;
+}
+
+static
+int append_lttng_trace_path_ust_uid(GString *path, const bt_trace_class *tc)
+{
+       const bt_value *v;
+       int ret;
+
+       v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "tracer_buffering_id");
+       if (!v || !bt_value_is_integer(v)) {
+               BT_LOGD_STR("Couldn't get environment value: name=\"tracer_buffering_id\"");
+               goto error;
+       }
+
+       g_string_append_printf(path, G_DIR_SEPARATOR_S "%" PRId64, bt_value_integer_get(v));
+
+       v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "isa_length");
+       if (!v || !bt_value_is_integer(v)) {
+               BT_LOGD_STR("Couldn't get environment value: name=\"isa_length\"");
+               goto error;
+       }
+
+       g_string_append_printf(path, G_DIR_SEPARATOR_S "%" PRIu64 "-bit", bt_value_integer_get(v));
+
+       ret = 0;
+       goto end;
+
+error:
+       ret = -1;
+
 end:
-       if (san_base) {
-               g_string_free(san_base, TRUE);
+       return ret;
+}
+
+static
+int append_lttng_trace_path_ust_pid(GString *path, const bt_trace_class *tc)
+{
+       const bt_value *v;
+       const char *datetime;
+       int ret;
+
+       v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "procname");
+       if (!v || !bt_value_is_string(v)) {
+               BT_LOGD_STR("Couldn't get environment value: name=\"procname\"");
+               goto error;
+       }
+
+       g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", bt_value_string_get(v));
+
+       v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "vpid");
+       if (!v || !bt_value_is_integer(v)) {
+               BT_LOGD_STR("Couldn't get environment value: name=\"vpid\"");
+               goto error;
+       }
+
+       g_string_append_printf(path, "-%" PRId64, bt_value_integer_get(v));
+
+       v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "vpid_datetime");
+       if (!v || !bt_value_is_string(v)) {
+               BT_LOGD_STR("Couldn't get environment value: name=\"vpid_datetime\"");
+               goto error;
+       }
+
+       datetime = bt_value_string_get(v);
+
+       if (lttng_validate_datetime(datetime)) {
+               goto error;
+       }
+
+       g_string_append_printf(path, "-%s", datetime);
+
+       ret = 0;
+       goto end;
+
+error:
+       ret = -1;
+
+end:
+       return ret;
+}
+
+/*
+ * Try to build a trace path based on environment values put in the trace
+ * environment by the LTTng tracer, starting with version 2.11.
+ */
+static
+GString *make_lttng_trace_path_rel(const struct fs_sink_trace *trace)
+{
+       const bt_trace_class *tc;
+       const bt_value *v;
+       const char *tracer_name, *domain, *datetime;
+       int64_t tracer_major, tracer_minor;
+       GString *path;
+
+       path = g_string_new(NULL);
+       if (!path) {
+               goto error;
+       }
+
+       tc = bt_trace_borrow_class_const(trace->ir_trace);
+
+       v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "tracer_name");
+       if (!v || !bt_value_is_string(v)) {
+               BT_LOGD_STR("Couldn't get environment value: name=\"tracer_name\"");
+               goto error;
+       }
+
+       tracer_name = bt_value_string_get(v);
+
+       if (!g_str_equal(tracer_name, "lttng-ust")
+                       && !g_str_equal(tracer_name, "lttng-modules")) {
+               BT_LOGD("Unrecognized tracer name: name=\"%s\"", tracer_name);
+               goto error;
        }
 
+       v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "tracer_major");
+       if (!v || !bt_value_is_integer(v)) {
+               BT_LOGD_STR("Couldn't get environment value: name=\"tracer_major\"");
+               goto error;
+       }
+
+       tracer_major = bt_value_integer_get(v);
+
+       v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "tracer_minor");
+       if (!v || !bt_value_is_integer(v)) {
+               BT_LOGD_STR("Couldn't get environment value: name=\"tracer_minor\"");
+               goto error;
+       }
+
+       tracer_minor = bt_value_integer_get(v);
+
+       if (!(tracer_major >= 3 || (tracer_major == 2 && tracer_minor >= 11))) {
+               BT_LOGD("Unsupported LTTng version for automatic trace path: major=%" PRId64 ", minor=%" PRId64,
+                       tracer_major, tracer_minor);
+               goto error;
+       }
+
+       v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "hostname");
+       if (!v || !bt_value_is_string(v)) {
+               BT_LOGD_STR("Couldn't get environment value: name=\"tracer_hostname\"");
+               goto error;
+       }
+
+       g_string_assign(path, bt_value_string_get(v));
+
+       v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "trace_name");
+       if (!v || !bt_value_is_string(v)) {
+               BT_LOGD_STR("Couldn't get environment value: name=\"trace_name\"");
+               goto error;
+       }
+
+       g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", bt_value_string_get(v));
+
+       v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "trace_creation_datetime");
+       if (!v || !bt_value_is_string(v)) {
+               BT_LOGD_STR("Couldn't get environment value: name=\"trace_creation_datetime\"");
+               goto error;
+       }
+
+       datetime = bt_value_string_get(v);
+
+       if (lttng_validate_datetime(datetime)) {
+               goto error;
+       }
+
+       g_string_append_printf(path, "-%s", datetime);
+
+       v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "domain");
+       if (!v || !bt_value_is_string(v)) {
+               BT_LOGD_STR("Couldn't get environment value: name=\"domain\"");
+               goto error;
+       }
+
+       domain = bt_value_string_get(v);
+       g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", domain);
+
+       if (g_str_equal(domain, "ust")) {
+               const char *tracer_buffering_scheme;
+
+               v = bt_trace_class_borrow_environment_entry_value_by_name_const(tc, "tracer_buffering_scheme");
+               if (!v || !bt_value_is_string(v)) {
+                       BT_LOGD_STR("Couldn't get environment value: name=\"tracer_buffering_scheme\"");
+                       goto error;
+               }
+
+               tracer_buffering_scheme = bt_value_string_get(v);
+               g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", tracer_buffering_scheme);
+
+               if (g_str_equal(tracer_buffering_scheme, "uid")) {
+                       if (append_lttng_trace_path_ust_uid(path, tc)) {
+                               goto error;
+                       }
+               } else if (g_str_equal(tracer_buffering_scheme, "pid")){
+                       if (append_lttng_trace_path_ust_pid(path, tc)) {
+                               goto error;
+                       }
+               } else {
+                       /* Unknown buffering scheme. */
+                       BT_LOGD("Unknown buffering scheme: tracer_buffering_scheme=\"%s\"", tracer_buffering_scheme);
+                       goto error;
+               }
+       } else if (!g_str_equal(domain, "kernel")) {
+               /* Unknown domain. */
+               BT_LOGD("Unknown domain: domain=\"%s\"", domain);
+               goto error;
+       }
+
+       goto end;
+
+error:
+       if (path) {
+               g_string_free(path, TRUE);
+               path = NULL;
+       }
+
+end:
+       return path;
+}
+
+/* Build the relative output path for `trace`. */
+
+static
+GString *make_trace_path_rel(const struct fs_sink_trace *trace)
+{
+       GString *path = NULL;
+
+       if (trace->fs_sink->assume_single_trace) {
+               /* Use output directory directly */
+               path = g_string_new("");
+               goto end;
+       }
+
+       /* First, try to build a path using environment fields written by LTTng. */
+       path = make_lttng_trace_path_rel(trace);
+       if (path) {
+               goto end;
+       }
+
+       /* Otherwise, use the trace name, if available. */
+       const char *trace_name = bt_trace_get_name(trace->ir_trace);
+       if (trace_name) {
+               path = g_string_new(trace_name);
+               goto end;
+       }
+
+       /* Otherwise, use "trace". */
+       path = g_string_new("trace");
+
+end:
        return path;
 }
 
+/*
+ * Compute the trace output path for `trace`, rooted at `output_base_directory`.
+ */
+
+static
+GString *make_trace_path(const struct fs_sink_trace *trace, const char *output_base_directory)
+{
+       GString *rel_path = NULL;
+       GString *rel_path_san = NULL;
+       GString *full_path = NULL;
+       GString *unique_full_path = NULL;
+
+       rel_path = make_trace_path_rel(trace);
+       if (!rel_path) {
+               goto end;
+       }
+
+       rel_path_san = sanitize_trace_path(rel_path->str);
+       if (!rel_path_san) {
+               goto end;
+       }
+
+       full_path = g_string_new(NULL);
+       if (!full_path) {
+               goto end;
+       }
+
+       g_string_printf(full_path, "%s" G_DIR_SEPARATOR_S "%s",
+               output_base_directory, rel_path_san->str);
+
+       unique_full_path = make_unique_trace_path(full_path->str);
+
+end:
+       if (rel_path) {
+               g_string_free(rel_path, TRUE);
+       }
+
+       if (rel_path_san) {
+               g_string_free(rel_path_san, TRUE);
+       }
+
+       if (full_path) {
+               g_string_free(full_path, TRUE);
+       }
+
+       return unique_full_path;
+}
+
 BT_HIDDEN
 void fs_sink_trace_destroy(struct fs_sink_trace *trace)
 {
@@ -242,17 +571,12 @@ struct fs_sink_trace *fs_sink_trace_create(struct fs_sink_comp *fs_sink,
 {
        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);
@@ -262,8 +586,7 @@ struct fs_sink_trace *fs_sink_trace_create(struct fs_sink_comp *fs_sink,
                goto error;
        }
 
-       trace->path = make_unique_trace_path(fs_sink,
-               fs_sink->output_dir_path->str, trace_name);
+       trace->path = make_trace_path(trace, fs_sink->output_dir_path->str);
        BT_ASSERT(trace->path);
        ret = g_mkdir_with_parents(trace->path->str, 0755);
        if (ret) {
This page took 0.028689 seconds and 4 git commands to generate.