Common Trace Format 2 generation sow-2021-0006-rev1
authorFrancis Deslauriers <francis.deslauriers@efficios.com>
Thu, 6 Jan 2022 23:30:51 +0000 (18:30 -0500)
committerFrancis Deslauriers <francis.deslauriers@efficios.com>
Thu, 13 Jan 2022 16:52:50 +0000 (11:52 -0500)
Signed-off-by: Francis Deslauriers <francis.deslauriers@efficios.com>
Change-Id: I327a85a2c3e259830795f181f218b1f3504f24fb

16 files changed:
include/instrumentation/syscalls/headers/syscalls_pointers_override.h
include/lttng/events.h
include/lttng/uuid.h [new file with mode: 0644]
src/Kbuild
src/clock-utils.c [new file with mode: 0644]
src/clock-utils.h [new file with mode: 0644]
src/ctf1-8.c [new file with mode: 0644]
src/ctf1-8.h [new file with mode: 0644]
src/ctf2.c [new file with mode: 0644]
src/ctf2.h [new file with mode: 0644]
src/field-path-resolving.c [new file with mode: 0644]
src/field-path-resolving.h [new file with mode: 0644]
src/lttng-events.c
src/lttng-uuid.c [new file with mode: 0644]
src/metadata-printer.c [new file with mode: 0644]
src/metadata-printer.h [new file with mode: 0644]

