+ }
+ stream->transport->ops.event_write(&ctx,
+ stream->metadata_cache->data + stream->metadata_in,
+ reserve_len);
+ stream->transport->ops.event_commit(&ctx);
+ stream->metadata_in += reserve_len;
+ ret = reserve_len;
+
+end:
+ mutex_unlock(&stream->metadata_cache->lock);
+ return ret;
+}
+
+/*
+ * 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.
+ */
+int lttng_metadata_printf(struct lttng_session *session,
+ const char *fmt, ...)
+{
+ char *str;
+ size_t len;
+ va_list ap;
+ struct lttng_metadata_stream *stream;
+
+ WARN_ON_ONCE(!READ_ONCE(session->active));
+
+ va_start(ap, fmt);
+ str = kvasprintf(GFP_KERNEL, fmt, ap);
+ va_end(ap);
+ if (!str)
+ return -ENOMEM;
+
+ len = strlen(str);
+ mutex_lock(&session->metadata_cache->lock);
+ if (session->metadata_cache->metadata_written + len >
+ session->metadata_cache->cache_alloc) {
+ char *tmp_cache_realloc;
+ unsigned int tmp_cache_alloc_size;
+
+ tmp_cache_alloc_size = max_t(unsigned int,
+ session->metadata_cache->cache_alloc + len,
+ session->metadata_cache->cache_alloc << 1);
+ tmp_cache_realloc = vzalloc(tmp_cache_alloc_size);
+ if (!tmp_cache_realloc)
+ goto err;
+ if (session->metadata_cache->data) {
+ memcpy(tmp_cache_realloc,
+ session->metadata_cache->data,
+ session->metadata_cache->cache_alloc);
+ vfree(session->metadata_cache->data);
+ }
+
+ session->metadata_cache->cache_alloc = tmp_cache_alloc_size;
+ session->metadata_cache->data = tmp_cache_realloc;
+ }
+ memcpy(session->metadata_cache->data +
+ session->metadata_cache->metadata_written,
+ str, len);
+ session->metadata_cache->metadata_written += len;
+ mutex_unlock(&session->metadata_cache->lock);
+ kfree(str);
+
+ list_for_each_entry(stream, &session->metadata_cache->metadata_stream, list)
+ wake_up_interruptible(&stream->read_wait);
+
+ return 0;
+
+err:
+ mutex_unlock(&session->metadata_cache->lock);
+ kfree(str);
+ return -ENOMEM;
+}
+
+static
+int print_tabs(struct lttng_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_session *session,
+ const struct lttng_event_field *field,
+ size_t nesting)
+{
+ return lttng_metadata_printf(session, " _%s;\n", field->name);
+}
+
+static
+int _lttng_integer_type_statedump(struct lttng_session *session,
+ const struct lttng_type *type,
+ size_t nesting)
+{
+ int ret;
+
+ WARN_ON_ONCE(type->atype != atype_integer);
+ 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->u.integer.size,
+ type->u.integer.alignment,
+ type->u.integer.signedness,
+ (type->u.integer.encoding == lttng_encode_none)
+ ? "none"
+ : (type->u.integer.encoding == lttng_encode_UTF8)
+ ? "UTF8"
+ : "ASCII",
+ type->u.integer.base,
+#if __BYTE_ORDER == __BIG_ENDIAN
+ type->u.integer.reverse_byte_order ? " byte_order = le;" : ""
+#else
+ type->u.integer.reverse_byte_order ? " byte_order = be;" : ""
+#endif
+ );
+ return ret;
+}
+
+/*
+ * Must be called with sessions_mutex held.
+ */
+static
+int _lttng_struct_type_statedump(struct lttng_session *session,
+ const struct lttng_type *type,
+ size_t nesting)
+{
+ int ret;
+ uint32_t i, nr_fields;
+ unsigned int alignment;
+
+ WARN_ON_ONCE(type->atype != atype_struct_nestable);
+
+ ret = print_tabs(session, nesting);
+ if (ret)
+ return ret;
+ ret = lttng_metadata_printf(session,
+ "struct {\n");
+ if (ret)
+ return ret;
+ nr_fields = type->u.struct_nestable.nr_fields;
+ for (i = 0; i < nr_fields; i++) {
+ const struct lttng_event_field *iter_field;
+
+ iter_field = &type->u.struct_nestable.fields[i];
+ ret = _lttng_field_statedump(session, iter_field, nesting + 1);
+ if (ret)
+ return ret;
+ }
+ ret = print_tabs(session, nesting);
+ if (ret)
+ return ret;
+ alignment = type->u.struct_nestable.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_session *session,
+ const struct lttng_event_field *field,
+ size_t nesting)
+{
+ int ret;
+
+ ret = _lttng_struct_type_statedump(session,
+ &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_session *session,
+ const struct lttng_type *type,
+ size_t nesting)
+{
+ int ret;
+ uint32_t i, nr_choices;
+
+ WARN_ON_ONCE(type->atype != atype_variant_nestable);
+ /*
+ * CTF 1.8 does not allow expressing nonzero variant alignment in a nestable way.
+ */
+ if (type->u.variant_nestable.alignment != 0)
+ return -EINVAL;
+ ret = print_tabs(session, nesting);
+ if (ret)
+ return ret;
+ ret = lttng_metadata_printf(session,
+ "variant <_%s> {\n",
+ type->u.variant_nestable.tag_name);
+ if (ret)
+ return ret;
+ nr_choices = type->u.variant_nestable.nr_choices;
+ for (i = 0; i < nr_choices; i++) {
+ const struct lttng_event_field *iter_field;
+
+ iter_field = &type->u.variant_nestable.choices[i];
+ ret = _lttng_field_statedump(session, iter_field, nesting + 1);
+ 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_session *session,
+ const struct lttng_event_field *field,
+ size_t nesting)
+{
+ int ret;
+
+ ret = _lttng_variant_type_statedump(session,
+ &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_array_field_statedump(struct lttng_session *session,
+ const struct lttng_event_field *field,
+ size_t nesting)
+{
+ int ret;
+ const struct lttng_type *elem_type;
+
+ WARN_ON_ONCE(field->type.atype != atype_array_nestable);
+
+ if (field->type.u.array_nestable.alignment) {
+ ret = print_tabs(session, nesting);
+ if (ret)
+ return ret;
+ ret = lttng_metadata_printf(session,
+ "struct { } align(%u) _%s_padding;\n",
+ field->type.u.array_nestable.alignment * CHAR_BIT,
+ field->name);
+ if (ret)
+ return ret;
+ }
+ /*
+ * Nested compound types: Only array of structures and variants are
+ * currently supported.
+ */
+ elem_type = field->type.u.array_nestable.elem_type;
+ switch (elem_type->atype) {
+ case atype_integer:
+ case atype_struct_nestable:
+ case atype_variant_nestable:
+ ret = _lttng_type_statedump(session, elem_type, nesting);
+ if (ret)
+ return ret;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ ret = lttng_metadata_printf(session,
+ " _%s[%u];\n",
+ field->name,
+ field->type.u.array_nestable.length);
+ return ret;
+}
+
+/*
+ * Must be called with sessions_mutex held.
+ */
+static
+int _lttng_sequence_field_statedump(struct lttng_session *session,
+ const struct lttng_event_field *field,
+ size_t nesting)
+{
+ int ret;
+ const char *length_name;
+ const struct lttng_type *elem_type;
+
+ WARN_ON_ONCE(field->type.atype != atype_sequence_nestable);
+
+ length_name = field->type.u.sequence_nestable.length_name;
+
+ if (field->type.u.sequence_nestable.alignment) {
+ ret = print_tabs(session, nesting);
+ if (ret)
+ return ret;
+ ret = lttng_metadata_printf(session,
+ "struct { } align(%u) _%s_padding;\n",
+ field->type.u.sequence_nestable.alignment * CHAR_BIT,
+ field->name);
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * Nested compound types: Only array of structures and variants are
+ * currently supported.
+ */
+ elem_type = field->type.u.sequence_nestable.elem_type;
+ switch (elem_type->atype) {
+ case atype_integer:
+ case atype_struct_nestable:
+ case atype_variant_nestable:
+ ret = _lttng_type_statedump(session, elem_type, nesting);
+ if (ret)
+ return ret;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ ret = lttng_metadata_printf(session,
+ " _%s[ _%s ];\n",
+ field->name,
+ field->type.u.sequence_nestable.length_name);
+ return ret;
+}
+
+/*
+ * Must be called with sessions_mutex held.
+ */
+static
+int _lttng_enum_type_statedump(struct lttng_session *session,
+ const struct lttng_type *type,
+ size_t nesting)
+{
+ const struct lttng_enum_desc *enum_desc;
+ const struct lttng_type *container_type;
+ int ret;
+ unsigned int i, nr_entries;
+
+ container_type = type->u.enum_nestable.container_type;
+ if (container_type->atype != atype_integer) {
+ ret = -EINVAL;
+ goto end;
+ }
+ enum_desc = type->u.enum_nestable.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, container_type, 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_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;