Add CTF2 metadata generation
authorFrancis Deslauriers <francis.deslauriers@efficios.com>
Thu, 9 Dec 2021 16:53:54 +0000 (11:53 -0500)
committerFrancis Deslauriers <francis.deslauriers@efficios.com>
Fri, 15 Jul 2022 18:08:38 +0000 (14:08 -0400)
Signed-off-by: Francis Deslauriers <francis.deslauriers@efficios.com>
Change-Id: If51d9d760a01c0411e8010fd7d721a1336a06edd

src/Kbuild
src/ctf2.c [new file with mode: 0644]
src/ctf2.h [new file with mode: 0644]

index b7292f9d84da3f72fa526475832e3eaeecf15f40..c309d8ef799e1c59f6b79da192498c3042c68afa 100644 (file)
@@ -51,6 +51,7 @@ lttng-tracer-objs := lib/msgpack/msgpack.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 \
diff --git a/src/ctf2.c b/src/ctf2.c
new file mode 100644 (file)
index 0000000..9cd09c8
--- /dev/null
@@ -0,0 +1,1717 @@
+/* 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_uuid(struct lttng_kernel_session *session,
+                                   unsigned char *uuid)
+{
+       unsigned int i, uuid_len = 16;
+       int ret;
+
+       ret = lttng_metadata_printf(session, "[");
+       if (ret)
+               goto end;
+
+       for (i = 0; i < uuid_len; i++) {
+               ret = lttng_metadata_printf(session, "%hhu", 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_preamble_fragment(struct lttng_kernel_session *session)
+{
+       unsigned char *session_uuid = session->priv->uuid.b;
+       int ret;
+
+       ret = ctf2_metadata_write_record_separator(session);
+       if (ret)
+               goto error;
+
+       /* Write the preamble */
+       ret = lttng_metadata_printf(session, "{\n\"type\":\"preamble\",\n"
+                                            "\"version\":2,\n"
+                                            "\"uuid\":");
+       if (ret)
+               goto error;
+
+       ret = ctf2_metadata_write_uuid(session, session_uuid);
+       if (ret) {
+               printk(KERN_WARNING
+                      "LTTng: CTF2: error producing metadata trace class UUID\n");
+               goto error;
+       }
+
+       ret = lttng_metadata_printf(session, "}\n");
+error:
+       return ret;
+}
+
+static int
+ctf2_metadata_write_clock_class_fragment(struct lttng_kernel_session *session)
+{
+       unsigned char clock_uuid_str[37] = { 0 };
+       const char *clock_name, *clock_desc;
+       long long offset, offset_seconds;
+       unsigned long long freq;
+       int offset_cycles, ret;
+
+       ret = ctf2_metadata_write_record_separator(session);
+       if (ret)
+               goto end;
+
+       clock_name = trace_clock_name();
+       clock_desc = trace_clock_description();
+       freq = trace_clock_freq();
+       offset = trace_clock_measure_offset();
+       offset_seconds = div_s64_rem(offset, freq, &offset_cycles);
+
+       ret = lttng_metadata_printf(
+               session,
+               "{\n"
+               "\"type\":\"clock-class\",\n"
+               "\"name\":\"%s\",\n"
+               "\"description\" : \"%s\",\n"
+               "\"frequency\" : %llu,\n"
+               "\"offset\" : { \"seconds\": %llu, \"cycles\":%lu}\n",
+               clock_name, clock_desc, freq, offset_seconds, offset_cycles);
+       if (ret)
+               goto end;
+
+       if (!trace_clock_uuid(clock_uuid_str)) {
+               lttng_uuid clock_uuid;
+
+               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\": [\"metadata-stream-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_environment(struct lttng_kernel_session *session)
+{
+       int ret;
+
+       ret = lttng_metadata_printf(
+               session,
+               "{\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"
+               "}\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);
+
+       return ret;
+}
+
+static int
+ctf2_metadata_write_trace_class_fragment(struct lttng_kernel_session *session)
+{
+       int ret;
+
+       ret = ctf2_metadata_write_record_separator(session);
+       if (ret)
+               goto end;
+
+       ret = lttng_metadata_printf(session, "{\n\"type\":\"trace-class\"");
+       if (ret)
+               goto end;
+
+       ret = lttng_metadata_printf(session, ",\n\"environment\":");
+       if (ret)
+               goto end;
+
+       ret = ctf2_metadata_write_environment(session);
+       if (ret)
+               goto end;
+
+       ret = lttng_metadata_printf(session, ",\n\"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\": [\"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-length\"]"
+               "      }"
+               "    },"
+               "    {"
+               "      \"name\": \"packet_size\","
+               "      \"field-class\": {"
+               "        \"type\": \"fixed-length-unsigned-integer\","
+               "        \"alignment\": %u,"
+               "        \"length\": 64,"
+               "        \"byte-order\": " NATIVE_BYTEORDER ","
+               "        \"roles\": [\"packet-total-length\"]"
+               "      }"
+               "    },"
+               "    {"
+               "      \"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, "{\n"
+                                            "\"type\": \"structure\",\n"
+                                            "\"member-classes\": [");
+       if (ret)
+               goto end;
+
+       field_location_ctx = field_location_ctx_create();
+       if (!field_location_ctx) {
+               ret = -ENOMEM;
+               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, "]\n}\n");
+
+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,
+                                   "{"
+                                   "\n\"type\": \"%s\","
+                                   "\n\"length\": %u,"
+                                   "\n\"byte-order\": %s",
+                                   enum_type_name, type->size, byte_order);
+       if (ret)
+               goto end;
+
+       if (type->alignment != 1) {
+               ret = lttng_metadata_printf(session, ",\n\"alignment\": %u",
+                                           type->alignment);
+               if (ret)
+                       goto end;
+       }
+
+       if (type->base != 10) {
+               ret = lttng_metadata_printf(session,
+                                           ",\n\"preferred-display-base\": %u",
+                                           type->base);
+               if (ret)
+                       goto end;
+       }
+
+       ret = lttng_metadata_printf(session, "\n}");
+       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_mapping_ranges(struct lttng_kernel_session *session,
+                                       struct ctf2_enum_mapping *mapping)
+{
+       unsigned int range_idx;
+       struct ctf2_enum_mapping_range *mapping_range;
+       int ret;
+
+       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++;
+       }
+
+end:
+       return ret;
+}
+
+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 *ctf2_enum;
+       struct ctf2_enum_mapping *mapping;
+       unsigned int mapping_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;
+
+               ret = ctf2_metadata_write_enum_mapping_ranges(session, mapping);
+               if (ret)
+                       goto end;
+
+               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 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) {
+               unsigned int i;
+
+               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;
+                       }
+               }
+
+               /*
+                * We are done visiting the struct field class. Remove all the
+                * concrete fields of that scope.
+                */
+               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)
+{
+       int ret;
+       const struct lttng_kernel_type_common *elem_type = type->elem_type;
+
+       /*
+        * If it's an array of integer encoded as characters, we translate it
+        * as a static-length-string.
+        */
+       if (elem_type->type == lttng_kernel_type_integer &&
+           (type->encoding == lttng_kernel_string_encoding_ASCII ||
+            type->encoding == lttng_kernel_string_encoding_UTF8)) {
+               ret = lttng_metadata_printf(
+                       session,
+                       "{"
+                       "  \"type\": \"static-length-string\","
+                       "  \"length\": %u }",
+                       type->length);
+       } else {
+               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;
+
+               /*
+                * Nested compound types: Only array of integers, structures,
+                * and variants are currently supported.
+                */
+               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,
+                                   "{\n"
+                                   "  \"type\": \"dynamic-length-array\",\n"
+                                   "  \"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,
+                                           ",\n\"minimum-alignment\": %u",
+                                           type->alignment * CHAR_BIT);
+               if (ret)
+                       goto end;
+       }
+
+       ret = lttng_metadata_printf(session, ",\n\"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, "}\n");
+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, "{"
+                                            "\n\"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, ",\n\"options\": [");
+       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 *var_opt;
+               struct ctf2_enum_mapping *mapping;
+
+               var_opt = 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(var_opt->name, &mapping->name[1]) != 0) {
+                               continue;
+                       }
+
+                       ret = lttng_metadata_printf(
+                               session,
+                               "{"
+                               "\n\"name\": \"_%s\","
+                               "\n\"selector-field-ranges\": [",
+                               var_opt->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;
+
+                       ret = ctf2_metadata_write_field_class(
+                               session, var_opt->type, field_location_ctx);
+                       if (ret)
+                               goto end;
+
+                       ret = lttng_metadata_printf(session, "}");
+                       if (ret)
+                               goto end;
+
+                       /* Was it the last one ? */
+                       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,
+                                   "{"
+                                   "\n\"name\": \"%s\","
+                                   "\n\"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, "\n}\n");
+
+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, "{"
+                                            "\n\"type\": \"structure\","
+                                            "\n\"member-classes\": [");
+       if (ret)
+               goto end;
+
+       field_location_ctx = field_location_ctx_create();
+       if (!field_location_ctx) {
+               ret = -ENOMEM;
+               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-payload", NULL);
+       if (ret)
+               goto destroy_field_loc;
+
+       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 destroy_field_loc;
+
+               ret = field_location_stack_push(field_location_ctx,
+                                               SCOPE_STACK_NODE_TYPE_FIELD,
+                                               field->name, field);
+               if (ret)
+                       goto destroy_field_loc;
+
+               if (i < desc->tp_class->nr_fields - 1) {
+                       ret = lttng_metadata_printf(session, ", ");
+                       if (ret)
+                               goto destroy_field_loc;
+               }
+       }
+
+       field_location_stack_pop_n_elem(field_location_ctx,
+                                       desc->tp_class->nr_fields + 1);
+
+       ret = lttng_metadata_printf(session, "]\n}\n");
+
+destroy_field_loc:
+       field_location_ctx_destroy(field_location_ctx);
+end:
+       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,
+               "{"
+               "\n\"type\": \"event-record-class\","
+               "\n\"id\": %u,"
+               "\n\"name\": \"%s\","
+               "\n\"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,
+                                           ",\n\"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}\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,
+                                   "{"
+                                   "\n\"type\":\"data-stream-class\","
+                                   "\n\"id\": %u,"
+                                   "\n\"default-clock-class-name\": \"%s\","
+                                   "\n\"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,
+                                   ",\n\"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,
+                       ",\n\"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}\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_trace_class_fragment(session);
+       if (ret) {
+               printk(KERN_WARNING
+                      "LTTng: CTF2: error producing metadata trace class 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;
+       }
+
+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_ */
This page took 0.065343 seconds and 5 git commands to generate.