index ed8041dd21a8ec2753f364afc03e45b3c8271fca..0b7134b68217f3e4ee6727fee50a200298851146 100644 (file)
@@ -505,7 +505,7 @@ static_assert(((ARRAY_SIZE(lttng_pollfd_flag_fields) - 1) + POLL_FLAGS_PADDING_S
 #endif /* LTTNG_TRACEPOINT_TYPE_EXTERN */
 
 LTTNG_TRACEPOINT_TYPE(PARAMS(const struct lttng_kernel_type_struct lttng_pollfd_flag_fields_struct),
-       PARAMS(lttng_kernel_static_type_struct_init( ARRAY_SIZE(lttng_pollfd_flag_fields), lttng_pollfd_flag_fields, 0)))
+       PARAMS(lttng_kernel_static_type_struct_init(ARRAY_SIZE(lttng_pollfd_flag_fields), lttng_pollfd_flag_fields, 0)))
 
 #ifndef LTTNG_TRACEPOINT_TYPE_EXTERN
 static const struct lttng_kernel_event_field *lttng_pollfd_fields[] = {
index fa3e7e90591fd05bf136d62f896bb7d821397a65..d25f4282dde15f79bd8e77202773307763fc6c1a 100644 (file)
@@ -94,7 +94,7 @@ struct lttng_kernel_type_array {
        struct lttng_kernel_type_common parent;
        const struct lttng_kernel_type_common *elem_type;
        unsigned int length;                    /* Num. elems. */
-       unsigned int alignment;
+       unsigned int alignment;         /* Alignment in bytes before elements. */
        enum lttng_kernel_string_encoding encoding;
 };
 
@@ -102,7 +102,7 @@ struct lttng_kernel_type_sequence {
        struct lttng_kernel_type_common parent;
        const char *length_name;        /* Length field name. If NULL, use previous field. */
        const struct lttng_kernel_type_common *elem_type;
-       unsigned int alignment;         /* Alignment before elements. */
+       unsigned int alignment;         /* Alignment in bytes before elements. */
        enum lttng_kernel_string_encoding encoding;
 };
 
@@ -110,7 +110,7 @@ struct lttng_kernel_type_struct {
        struct lttng_kernel_type_common parent;
        unsigned int nr_fields;
        const struct lttng_kernel_event_field * const *fields;  /* Array of pointers to fields. */
-       unsigned int alignment;
+       unsigned int alignment;         /* Alignment in bits */
 };
 
 struct lttng_kernel_type_variant {
@@ -118,7 +118,7 @@ struct lttng_kernel_type_variant {
        const char *tag_name;           /* Tag field name. If NULL, use previous field. */
        const struct lttng_kernel_event_field * const *choices; /* Array of pointers to fields. */
        unsigned int nr_choices;
-       unsigned int alignment;
+       unsigned int alignment;         /* Alignment in bytes */
 };
 
 struct lttng_kernel_enum_desc {
diff --git a/include/lttng/uuid.h b/include/lttng/uuid.h
new file mode 100644 (file)
index 0000000..76e3ce8
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ */
+
+#ifndef LTTNG_UUID_H
+#define LTTNG_UUID_H
+
+/*
+ * Includes final \0.
+ */
+#define LTTNG_UUID_STR_LEN     37
+#define LTTNG_UUID_LEN         16
+#define LTTNG_UUID_VER         4
+
+#define LTTNG_UUID_FMT \
+       "%02hhx%02hhx%02hhx%02hhx-%02hhx" \
+       "%02hhx-%02hhx%02hhx-%02hhx%02hhx" \
+       "-%02hhx%02hhx%02hhx%02hhx%02hhx" \
+       "%02hhx"
+
+#define LTTNG_UUID_FMT_VALUES(uuid) \
+       (uuid)[0], (uuid)[1], (uuid)[2], (uuid)[3], (uuid)[4], (uuid)[5], \
+       (uuid)[6], (uuid)[7], (uuid)[8], (uuid)[9], (uuid)[10], (uuid)[11], \
+       (uuid)[12], (uuid)[13], (uuid)[14], (uuid)[15]
+
+#define LTTNG_UUID_SCAN_VALUES(uuid) \
+       &(uuid)[0], &(uuid)[1], &(uuid)[2], &(uuid)[3], &(uuid)[4], &(uuid)[5], \
+       &(uuid)[6], &(uuid)[7], &(uuid)[8], &(uuid)[9], &(uuid)[10], &(uuid)[11], \
+       &(uuid)[12], &(uuid)[13], &(uuid)[14], &(uuid)[15]
+
+typedef unsigned char lttng_uuid[LTTNG_UUID_LEN];
+
+int lttng_uuid_from_str(const char *str_in, lttng_uuid uuid_out);
+
+#endif /* LTTNG_UUID_H */
index 7137874fa4c7a0bf0a7ce899ff97a2c340f91aa8..c309d8ef799e1c59f6b79da192498c3042c68afa 100644 (file)
@@ -48,6 +48,11 @@ obj-$(CONFIG_LTTNG) += lttng-wrapper.o
 lttng-tracer-objs := lib/msgpack/msgpack.o \
                      lttng-events.o lttng-abi.o lttng-string-utils.o \
                      lttng-probes.o lttng-context.o \
+                     metadata-printer.o \
+                     clock-utils.o \
+                     ctf1-8.o \
+                     ctf2.o \
+                     field-path-resolving.o \
                      lttng-context-pid.o lttng-context-procname.o \
                      lttng-context-prio.o lttng-context-nice.o \
                      lttng-context-vpid.o lttng-context-tid.o \
@@ -69,7 +74,7 @@ lttng-tracer-objs := lib/msgpack/msgpack.o \
                      lttng-context-need-reschedule.o \
                      lttng-calibrate.o \
                      lttng-context-hostname.o \
-                    lttng-context-callstack.o \
+                     lttng-context-callstack.o \
                      probes/lttng.o \
                      lttng-tracker-id.o \
                      lttng-bytecode.o lttng-bytecode-interpreter.o \
@@ -77,7 +82,8 @@ lttng-tracer-objs := lib/msgpack/msgpack.o \
                      lttng-bytecode-validator.o \
                      probes/lttng-probe-user.o \
                      lttng-tp-mempool.o \
-                     lttng-event-notifier-notification.o
+                     lttng-event-notifier-notification.o \
+                     lttng-uuid.o
 
 lttng-wrapper-objs := wrapper/page_alloc.o \
                       wrapper/random.o \
diff --git a/src/clock-utils.c b/src/clock-utils.c
new file mode 100644 (file)
index 0000000..5e61d1e
--- /dev/null
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: (GPL-2.0-only or LGPL-2.1-only)
+ *
+ * src/clock-utils.h
+ *
+ * Copyright (C) 2011-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Copyright (C) 2022 Francis Deslauriers <francis.deslauriers@efficios.com>
+ */
+
+/*
+ * Approximation of NTP time of day to clock monotonic correlation,
+ * taken at start of trace.
+ * Yes, this is only an approximation. Yes, we can (and will) do better
+ * in future versions.
+ * This function may return a negative offset. It may happen if the
+ * system sets the REALTIME clock to 0 after boot.
+ *
+ * Use 64bit timespec on kernels that have it, this makes 32bit arch
+ * y2038 compliant.
+ */
+
+#include <linux/types.h>
+
+#include <wrapper/time.h>
+#include <wrapper/trace-clock.h>
+
+int64_t trace_clock_offset(void)
+{
+       uint64_t monotonic_avg, monotonic[2], realtime;
+       uint64_t tcf = trace_clock_freq();
+       int64_t offset;
+       unsigned long flags;
+#ifdef LTTNG_KERNEL_HAS_TIMESPEC64
+       struct timespec64 rts = { 0, 0 };
+#else
+       struct timespec rts = { 0, 0 };
+#endif
+
+       /* Disable interrupts to increase correlation precision. */
+       local_irq_save(flags);
+       monotonic[0] = trace_clock_read64();
+#ifdef LTTNG_KERNEL_HAS_TIMESPEC64
+       ktime_get_real_ts64(&rts);
+#else
+       getnstimeofday(&rts);
+#endif
+       monotonic[1] = trace_clock_read64();
+       local_irq_restore(flags);
+
+       monotonic_avg = (monotonic[0] + monotonic[1]) >> 1;
+       realtime = (uint64_t)rts.tv_sec * tcf;
+       if (tcf == NSEC_PER_SEC) {
+               realtime += rts.tv_nsec;
+       } else {
+               uint64_t n = rts.tv_nsec * tcf;
+
+               do_div(n, NSEC_PER_SEC);
+               realtime += n;
+       }
+       offset = (int64_t)realtime - monotonic_avg;
+       return offset;
+}
diff --git a/src/clock-utils.h b/src/clock-utils.h
new file mode 100644 (file)
index 0000000..21b7fdf
--- /dev/null
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: (GPL-2.0-only or LGPL-2.1-only)
+ *
+ * src/clock-utils.h
+ *
+ * Copyright (C) 2011-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Copyright (C) 2022 Francis Deslauriers <francis.deslauriers@efficios.com>
+ */
+
+#include <linux/types.h>
+
+int64_t trace_clock_offset(void);
diff --git a/src/ctf1-8.c b/src/ctf1-8.c
new file mode 100644 (file)
index 0000000..00c4be5
--- /dev/null
@@ -0,0 +1,1072 @@
+/* SPDX-License-Identifier: (GPL-2.0-only or LGPL-2.1-only)
+ *
+ * src/ctf1-8.c
+ *
+ * Copyright (C) 2010-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Copyright (C) 2021 Francis Deslauriers <francis.deslauriers@efficios.com>
+ */
+
+#include <linux/dmi.h>
+#include <linux/utsname.h>
+#include <linux/ktime.h>
+#include <linux/timekeeping.h>
+
+#include <lttng/events.h>
+#include <lttng/events-internal.h>
+
+#include "metadata-printer.h"
+#include "clock-utils.h"
+
+static
+int _lttng_field_statedump(struct lttng_kernel_session *session,
+               const struct lttng_kernel_event_field *field,
+               size_t nesting, const char **prev_field_name_p);
+
+static
+int _lttng_type_statedump(struct lttng_kernel_session *session,
+               const struct lttng_kernel_type_common *type,
+               enum lttng_kernel_string_encoding parent_encoding,
+               size_t nesting);
+
+static
+int print_tabs(struct lttng_kernel_session *session, size_t nesting)
+{
+       size_t i;
+
+       for (i = 0; i < nesting; i++) {
+               int ret;
+
+               ret = lttng_metadata_printf(session, "  ");
+               if (ret) {
+                       return ret;
+               }
+       }
+       return 0;
+}
+
+static
+int lttng_field_name_statedump(struct lttng_kernel_session *session,
+               const struct lttng_kernel_event_field *field,
+               size_t nesting)
+{
+       return lttng_metadata_printf(session, " _%s;\n", field->name);
+}
+
+static
+int _lttng_integer_type_statedump(struct lttng_kernel_session *session,
+               const struct lttng_kernel_type_integer *type,
+               enum lttng_kernel_string_encoding parent_encoding,
+               size_t nesting)
+{
+       int ret;
+
+       ret = print_tabs(session, nesting);
+       if (ret)
+               return ret;
+       ret = lttng_metadata_printf(session,
+               "integer { size = %u; align = %u; signed = %u; encoding = %s; base = %u;%s }",
+               type->size,
+               type->alignment,
+               type->signedness,
+               (parent_encoding == lttng_kernel_string_encoding_none)
+                       ? "none"
+                       : (parent_encoding == lttng_kernel_string_encoding_UTF8)
+                               ? "UTF8"
+                               : "ASCII",
+               type->base,
+#if __BYTE_ORDER == __BIG_ENDIAN
+               type->reverse_byte_order ? " byte_order = le;" : ""
+#else
+               type->reverse_byte_order ? " byte_order = be;" : ""
+#endif
+       );
+       return ret;
+}
+
+/*
+ * Must be called with sessions_mutex held.
+ */
+static
+int _lttng_struct_type_statedump(struct lttng_kernel_session *session,
+               const struct lttng_kernel_type_struct *type,
+               size_t nesting)
+{
+       const char *prev_field_name = NULL;
+       int ret;
+       uint32_t i, nr_fields;
+       unsigned int alignment;
+
+       ret = print_tabs(session, nesting);
+       if (ret)
+               return ret;
+       ret = lttng_metadata_printf(session,
+               "struct {\n");
+       if (ret)
+               return ret;
+       nr_fields = type->nr_fields;
+       for (i = 0; i < nr_fields; i++) {
+               const struct lttng_kernel_event_field *iter_field;
+
+               iter_field = type->fields[i];
+               ret = _lttng_field_statedump(session, iter_field, nesting + 1, &prev_field_name);
+               if (ret)
+                       return ret;
+       }
+       ret = print_tabs(session, nesting);
+       if (ret)
+               return ret;
+       alignment = type->alignment;
+       if (alignment) {
+               ret = lttng_metadata_printf(session,
+                       "} align(%u)",
+                       alignment);
+       } else {
+               ret = lttng_metadata_printf(session,
+                       "}");
+       }
+       return ret;
+}
+
+/*
+ * Must be called with sessions_mutex held.
+ */
+static
+int _lttng_struct_field_statedump(struct lttng_kernel_session *session,
+               const struct lttng_kernel_event_field *field,
+               size_t nesting)
+{
+       int ret;
+
+       ret = _lttng_struct_type_statedump(session,
+                       lttng_kernel_get_type_struct(field->type), nesting);
+       if (ret)
+               return ret;
+       return lttng_field_name_statedump(session, field, nesting);
+}
+
+/*
+ * Must be called with sessions_mutex held.
+ */
+static
+int _lttng_variant_type_statedump(struct lttng_kernel_session *session,
+               const struct lttng_kernel_type_variant *type,
+               size_t nesting,
+               const char *prev_field_name)
+{
+       const char *tag_name;
+       int ret;
+       uint32_t i, nr_choices;
+
+       tag_name = type->tag_name;
+       if (!tag_name)
+               tag_name = prev_field_name;
+       if (!tag_name)
+               return -EINVAL;
+       /*
+        * CTF 1.8 does not allow expressing nonzero variant alignment in a nestable way.
+        */
+       if (type->alignment != 0)
+               return -EINVAL;
+       ret = print_tabs(session, nesting);
+       if (ret)
+               return ret;
+       ret = lttng_metadata_printf(session,
+               "variant <_%s> {\n",
+               tag_name);
+       if (ret)
+               return ret;
+       nr_choices = type->nr_choices;
+       for (i = 0; i < nr_choices; i++) {
+               const struct lttng_kernel_event_field *iter_field;
+
+               iter_field = type->choices[i];
+               ret = _lttng_field_statedump(session, iter_field, nesting + 1, NULL);
+               if (ret)
+                       return ret;
+       }
+       ret = print_tabs(session, nesting);
+       if (ret)
+               return ret;
+       ret = lttng_metadata_printf(session,
+               "}");
+       return ret;
+}
+
+/*
+ * Must be called with sessions_mutex held.
+ */
+static
+int _lttng_variant_field_statedump(struct lttng_kernel_session *session,
+               const struct lttng_kernel_event_field *field,
+               size_t nesting,
+               const char *prev_field_name)
+{
+       int ret;
+
+       ret = _lttng_variant_type_statedump(session,
+                       lttng_kernel_get_type_variant(field->type), nesting,
+                       prev_field_name);
+       if (ret)
+               return ret;
+       return lttng_field_name_statedump(session, field, nesting);
+}
+
+/*
+ * Must be called with sessions_mutex held.
+ */
+static
+int _lttng_array_field_statedump(struct lttng_kernel_session *session,
+               const struct lttng_kernel_event_field *field,
+               size_t nesting)
+{
+       int ret;
+       const struct lttng_kernel_type_array *array_type;
+       const struct lttng_kernel_type_common *elem_type;
+
+       array_type = lttng_kernel_get_type_array(field->type);
+       WARN_ON_ONCE(!array_type);
+
+       if (array_type->alignment) {
+               ret = print_tabs(session, nesting);
+               if (ret)
+                       return ret;
+               ret = lttng_metadata_printf(session,
+               "struct { } align(%u) _%s_padding;\n",
+                               array_type->alignment * CHAR_BIT,
+                               field->name);
+               if (ret)
+                       return ret;
+       }
+       /*
+        * Nested compound types: Only array of structures and variants are
+        * currently supported.
+        */
+       elem_type = array_type->elem_type;
+       switch (elem_type->type) {
+       case lttng_kernel_type_integer:
+       case lttng_kernel_type_struct:
+       case lttng_kernel_type_variant:
+               ret = _lttng_type_statedump(session, elem_type,
+                               array_type->encoding, nesting);
+               if (ret)
+                       return ret;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+       ret = lttng_metadata_printf(session,
+               " _%s[%u];\n",
+               field->name,
+               array_type->length);
+       return ret;
+}
+
+/*
+ * Must be called with sessions_mutex held.
+ */
+static
+int _lttng_sequence_field_statedump(struct lttng_kernel_session *session,
+               const struct lttng_kernel_event_field *field,
+               size_t nesting,
+               const char *prev_field_name)
+{
+       int ret;
+       const char *length_name;
+       const struct lttng_kernel_type_sequence *sequence_type;
+       const struct lttng_kernel_type_common *elem_type;
+
+       sequence_type = lttng_kernel_get_type_sequence(field->type);
+       WARN_ON_ONCE(!sequence_type);
+
+       length_name = sequence_type->length_name;
+       if (!length_name)
+               length_name = prev_field_name;
+       if (!length_name)
+               return -EINVAL;
+
+       if (sequence_type->alignment) {
+               ret = print_tabs(session, nesting);
+               if (ret)
+                       return ret;
+               ret = lttng_metadata_printf(session,
+               "struct { } align(%u) _%s_padding;\n",
+                               sequence_type->alignment * CHAR_BIT,
+                               field->name);
+               if (ret)
+                       return ret;
+       }
+
+       /*
+        * Nested compound types: Only array of structures and variants are
+        * currently supported.
+        */
+       elem_type = sequence_type->elem_type;
+       switch (elem_type->type) {
+       case lttng_kernel_type_integer:
+       case lttng_kernel_type_struct:
+       case lttng_kernel_type_variant:
+               ret = _lttng_type_statedump(session, elem_type,
+                               sequence_type->encoding, nesting);
+               if (ret)
+                       return ret;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+       ret = lttng_metadata_printf(session,
+               " _%s[ _%s ];\n",
+               field->name,
+               length_name);
+       return ret;
+}
+
+/*
+ * Must be called with sessions_mutex held.
+ */
+static
+int _lttng_enum_type_statedump(struct lttng_kernel_session *session,
+               const struct lttng_kernel_type_enum *type,
+               size_t nesting)
+{
+       const struct lttng_kernel_enum_desc *enum_desc;
+       const struct lttng_kernel_type_common *container_type;
+       int ret;
+       unsigned int i, nr_entries;
+
+       container_type = type->container_type;
+       if (container_type->type != lttng_kernel_type_integer) {
+               ret = -EINVAL;
+               goto end;
+       }
+       enum_desc = type->desc;
+       nr_entries = enum_desc->nr_entries;
+
+       ret = print_tabs(session, nesting);
+       if (ret)
+               goto end;
+       ret = lttng_metadata_printf(session, "enum : ");
+       if (ret)
+               goto end;
+       ret = _lttng_integer_type_statedump(session, lttng_kernel_get_type_integer(container_type),
+                       lttng_kernel_string_encoding_none, 0);
+       if (ret)
+               goto end;
+       ret = lttng_metadata_printf(session, " {\n");
+       if (ret)
+               goto end;
+       /* Dump all entries */
+       for (i = 0; i < nr_entries; i++) {
+               const struct lttng_kernel_enum_entry *entry = enum_desc->entries[i];
+               int j, len;
+
+               ret = print_tabs(session, nesting + 1);
+               if (ret)
+                       goto end;
+               ret = lttng_metadata_printf(session,
+                               "\"");
+               if (ret)
+                       goto end;
+               len = strlen(entry->string);
+               /* Escape the character '"' */
+               for (j = 0; j < len; j++) {
+                       char c = entry->string[j];
+
+                       switch (c) {
+                       case '"':
+                               ret = lttng_metadata_printf(session,
+                                               "\\\"");
+                               break;
+                       case '\\':
+                               ret = lttng_metadata_printf(session,
+                                               "\\\\");
+                               break;
+                       default:
+                               ret = lttng_metadata_printf(session,
+                                               "%c", c);
+                               break;
+                       }
+                       if (ret)
+                               goto end;
+               }
+               ret = lttng_metadata_printf(session, "\"");
+               if (ret)
+                       goto end;
+
+               if (entry->options.is_auto) {
+                       ret = lttng_metadata_printf(session, ",\n");
+                       if (ret)
+                               goto end;
+               } else {
+                       ret = lttng_metadata_printf(session,
+                                       " = ");
+                       if (ret)
+                               goto end;
+                       if (entry->start.signedness)
+                               ret = lttng_metadata_printf(session,
+                                       "%lld", (long long) entry->start.value);
+                       else
+                               ret = lttng_metadata_printf(session,
+                                       "%llu", entry->start.value);
+                       if (ret)
+                               goto end;
+                       if (entry->start.signedness == entry->end.signedness &&
+                                       entry->start.value
+                                               == entry->end.value) {
+                               ret = lttng_metadata_printf(session,
+                                       ",\n");
+                       } else {
+                               if (entry->end.signedness) {
+                                       ret = lttng_metadata_printf(session,
+                                               " ... %lld,\n",
+                                               (long long) entry->end.value);
+                               } else {
+                                       ret = lttng_metadata_printf(session,
+                                               " ... %llu,\n",
+                                               entry->end.value);
+                               }
+                       }
+                       if (ret)
+                               goto end;
+               }
+       }
+       ret = print_tabs(session, nesting);
+       if (ret)
+               goto end;
+       ret = lttng_metadata_printf(session, "}");
+end:
+       return ret;
+}
+
+/*
+ * Must be called with sessions_mutex held.
+ */
+static
+int _lttng_enum_field_statedump(struct lttng_kernel_session *session,
+               const struct lttng_kernel_event_field *field,
+               size_t nesting)
+{
+       int ret;
+       const struct lttng_kernel_type_enum *enum_type;
+
+       enum_type = lttng_kernel_get_type_enum(field->type);
+       WARN_ON_ONCE(!enum_type);
+       ret = _lttng_enum_type_statedump(session, enum_type, nesting);
+       if (ret)
+               return ret;
+       return lttng_field_name_statedump(session, field, nesting);
+}
+
+static
+int _lttng_integer_field_statedump(struct lttng_kernel_session *session,
+               const struct lttng_kernel_event_field *field,
+               size_t nesting)
+{
+       int ret;
+
+       ret = _lttng_integer_type_statedump(session, lttng_kernel_get_type_integer(field->type),
+                       lttng_kernel_string_encoding_none, nesting);
+       if (ret)
+               return ret;
+       return lttng_field_name_statedump(session, field, nesting);
+}
+
+static
+int _lttng_string_type_statedump(struct lttng_kernel_session *session,
+               const struct lttng_kernel_type_string *type,
+               size_t nesting)
+{
+       int ret;
+
+       /* Default encoding is UTF8 */
+       ret = print_tabs(session, nesting);
+       if (ret)
+               return ret;
+       ret = lttng_metadata_printf(session,
+               "string%s",
+               type->encoding == lttng_kernel_string_encoding_ASCII ?
+                       " { encoding = ASCII; }" : "");
+       return ret;
+}
+
+static
+int _lttng_string_field_statedump(struct lttng_kernel_session *session,
+               const struct lttng_kernel_event_field *field,
+               size_t nesting)
+{
+       const struct lttng_kernel_type_string *string_type;
+       int ret;
+
+       string_type = lttng_kernel_get_type_string(field->type);
+       WARN_ON_ONCE(!string_type);
+       ret = _lttng_string_type_statedump(session, string_type, nesting);
+       if (ret)
+               return ret;
+       return lttng_field_name_statedump(session, field, nesting);
+}
+
+/*
+ * Must be called with sessions_mutex held.
+ */
+static
+int _lttng_type_statedump(struct lttng_kernel_session *session,
+               const struct lttng_kernel_type_common *type,
+               enum lttng_kernel_string_encoding parent_encoding,
+               size_t nesting)
+{
+       int ret = 0;
+
+       switch (type->type) {
+       case lttng_kernel_type_integer:
+               ret = _lttng_integer_type_statedump(session,
+                               lttng_kernel_get_type_integer(type),
+                               parent_encoding, nesting);
+               break;
+       case lttng_kernel_type_enum:
+               ret = _lttng_enum_type_statedump(session,
+                               lttng_kernel_get_type_enum(type),
+                               nesting);
+               break;
+       case lttng_kernel_type_string:
+               ret = _lttng_string_type_statedump(session,
+                               lttng_kernel_get_type_string(type),
+                               nesting);
+               break;
+       case lttng_kernel_type_struct:
+               ret = _lttng_struct_type_statedump(session,
+                               lttng_kernel_get_type_struct(type),
+                               nesting);
+               break;
+       case lttng_kernel_type_variant:
+               ret = _lttng_variant_type_statedump(session,
+                               lttng_kernel_get_type_variant(type),
+                               nesting, NULL);
+               break;
+
+       /* Nested arrays and sequences are not supported yet. */
+       case lttng_kernel_type_array:
+       case lttng_kernel_type_sequence:
+       default:
+               WARN_ON_ONCE(1);
+               return -EINVAL;
+       }
+       return ret;
+}
+
+/*
+ * Must be called with sessions_mutex held.
+ */
+static
+int _lttng_field_statedump(struct lttng_kernel_session *session,
+               const struct lttng_kernel_event_field *field,
+               size_t nesting,
+               const char **prev_field_name_p)
+{
+       const char *prev_field_name = NULL;
+       int ret = 0;
+
+       if (prev_field_name_p)
+               prev_field_name = *prev_field_name_p;
+       switch (field->type->type) {
+       case lttng_kernel_type_integer:
+               ret = _lttng_integer_field_statedump(session, field, nesting);
+               break;
+       case lttng_kernel_type_enum:
+               ret = _lttng_enum_field_statedump(session, field, nesting);
+               break;
+       case lttng_kernel_type_string:
+               ret = _lttng_string_field_statedump(session, field, nesting);
+               break;
+       case lttng_kernel_type_struct:
+               ret = _lttng_struct_field_statedump(session, field, nesting);
+               break;
+       case lttng_kernel_type_array:
+               ret = _lttng_array_field_statedump(session, field, nesting);
+               break;
+       case lttng_kernel_type_sequence:
+               ret = _lttng_sequence_field_statedump(session, field, nesting, prev_field_name);
+               break;
+       case lttng_kernel_type_variant:
+               ret = _lttng_variant_field_statedump(session, field, nesting, prev_field_name);
+               break;
+
+       default:
+               WARN_ON_ONCE(1);
+               return -EINVAL;
+       }
+       if (prev_field_name_p)
+               *prev_field_name_p = field->name;
+       return ret;
+}
+
+static
+int _lttng_context_metadata_statedump(struct lttng_kernel_session *session,
+                                   struct lttng_kernel_ctx *ctx)
+{
+       const char *prev_field_name = NULL;
+       int ret = 0;
+       int i;
+
+       if (!ctx)
+               return 0;
+       for (i = 0; i < ctx->nr_fields; i++) {
+               const struct lttng_kernel_ctx_field *field = &ctx->fields[i];
+
+               ret = _lttng_field_statedump(session, field->event_field, 2, &prev_field_name);
+               if (ret)
+                       return ret;
+       }
+       return ret;
+}
+
+static
+int _lttng_fields_metadata_statedump(struct lttng_kernel_session *session,
+                                  struct lttng_kernel_event_recorder *event_recorder)
+{
+       const char *prev_field_name = NULL;
+       const struct lttng_kernel_event_desc *desc = event_recorder->priv->parent.desc;
+       int ret = 0;
+       int i;
+
+       for (i = 0; i < desc->tp_class->nr_fields; i++) {
+               const struct lttng_kernel_event_field *field = desc->tp_class->fields[i];
+
+               ret = _lttng_field_statedump(session, field, 2, &prev_field_name);
+               if (ret)
+                       return ret;
+       }
+       return ret;
+}
+
+/*
+ * Must be called with sessions_mutex held.
+ * The entire event metadata is printed as a single atomic metadata
+ * transaction.
+ */
+int _lttng_event_recorder_metadata_statedump(struct lttng_kernel_event_common *event)
+{
+       struct lttng_kernel_event_recorder *event_recorder;
+       struct lttng_kernel_channel_buffer *chan;
+       struct lttng_kernel_session *session;
+       int ret = 0;
+
+       if (event->type != LTTNG_KERNEL_EVENT_TYPE_RECORDER)
+               return 0;
+       event_recorder = container_of(event, struct lttng_kernel_event_recorder, parent);
+       chan = event_recorder->chan;
+       session = chan->parent.session;
+
+       if (event_recorder->priv->metadata_dumped || !LTTNG_READ_ONCE(session->active))
+               return 0;
+       if (chan->priv->channel_type == METADATA_CHANNEL)
+               return 0;
+
+       lttng_metadata_begin(session);
+
+       ret = lttng_metadata_printf(session,
+               "event {\n"
+               "       name = \"%s\";\n"
+               "       id = %u;\n"
+               "       stream_id = %u;\n",
+               event_recorder->priv->parent.desc->event_name,
+               event_recorder->priv->id,
+               event_recorder->chan->priv->id);
+       if (ret)
+               goto end;
+
+       ret = lttng_metadata_printf(session,
+               "       fields := struct {\n"
+               );
+       if (ret)
+               goto end;
+
+       ret = _lttng_fields_metadata_statedump(session, event_recorder);
+       if (ret)
+               goto end;
+
+       /*
+        * LTTng space reservation can only reserve multiples of the
+        * byte size.
+        */
+       ret = lttng_metadata_printf(session,
+               "       };\n"
+               "};\n\n");
+       if (ret)
+               goto end;
+
+       event_recorder->priv->metadata_dumped = 1;
+end:
+       lttng_metadata_end(session);
+       return ret;
+
+}
+
+/*
+ * Must be called with sessions_mutex held.
+ * The entire channel metadata is printed as a single atomic metadata
+ * transaction.
+ */
+static
+int lttng_channel_metadata_statedump(struct lttng_kernel_session *session,
+                                   struct lttng_kernel_channel_buffer *chan)
+{
+       int ret = 0;
+
+       if (chan->priv->metadata_dumped || !LTTNG_READ_ONCE(session->active))
+               return 0;
+
+       if (chan->priv->channel_type == METADATA_CHANNEL)
+               return 0;
+
+       lttng_metadata_begin(session);
+
+       WARN_ON_ONCE(!chan->priv->header_type);
+       ret = lttng_metadata_printf(session,
+               "stream {\n"
+               "       id = %u;\n"
+               "       event.header := %s;\n"
+               "       packet.context := struct packet_context;\n",
+               chan->priv->id,
+               chan->priv->header_type == 1 ? "struct event_header_compact" :
+                       "struct event_header_large");
+       if (ret)
+               goto end;
+
+       if (chan->priv->ctx) {
+               ret = lttng_metadata_printf(session,
+                       "       event.context := struct {\n");
+               if (ret)
+                       goto end;
+       }
+       ret = _lttng_context_metadata_statedump(session, chan->priv->ctx);
+       if (ret)
+               goto end;
+       if (chan->priv->ctx) {
+               ret = lttng_metadata_printf(session,
+                       "       };\n");
+               if (ret)
+                       goto end;
+       }
+
+       ret = lttng_metadata_printf(session,
+               "};\n\n");
+
+       chan->priv->metadata_dumped = 1;
+end:
+       lttng_metadata_end(session);
+       return ret;
+}
+
+/*
+ * Must be called with sessions_mutex held.
+ */
+static
+int _lttng_stream_packet_context_declare(struct lttng_kernel_session *session)
+{
+       return lttng_metadata_printf(session,
+               "struct packet_context {\n"
+               "       uint64_clock_monotonic_t timestamp_begin;\n"
+               "       uint64_clock_monotonic_t timestamp_end;\n"
+               "       uint64_t content_size;\n"
+               "       uint64_t packet_size;\n"
+               "       uint64_t packet_seq_num;\n"
+               "       unsigned long events_discarded;\n"
+               "       uint32_t cpu_id;\n"
+               "};\n\n"
+               );
+}
+
+/*
+ * Compact header:
+ * id: range: 0 - 30.
+ * id 31 is reserved to indicate an extended header.
+ *
+ * Large header:
+ * id: range: 0 - 65534.
+ * id 65535 is reserved to indicate an extended header.
+ *
+ * Must be called with sessions_mutex held.
+ */
+static
+int _lttng_event_header_declare(struct lttng_kernel_session *session)
+{
+       return lttng_metadata_printf(session,
+       "struct event_header_compact {\n"
+       "       enum : uint5_t { compact = 0 ... 30, extended = 31 } id;\n"
+       "       variant <id> {\n"
+       "               struct {\n"
+       "                       uint27_clock_monotonic_t timestamp;\n"
+       "               } compact;\n"
+       "               struct {\n"
+       "                       uint32_t id;\n"
+       "                       uint64_clock_monotonic_t timestamp;\n"
+       "               } extended;\n"
+       "       } v;\n"
+       "} align(%u);\n"
+       "\n"
+       "struct event_header_large {\n"
+       "       enum : uint16_t { compact = 0 ... 65534, extended = 65535 } id;\n"
+       "       variant <id> {\n"
+       "               struct {\n"
+       "                       uint32_clock_monotonic_t timestamp;\n"
+       "               } compact;\n"
+       "               struct {\n"
+       "                       uint32_t id;\n"
+       "                       uint64_clock_monotonic_t timestamp;\n"
+       "               } extended;\n"
+       "       } v;\n"
+       "} align(%u);\n\n",
+       lttng_alignof(uint32_t) * CHAR_BIT,
+       lttng_alignof(uint16_t) * CHAR_BIT
+       );
+}
+
+static
+int print_escaped_ctf_string(struct lttng_kernel_session *session, const char *string)
+{
+       int ret = 0;
+       size_t i;
+       char cur;
+
+       i = 0;
+       cur = string[i];
+       while (cur != '\0') {
+               switch (cur) {
+               case '\n':
+                       ret = lttng_metadata_printf(session, "%s", "\\n");
+                       break;
+               case '\\':
+               case '"':
+                       ret = lttng_metadata_printf(session, "%c", '\\');
+                       if (ret)
+                               goto error;
+                       /* We still print the current char */
+                       lttng_fallthrough;
+               default:
+                       ret = lttng_metadata_printf(session, "%c", cur);
+                       break;
+               }
+
+               if (ret)
+                       goto error;
+
+               cur = string[++i];
+       }
+error:
+       return ret;
+}
+
+static
+int print_metadata_escaped_field(struct lttng_kernel_session *session, const char *field,
+               const char *field_value)
+{
+       int ret;
+
+       ret = lttng_metadata_printf(session, "  %s = \"", field);
+       if (ret)
+               goto error;
+
+       ret = print_escaped_ctf_string(session, field_value);
+       if (ret)
+               goto error;
+
+       ret = lttng_metadata_printf(session, "\";\n");
+
+error:
+       return ret;
+}
+
+/*
+ * Output metadata into this session's metadata buffers.
+ * Must be called with sessions_mutex held.
+ */
+int _lttng_session_metadata_statedump(struct lttng_kernel_session *session)
+{
+       unsigned char *uuid_c = session->priv->uuid.b;
+       unsigned char uuid_s[37], clock_uuid_s[BOOT_ID_LEN];
+       const char *product_uuid;
+       struct lttng_kernel_channel_buffer_private *chan_priv;
+       struct lttng_kernel_event_recorder_private *event_recorder_priv;
+       int ret = 0;
+
+       if (!LTTNG_READ_ONCE(session->active))
+               return 0;
+
+       lttng_metadata_begin(session);
+
+       if (session->priv->metadata_dumped)
+               goto skip_session;
+
+       snprintf(uuid_s, sizeof(uuid_s),
+               "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+               uuid_c[0], uuid_c[1], uuid_c[2], uuid_c[3],
+               uuid_c[4], uuid_c[5], uuid_c[6], uuid_c[7],
+               uuid_c[8], uuid_c[9], uuid_c[10], uuid_c[11],
+               uuid_c[12], uuid_c[13], uuid_c[14], uuid_c[15]);
+
+       ret = lttng_metadata_printf(session,
+               "typealias integer { size = 8; align = %u; signed = false; } := uint8_t;\n"
+               "typealias integer { size = 16; align = %u; signed = false; } := uint16_t;\n"
+               "typealias integer { size = 32; align = %u; signed = false; } := uint32_t;\n"
+               "typealias integer { size = 64; align = %u; signed = false; } := uint64_t;\n"
+               "typealias integer { size = %u; align = %u; signed = false; } := unsigned long;\n"
+               "typealias integer { size = 5; align = 1; signed = false; } := uint5_t;\n"
+               "typealias integer { size = 27; align = 1; signed = false; } := uint27_t;\n"
+               "\n"
+               "trace {\n"
+               "       major = %u;\n"
+               "       minor = %u;\n"
+               "       uuid = \"%s\";\n"
+               "       byte_order = %s;\n"
+               "       packet.header := struct {\n"
+               "               uint32_t magic;\n"
+               "               uint8_t  uuid[16];\n"
+               "               uint32_t stream_id;\n"
+               "               uint64_t stream_instance_id;\n"
+               "       };\n"
+               "};\n\n",
+               lttng_alignof(uint8_t) * CHAR_BIT,
+               lttng_alignof(uint16_t) * CHAR_BIT,
+               lttng_alignof(uint32_t) * CHAR_BIT,
+               lttng_alignof(uint64_t) * CHAR_BIT,
+               sizeof(unsigned long) * CHAR_BIT,
+               lttng_alignof(unsigned long) * CHAR_BIT,
+               CTF_SPEC_MAJOR,
+               CTF_SPEC_MINOR,
+               uuid_s,
+#if __BYTE_ORDER == __BIG_ENDIAN
+               "be"
+#else
+               "le"
+#endif
+               );
+       if (ret)
+               goto end;
+
+       ret = lttng_metadata_printf(session,
+               "env {\n"
+               "       hostname = \"%s\";\n"
+               "       domain = \"kernel\";\n"
+               "       sysname = \"%s\";\n"
+               "       kernel_release = \"%s\";\n"
+               "       kernel_version = \"%s\";\n"
+               "       tracer_name = \"lttng-modules\";\n"
+               "       tracer_major = %d;\n"
+               "       tracer_minor = %d;\n"
+               "       tracer_patchlevel = %d;\n"
+               "       trace_buffering_scheme = \"global\";\n",
+               current->nsproxy->uts_ns->name.nodename,
+               utsname()->sysname,
+               utsname()->release,
+               utsname()->version,
+               LTTNG_MODULES_MAJOR_VERSION,
+               LTTNG_MODULES_MINOR_VERSION,
+               LTTNG_MODULES_PATCHLEVEL_VERSION
+               );
+       if (ret)
+               goto end;
+
+       ret = print_metadata_escaped_field(session, "trace_name", session->priv->name);
+       if (ret)
+               goto end;
+       ret = print_metadata_escaped_field(session, "trace_creation_datetime",
+                       session->priv->creation_time);
+       if (ret)
+               goto end;
+
+       /* Add the product UUID to the 'env' section */
+       product_uuid = dmi_get_system_info(DMI_PRODUCT_UUID);
+       if (product_uuid) {
+               ret = lttng_metadata_printf(session,
+                               "       product_uuid = \"%s\";\n",
+                               product_uuid
+                               );
+               if (ret)
+                       goto end;
+       }
+
+       /* Close the 'env' section */
+       ret = lttng_metadata_printf(session, "};\n\n");
+       if (ret)
+               goto end;
+
+       ret = lttng_metadata_printf(session,
+               "clock {\n"
+               "       name = \"%s\";\n",
+               trace_clock_name()
+               );
+       if (ret)
+               goto end;
+
+       if (!trace_clock_uuid(clock_uuid_s)) {
+               ret = lttng_metadata_printf(session,
+                       "       uuid = \"%s\";\n",
+                       clock_uuid_s
+                       );
+               if (ret)
+                       goto end;
+       }
+
+       ret = lttng_metadata_printf(session,
+               "       description = \"%s\";\n"
+               "       freq = %llu; /* Frequency, in Hz */\n"
+               "       /* clock value offset from Epoch is: offset * (1/freq) */\n"
+               "       offset = %lld;\n"
+               "};\n\n",
+               trace_clock_description(),
+               (unsigned long long) trace_clock_freq(),
+               (long long) trace_clock_offset()
+               );
+       if (ret)
+               goto end;
+
+       ret = lttng_metadata_printf(session,
+               "typealias integer {\n"
+               "       size = 27; align = 1; signed = false;\n"
+               "       map = clock.%s.value;\n"
+               "} := uint27_clock_monotonic_t;\n"
+               "\n"
+               "typealias integer {\n"
+               "       size = 32; align = %u; signed = false;\n"
+               "       map = clock.%s.value;\n"
+               "} := uint32_clock_monotonic_t;\n"
+               "\n"
+               "typealias integer {\n"
+               "       size = 64; align = %u; signed = false;\n"
+               "       map = clock.%s.value;\n"
+               "} := uint64_clock_monotonic_t;\n\n",
+               trace_clock_name(),
+               lttng_alignof(uint32_t) * CHAR_BIT,
+               trace_clock_name(),
+               lttng_alignof(uint64_t) * CHAR_BIT,
+               trace_clock_name()
+               );
+       if (ret)
+               goto end;
+
+       ret = _lttng_stream_packet_context_declare(session);
+       if (ret)
+               goto end;
+
+       ret = _lttng_event_header_declare(session);
+       if (ret)
+               goto end;
+
+skip_session:
+       list_for_each_entry(chan_priv, &session->priv->chan, node) {
+               ret = lttng_channel_metadata_statedump(session, chan_priv->pub);
+               if (ret)
+                       goto end;
+       }
+
+       list_for_each_entry(event_recorder_priv, &session->priv->events, parent.node) {
+               ret = _lttng_event_recorder_metadata_statedump(&event_recorder_priv->pub->parent);
+               if (ret)
+                       goto end;
+       }
+       session->priv->metadata_dumped = 1;
+end:
+       lttng_metadata_end(session);
+       return ret;
+}
diff --git a/src/ctf1-8.h b/src/ctf1-8.h
new file mode 100644 (file)
index 0000000..059ff08
--- /dev/null
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: (GPL-2.0-only or LGPL-2.1-only)
+ *
+ * src/ctf1-8.h
+ *
+ * Copyright (C) 2010-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Copyright (C) 2021 Francis Deslauriers <francis.deslauriers@efficios.com>
+ */
+
+#ifndef _LTTNG_CTF1_8_H_
+#define _LTTNG_CTF1_8_H_
+
+#include <lttng/events.h>
+#include <lttng/events-internal.h>
+
+int _lttng_session_metadata_statedump(struct lttng_kernel_session *session);
+int _lttng_event_recorder_metadata_statedump(struct lttng_kernel_event_common *event);
+
+#endif /* _LTTNG_CTF1_8_H_ */
diff --git a/src/ctf2.c b/src/ctf2.c
new file mode 100644 (file)
index 0000000..8614be4
--- /dev/null
@@ -0,0 +1,1667 @@
+/* SPDX-License-Identifier: (GPL-2.0-only or LGPL-2.1-only)
+ *
+ * src/ctf2.c
+ *
+ * Copyright (C) 2010-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Copyright (C) 2021 Francis Deslauriers <francis.deslauriers@efficios.com>
+ */
+
+#include <linux/dmi.h>
+#include <linux/ktime.h>
+#include <linux/list.h>
+#include <linux/math64.h>
+#include <linux/slab.h>
+#include <linux/timekeeping.h>
+#include <linux/utsname.h>
+
+#include <lttng/events.h>
+#include <lttng/events-internal.h>
+#include <lttng/uuid.h>
+
+#include <wrapper/time.h>
+
+#include "clock-utils.h"
+#include "field-path-resolving.h"
+#include "metadata-printer.h"
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define NATIVE_BYTEORDER "\"big-endian\""
+#define REVERSE_BYTEORDER "\"little-endian\""
+#else
+#define NATIVE_BYTEORDER "\"little-endian\""
+#define REVERSE_BYTEORDER "\"big-endian\""
+#endif
+
+struct ctf2_enum_mapping_range {
+       struct list_head node;
+       struct lttng_kernel_enum_value start;
+       struct lttng_kernel_enum_value end;
+};
+
+struct ctf2_enum_mapping {
+       struct list_head node;
+       const char *name;
+       struct list_head ranges;
+       unsigned int nr_ranges;
+};
+
+struct ctf2_enum {
+       struct list_head mappings;
+       unsigned int nr_mappings;
+};
+
+static int
+ctf2_metadata_write_field(struct lttng_kernel_session *session,
+                         const struct lttng_kernel_event_field *field,
+                         struct field_location_ctx *field_location_ctx);
+static int
+ctf2_metadata_write_field_class(struct lttng_kernel_session *session,
+                               const struct lttng_kernel_type_common *type,
+                               struct field_location_ctx *field_location_ctx);
+
+static int
+ctf2_metadata_write_record_separator(struct lttng_kernel_session *session)
+{
+       return lttng_metadata_printf(session, "%c\n", 0x1E);
+}
+
+static int
+ctf2_metadata_write_preamble_fragment(struct lttng_kernel_session *session)
+{
+       int ret;
+       ret = ctf2_metadata_write_record_separator(session);
+       /* Write the preamble */
+       ret = lttng_metadata_printf(session,
+                                   "{\"type\":\"preamble\",\"version\":2}\n");
+       return ret;
+}
+
+static int ctf2_metadata_write_uuid(struct lttng_kernel_session *session,
+                                   unsigned char *uuid)
+{
+       int ret, i, uuid_len = 16;
+
+       ret = lttng_metadata_printf(session, "[");
+       if (ret)
+               goto end;
+
+       for (i = 0; i < uuid_len; i++) {
+               ret = lttng_metadata_printf(session, "%d", uuid[i]);
+               if (ret)
+                       goto end;
+
+               if (i < uuid_len - 1) {
+                       ret = lttng_metadata_printf(session, ",");
+                       if (ret)
+                               goto end;
+               }
+       }
+
+       ret = lttng_metadata_printf(session, "]");
+
+end:
+       return ret;
+}
+
+static int
+ctf2_metadata_write_clock_class_fragment(struct lttng_kernel_session *session)
+{
+       const char *clock_name = trace_clock_name(),
+                  *clock_desc = trace_clock_description();
+       unsigned char clock_uuid_str[37] = { 0 };
+       lttng_uuid clock_uuid;
+       unsigned long long freq = trace_clock_freq();
+       long long offset = trace_clock_offset(), offset_seconds;
+       int offset_cycles, ret;
+
+       ret = ctf2_metadata_write_record_separator(session);
+       if (ret)
+               goto end;
+
+       offset_seconds = div_s64_rem(offset, freq, &offset_cycles);
+
+       ret = lttng_metadata_printf(
+               session,
+               "{"
+               "\"type\":\"clock-class\","
+               "\"name\":\"%s\","
+               "\"description\" : \"%s\","
+               "\"frequency\" : %llu,"
+               "\"offset\" : { \"seconds\": %llu, \"cycles\":%lu}",
+               clock_name, clock_desc, freq, offset_seconds, offset_cycles);
+       if (ret)
+               goto end;
+
+       if (!trace_clock_uuid(clock_uuid_str)) {
+               ret = lttng_metadata_printf(session, ",\n\"uuid\": ");
+               if (ret)
+                       goto end;
+
+               ret = lttng_uuid_from_str(clock_uuid_str, clock_uuid);
+               if (ret) {
+                       printk(KERN_WARNING
+                              "LTTng: CTF2: error creating UUID from string\n");
+                       goto end;
+               }
+
+               ret = ctf2_metadata_write_uuid(session, clock_uuid);
+               if (ret)
+                       goto end;
+       }
+
+       ret = lttng_metadata_printf(session, "}\n");
+
+end:
+       return ret;
+}
+
+static int ctf2_metadata_write_packet_header_field_class(
+       struct lttng_kernel_session *session)
+{
+       int ret;
+
+       ret = lttng_metadata_printf(
+               session,
+               "{"
+               "  \"type\": \"structure\","
+               "  \"member-classes\": ["
+               "     {"
+               "      \"name\": \"magic\","
+               "      \"field-class\": {"
+               "         \"type\": \"fixed-length-unsigned-integer\","
+               "         \"alignment\": %u,"
+               "         \"length\": 32,"
+               "        \"byte-order\": " NATIVE_BYTEORDER ","
+               "         \"preferred-display-base\": 16,"
+               "         \"roles\": [\"packet-magic-number\"]"
+               "       }"
+               "     },"
+               "     {"
+               "      \"name\": \"uuid\","
+               "      \"field-class\": {"
+               "        \"type\": \"static-length-blob\","
+               "        \"length\": 16,"
+               "        \"roles\": [\"trace-class-uuid\"]"
+               "        }"
+               "     },"
+               "     {"
+               "      \"name\": \"stream_id\","
+               "      \"field-class\": {"
+               "        \"type\": \"fixed-length-unsigned-integer\","
+               "         \"alignment\": %u,"
+               "        \"length\": 32,"
+               "        \"byte-order\": " NATIVE_BYTEORDER ","
+               "        \"roles\": [\"data-stream-class-id\"]"
+               "        }"
+               "     },"
+               "     {"
+               "      \"name\": \"stream_instance_id\","
+               "      \"field-class\": {"
+               "        \"type\": \"fixed-length-unsigned-integer\","
+               "         \"alignment\": %u,"
+               "        \"length\": 64,"
+               "        \"byte-order\": " NATIVE_BYTEORDER ","
+               "        \"roles\": [\"data-stream-id\"]"
+               "       }"
+               "     }"
+               "  ]"
+               "}",
+               lttng_alignof(uint32_t) * CHAR_BIT,
+               lttng_alignof(uint32_t) * CHAR_BIT,
+               lttng_alignof(uint64_t) * CHAR_BIT);
+
+       return ret;
+}
+
+static int
+ctf2_metadata_write_trace_class_fragment(struct lttng_kernel_session *session)
+{
+       int ret;
+       unsigned char *session_uuid = session->priv->uuid.b;
+
+       ret = ctf2_metadata_write_record_separator(session);
+       if (ret)
+               goto end;
+
+       ret = lttng_metadata_printf(session, "{\"type\":\"trace-class\","
+                                            "\"uuid\":");
+       if (ret)
+               goto end;
+
+       if (ret)
+               goto end;
+
+       ret = ctf2_metadata_write_uuid(session, session_uuid);
+       if (ret) {
+               printk(KERN_WARNING
+                      "LTTng: CTF2: error producing metadata trace class UUID\n");
+               goto end;
+       }
+
+       ret = lttng_metadata_printf(session, ",\"packet-header-field-class\":");
+       if (ret)
+               goto end;
+
+       ret = ctf2_metadata_write_packet_header_field_class(session);
+       if (ret) {
+               printk(KERN_WARNING
+                      "LTTng: CTF2: error producing metadata trace class packet header field class\n");
+               goto end;
+       }
+
+       ret = lttng_metadata_printf(session, "}\n");
+end:
+       return ret;
+}
+
+static int ctf2_metadata_write_packet_context_field_class(
+       struct lttng_kernel_session *session,
+       struct lttng_kernel_channel_buffer *chan)
+{
+       int ret;
+       ret = lttng_metadata_printf(
+               session,
+               " {"
+               "  \"type\": \"structure\","
+               "  \"member-classes\": ["
+               "    {"
+               "      \"name\": \"timestamp_begin\","
+               "      \"field-class\": {"
+               "        \"type\": \"fixed-length-unsigned-integer\","
+               "        \"alignment\": %u,"
+               "        \"length\": 64,"
+               "        \"byte-order\": " NATIVE_BYTEORDER ","
+               "        \"roles\": [\"packet-beginning-default-clock-timestamp\"]"
+               "      }"
+               "    },"
+               "    {"
+               "      \"name\": \"timestamp_end\","
+               "      \"field-class\": {"
+               "        \"type\": \"fixed-length-unsigned-integer\","
+               "        \"alignment\": %u,"
+               "        \"length\": 64,"
+               "        \"byte-order\": " NATIVE_BYTEORDER ","
+               "        \"roles\": [\"packet-end-default-clock-timestamp\"]"
+               "      }"
+               "    },"
+               "    {"
+               "      \"name\": \"content_size\","
+               "      \"field-class\": {"
+               "        \"type\": \"fixed-length-unsigned-integer\","
+               "        \"alignment\": %u,"
+               "        \"length\": 64,"
+               "        \"byte-order\": " NATIVE_BYTEORDER ","
+               "        \"roles\": [\"packet-content-size\"]"
+               "      }"
+               "    },"
+               "    {"
+               "      \"name\": \"packet_size\","
+               "      \"field-class\": {"
+               "        \"type\": \"fixed-length-unsigned-integer\","
+               "        \"alignment\": %u,"
+               "        \"length\": 64,"
+               "        \"byte-order\": " NATIVE_BYTEORDER ","
+               "        \"roles\": [\"packet-total-size\"]"
+               "      }"
+               "    },"
+               "    {"
+               "      \"name\": \"packet_seq_num\","
+               "      \"field-class\": {"
+               "        \"type\": \"fixed-length-unsigned-integer\","
+               "        \"alignment\": %u,"
+               "        \"length\": 64,"
+               "        \"byte-order\": " NATIVE_BYTEORDER ","
+               "        \"roles\": [\"packet-sequence-number\" ]"
+               "      }"
+               "    },"
+               "    {"
+               "      \"name\": \"events_discarded\","
+               "      \"field-class\": {"
+               "        \"type\": \"fixed-length-unsigned-integer\","
+               "        \"alignment\": %u,"
+               "        \"byte-order\": " NATIVE_BYTEORDER ","
+               "        \"length\": %u,"
+               "        \"roles\": [\"discarded-event-record-counter-snapshot\"]"
+               "      }"
+               "    },"
+               "    {"
+               "      \"name\": \"cpu_id\","
+               "      \"field-class\": {"
+               "        \"type\": \"fixed-length-unsigned-integer\","
+               "        \"alignment\": 8,"
+               "        \"byte-order\": " NATIVE_BYTEORDER ","
+               "        \"length\": 32"
+               "      }"
+               "    }"
+               "  ]"
+               "}",
+               lttng_alignof(uint64_t) * CHAR_BIT,
+               lttng_alignof(uint64_t) * CHAR_BIT,
+               lttng_alignof(uint64_t) * CHAR_BIT,
+               lttng_alignof(uint64_t) * CHAR_BIT,
+               lttng_alignof(uint64_t) * CHAR_BIT,
+               lttng_alignof(unsigned long) * CHAR_BIT,
+               sizeof(unsigned long) * CHAR_BIT);
+       return 0;
+}
+
+static int ctf2_metadata_write_common_context_field_class(
+       struct lttng_kernel_session *session, struct lttng_kernel_ctx *ctx)
+{
+       struct field_location_ctx *field_location_ctx;
+       int ret = 0, i;
+
+       if (!ctx)
+               return 0;
+
+       ret = lttng_metadata_printf(session, " {"
+                                            "  \"type\": \"structure\","
+                                            "  \"member-classes\": [");
+       if (ret)
+               goto end;
+
+       field_location_ctx = field_location_ctx_create();
+       if (!field_location_ctx) {
+               ret = -1;
+               printk(KERN_WARNING
+                      "LTTng: CTF2: error allocating field location context\n");
+               goto end;
+       }
+
+       ret = field_location_stack_push(field_location_ctx,
+                                       SCOPE_STACK_NODE_TYPE_SCOPE,
+                                       "event-record-common-context", NULL);
+       if (ret) {
+               printk(KERN_WARNING
+                      "LTTng: CTF2: error pushing new scope on scope stack\n");
+               goto destroy_field_loc;
+       }
+
+       for (i = 0; i < ctx->nr_fields; i++) {
+               const struct lttng_kernel_ctx_field *iter_field =
+                       &ctx->fields[i];
+
+               ret = ctf2_metadata_write_field(
+                       session, iter_field->event_field, field_location_ctx);
+               if (ret)
+                       goto destroy_field_loc;
+
+               ret = field_location_stack_push(field_location_ctx,
+                                               SCOPE_STACK_NODE_TYPE_FIELD,
+                                               iter_field->event_field->name,
+                                               iter_field->event_field);
+               if (ret) {
+                       printk(KERN_WARNING
+                              "LTTng: CTF2: error pushing new field on scope stack\n");
+                       goto destroy_field_loc;
+               }
+
+               if (i < ctx->nr_fields - 1) {
+                       ret = lttng_metadata_printf(session, ", ");
+                       if (ret)
+                               goto destroy_field_loc;
+               }
+       }
+
+       field_location_stack_pop_n_elem(field_location_ctx, ctx->nr_fields + 1);
+
+       ret = lttng_metadata_printf(session, "  ]}");
+
+       if (ret)
+               goto destroy_field_loc;
+
+       return ret;
+
+destroy_field_loc:
+       field_location_ctx_destroy(field_location_ctx);
+end:
+       return ret;
+}
+
+static int ctf2_metadata_write_compact_event_header_field_class(
+       struct lttng_kernel_session *session)
+{
+       int ret;
+       ret = lttng_metadata_printf(
+               session,
+               "{"
+               "  \"type\": \"structure\","
+               "  \"minimum-alignment\": 8,"
+               "  \"member-classes\": ["
+               "  {"
+               "    \"name\": \"id\","
+               "    \"field-class\": {"
+               "      \"type\": \"fixed-length-unsigned-enumeration\","
+               "      \"length\": 5,"
+               "        \"byte-order\": " NATIVE_BYTEORDER ","
+               "      \"mappings\": {"
+               "        \"compact\": [[0,30]],"
+               "        \"extended\": [[31,31]]"
+               "      },"
+               "      \"roles\": [\"event-record-class-id\"]"
+               "    }"
+               "  },"
+               "  {"
+               "    \"name\": \"v\","
+               "    \"field-class\": {"
+               "      \"type\": \"variant\","
+               "      \"selector-field-location\": ["
+               "        \"event-record-header\","
+               "        \"id\""
+               "      ],"
+               "      \"options\": ["
+               "        {"
+               "          \"selector-field-ranges\": [[0, 30]],"
+               "          \"field-class\": {"
+               "            \"type\": \"structure\","
+               "            \"member-classes\": ["
+               "              {"
+               "                \"name\": \"timestamp\","
+               "                \"field-class\":{"
+               "                  \"type\": \"fixed-length-unsigned-integer\","
+               "                  \"byte-order\": " NATIVE_BYTEORDER ","
+               "                  \"length\": 27,"
+               "                  \"roles\": [\"default-clock-timestamp\"]"
+               "                }"
+               "              }"
+               "            ]"
+               "          }"
+               "        },"
+               "        {"
+               "          \"selector-field-ranges\": [[31, 31]],"
+               "          \"field-class\": {"
+               "            \"type\": \"structure\","
+               "            \"member-classes\": ["
+               "              {"
+               "                \"name\": \"id\","
+               "                \"field-class\": {"
+               "                  \"type\": \"fixed-length-unsigned-integer\","
+               "                  \"length\": 32,"
+               "                  \"alignment\": %u,"
+               "               \"byte-order\": " NATIVE_BYTEORDER ","
+               "                  \"roles\": [\"event-record-class-id\" ]"
+               "                }"
+               "              },"
+               "              {"
+               "                \"name\": \"timestamp\","
+               "                \"field-class\": {"
+               "                  \"type\": \"fixed-length-unsigned-integer\","
+               "                  \"alignment\": %u,"
+               "                  \"length\": 64,"
+               "                  \"byte-order\": " NATIVE_BYTEORDER ","
+               "                  \"roles\": [\"default-clock-timestamp\"]"
+               "                }"
+               "              }"
+               "            ]"
+               "          }"
+               "        }"
+               "      ]"
+               "    }"
+               "  }"
+               "  ]"
+               "}",
+               lttng_alignof(uint32_t) * CHAR_BIT,
+               lttng_alignof(uint64_t) * CHAR_BIT);
+       return ret;
+}
+
+static int ctf2_metadata_write_large_event_header_field_class(
+       struct lttng_kernel_session *session)
+{
+       int ret;
+       ret = lttng_metadata_printf(
+               session,
+               "  {"
+               "  \"type\": \"structure\","
+               "  \"minimum-alignment\": 8,"
+               "  \"member-classes\": ["
+               "    {"
+               "      \"name\": \"id\","
+               "      \"field-class\": {"
+               "        \"type\": \"fixed-length-unsigned-enumeration\","
+               "        \"length\": 16,"
+               "        \"alignment\": %u,"
+               "        \"byte-order\": " NATIVE_BYTEORDER ","
+               "        \"mappings\": {"
+               "          \"compact\": [[0,65534]],"
+               "          \"extended\": [[65535,65535]]"
+               "        },"
+               "        \"roles\": [\"event-record-class-id\"]"
+               "      }"
+               "    },"
+               "    {"
+               "      \"name\": \"v\","
+               "      \"field-class\": {"
+               "        \"type\": \"variant\","
+               "        \"selector-field-location\": ["
+               "          \"event-record-header\","
+               "          \"id\""
+               "        ],"
+               "        \"options\": ["
+               "          {"
+               "            \"selector-field-ranges\": [[0,65534]],"
+               "            \"field-class\": {"
+               "              \"type\": \"structure\","
+               "              \"member-classes\": ["
+               "                {"
+               "                  \"name\": \"timestamp\","
+               "                  \"field-class\": {"
+               "                    \"type\": \"fixed-length-unsigned-integer\","
+               "                    \"alignment\": %u,"
+               "                    \"byte-order\": " NATIVE_BYTEORDER ","
+               "                    \"length\": 32,"
+               "                    \"roles\": [\"default-clock-timestamp\"]"
+               "                  }"
+               "                }"
+               "              ]"
+               "            }"
+               "          },"
+               "          {"
+               "            \"selector-field-ranges\": [ [65525,65535]  ],"
+               "            \"field-class\": {"
+               "              \"type\": \"structure\","
+               "              \"member-classes\": ["
+               "                {"
+               "                  \"name\": \"id\","
+               "                  \"field-class\": {"
+               "                    \"type\": \"fixed-length-unsigned-integer\","
+               "                    \"length\": 32,"
+               "                    \"alignment\": %u,"
+               "                    \"byte-order\": " NATIVE_BYTEORDER ","
+               "                    \"roles\": [\"event-record-class-id\" ]"
+               "                  }"
+               "                },"
+               "                {"
+               "                  \"name\": \"timestamp\","
+               "                  \"field-class\": {"
+               "                    \"type\": \"fixed-length-unsigned-integer\","
+               "                    \"length\": 64,"
+               "                    \"alignment\": %u,"
+               "                    \"byte-order\": " NATIVE_BYTEORDER ","
+               "                    \"roles\": [\"default-clock-timestamp\"]"
+               "                  }"
+               "                }"
+               "              ]"
+               "            }"
+               "          }"
+               "        ]"
+               "      }"
+               "    }"
+               "  ]"
+               "}",
+               lttng_alignof(uint16_t) * CHAR_BIT,
+               lttng_alignof(uint32_t) * CHAR_BIT,
+               lttng_alignof(uint32_t) * CHAR_BIT,
+               lttng_alignof(uint64_t) * CHAR_BIT);
+       return ret;
+}
+
+static int ctf2_metadata_write_integer_field_class(
+       struct lttng_kernel_session *session,
+       const struct lttng_kernel_type_integer *type)
+{
+       char *enum_type_name, *byte_order;
+       int ret;
+
+       if (type->signedness) {
+               enum_type_name = "fixed-length-signed-integer";
+       } else {
+               enum_type_name = "fixed-length-unsigned-integer";
+       }
+
+       byte_order =
+               type->reverse_byte_order ? REVERSE_BYTEORDER : NATIVE_BYTEORDER;
+
+       ret = lttng_metadata_printf(session,
+                                   "{"
+                                   "   \"type\": \"%s\","
+                                   "   \"length\": %u,"
+                                   "   \"byte-order\": %s",
+                                   enum_type_name, type->size, byte_order);
+       if (ret)
+               goto end;
+
+       if (type->alignment != 1) {
+               ret = lttng_metadata_printf(session, ", \"alignment\": %u",
+                                           type->alignment);
+               if (ret)
+                       goto end;
+       }
+
+       if (type->base != 10) {
+               ret = lttng_metadata_printf(session,
+                                           ", \"preferred-display-base\": %u",
+                                           type->base);
+               if (ret)
+                       goto end;
+       }
+
+       ret = lttng_metadata_printf(session, "}");
+       if (ret)
+               goto end;
+end:
+       return ret;
+}
+
+static void
+ctf2_enum_mapping_destroy(struct ctf2_enum_mapping *ctf2_enum_mapping)
+{
+       struct ctf2_enum_mapping_range *range, *tmp;
+
+       if (!ctf2_enum_mapping) {
+               return;
+       }
+
+       list_for_each_entry_safe (range, tmp, &ctf2_enum_mapping->ranges,
+                                 node) {
+               kfree(range);
+       }
+
+       kfree(ctf2_enum_mapping->name);
+       kfree(ctf2_enum_mapping);
+}
+
+static void ctf2_enum_destroy(struct ctf2_enum *ctf2_enum)
+{
+       struct ctf2_enum_mapping *mapping, *tmp;
+
+       if (!ctf2_enum) {
+               return;
+       }
+
+       list_for_each_entry_safe (mapping, tmp, &ctf2_enum->mappings, node) {
+               ctf2_enum_mapping_destroy(mapping);
+       }
+
+       kfree(ctf2_enum);
+}
+
+static struct ctf2_enum *
+ctf2_enum_create_from_type_enum(const struct lttng_kernel_type_enum *cur_enum)
+{
+       struct ctf2_enum *ctf2_enum;
+       const struct lttng_kernel_enum_desc *enum_desc;
+       unsigned int i, nr_entries;
+       struct ctf2_enum_mapping *mapping;
+       struct lttng_kernel_enum_value next_value = { .value = 0,
+                                                     .signedness = false };
+
+       nr_entries = cur_enum->desc->nr_entries;
+       enum_desc = cur_enum->desc;
+
+       ctf2_enum = kzalloc(sizeof(*ctf2_enum), GFP_KERNEL);
+       if (!ctf2_enum)
+               goto end;
+
+       INIT_LIST_HEAD(&ctf2_enum->mappings);
+
+       for (i = 0; i < nr_entries; i++) {
+               const struct lttng_kernel_enum_entry *entry =
+                       enum_desc->entries[i];
+               struct ctf2_enum_mapping_range *mapping_range;
+               bool found = false;
+
+               list_for_each_entry (mapping, &ctf2_enum->mappings, node) {
+                       if (strcmp(mapping->name, entry->string) == 0) {
+                               found = true;
+                               break;
+                       }
+               }
+
+               mapping_range = kzalloc(sizeof(*mapping_range), GFP_KERNEL);
+               if (!mapping_range)
+                       goto destroy_ctf2_enum;
+
+               if (entry->options.is_auto) {
+                       mapping_range->start.value = next_value.value;
+                       mapping_range->start.signedness = next_value.signedness;
+                       mapping_range->end.value = next_value.value;
+                       mapping_range->end.signedness = next_value.signedness;
+
+                       next_value.value++;
+               } else {
+                       mapping_range->start.value = entry->start.value;
+                       mapping_range->start.signedness =
+                               entry->start.signedness;
+                       mapping_range->end.value = entry->end.value;
+                       mapping_range->end.signedness = entry->end.signedness;
+
+                       next_value.value = entry->end.value + 1;
+                       next_value.signedness = entry->end.signedness;
+               }
+
+               if (found) {
+                       /*
+                        * We already have a mapping with that name, add the
+                        * new range to the range list.
+                        */
+                       list_add_tail(&mapping_range->node, &mapping->ranges);
+               } else {
+                       /*
+                        * Allocate the new mapping, add its range to the range
+                        * list, and add the mapping to the mapping list
+                        */
+                       mapping = kzalloc(sizeof(*mapping), GFP_KERNEL);
+                       if (!mapping) {
+                               kfree(mapping_range);
+                               goto destroy_ctf2_enum;
+                       }
+
+                       mapping->name = kstrdup(entry->string, GFP_KERNEL);
+
+                       INIT_LIST_HEAD(&mapping->ranges);
+
+                       list_add_tail(&mapping->node, &ctf2_enum->mappings);
+                       ctf2_enum->nr_mappings++;
+
+                       list_add_tail(&mapping_range->node, &mapping->ranges);
+               }
+
+               mapping->nr_ranges++;
+       }
+
+       goto end;
+
+destroy_ctf2_enum:
+       ctf2_enum_destroy(ctf2_enum);
+       ctf2_enum = NULL;
+end:
+       return ctf2_enum;
+}
+
+static int ctf2_metadata_write_enum_field_class(
+       struct lttng_kernel_session *session,
+       const struct lttng_kernel_type_enum *enum_type)
+{
+       const struct lttng_kernel_enum_desc *enum_desc;
+       const struct lttng_kernel_type_common *container_type;
+       const struct lttng_kernel_type_integer *base_type;
+       struct ctf2_enum_mapping_range *mapping_range;
+       struct ctf2_enum *ctf2_enum;
+       struct ctf2_enum_mapping *mapping;
+       unsigned int mapping_idx, range_idx, nr_entries;
+       const char *byte_order;
+       char *enum_type_name;
+       int ret;
+
+       enum_desc = enum_type->desc;
+       container_type = enum_type->container_type;
+       nr_entries = enum_desc->nr_entries;
+
+       if (container_type->type != lttng_kernel_type_integer) {
+               ret = -EINVAL;
+               goto end;
+       }
+
+       base_type = lttng_kernel_get_type_integer(container_type);
+
+       if (base_type->signedness) {
+               enum_type_name = "fixed-length-signed-enumeration";
+       } else {
+               enum_type_name = "fixed-length-unsigned-enumeration";
+       }
+
+       byte_order = base_type->reverse_byte_order ? REVERSE_BYTEORDER :
+                                                    NATIVE_BYTEORDER;
+
+       ret = lttng_metadata_printf(session,
+                                   "{"
+                                   "   \"type\": \"%s\","
+                                   "   \"length\": %u,"
+                                   "   \"byte-order\": %s",
+                                   enum_type_name, base_type->size,
+                                   byte_order);
+       if (ret)
+               goto end;
+
+       if (base_type->alignment != 1) {
+               ret = lttng_metadata_printf(session, ", \"alignment\": %u",
+                                           base_type->alignment);
+               if (ret)
+                       goto end;
+       }
+
+       if (base_type->base != 10) {
+               ret = lttng_metadata_printf(session,
+                                           ", \"preferred-display-base\": %u",
+                                           base_type->base);
+               if (ret)
+                       goto end;
+       }
+
+       ret = lttng_metadata_printf(session, ",  \"mappings\": {");
+       if (ret)
+               goto end;
+
+       ctf2_enum = ctf2_enum_create_from_type_enum(enum_type);
+       mapping_idx = 0;
+       list_for_each_entry (mapping, &ctf2_enum->mappings, node) {
+               ret = lttng_metadata_printf(session, "  \"%s\": [",
+                                           mapping->name);
+               if (ret)
+                       goto end;
+
+               range_idx = 0;
+               list_for_each_entry (mapping_range, &mapping->ranges, node) {
+                       if (mapping_range->start.signedness)
+                               ret = lttng_metadata_printf(
+                                       session, "[%lld, ",
+                                       (long long)mapping_range->start.value);
+                       else
+                               ret = lttng_metadata_printf(
+                                       session, "[%llu, ",
+                                       mapping_range->start.value);
+                       if (ret)
+                               goto end;
+
+                       if (mapping_range->end.signedness)
+                               ret = lttng_metadata_printf(
+                                       session, "%lld]",
+                                       (long long)mapping_range->end.value);
+                       else
+                               ret = lttng_metadata_printf(
+                                       session, "%llu]",
+                                       mapping_range->end.value);
+                       if (ret)
+                               goto end;
+
+                       if (range_idx < mapping->nr_ranges - 1) {
+                               ret = lttng_metadata_printf(session, ",\n");
+                               if (ret)
+                                       goto end;
+                       }
+
+                       range_idx++;
+               }
+
+               ret = lttng_metadata_printf(session, " ]");
+               if (ret)
+                       goto end;
+
+               if (mapping_idx < ctf2_enum->nr_mappings - 1) {
+                       ret = lttng_metadata_printf(session, ",\n");
+                       if (ret)
+                               goto end;
+               }
+
+               mapping_idx++;
+       }
+       ret = lttng_metadata_printf(session, "}}");
+
+end:
+       ctf2_enum_destroy(ctf2_enum);
+       return ret;
+}
+
+static int ctf2_metadata_write_string_field_class(
+       struct lttng_kernel_session *session,
+       const struct lttng_kernel_type_string *type)
+{
+       int ret = 0;
+
+       ret = lttng_metadata_printf(session,
+                                   "{"
+                                   "  \"type\": \"null-terminated-string\""
+                                   "}");
+       return ret;
+}
+
+static int ctf2_metadata_write_struct_field_class(
+       struct lttng_kernel_session *session,
+       const struct lttng_kernel_type_struct *type,
+       struct field_location_ctx *field_location_ctx)
+{
+       unsigned int i, nr_fields;
+       int ret = 0;
+
+       ret = lttng_metadata_printf(session, "{"
+                                            "  \"type\": \"structure\"");
+       if (ret)
+               goto end;
+
+       if (type->alignment != 0 && type->alignment != 1) {
+               ret = lttng_metadata_printf(session,
+                                           ", \"minimum-alignment\": %u",
+                                           type->alignment);
+               if (ret)
+                       goto end;
+       }
+
+       nr_fields = type->nr_fields;
+
+       if (nr_fields) {
+               ret = lttng_metadata_printf(session, ", \"member-classes\":[ ");
+
+               for (i = 0; i < nr_fields; i++) {
+                       const struct lttng_kernel_event_field *iter_field;
+
+                       iter_field = type->fields[i];
+
+                       ret = ctf2_metadata_write_field(session, iter_field,
+                                                       field_location_ctx);
+                       if (ret)
+                               goto end;
+
+                       if (field_location_ctx) {
+                               ret = field_location_stack_push(
+                                       field_location_ctx,
+                                       SCOPE_STACK_NODE_TYPE_FIELD,
+                                       iter_field->name, iter_field);
+                               if (ret)
+                                       goto end;
+                       }
+
+                       if (i < nr_fields - 1) {
+                               ret = lttng_metadata_printf(session, ", ");
+                               if (ret)
+                                       goto end;
+                       }
+               }
+
+               if (field_location_ctx) {
+                       field_location_stack_pop_n_elem(field_location_ctx,
+                                                       nr_fields);
+               }
+
+               ret = lttng_metadata_printf(session, "]");
+               if (ret)
+                       goto end;
+       }
+       ret = lttng_metadata_printf(session, "}");
+end:
+       return ret;
+}
+
+static int ctf2_metadata_write_static_length_array_field_class(
+       struct lttng_kernel_session *session,
+       const struct lttng_kernel_type_array *type)
+{
+       const struct lttng_kernel_type_common *elem_type;
+       int ret;
+
+       /*
+        * Nested compound types: Only array of integers, structures, and variants are
+        * currently supported.
+        */
+       ret = lttng_metadata_printf(session,
+                                   "{"
+                                   "  \"type\": \"static-length-array\","
+                                   "  \"length\": %u",
+                                   type->length);
+       if (ret)
+               goto end;
+
+       if (type->alignment != 0 && type->alignment != 1) {
+               ret = lttng_metadata_printf(session,
+                                           ", \"minimum-alignment\": %u",
+                                           type->alignment * CHAR_BIT);
+               if (ret)
+                       goto end;
+       }
+
+       ret = lttng_metadata_printf(session, ", \"element-field-class\":");
+       if (ret)
+               goto end;
+
+       elem_type = type->elem_type;
+       switch (elem_type->type) {
+       case lttng_kernel_type_integer:
+       case lttng_kernel_type_struct:
+       case lttng_kernel_type_variant:
+               ret = ctf2_metadata_write_field_class(session, elem_type, NULL);
+               if (ret)
+                       goto end;
+               break;
+
+       default:
+               ret = -EINVAL;
+               goto end;
+       }
+
+       ret = lttng_metadata_printf(session, "}");
+
+end:
+       return ret;
+}
+
+static int print_field_location(struct lttng_kernel_session *session,
+                               struct field_path *field_path)
+{
+       struct field_path_node *field_path_node;
+       unsigned int entry_idx = 0;
+       int ret;
+
+       ret = lttng_metadata_printf(session, "[");
+       if (ret)
+               goto end;
+
+       list_for_each_entry (field_path_node, &(field_path->path), node) {
+               ret = lttng_metadata_printf(session, "\"%s\"",
+                                           field_path_node->name);
+               if (ret)
+                       goto end;
+
+               if (entry_idx < field_path->path_entry_count - 1) {
+                       ret = lttng_metadata_printf(session, ", ");
+                       if (ret)
+                               goto end;
+               }
+
+               entry_idx++;
+       }
+       ret = lttng_metadata_printf(session, "]");
+
+end:
+       return ret;
+}
+
+static int ctf2_metadata_write_dynamic_length_array_field_class(
+       struct lttng_kernel_session *session,
+       const struct lttng_kernel_type_sequence *type,
+       struct field_location_ctx *field_location_ctx)
+{
+       const struct lttng_kernel_type_common *elem_type;
+       struct field_path *field_path;
+       int ret;
+
+       field_path = field_path_resolve(field_location_ctx, type->length_name);
+       if (!field_path) {
+               ret = -1;
+               goto end;
+       }
+
+       /*
+        * Nested compound types: Only array of integers, structures, and
+        * variants are currently supported.
+        */
+       ret = lttng_metadata_printf(session,
+                                   "{"
+                                   "  \"type\": \"dynamic-length-array\","
+                                   "  \"length-field-location\": ");
+       if (ret)
+               goto end;
+
+       ret = print_field_location(session, field_path);
+       if (ret)
+               goto end;
+
+       if (type->alignment != 0 && type->alignment != 1) {
+               ret = lttng_metadata_printf(session,
+                                           ", \"minimum-alignment\": %u",
+                                           type->alignment * CHAR_BIT);
+               if (ret)
+                       goto end;
+       }
+
+       ret = lttng_metadata_printf(session, ",  \"element-field-class\":");
+       if (ret)
+               goto end;
+
+       elem_type = type->elem_type;
+       switch (elem_type->type) {
+       case lttng_kernel_type_integer:
+       case lttng_kernel_type_struct:
+       case lttng_kernel_type_variant:
+               ret = ctf2_metadata_write_field_class(session, elem_type, NULL);
+               if (ret)
+                       goto end;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       ret = lttng_metadata_printf(session, "}");
+end:
+       field_path_destroy(field_path);
+       return ret;
+}
+
+static int ctf2_metadata_write_variant_field_class(
+       struct lttng_kernel_session *session,
+       const struct lttng_kernel_type_variant *variant_type,
+       struct field_location_ctx *field_location_ctx)
+{
+       const char *tag_name;
+       struct field_path *field_path = NULL;
+       struct field_path_node *field_path_node;
+       const struct lttng_kernel_type_enum *enum_type;
+       struct ctf2_enum *ctf2_enum = NULL;
+       unsigned int variant_choice_idx, variant_nr_choices, enum_nr_entries;
+       int ret = 0;
+
+       tag_name = variant_type->tag_name;
+
+       field_path = field_path_resolve(field_location_ctx, tag_name);
+       if (!field_path) {
+               ret = -1;
+               goto end;
+       }
+
+       ret = lttng_metadata_printf(session, "{"
+                                            "  \"type\": \"variant\",\n"
+                                            "  \"selector-field-location\":");
+       if (ret)
+               goto end;
+
+       ret = print_field_location(session, field_path);
+       if (ret)
+               goto end;
+
+       ret = lttng_metadata_printf(session, ",  \"options\": \n[");
+       if (ret)
+               goto end;
+
+       field_path_node = list_last_entry(&(field_path->path),
+                                         struct field_path_node, node);
+       if (!field_path_node) {
+               ret = -1;
+               goto end;
+       }
+
+       enum_type = lttng_kernel_get_type_enum(field_path_node->field->type);
+       enum_nr_entries = enum_type->desc->nr_entries;
+
+       variant_nr_choices = variant_type->nr_choices;
+
+       if (enum_nr_entries != variant_nr_choices) {
+               ret = -1;
+               goto end;
+       }
+
+       ctf2_enum = ctf2_enum_create_from_type_enum(enum_type);
+
+       /*
+        * Iterate over all the options of the variant, find the mapping matching
+        * the option name in the selector enum. Once found, print all ranges
+        * and the field class of this option.
+        */
+       for (variant_choice_idx = 0; variant_choice_idx < variant_nr_choices;
+            variant_choice_idx++) {
+               const struct lttng_kernel_event_field *variant_field;
+               struct ctf2_enum_mapping *mapping;
+
+               variant_field = variant_type->choices[variant_choice_idx];
+
+               list_for_each_entry (mapping, &ctf2_enum->mappings, node) {
+                       struct ctf2_enum_mapping_range *mapping_range;
+                       unsigned int range_idx;
+
+                       /*
+                        * Omit the leading underscore when comparing enum
+                        * mappings.
+                        */
+                       if (strcmp(variant_field->name, &mapping->name[1]) !=
+                           0) {
+                               continue;
+                       }
+
+                       ret = lttng_metadata_printf(
+                               session,
+                               "{"
+                               "  \"name\": \"_%s\","
+                               "  \"selector-field-ranges\": [",
+                               variant_field->name);
+                       if (ret)
+                               goto end;
+
+                       range_idx = 0;
+                       list_for_each_entry (mapping_range, &mapping->ranges,
+                                            node) {
+                               if (mapping_range->start.signedness)
+                                       ret = lttng_metadata_printf(
+                                               session, "[%lld, ",
+                                               (long long)mapping_range->start
+                                                       .value);
+                               else
+                                       ret = lttng_metadata_printf(
+                                               session, "[%llu, ",
+                                               mapping_range->start.value);
+
+                               if (ret)
+                                       goto end;
+
+                               if (mapping_range->end.signedness)
+                                       ret = lttng_metadata_printf(
+                                               session, "%lld]",
+                                               (long long)mapping_range->end
+                                                       .value);
+                               else
+                                       ret = lttng_metadata_printf(
+                                               session, "%llu]",
+                                               mapping_range->end.value);
+                               if (ret)
+                                       goto end;
+
+                               if (range_idx < mapping->nr_ranges - 1) {
+                                       ret = lttng_metadata_printf(session,
+                                                                   ",\n");
+                                       if (ret)
+                                               goto end;
+                               }
+
+                               range_idx++;
+                       }
+
+                       ret = lttng_metadata_printf(session,
+                                                   " ],"
+                                                   "  \"field-class\": ");
+                       if (ret)
+                               goto end;
+
+                       /*
+                        * Push a new scope only if we are entering a variant
+                        * or struct field class.
+                        */
+                       if (field_location_ctx &&
+                           (variant_field->type->type ==
+                                    lttng_kernel_type_struct ||
+                            variant_field->type->type ==
+                                    lttng_kernel_type_variant)) {
+                               ret = field_location_stack_push(
+                                       field_location_ctx,
+                                       SCOPE_STACK_NODE_TYPE_SCOPE,
+                                       variant_field->name, variant_field);
+                               if (ret)
+                                       goto end;
+                       }
+
+                       ret = ctf2_metadata_write_field_class(
+                               session, variant_field->type,
+                               field_location_ctx);
+                       if (ret)
+                               goto end;
+
+                       if (field_location_ctx &&
+                           (variant_field->type->type ==
+                                    lttng_kernel_type_struct ||
+                            variant_field->type->type ==
+                                    lttng_kernel_type_variant)) {
+                               field_location_stack_pop_n_elem(
+                                       field_location_ctx, 1);
+                       }
+
+                       ret = lttng_metadata_printf(session, "}");
+                       if (ret)
+                               goto end;
+
+                       if (variant_choice_idx < variant_nr_choices - 1) {
+                               ret = lttng_metadata_printf(session, ",");
+                               if (ret)
+                                       goto end;
+                       }
+               }
+       }
+       ret = lttng_metadata_printf(session, "] }");
+
+end:
+       ctf2_enum_destroy(ctf2_enum);
+       field_path_destroy(field_path);
+       return ret;
+}
+
+/*
+ * Must be called with sessions_mutex held.
+ */
+static int
+ctf2_metadata_write_field_class(struct lttng_kernel_session *session,
+                               const struct lttng_kernel_type_common *type,
+                               struct field_location_ctx *field_location_ctx)
+{
+       int ret = 0;
+       switch (type->type) {
+       case lttng_kernel_type_integer:
+               ret = ctf2_metadata_write_integer_field_class(
+                       session, lttng_kernel_get_type_integer(type));
+               break;
+       case lttng_kernel_type_enum:
+               ret = ctf2_metadata_write_enum_field_class(
+                       session, lttng_kernel_get_type_enum(type));
+               break;
+       case lttng_kernel_type_string:
+               ret = ctf2_metadata_write_string_field_class(
+                       session, lttng_kernel_get_type_string(type));
+               break;
+       case lttng_kernel_type_struct:
+               ret = ctf2_metadata_write_struct_field_class(
+                       session, lttng_kernel_get_type_struct(type),
+                       field_location_ctx);
+               break;
+       case lttng_kernel_type_array:
+               ret = ctf2_metadata_write_static_length_array_field_class(
+                       session, lttng_kernel_get_type_array(type));
+               break;
+       case lttng_kernel_type_sequence:
+               ret = ctf2_metadata_write_dynamic_length_array_field_class(
+                       session, lttng_kernel_get_type_sequence(type),
+                       field_location_ctx);
+               break;
+       case lttng_kernel_type_variant:
+               ret = ctf2_metadata_write_variant_field_class(
+                       session, lttng_kernel_get_type_variant(type),
+                       field_location_ctx);
+               break;
+       default:
+               WARN_ON_ONCE(1);
+               return -EINVAL;
+       }
+
+       return ret;
+}
+
+/*
+ * Must be called with sessions_mutex held.
+ */
+static int
+ctf2_metadata_write_field(struct lttng_kernel_session *session,
+                         const struct lttng_kernel_event_field *field,
+                         struct field_location_ctx *field_location_ctx)
+{
+       int ret = 0;
+
+       ret = lttng_metadata_printf(session,
+                                   "{"
+                                   "  \"name\": \"%s\","
+                                   "  \"field-class\":",
+                                   field->name);
+       if (ret)
+               goto end;
+
+       /*
+        * Push a new scope only if we are entering a variant or struct field
+        * class.
+        */
+       if (field_location_ctx &&
+           (field->type->type == lttng_kernel_type_struct ||
+            field->type->type == lttng_kernel_type_variant)) {
+               ret = field_location_stack_push(field_location_ctx,
+                                               SCOPE_STACK_NODE_TYPE_SCOPE,
+                                               field->name, field);
+               if (ret)
+                       goto end;
+       }
+
+       ret = ctf2_metadata_write_field_class(session, field->type,
+                                             field_location_ctx);
+       if (ret) {
+               printk(KERN_WARNING
+                      "LTTng: CTF2: error producing metadata for field class \"%s\"\n",
+                      field->name);
+               goto end;
+       }
+
+       if (field_location_ctx &&
+           (field->type->type == lttng_kernel_type_struct ||
+            field->type->type == lttng_kernel_type_variant)) {
+               field_location_stack_pop_n_elem(field_location_ctx, 1);
+       }
+
+       ret = lttng_metadata_printf(session, "}");
+
+end:
+       return ret;
+}
+
+static int ctf2_metadata_write_payload_field_class(
+       struct lttng_kernel_session *session,
+       struct lttng_kernel_event_recorder *event_recorder)
+{
+       const struct lttng_kernel_event_desc *desc =
+               event_recorder->priv->parent.desc;
+       struct field_location_ctx *field_location_ctx;
+       unsigned int i;
+       int ret = 0;
+
+       ret = lttng_metadata_printf(session, "{"
+                                            "    \"type\": \"structure\","
+                                            "    \"member-classes\": [");
+       if (ret)
+               goto end;
+
+       field_location_ctx = field_location_ctx_create();
+       if (!field_location_ctx) {
+               ret = -ENOMEM;
+               goto end;
+       }
+
+       ret = field_location_stack_push(field_location_ctx,
+                                       SCOPE_STACK_NODE_TYPE_SCOPE,
+                                       "event-record-payload", NULL);
+       if (ret)
+               goto end;
+
+       for (i = 0; i < desc->tp_class->nr_fields; i++) {
+               const struct lttng_kernel_event_field *field =
+                       desc->tp_class->fields[i];
+
+               ret = ctf2_metadata_write_field(session, field,
+                                               field_location_ctx);
+               if (ret)
+                       goto end;
+
+               ret = field_location_stack_push(field_location_ctx,
+                                               SCOPE_STACK_NODE_TYPE_FIELD,
+                                               field->name, field);
+               if (ret)
+                       goto end;
+
+               if (i < desc->tp_class->nr_fields - 1) {
+                       ret = lttng_metadata_printf(session, ",");
+                       if (ret)
+                               goto end;
+               }
+       }
+
+       field_location_stack_pop_n_elem(field_location_ctx,
+                                       desc->tp_class->nr_fields + 1);
+
+       ret = lttng_metadata_printf(session, "    ]"
+                                            "}");
+end:
+       field_location_ctx_destroy(field_location_ctx);
+       return ret;
+}
+
+int ctf2_metadata_write_event_record_class_fragment(
+       struct lttng_kernel_event_common *event)
+{
+       struct lttng_kernel_event_recorder *event_recorder;
+       struct lttng_kernel_channel_buffer *chan;
+       struct lttng_kernel_session *session;
+       unsigned int nr_fields;
+       int ret = 0;
+
+       if (event->type != LTTNG_KERNEL_EVENT_TYPE_RECORDER)
+               return 0;
+
+       event_recorder =
+               container_of(event, struct lttng_kernel_event_recorder, parent);
+       chan = event_recorder->chan;
+       session = chan->parent.session;
+
+       if (event_recorder->priv->metadata_dumped ||
+           !LTTNG_READ_ONCE(session->active))
+               return 0;
+
+       if (chan->priv->channel_type == METADATA_CHANNEL)
+               return 0;
+
+       lttng_metadata_begin(session);
+
+       ret = ctf2_metadata_write_record_separator(session);
+       if (ret)
+               goto end;
+
+       ret = lttng_metadata_printf(
+               session,
+               "{"
+               "\"type\": \"event-record-class\","
+               "\"id\": %u,"
+               "\"name\": \"%s\","
+               "\"data-stream-class-id\":%u",
+               event_recorder->priv->id,
+               event_recorder->priv->parent.desc->event_name,
+               event_recorder->chan->priv->id);
+       if (ret)
+               goto end;
+
+       nr_fields = event_recorder->priv->parent.desc->tp_class->nr_fields;
+       if (nr_fields) {
+               ret = lttng_metadata_printf(session,
+                                           ", \"payload-field-class\":");
+               if (ret)
+                       goto end;
+
+               ret = ctf2_metadata_write_payload_field_class(session,
+                                                             event_recorder);
+               if (ret) {
+                       printk(KERN_WARNING
+                              "LTTng: CTF2: error producing metadata event record payload field class\n");
+                       goto end;
+               }
+       }
+
+       ret = lttng_metadata_printf(session, "}\n");
+       if (ret)
+               goto end;
+
+       event_recorder->priv->metadata_dumped = 1;
+
+end:
+       lttng_metadata_end(session);
+       return ret;
+}
+
+static int ctf2_metadata_write_data_stream_class_fragment(
+       struct lttng_kernel_session *session,
+       struct lttng_kernel_channel_buffer *chan)
+{
+       int ret;
+
+       ret = ctf2_metadata_write_record_separator(session);
+       if (ret)
+               goto end;
+
+       ret = lttng_metadata_printf(session,
+                                   "{"
+                                   "  \"type\":\"data-stream-class\","
+                                   "  \"id\": %u,"
+                                   "  \"default-clock-class-name\": \"%s\","
+                                   "  \"packet-context-field-class\":",
+                                   chan->priv->id, trace_clock_name());
+       if (ret)
+               goto end;
+
+       ret = ctf2_metadata_write_packet_context_field_class(session, chan);
+       if (ret) {
+               printk(KERN_WARNING
+                      "LTTng: CTF2: error producing metadata stream class packet context field class\n");
+               goto end;
+       }
+
+       ret = lttng_metadata_printf(session,
+                                   ",\"event-record-header-field-class\":");
+       if (ret)
+               goto end;
+
+       if (chan->priv->header_type == 1) {
+               ret = ctf2_metadata_write_compact_event_header_field_class(
+                       session);
+               if (ret) {
+                       printk(KERN_WARNING
+                              "LTTng: CTF2: error producing metadata stream class compact event header field class\n");
+                       goto end;
+               }
+       } else {
+               ret = ctf2_metadata_write_large_event_header_field_class(
+                       session);
+               if (ret) {
+                       printk(KERN_WARNING
+                              "LTTng: CTF2: error producing metadata stream class large event header field class\n");
+                       goto end;
+               }
+       }
+
+       if (chan->priv->ctx) {
+               ret = lttng_metadata_printf(
+                       session,
+                       ",\"event-record-common-context-field-class\":\n");
+               if (ret)
+                       goto end;
+
+               ret = ctf2_metadata_write_common_context_field_class(
+                       session, chan->priv->ctx);
+               if (ret) {
+                       printk(KERN_WARNING
+                              "LTTng: CTF2: error producing metadata stream class common context field class\n");
+                       goto end;
+               }
+       }
+
+       ret = lttng_metadata_printf(session, "}\n");
+
+end:
+       return ret;
+}
+
+int ctf2_metadata_write(struct lttng_kernel_session *session)
+{
+       struct lttng_kernel_channel_buffer_private *chan_priv;
+       struct lttng_kernel_event_recorder_private *event_recorder_priv;
+       int ret;
+
+       if (!LTTNG_READ_ONCE(session->active))
+               return 0;
+
+       lttng_metadata_begin(session);
+
+       if (session->priv->metadata_dumped)
+               goto skip_session;
+
+       ret = ctf2_metadata_write_preamble_fragment(session);
+       if (ret) {
+               printk(KERN_WARNING
+                      "LTTng: CTF2: error producing metadata preamble fragment\n");
+               goto end;
+       }
+
+       ret = ctf2_metadata_write_clock_class_fragment(session);
+       if (ret) {
+               printk(KERN_WARNING
+                      "LTTng: CTF2: error producing metadata clock class fragment\n");
+               goto end;
+       }
+
+       ret = ctf2_metadata_write_trace_class_fragment(session);
+       if (ret) {
+               printk(KERN_WARNING
+                      "LTTng: CTF2: error producing metadata trace class fragment\n");
+               goto end;
+       }
+
+skip_session:
+       list_for_each_entry (chan_priv, &session->priv->chan, node) {
+               ret = ctf2_metadata_write_data_stream_class_fragment(
+                       session, chan_priv->pub);
+               if (ret) {
+                       printk(KERN_WARNING
+                              "LTTng: CTF2: error producing metadata stream class fragment\n");
+                       goto end;
+               }
+       }
+       list_for_each_entry (event_recorder_priv, &session->priv->events,
+                            parent.node) {
+               ret = ctf2_metadata_write_event_record_class_fragment(
+                       &event_recorder_priv->pub->parent);
+               if (ret) {
+                       printk(KERN_WARNING
+                              "LTTng: CTF2: error producing metadata event class fragment\n");
+                       goto end;
+               }
+       }
+       session->priv->metadata_dumped = 1;
+
+end:
+       lttng_metadata_end(session);
+       return ret;
+}
diff --git a/src/ctf2.h b/src/ctf2.h
new file mode 100644 (file)
index 0000000..bbd0e17
--- /dev/null
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: (GPL-2.0-only or LGPL-2.1-only)
+ *
+ * src/ctf2.h
+ *
+ * Copyright (C) 2021 Francis Deslauriers <francis.deslauriers@efficios.com>
+ */
+
+#ifndef _LTTNG_CTF2_H_
+#define _LTTNG_CTF2_H_
+
+#include <lttng/events.h>
+#include <lttng/events-internal.h>
+
+int ctf2_metadata_write(struct lttng_kernel_session *session);
+int ctf2_metadata_write_event_record_class_fragment(
+       struct lttng_kernel_event_common *event);
+
+#endif /* _LTTNG_CTF2_H_ */
diff --git a/src/field-path-resolving.c b/src/field-path-resolving.c
new file mode 100644 (file)
index 0000000..6b221c3
--- /dev/null
@@ -0,0 +1,195 @@
+/* SPDX-License-Identifier: (GPL-2.0-only or LGPL-2.1-only)
+ *
+ * src/field-path-resolving.c
+ *
+ * Copyright (C) 2010-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Copyright (C) 2021 Francis Deslauriers <francis.deslauriers@efficios.com>
+ */
+
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include <lttng/events.h>
+#include <lttng/events-internal.h>
+
+#include "field-path-resolving.h"
+
+static int append_path_element(struct field_path *field_path, const char *name,
+                              const struct lttng_kernel_event_field *field)
+{
+       struct field_path_node *field_path_node;
+
+       field_path_node = kzalloc(sizeof(*field_path_node), GFP_KERNEL);
+       if (!field_path_node)
+               return -ENOMEM;
+
+       field_path_node->field = field;
+       field_path_node->name = name;
+
+       list_add(&field_path_node->node, &field_path->path);
+       field_path->path_entry_count++;
+
+       return 0;
+}
+
+struct field_path *field_path_resolve(struct field_location_ctx *ctx,
+                                     const char *target_field_name)
+{
+       struct field_path *field_path = NULL;
+       struct field_location_stack_node *node;
+       bool field_found = false;
+       int ret;
+
+       field_path = kzalloc(sizeof(*field_path), GFP_KERNEL);
+       if (!field_path)
+               return NULL;
+
+       INIT_LIST_HEAD(&field_path->path);
+
+       /*
+        * The scope stack contains all the fields processed up until this
+        * point in the current scope and outter scopes.
+        *
+        * Iterate on the stack from the top until we find a integer field
+        * matching the target field name.
+        *
+        * Once found, continue the iteration but only record struct fields.
+        */
+
+       list_for_each_entry (node, &ctx->field_location_stack, node) {
+               if (!field_found) {
+                       /* The location can't be a scope. */
+                       if (node->type == SCOPE_STACK_NODE_TYPE_SCOPE) {
+                               continue;
+                       }
+
+                       /* The location field must be a integer or an enum. */
+                       if (node->field->type->type !=
+                                   lttng_kernel_type_integer &&
+                           node->field->type->type != lttng_kernel_type_enum) {
+                               continue;
+                       }
+
+                       /*
+                        * If the target field name is NULL, use the first
+                        * element encountered, else compare the field names.
+                        */
+                       if (target_field_name == NULL ||
+                           strcmp(node->field_name, target_field_name) == 0) {
+                               field_found = true;
+                               ret = append_path_element(field_path,
+                                                         node->field_name,
+                                                         node->field);
+                               if (ret)
+                                       goto err;
+                       }
+               } else {
+                       /*
+                        * The actual location field was found, from this point
+                        * on we record all surrounding scopes to create the
+                        * full path.
+                        */
+                       if (node->type == SCOPE_STACK_NODE_TYPE_SCOPE) {
+                               ret = append_path_element(
+                                       field_path, node->field_name, NULL);
+                               if (ret)
+                                       goto err;
+                       }
+               }
+       }
+
+       return field_path;
+
+err:
+       field_path_destroy(field_path);
+       return NULL;
+}
+
+void field_path_destroy(struct field_path *fp)
+{
+       struct field_path_node *node, *tmp;
+
+       if (!fp) {
+               return;
+       }
+
+       list_for_each_entry_safe (node, tmp, &fp->path, node) {
+               kfree(node);
+       }
+
+       kfree(fp);
+}
+
+int field_location_stack_push(struct field_location_ctx *ctx,
+                             enum field_location_stack_node_type type,
+                             const char *field_name,
+                             const struct lttng_kernel_event_field *field)
+{
+       struct field_location_stack_node *field_location_stack_node;
+
+       field_location_stack_node =
+               kzalloc(sizeof(*field_location_stack_node), GFP_KERNEL);
+       if (!field_location_stack_node)
+               return -ENOMEM;
+
+       field_location_stack_node->type = type;
+       field_location_stack_node->field = field;
+       field_location_stack_node->field_name = field_name;
+
+       list_add(&field_location_stack_node->node, &ctx->field_location_stack);
+       ctx->field_location_stack_len++;
+
+       return 0;
+}
+
+static void field_location_stack_pop(struct field_location_ctx *ctx)
+{
+       struct field_location_stack_node *node;
+
+       node = list_first_entry(&ctx->field_location_stack,
+                               struct field_location_stack_node, node);
+
+       list_del(&node->node);
+       kfree(node);
+       ctx->field_location_stack_len--;
+}
+
+void field_location_stack_pop_n_elem(struct field_location_ctx *ctx,
+                                    unsigned int number_to_pop)
+{
+       unsigned int i;
+
+       for (i = 0; i < number_to_pop; i++) {
+               field_location_stack_pop(ctx);
+       }
+}
+
+struct field_location_ctx *field_location_ctx_create(void)
+{
+       struct field_location_ctx *ctx;
+
+       ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+       if (!ctx)
+               return NULL;
+
+       INIT_LIST_HEAD(&ctx->field_location_stack);
+
+       return ctx;
+}
+
+void field_location_ctx_destroy(struct field_location_ctx *ctx)
+{
+       struct field_location_stack_node *node, *tmp;
+
+       if (!ctx) {
+               return;
+       }
+
+       list_for_each_entry_safe (node, tmp, &ctx->field_location_stack, node) {
+               kfree(node);
+       }
+
+       kfree(ctx);
+}
diff --git a/src/field-path-resolving.h b/src/field-path-resolving.h
new file mode 100644 (file)
index 0000000..7beeda4
--- /dev/null
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: (GPL-2.0-only or LGPL-2.1-only)
+ *
+ * src/field-path-resolving.h
+ *
+ * Copyright (C) 2010-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Copyright (C) 2021 Francis Deslauriers <francis.deslauriers@efficios.com>
+ */
+
+#include <wrapper/list.h>
+
+enum field_location_stack_node_type {
+       SCOPE_STACK_NODE_TYPE_FIELD,
+       SCOPE_STACK_NODE_TYPE_SCOPE,
+};
+struct field_location_stack_node {
+       struct list_head node;
+       enum field_location_stack_node_type type;
+       const char *field_name;
+       const struct lttng_kernel_event_field *field;
+};
+
+struct field_location_ctx {
+       struct list_head field_location_stack;
+       unsigned int field_location_stack_len;
+};
+
+struct field_path_node {
+       struct list_head node;
+       const char *name;
+       const struct lttng_kernel_event_field *field;
+};
+
+struct field_path {
+       unsigned int path_entry_count;
+       struct list_head path;
+};
+
+struct field_path *field_path_resolve(struct field_location_ctx *ctx,
+                                     const char *target_field_name);
+
+void field_path_destroy(struct field_path *fp);
+
+struct field_location_ctx *field_location_ctx_create(void);
+
+void field_location_ctx_destroy(struct field_location_ctx *ctx);
+
+int field_location_stack_push(struct field_location_ctx *ctx,
+                             enum field_location_stack_node_type type,
+                             const char *field_name,
+                             const struct lttng_kernel_event_field *field);
+
+void field_location_stack_pop_n_elem(struct field_location_ctx *ctx,
+                                    unsigned int number);
index 4ddf4a77774a89d22ff74bdf5f3e7dd96e0c93c8..f0beaaa3fd64bce2fb293239e9809630de7e843a 100644 (file)
 #include <stdarg.h>
 #endif
 
+#include "metadata-printer.h"
+#include "ctf1-8.h"
+#include "ctf2.h"
+
 #define METADATA_CACHE_DEFAULT_SIZE 4096
 
 static LIST_HEAD(sessions);
@@ -79,20 +83,7 @@ static void _lttng_event_destroy(struct lttng_kernel_event_common *event);
 static void _lttng_channel_destroy(struct lttng_kernel_channel_buffer *chan);
 static void _lttng_event_unregister(struct lttng_kernel_event_common *event);
 static
-int _lttng_event_recorder_metadata_statedump(struct lttng_kernel_event_common *event);
-static
-int _lttng_session_metadata_statedump(struct lttng_kernel_session *session);
-static
 void _lttng_metadata_channel_hangup(struct lttng_metadata_stream *stream);
-static
-int _lttng_type_statedump(struct lttng_kernel_session *session,
-               const struct lttng_kernel_type_common *type,
-               enum lttng_kernel_string_encoding parent_encoding,
-               size_t nesting);
-static
-int _lttng_field_statedump(struct lttng_kernel_session *session,
-               const struct lttng_kernel_event_field *field,
-               size_t nesting, const char **prev_field_name_p);
 
 void synchronize_trace(void)
 {
@@ -498,7 +489,11 @@ int lttng_session_enable(struct lttng_kernel_session *session)
 
        WRITE_ONCE(session->active, 1);
        WRITE_ONCE(session->priv->been_active, 1);
+#if 0
        ret = _lttng_session_metadata_statedump(session);
+#else
+       ret = ctf2_metadata_write(session);
+#endif
        if (ret) {
                WRITE_ONCE(session->active, 0);
                goto end;
@@ -1199,7 +1194,11 @@ struct lttng_kernel_event_common *_lttng_kernel_event_create(struct lttng_event_
                WARN_ON_ONCE(!ret);
                ret = try_module_get(event_return->priv->desc->owner);
                WARN_ON_ONCE(!ret);
+#if 0
                ret = _lttng_event_recorder_metadata_statedump(event_return);
+#else
+               ret = ctf2_metadata_write_event_record_class_fragment(event_return);
+#endif
                WARN_ON_ONCE(ret > 0);
                if (ret) {
                        lttng_kernel_event_free(event_return);
@@ -1280,7 +1279,11 @@ struct lttng_kernel_event_common *_lttng_kernel_event_create(struct lttng_event_
                goto register_error;
        }
 
+#if 0
        ret = _lttng_event_recorder_metadata_statedump(event);
+#else
+       ret = ctf2_metadata_write_event_record_class_fragment(event);
+#endif
        WARN_ON_ONCE(ret > 0);
        if (ret) {
                goto statedump_error;
@@ -2801,1179 +2804,6 @@ end:
        return ret;
 }
 
-static
-void lttng_metadata_begin(struct lttng_kernel_session *session)
-{
-       if (atomic_inc_return(&session->priv->metadata_cache->producing) == 1)
-               mutex_lock(&session->priv->metadata_cache->lock);
-}
-
-static
-void lttng_metadata_end(struct lttng_kernel_session *session)
-{
-       WARN_ON_ONCE(!atomic_read(&session->priv->metadata_cache->producing));
-       if (atomic_dec_return(&session->priv->metadata_cache->producing) == 0) {
-               struct lttng_metadata_stream *stream;
-
-               list_for_each_entry(stream, &session->priv->metadata_cache->metadata_stream, list)
-                       wake_up_interruptible(&stream->read_wait);
-               mutex_unlock(&session->priv->metadata_cache->lock);
-       }
-}
-
-/*
- * Write the metadata to the metadata cache.
- * Must be called with sessions_mutex held.
- * The metadata cache lock protects us from concurrent read access from
- * thread outputting metadata content to ring buffer.
- * The content of the printf is printed as a single atomic metadata
- * transaction.
- */
-int lttng_metadata_printf(struct lttng_kernel_session *session,
-                         const char *fmt, ...)
-{
-       char *str;
-       size_t len;
-       va_list ap;
-
-       WARN_ON_ONCE(!LTTNG_READ_ONCE(session->active));
-
-       va_start(ap, fmt);
-       str = kvasprintf(GFP_KERNEL, fmt, ap);
-       va_end(ap);
-       if (!str)
-               return -ENOMEM;
-
-       len = strlen(str);
-       WARN_ON_ONCE(!atomic_read(&session->priv->metadata_cache->producing));
-       if (session->priv->metadata_cache->metadata_written + len >
-                       session->priv->metadata_cache->cache_alloc) {
-               char *tmp_cache_realloc;
-               unsigned int tmp_cache_alloc_size;
-
-               tmp_cache_alloc_size = max_t(unsigned int,
-                               session->priv->metadata_cache->cache_alloc + len,
-                               session->priv->metadata_cache->cache_alloc << 1);
-               tmp_cache_realloc = vzalloc(tmp_cache_alloc_size);
-               if (!tmp_cache_realloc)
-                       goto err;
-               if (session->priv->metadata_cache->data) {
-                       memcpy(tmp_cache_realloc,
-                               session->priv->metadata_cache->data,
-                               session->priv->metadata_cache->cache_alloc);
-                       vfree(session->priv->metadata_cache->data);
-               }
-
-               session->priv->metadata_cache->cache_alloc = tmp_cache_alloc_size;
-               session->priv->metadata_cache->data = tmp_cache_realloc;
-       }
-       memcpy(session->priv->metadata_cache->data +
-                       session->priv->metadata_cache->metadata_written,
-                       str, len);
-       session->priv->metadata_cache->metadata_written += len;
-       kfree(str);
-
-       return 0;
-
-err:
-       kfree(str);
-       return -ENOMEM;
-}
-
-static
-int print_tabs(struct lttng_kernel_session *session, size_t nesting)
-{
-       size_t i;
-
-       for (i = 0; i < nesting; i++) {
-               int ret;
-
-               ret = lttng_metadata_printf(session, "  ");
-               if (ret) {
-                       return ret;
-               }
-       }
-       return 0;
-}
-
-static
-int lttng_field_name_statedump(struct lttng_kernel_session *session,
-               const struct lttng_kernel_event_field *field,
-               size_t nesting)
-{
-       return lttng_metadata_printf(session, " _%s;\n", field->name);
-}
-
-static
-int _lttng_integer_type_statedump(struct lttng_kernel_session *session,
-               const struct lttng_kernel_type_integer *type,
-               enum lttng_kernel_string_encoding parent_encoding,
-               size_t nesting)
-{
-       int ret;
-
-       ret = print_tabs(session, nesting);
-       if (ret)
-               return ret;
-       ret = lttng_metadata_printf(session,
-               "integer { size = %u; align = %u; signed = %u; encoding = %s; base = %u;%s }",
-               type->size,
-               type->alignment,
-               type->signedness,
-               (parent_encoding == lttng_kernel_string_encoding_none)
-                       ? "none"
-                       : (parent_encoding == lttng_kernel_string_encoding_UTF8)
-                               ? "UTF8"
-                               : "ASCII",
-               type->base,
-#if __BYTE_ORDER == __BIG_ENDIAN
-               type->reverse_byte_order ? " byte_order = le;" : ""
-#else
-               type->reverse_byte_order ? " byte_order = be;" : ""
-#endif
-       );
-       return ret;
-}
-
-/*
- * Must be called with sessions_mutex held.
- */
-static
-int _lttng_struct_type_statedump(struct lttng_kernel_session *session,
-               const struct lttng_kernel_type_struct *type,
-               size_t nesting)
-{
-       const char *prev_field_name = NULL;
-       int ret;
-       uint32_t i, nr_fields;
-       unsigned int alignment;
-
-       ret = print_tabs(session, nesting);
-       if (ret)
-               return ret;
-       ret = lttng_metadata_printf(session,
-               "struct {\n");
-       if (ret)
-               return ret;
-       nr_fields = type->nr_fields;
-       for (i = 0; i < nr_fields; i++) {
-               const struct lttng_kernel_event_field *iter_field;
-
-               iter_field = type->fields[i];
-               ret = _lttng_field_statedump(session, iter_field, nesting + 1, &prev_field_name);
-               if (ret)
-                       return ret;
-       }
-       ret = print_tabs(session, nesting);
-       if (ret)
-               return ret;
-       alignment = type->alignment;
-       if (alignment) {
-               ret = lttng_metadata_printf(session,
-                       "} align(%u)",
-                       alignment);
-       } else {
-               ret = lttng_metadata_printf(session,
-                       "}");
-       }
-       return ret;
-}
-
-/*
- * Must be called with sessions_mutex held.
- */
-static
-int _lttng_struct_field_statedump(struct lttng_kernel_session *session,
-               const struct lttng_kernel_event_field *field,
-               size_t nesting)
-{
-       int ret;
-
-       ret = _lttng_struct_type_statedump(session,
-                       lttng_kernel_get_type_struct(field->type), nesting);
-       if (ret)
-               return ret;
-       return lttng_field_name_statedump(session, field, nesting);
-}
-
-/*
- * Must be called with sessions_mutex held.
- */
-static
-int _lttng_variant_type_statedump(struct lttng_kernel_session *session,
-               const struct lttng_kernel_type_variant *type,
-               size_t nesting,
-               const char *prev_field_name)
-{
-       const char *tag_name;
-       int ret;
-       uint32_t i, nr_choices;
-
-       tag_name = type->tag_name;
-       if (!tag_name)
-               tag_name = prev_field_name;
-       if (!tag_name)
-               return -EINVAL;
-       /*
-        * CTF 1.8 does not allow expressing nonzero variant alignment in a nestable way.
-        */
-       if (type->alignment != 0)
-               return -EINVAL;
-       ret = print_tabs(session, nesting);
-       if (ret)
-               return ret;
-       ret = lttng_metadata_printf(session,
-               "variant <_%s> {\n",
-               tag_name);
-       if (ret)
-               return ret;
-       nr_choices = type->nr_choices;
-       for (i = 0; i < nr_choices; i++) {
-               const struct lttng_kernel_event_field *iter_field;
-
-               iter_field = type->choices[i];
-               ret = _lttng_field_statedump(session, iter_field, nesting + 1, NULL);
-               if (ret)
-                       return ret;
-       }
-       ret = print_tabs(session, nesting);
-       if (ret)
-               return ret;
-       ret = lttng_metadata_printf(session,
-               "}");
-       return ret;
-}
-
-/*
- * Must be called with sessions_mutex held.
- */
-static
-int _lttng_variant_field_statedump(struct lttng_kernel_session *session,
-               const struct lttng_kernel_event_field *field,
-               size_t nesting,
-               const char *prev_field_name)
-{
-       int ret;
-
-       ret = _lttng_variant_type_statedump(session,
-                       lttng_kernel_get_type_variant(field->type), nesting,
-                       prev_field_name);
-       if (ret)
-               return ret;
-       return lttng_field_name_statedump(session, field, nesting);
-}
-
-/*
- * Must be called with sessions_mutex held.
- */
-static
-int _lttng_array_field_statedump(struct lttng_kernel_session *session,
-               const struct lttng_kernel_event_field *field,
-               size_t nesting)
-{
-       int ret;
-       const struct lttng_kernel_type_array *array_type;
-       const struct lttng_kernel_type_common *elem_type;
-
-       array_type = lttng_kernel_get_type_array(field->type);
-       WARN_ON_ONCE(!array_type);
-
-       if (array_type->alignment) {
-               ret = print_tabs(session, nesting);
-               if (ret)
-                       return ret;
-               ret = lttng_metadata_printf(session,
-               "struct { } align(%u) _%s_padding;\n",
-                               array_type->alignment * CHAR_BIT,
-                               field->name);
-               if (ret)
-                       return ret;
-       }
-       /*
-        * Nested compound types: Only array of structures and variants are
-        * currently supported.
-        */
-       elem_type = array_type->elem_type;
-       switch (elem_type->type) {
-       case lttng_kernel_type_integer:
-       case lttng_kernel_type_struct:
-       case lttng_kernel_type_variant:
-               ret = _lttng_type_statedump(session, elem_type,
-                               array_type->encoding, nesting);
-               if (ret)
-                       return ret;
-               break;
-
-       default:
-               return -EINVAL;
-       }
-       ret = lttng_metadata_printf(session,
-               " _%s[%u];\n",
-               field->name,
-               array_type->length);
-       return ret;
-}
-
-/*
- * Must be called with sessions_mutex held.
- */
-static
-int _lttng_sequence_field_statedump(struct lttng_kernel_session *session,
-               const struct lttng_kernel_event_field *field,
-               size_t nesting,
-               const char *prev_field_name)
-{
-       int ret;
-       const char *length_name;
-       const struct lttng_kernel_type_sequence *sequence_type;
-       const struct lttng_kernel_type_common *elem_type;
-
-       sequence_type = lttng_kernel_get_type_sequence(field->type);
-       WARN_ON_ONCE(!sequence_type);
-
-       length_name = sequence_type->length_name;
-       if (!length_name)
-               length_name = prev_field_name;
-       if (!length_name)
-               return -EINVAL;
-
-       if (sequence_type->alignment) {
-               ret = print_tabs(session, nesting);
-               if (ret)
-                       return ret;
-               ret = lttng_metadata_printf(session,
-               "struct { } align(%u) _%s_padding;\n",
-                               sequence_type->alignment * CHAR_BIT,
-                               field->name);
-               if (ret)
-                       return ret;
-       }
-
-       /*
-        * Nested compound types: Only array of structures and variants are
-        * currently supported.
-        */
-       elem_type = sequence_type->elem_type;
-       switch (elem_type->type) {
-       case lttng_kernel_type_integer:
-       case lttng_kernel_type_struct:
-       case lttng_kernel_type_variant:
-               ret = _lttng_type_statedump(session, elem_type,
-                               sequence_type->encoding, nesting);
-               if (ret)
-                       return ret;
-               break;
-
-       default:
-               return -EINVAL;
-       }
-       ret = lttng_metadata_printf(session,
-               " _%s[ _%s ];\n",
-               field->name,
-               length_name);
-       return ret;
-}
-
-/*
- * Must be called with sessions_mutex held.
- */
-static
-int _lttng_enum_type_statedump(struct lttng_kernel_session *session,
-               const struct lttng_kernel_type_enum *type,
-               size_t nesting)
-{
-       const struct lttng_kernel_enum_desc *enum_desc;
-       const struct lttng_kernel_type_common *container_type;
-       int ret;
-       unsigned int i, nr_entries;
-
-       container_type = type->container_type;
-       if (container_type->type != lttng_kernel_type_integer) {
-               ret = -EINVAL;
-               goto end;
-       }
-       enum_desc = type->desc;
-       nr_entries = enum_desc->nr_entries;
-
-       ret = print_tabs(session, nesting);
-       if (ret)
-               goto end;
-       ret = lttng_metadata_printf(session, "enum : ");
-       if (ret)
-               goto end;
-       ret = _lttng_integer_type_statedump(session, lttng_kernel_get_type_integer(container_type),
-                       lttng_kernel_string_encoding_none, 0);
-       if (ret)
-               goto end;
-       ret = lttng_metadata_printf(session, " {\n");
-       if (ret)
-               goto end;
-       /* Dump all entries */
-       for (i = 0; i < nr_entries; i++) {
-               const struct lttng_kernel_enum_entry *entry = enum_desc->entries[i];
-               int j, len;
-
-               ret = print_tabs(session, nesting + 1);
-               if (ret)
-                       goto end;
-               ret = lttng_metadata_printf(session,
-                               "\"");
-               if (ret)
-                       goto end;
-               len = strlen(entry->string);
-               /* Escape the character '"' */
-               for (j = 0; j < len; j++) {
-                       char c = entry->string[j];
-
-                       switch (c) {
-                       case '"':
-                               ret = lttng_metadata_printf(session,
-                                               "\\\"");
-                               break;
-                       case '\\':
-                               ret = lttng_metadata_printf(session,
-                                               "\\\\");
-                               break;
-                       default:
-                               ret = lttng_metadata_printf(session,
-                                               "%c", c);
-                               break;
-                       }
-                       if (ret)
-                               goto end;
-               }
-               ret = lttng_metadata_printf(session, "\"");
-               if (ret)
-                       goto end;
-
-               if (entry->options.is_auto) {
-                       ret = lttng_metadata_printf(session, ",\n");
-                       if (ret)
-                               goto end;
-               } else {
-                       ret = lttng_metadata_printf(session,
-                                       " = ");
-                       if (ret)
-                               goto end;
-                       if (entry->start.signedness)
-                               ret = lttng_metadata_printf(session,
-                                       "%lld", (long long) entry->start.value);
-                       else
-                               ret = lttng_metadata_printf(session,
-                                       "%llu", entry->start.value);
-                       if (ret)
-                               goto end;
-                       if (entry->start.signedness == entry->end.signedness &&
-                                       entry->start.value
-                                               == entry->end.value) {
-                               ret = lttng_metadata_printf(session,
-                                       ",\n");
-                       } else {
-                               if (entry->end.signedness) {
-                                       ret = lttng_metadata_printf(session,
-                                               " ... %lld,\n",
-                                               (long long) entry->end.value);
-                               } else {
-                                       ret = lttng_metadata_printf(session,
-                                               " ... %llu,\n",
-                                               entry->end.value);
-                               }
-                       }
-                       if (ret)
-                               goto end;
-               }
-       }
-       ret = print_tabs(session, nesting);
-       if (ret)
-               goto end;
-       ret = lttng_metadata_printf(session, "}");
-end:
-       return ret;
-}
-
-/*
- * Must be called with sessions_mutex held.
- */
-static
-int _lttng_enum_field_statedump(struct lttng_kernel_session *session,
-               const struct lttng_kernel_event_field *field,
-               size_t nesting)
-{
-       int ret;
-       const struct lttng_kernel_type_enum *enum_type;
-
-       enum_type = lttng_kernel_get_type_enum(field->type);
-       WARN_ON_ONCE(!enum_type);
-       ret = _lttng_enum_type_statedump(session, enum_type, nesting);
-       if (ret)
-               return ret;
-       return lttng_field_name_statedump(session, field, nesting);
-}
-
-static
-int _lttng_integer_field_statedump(struct lttng_kernel_session *session,
-               const struct lttng_kernel_event_field *field,
-               size_t nesting)
-{
-       int ret;
-
-       ret = _lttng_integer_type_statedump(session, lttng_kernel_get_type_integer(field->type),
-                       lttng_kernel_string_encoding_none, nesting);
-       if (ret)
-               return ret;
-       return lttng_field_name_statedump(session, field, nesting);
-}
-
-static
-int _lttng_string_type_statedump(struct lttng_kernel_session *session,
-               const struct lttng_kernel_type_string *type,
-               size_t nesting)
-{
-       int ret;
-
-       /* Default encoding is UTF8 */
-       ret = print_tabs(session, nesting);
-       if (ret)
-               return ret;
-       ret = lttng_metadata_printf(session,
-               "string%s",
-               type->encoding == lttng_kernel_string_encoding_ASCII ?
-                       " { encoding = ASCII; }" : "");
-       return ret;
-}
-
-static
-int _lttng_string_field_statedump(struct lttng_kernel_session *session,
-               const struct lttng_kernel_event_field *field,
-               size_t nesting)
-{
-       const struct lttng_kernel_type_string *string_type;
-       int ret;
-
-       string_type = lttng_kernel_get_type_string(field->type);
-       WARN_ON_ONCE(!string_type);
-       ret = _lttng_string_type_statedump(session, string_type, nesting);
-       if (ret)
-               return ret;
-       return lttng_field_name_statedump(session, field, nesting);
-}
-
-/*
- * Must be called with sessions_mutex held.
- */
-static
-int _lttng_type_statedump(struct lttng_kernel_session *session,
-               const struct lttng_kernel_type_common *type,
-               enum lttng_kernel_string_encoding parent_encoding,
-               size_t nesting)
-{
-       int ret = 0;
-
-       switch (type->type) {
-       case lttng_kernel_type_integer:
-               ret = _lttng_integer_type_statedump(session,
-                               lttng_kernel_get_type_integer(type),
-                               parent_encoding, nesting);
-               break;
-       case lttng_kernel_type_enum:
-               ret = _lttng_enum_type_statedump(session,
-                               lttng_kernel_get_type_enum(type),
-                               nesting);
-               break;
-       case lttng_kernel_type_string:
-               ret = _lttng_string_type_statedump(session,
-                               lttng_kernel_get_type_string(type),
-                               nesting);
-               break;
-       case lttng_kernel_type_struct:
-               ret = _lttng_struct_type_statedump(session,
-                               lttng_kernel_get_type_struct(type),
-                               nesting);
-               break;
-       case lttng_kernel_type_variant:
-               ret = _lttng_variant_type_statedump(session,
-                               lttng_kernel_get_type_variant(type),
-                               nesting, NULL);
-               break;
-
-       /* Nested arrays and sequences are not supported yet. */
-       case lttng_kernel_type_array:
-       case lttng_kernel_type_sequence:
-       default:
-               WARN_ON_ONCE(1);
-               return -EINVAL;
-       }
-       return ret;
-}
-
-/*
- * Must be called with sessions_mutex held.
- */
-static
-int _lttng_field_statedump(struct lttng_kernel_session *session,
-               const struct lttng_kernel_event_field *field,
-               size_t nesting,
-               const char **prev_field_name_p)
-{
-       const char *prev_field_name = NULL;
-       int ret = 0;
-
-       if (prev_field_name_p)
-               prev_field_name = *prev_field_name_p;
-       switch (field->type->type) {
-       case lttng_kernel_type_integer:
-               ret = _lttng_integer_field_statedump(session, field, nesting);
-               break;
-       case lttng_kernel_type_enum:
-               ret = _lttng_enum_field_statedump(session, field, nesting);
-               break;
-       case lttng_kernel_type_string:
-               ret = _lttng_string_field_statedump(session, field, nesting);
-               break;
-       case lttng_kernel_type_struct:
-               ret = _lttng_struct_field_statedump(session, field, nesting);
-               break;
-       case lttng_kernel_type_array:
-               ret = _lttng_array_field_statedump(session, field, nesting);
-               break;
-       case lttng_kernel_type_sequence:
-               ret = _lttng_sequence_field_statedump(session, field, nesting, prev_field_name);
-               break;
-       case lttng_kernel_type_variant:
-               ret = _lttng_variant_field_statedump(session, field, nesting, prev_field_name);
-               break;
-
-       default:
-               WARN_ON_ONCE(1);
-               return -EINVAL;
-       }
-       if (prev_field_name_p)
-               *prev_field_name_p = field->name;
-       return ret;
-}
-
-static
-int _lttng_context_metadata_statedump(struct lttng_kernel_session *session,
-                                   struct lttng_kernel_ctx *ctx)
-{
-       const char *prev_field_name = NULL;
-       int ret = 0;
-       int i;
-
-       if (!ctx)
-               return 0;
-       for (i = 0; i < ctx->nr_fields; i++) {
-               const struct lttng_kernel_ctx_field *field = &ctx->fields[i];
-
-               ret = _lttng_field_statedump(session, field->event_field, 2, &prev_field_name);
-               if (ret)
-                       return ret;
-       }
-       return ret;
-}
-
-static
-int _lttng_fields_metadata_statedump(struct lttng_kernel_session *session,
-                                  struct lttng_kernel_event_recorder *event_recorder)
-{
-       const char *prev_field_name = NULL;
-       const struct lttng_kernel_event_desc *desc = event_recorder->priv->parent.desc;
-       int ret = 0;
-       int i;
-
-       for (i = 0; i < desc->tp_class->nr_fields; i++) {
-               const struct lttng_kernel_event_field *field = desc->tp_class->fields[i];
-
-               ret = _lttng_field_statedump(session, field, 2, &prev_field_name);
-               if (ret)
-                       return ret;
-       }
-       return ret;
-}
-
-/*
- * Must be called with sessions_mutex held.
- * The entire event metadata is printed as a single atomic metadata
- * transaction.
- */
-static
-int _lttng_event_recorder_metadata_statedump(struct lttng_kernel_event_common *event)
-{
-       struct lttng_kernel_event_recorder *event_recorder;
-       struct lttng_kernel_channel_buffer *chan;
-       struct lttng_kernel_session *session;
-       int ret = 0;
-
-       if (event->type != LTTNG_KERNEL_EVENT_TYPE_RECORDER)
-               return 0;
-       event_recorder = container_of(event, struct lttng_kernel_event_recorder, parent);
-       chan = event_recorder->chan;
-       session = chan->parent.session;
-
-       if (event_recorder->priv->metadata_dumped || !LTTNG_READ_ONCE(session->active))
-               return 0;
-       if (chan->priv->channel_type == METADATA_CHANNEL)
-               return 0;
-
-       lttng_metadata_begin(session);
-
-       ret = lttng_metadata_printf(session,
-               "event {\n"
-               "       name = \"%s\";\n"
-               "       id = %u;\n"
-               "       stream_id = %u;\n",
-               event_recorder->priv->parent.desc->event_name,
-               event_recorder->priv->id,
-               event_recorder->chan->priv->id);
-       if (ret)
-               goto end;
-
-       ret = lttng_metadata_printf(session,
-               "       fields := struct {\n"
-               );
-       if (ret)
-               goto end;
-
-       ret = _lttng_fields_metadata_statedump(session, event_recorder);
-       if (ret)
-               goto end;
-
-       /*
-        * LTTng space reservation can only reserve multiples of the
-        * byte size.
-        */
-       ret = lttng_metadata_printf(session,
-               "       };\n"
-               "};\n\n");
-       if (ret)
-               goto end;
-
-       event_recorder->priv->metadata_dumped = 1;
-end:
-       lttng_metadata_end(session);
-       return ret;
-
-}
-
-/*
- * Must be called with sessions_mutex held.
- * The entire channel metadata is printed as a single atomic metadata
- * transaction.
- */
-static
-int _lttng_channel_metadata_statedump(struct lttng_kernel_session *session,
-                                   struct lttng_kernel_channel_buffer *chan)
-{
-       int ret = 0;
-
-       if (chan->priv->metadata_dumped || !LTTNG_READ_ONCE(session->active))
-               return 0;
-
-       if (chan->priv->channel_type == METADATA_CHANNEL)
-               return 0;
-
-       lttng_metadata_begin(session);
-
-       WARN_ON_ONCE(!chan->priv->header_type);
-       ret = lttng_metadata_printf(session,
-               "stream {\n"
-               "       id = %u;\n"
-               "       event.header := %s;\n"
-               "       packet.context := struct packet_context;\n",
-               chan->priv->id,
-               chan->priv->header_type == 1 ? "struct event_header_compact" :
-                       "struct event_header_large");
-       if (ret)
-               goto end;
-
-       if (chan->priv->ctx) {
-               ret = lttng_metadata_printf(session,
-                       "       event.context := struct {\n");
-               if (ret)
-                       goto end;
-       }
-       ret = _lttng_context_metadata_statedump(session, chan->priv->ctx);
-       if (ret)
-               goto end;
-       if (chan->priv->ctx) {
-               ret = lttng_metadata_printf(session,
-                       "       };\n");
-               if (ret)
-                       goto end;
-       }
-
-       ret = lttng_metadata_printf(session,
-               "};\n\n");
-
-       chan->priv->metadata_dumped = 1;
-end:
-       lttng_metadata_end(session);
-       return ret;
-}
-
-/*
- * Must be called with sessions_mutex held.
- */
-static
-int _lttng_stream_packet_context_declare(struct lttng_kernel_session *session)
-{
-       return lttng_metadata_printf(session,
-               "struct packet_context {\n"
-               "       uint64_clock_monotonic_t timestamp_begin;\n"
-               "       uint64_clock_monotonic_t timestamp_end;\n"
-               "       uint64_t content_size;\n"
-               "       uint64_t packet_size;\n"
-               "       uint64_t packet_seq_num;\n"
-               "       unsigned long events_discarded;\n"
-               "       uint32_t cpu_id;\n"
-               "};\n\n"
-               );
-}
-
-/*
- * Compact header:
- * id: range: 0 - 30.
- * id 31 is reserved to indicate an extended header.
- *
- * Large header:
- * id: range: 0 - 65534.
- * id 65535 is reserved to indicate an extended header.
- *
- * Must be called with sessions_mutex held.
- */
-static
-int _lttng_event_header_declare(struct lttng_kernel_session *session)
-{
-       return lttng_metadata_printf(session,
-       "struct event_header_compact {\n"
-       "       enum : uint5_t { compact = 0 ... 30, extended = 31 } id;\n"
-       "       variant <id> {\n"
-       "               struct {\n"
-       "                       uint27_clock_monotonic_t timestamp;\n"
-       "               } compact;\n"
-       "               struct {\n"
-       "                       uint32_t id;\n"
-       "                       uint64_clock_monotonic_t timestamp;\n"
-       "               } extended;\n"
-       "       } v;\n"
-       "} align(%u);\n"
-       "\n"
-       "struct event_header_large {\n"
-       "       enum : uint16_t { compact = 0 ... 65534, extended = 65535 } id;\n"
-       "       variant <id> {\n"
-       "               struct {\n"
-       "                       uint32_clock_monotonic_t timestamp;\n"
-       "               } compact;\n"
-       "               struct {\n"
-       "                       uint32_t id;\n"
-       "                       uint64_clock_monotonic_t timestamp;\n"
-       "               } extended;\n"
-       "       } v;\n"
-       "} align(%u);\n\n",
-       lttng_alignof(uint32_t) * CHAR_BIT,
-       lttng_alignof(uint16_t) * CHAR_BIT
-       );
-}
-
- /*
- * Approximation of NTP time of day to clock monotonic correlation,
- * taken at start of trace.
- * Yes, this is only an approximation. Yes, we can (and will) do better
- * in future versions.
- * This function may return a negative offset. It may happen if the
- * system sets the REALTIME clock to 0 after boot.
- *
- * Use 64bit timespec on kernels that have it, this makes 32bit arch
- * y2038 compliant.
- */
-static
-int64_t measure_clock_offset(void)
-{
-       uint64_t monotonic_avg, monotonic[2], realtime;
-       uint64_t tcf = trace_clock_freq();
-       int64_t offset;
-       unsigned long flags;
-#ifdef LTTNG_KERNEL_HAS_TIMESPEC64
-       struct timespec64 rts = { 0, 0 };
-#else
-       struct timespec rts = { 0, 0 };
-#endif
-
-       /* Disable interrupts to increase correlation precision. */
-       local_irq_save(flags);
-       monotonic[0] = trace_clock_read64();
-#ifdef LTTNG_KERNEL_HAS_TIMESPEC64
-       ktime_get_real_ts64(&rts);
-#else
-       getnstimeofday(&rts);
-#endif
-       monotonic[1] = trace_clock_read64();
-       local_irq_restore(flags);
-
-       monotonic_avg = (monotonic[0] + monotonic[1]) >> 1;
-       realtime = (uint64_t) rts.tv_sec * tcf;
-       if (tcf == NSEC_PER_SEC) {
-               realtime += rts.tv_nsec;
-       } else {
-               uint64_t n = rts.tv_nsec * tcf;
-
-               do_div(n, NSEC_PER_SEC);
-               realtime += n;
-       }
-       offset = (int64_t) realtime - monotonic_avg;
-       return offset;
-}
-
-static
-int print_escaped_ctf_string(struct lttng_kernel_session *session, const char *string)
-{
-       int ret = 0;
-       size_t i;
-       char cur;
-
-       i = 0;
-       cur = string[i];
-       while (cur != '\0') {
-               switch (cur) {
-               case '\n':
-                       ret = lttng_metadata_printf(session, "%s", "\\n");
-                       break;
-               case '\\':
-               case '"':
-                       ret = lttng_metadata_printf(session, "%c", '\\');
-                       if (ret)
-                               goto error;
-                       /* We still print the current char */
-                       lttng_fallthrough;
-               default:
-                       ret = lttng_metadata_printf(session, "%c", cur);
-                       break;
-               }
-
-               if (ret)
-                       goto error;
-
-               cur = string[++i];
-       }
-error:
-       return ret;
-}
-
-static
-int print_metadata_escaped_field(struct lttng_kernel_session *session, const char *field,
-               const char *field_value)
-{
-       int ret;
-
-       ret = lttng_metadata_printf(session, "  %s = \"", field);
-       if (ret)
-               goto error;
-
-       ret = print_escaped_ctf_string(session, field_value);
-       if (ret)
-               goto error;
-
-       ret = lttng_metadata_printf(session, "\";\n");
-
-error:
-       return ret;
-}
-
-/*
- * Output metadata into this session's metadata buffers.
- * Must be called with sessions_mutex held.
- */
-static
-int _lttng_session_metadata_statedump(struct lttng_kernel_session *session)
-{
-       unsigned char *uuid_c = session->priv->uuid.b;
-       unsigned char uuid_s[37], clock_uuid_s[BOOT_ID_LEN];
-       const char *product_uuid;
-       struct lttng_kernel_channel_buffer_private *chan_priv;
-       struct lttng_kernel_event_recorder_private *event_recorder_priv;
-       int ret = 0;
-
-       if (!LTTNG_READ_ONCE(session->active))
-               return 0;
-
-       lttng_metadata_begin(session);
-
-       if (session->priv->metadata_dumped)
-               goto skip_session;
-
-       snprintf(uuid_s, sizeof(uuid_s),
-               "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
-               uuid_c[0], uuid_c[1], uuid_c[2], uuid_c[3],
-               uuid_c[4], uuid_c[5], uuid_c[6], uuid_c[7],
-               uuid_c[8], uuid_c[9], uuid_c[10], uuid_c[11],
-               uuid_c[12], uuid_c[13], uuid_c[14], uuid_c[15]);
-
-       ret = lttng_metadata_printf(session,
-               "typealias integer { size = 8; align = %u; signed = false; } := uint8_t;\n"
-               "typealias integer { size = 16; align = %u; signed = false; } := uint16_t;\n"
-               "typealias integer { size = 32; align = %u; signed = false; } := uint32_t;\n"
-               "typealias integer { size = 64; align = %u; signed = false; } := uint64_t;\n"
-               "typealias integer { size = %u; align = %u; signed = false; } := unsigned long;\n"
-               "typealias integer { size = 5; align = 1; signed = false; } := uint5_t;\n"
-               "typealias integer { size = 27; align = 1; signed = false; } := uint27_t;\n"
-               "\n"
-               "trace {\n"
-               "       major = %u;\n"
-               "       minor = %u;\n"
-               "       uuid = \"%s\";\n"
-               "       byte_order = %s;\n"
-               "       packet.header := struct {\n"
-               "               uint32_t magic;\n"
-               "               uint8_t  uuid[16];\n"
-               "               uint32_t stream_id;\n"
-               "               uint64_t stream_instance_id;\n"
-               "       };\n"
-               "};\n\n",
-               lttng_alignof(uint8_t) * CHAR_BIT,
-               lttng_alignof(uint16_t) * CHAR_BIT,
-               lttng_alignof(uint32_t) * CHAR_BIT,
-               lttng_alignof(uint64_t) * CHAR_BIT,
-               sizeof(unsigned long) * CHAR_BIT,
-               lttng_alignof(unsigned long) * CHAR_BIT,
-               CTF_SPEC_MAJOR,
-               CTF_SPEC_MINOR,
-               uuid_s,
-#if __BYTE_ORDER == __BIG_ENDIAN
-               "be"
-#else
-               "le"
-#endif
-               );
-       if (ret)
-               goto end;
-
-       ret = lttng_metadata_printf(session,
-               "env {\n"
-               "       hostname = \"%s\";\n"
-               "       domain = \"kernel\";\n"
-               "       sysname = \"%s\";\n"
-               "       kernel_release = \"%s\";\n"
-               "       kernel_version = \"%s\";\n"
-               "       tracer_name = \"lttng-modules\";\n"
-               "       tracer_major = %d;\n"
-               "       tracer_minor = %d;\n"
-               "       tracer_patchlevel = %d;\n"
-               "       trace_buffering_scheme = \"global\";\n",
-               current->nsproxy->uts_ns->name.nodename,
-               utsname()->sysname,
-               utsname()->release,
-               utsname()->version,
-               LTTNG_MODULES_MAJOR_VERSION,
-               LTTNG_MODULES_MINOR_VERSION,
-               LTTNG_MODULES_PATCHLEVEL_VERSION
-               );
-       if (ret)
-               goto end;
-
-       ret = print_metadata_escaped_field(session, "trace_name", session->priv->name);
-       if (ret)
-               goto end;
-       ret = print_metadata_escaped_field(session, "trace_creation_datetime",
-                       session->priv->creation_time);
-       if (ret)
-               goto end;
-
-       /* Add the product UUID to the 'env' section */
-       product_uuid = dmi_get_system_info(DMI_PRODUCT_UUID);
-       if (product_uuid) {
-               ret = lttng_metadata_printf(session,
-                               "       product_uuid = \"%s\";\n",
-                               product_uuid
-                               );
-               if (ret)
-                       goto end;
-       }
-
-       /* Close the 'env' section */
-       ret = lttng_metadata_printf(session, "};\n\n");
-       if (ret)
-               goto end;
-
-       ret = lttng_metadata_printf(session,
-               "clock {\n"
-               "       name = \"%s\";\n",
-               trace_clock_name()
-               );
-       if (ret)
-               goto end;
-
-       if (!trace_clock_uuid(clock_uuid_s)) {
-               ret = lttng_metadata_printf(session,
-                       "       uuid = \"%s\";\n",
-                       clock_uuid_s
-                       );
-               if (ret)
-                       goto end;
-       }
-
-       ret = lttng_metadata_printf(session,
-               "       description = \"%s\";\n"
-               "       freq = %llu; /* Frequency, in Hz */\n"
-               "       /* clock value offset from Epoch is: offset * (1/freq) */\n"
-               "       offset = %lld;\n"
-               "};\n\n",
-               trace_clock_description(),
-               (unsigned long long) trace_clock_freq(),
-               (long long) measure_clock_offset()
-               );
-       if (ret)
-               goto end;
-
-       ret = lttng_metadata_printf(session,
-               "typealias integer {\n"
-               "       size = 27; align = 1; signed = false;\n"
-               "       map = clock.%s.value;\n"
-               "} := uint27_clock_monotonic_t;\n"
-               "\n"
-               "typealias integer {\n"
-               "       size = 32; align = %u; signed = false;\n"
-               "       map = clock.%s.value;\n"
-               "} := uint32_clock_monotonic_t;\n"
-               "\n"
-               "typealias integer {\n"
-               "       size = 64; align = %u; signed = false;\n"
-               "       map = clock.%s.value;\n"
-               "} := uint64_clock_monotonic_t;\n\n",
-               trace_clock_name(),
-               lttng_alignof(uint32_t) * CHAR_BIT,
-               trace_clock_name(),
-               lttng_alignof(uint64_t) * CHAR_BIT,
-               trace_clock_name()
-               );
-       if (ret)
-               goto end;
-
-       ret = _lttng_stream_packet_context_declare(session);
-       if (ret)
-               goto end;
-
-       ret = _lttng_event_header_declare(session);
-       if (ret)
-               goto end;
-
-skip_session:
-       list_for_each_entry(chan_priv, &session->priv->chan, node) {
-               ret = _lttng_channel_metadata_statedump(session, chan_priv->pub);
-               if (ret)
-                       goto end;
-       }
-
-       list_for_each_entry(event_recorder_priv, &session->priv->events, parent.node) {
-               ret = _lttng_event_recorder_metadata_statedump(&event_recorder_priv->pub->parent);
-               if (ret)
-                       goto end;
-       }
-       session->priv->metadata_dumped = 1;
-end:
-       lttng_metadata_end(session);
-       return ret;
-}
-
 /**
  * lttng_transport_register - LTT transport registration
  * @transport: transport structure
diff --git a/src/lttng-uuid.c b/src/lttng-uuid.c
new file mode 100644 (file)
index 0000000..151b2fe
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ * Copyright (C) 2019 Michael Jeanson <mjeanson@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <linux/module.h>
+#include <lttng/uuid.h>
+
+int lttng_uuid_from_str(const char *str_in, lttng_uuid uuid_out)
+{
+       int ret = 0;
+       lttng_uuid uuid_scan;
+
+       if ((str_in == NULL) || (uuid_out == NULL)) {
+               ret = -1;
+               goto end;
+       }
+
+       if (strnlen(str_in, LTTNG_UUID_STR_LEN) != LTTNG_UUID_STR_LEN - 1) {
+               ret = -1;
+               goto end;
+       }
+
+       /* Scan to a temporary location in case of a partial match. */
+       if (sscanf(str_in, LTTNG_UUID_FMT, LTTNG_UUID_SCAN_VALUES(uuid_scan)) !=
+                       LTTNG_UUID_LEN) {
+               ret = -1;
+       }
+
+       memcpy(uuid_out, uuid_scan, LTTNG_UUID_LEN);
+end:
+       return ret;
+}
diff --git a/src/metadata-printer.c b/src/metadata-printer.c
new file mode 100644 (file)
index 0000000..e2b5847
--- /dev/null
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: (GPL-2.0-only or LGPL-2.1-only)
+ *
+ * src/metadata-printer.h
+ *
+ * Copyright (C) 2010-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <lttng/events-internal.h>
+#include <lttng/events.h>
+#include <lttng/string-utils.h>
+
+#include <wrapper/compiler_attributes.h>
+#include <wrapper/uuid.h>
+
+void lttng_metadata_begin(struct lttng_kernel_session *session)
+{
+    if (atomic_inc_return(&session->priv->metadata_cache->producing) == 1)
+        mutex_lock(&session->priv->metadata_cache->lock);
+}
+
+void lttng_metadata_end(struct lttng_kernel_session *session)
+{
+    WARN_ON_ONCE(!atomic_read(&session->priv->metadata_cache->producing));
+    if (atomic_dec_return(&session->priv->metadata_cache->producing) == 0) {
+        struct lttng_metadata_stream *stream;
+
+        list_for_each_entry(stream, &session->priv->metadata_cache->metadata_stream, list)
+            wake_up_interruptible(&stream->read_wait);
+        mutex_unlock(&session->priv->metadata_cache->lock);
+    }
+}
+
+int lttng_metadata_printf(struct lttng_kernel_session *session,
+                         const char *fmt, ...)
+{
+       char *str;
+       size_t len;
+       va_list ap;
+
+       WARN_ON_ONCE(!LTTNG_READ_ONCE(session->active));
+
+       va_start(ap, fmt);
+       str = kvasprintf(GFP_KERNEL, fmt, ap);
+       va_end(ap);
+       if (!str)
+               return -ENOMEM;
+
+       len = strlen(str);
+       WARN_ON_ONCE(!atomic_read(&session->priv->metadata_cache->producing));
+       if (session->priv->metadata_cache->metadata_written + len >
+                       session->priv->metadata_cache->cache_alloc) {
+               char *tmp_cache_realloc;
+               unsigned int tmp_cache_alloc_size;
+
+               tmp_cache_alloc_size = max_t(unsigned int,
+                               session->priv->metadata_cache->cache_alloc + len,
+                               session->priv->metadata_cache->cache_alloc << 1);
+               tmp_cache_realloc = vzalloc(tmp_cache_alloc_size);
+               if (!tmp_cache_realloc)
+                       goto err;
+               if (session->priv->metadata_cache->data) {
+                       memcpy(tmp_cache_realloc,
+                               session->priv->metadata_cache->data,
+                               session->priv->metadata_cache->cache_alloc);
+                       vfree(session->priv->metadata_cache->data);
+               }
+
+               session->priv->metadata_cache->cache_alloc = tmp_cache_alloc_size;
+               session->priv->metadata_cache->data = tmp_cache_realloc;
+       }
+       memcpy(session->priv->metadata_cache->data +
+                       session->priv->metadata_cache->metadata_written,
+                       str, len);
+       session->priv->metadata_cache->metadata_written += len;
+       kfree(str);
+
+       return 0;
+
+err:
+       kfree(str);
+       return -ENOMEM;
+}
diff --git a/src/metadata-printer.h b/src/metadata-printer.h
new file mode 100644 (file)
index 0000000..b8cfd35
--- /dev/null
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: (GPL-2.0-only or LGPL-2.1-only)
+ *
+ * src/metadata-printer.h
+ *
+ * Copyright (C) 2010-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ */
+
+#ifndef _LTTNG_METADATA_PRINTER_H
+#define _LTTNG_METADATA_PRINTER_H
+
+#include <lttng/events.h>
+
+void lttng_metadata_begin(struct lttng_kernel_session *session);
+void lttng_metadata_end(struct lttng_kernel_session *session);
+
+/*
+ * Write the metadata to the metadata cache.
+ * Must be called with sessions_mutex held.
+ * The metadata cache lock protects us from concurrent read access from
+ * thread outputting metadata content to ring buffer.
+ * The content of the printf is printed as a single atomic metadata
+ * transaction.
+ */
+int lttng_metadata_printf(struct lttng_kernel_session *session,
+                         const char *fmt, ...);
+#endif /* _LTTNG_METADATA_PRINTER_H */
This page took 0.084952 seconds and 5 git commands to generate.