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