--- /dev/null
+/* 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;
+}