ctfser->offset_in_cur_packet_bits = offset_bits;
}
+static inline
+const char *bt_ctfser_get_file_path(struct bt_ctfser *ctfser)
+{
+ return ctfser->path->str;
+}
+
#endif /* BABELTRACE_CTFSER_INTERNAL_H */
-SUBDIRS = common fs-src
-# fs-sink lttng-live
+SUBDIRS = common fs-src fs-sink
+# lttng-live
noinst_HEADERS = print.h
-avoid-version -module
babeltrace_plugin_ctf_la_LIBADD = \
- fs-src/libbabeltrace-plugin-ctf-fs.la \
- common/libbabeltrace-plugin-ctf-common.la
-
-# lttng-live/libbabeltrace-plugin-ctf-lttng-live.la \
-# fs-sink/libbabeltrace-plugin-ctf-writer.la
+ fs-src/libbabeltrace-plugin-ctf-fs-src.la \
+ common/libbabeltrace-plugin-ctf-common.la \
+ fs-sink/libbabeltrace-plugin-ctf-fs-sink.la
if !ENABLE_BUILT_IN_PLUGINS
babeltrace_plugin_ctf_la_LIBADD += \
$(top_builddir)/lib/libbabeltrace.la \
$(top_builddir)/logging/libbabeltrace-logging.la \
- $(top_builddir)/common/libbabeltrace-common.la
+ $(top_builddir)/common/libbabeltrace-common.la \
+ $(top_builddir)/ctfser/libbabeltrace-ctfser.la
endif
-AM_CPPFLAGS += -I$(top_srcdir)/plugins \
- -I$(top_srcdir)/plugins/libctfcopytrace
+noinst_LTLIBRARIES = libbabeltrace-plugin-ctf-fs-sink.la
-noinst_LTLIBRARIES = libbabeltrace-plugin-ctf-writer.la
-
-libbabeltrace_plugin_ctf_writer_la_LIBADD =
-libbabeltrace_plugin_ctf_writer_la_SOURCES = writer.c writer.h write.c \
- logging.c logging.h
-
-if !ENABLE_BUILT_IN_PLUGINS
-libbabeltrace_plugin_ctf_writer_la_LIBADD += \
- $(top_builddir)/plugins/libctfcopytrace/libctfcopytrace.la
-endif
+libbabeltrace_plugin_ctf_fs_sink_la_LIBADD =
+libbabeltrace_plugin_ctf_fs_sink_la_SOURCES = \
+ fs-sink.c \
+ fs-sink.h \
+ logging.c \
+ logging.h \
+ fs-sink-ctf-meta.h \
+ translate-trace-ir-to-ctf-ir.c \
+ translate-ctf-ir-to-tsdl.c \
+ fs-sink-stream.c \
+ fs-sink-trace.c
--- /dev/null
+#ifndef BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_CTF_META_H
+#define BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_CTF_META_H
+
+/*
+ * Copyright 2018-2019 - Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ */
+
+#include <babeltrace/babeltrace.h>
+#include <babeltrace/common-internal.h>
+#include <babeltrace/assert-internal.h>
+#include <babeltrace/compat/uuid-internal.h>
+#include <glib.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdbool.h>
+#include <ctype.h>
+
+enum fs_sink_ctf_field_class_type {
+ FS_SINK_CTF_FIELD_CLASS_TYPE_INT,
+ FS_SINK_CTF_FIELD_CLASS_TYPE_FLOAT,
+ FS_SINK_CTF_FIELD_CLASS_TYPE_STRING,
+ FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT,
+ FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY,
+ FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE,
+ FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT,
+};
+
+struct fs_sink_ctf_field_class {
+ enum fs_sink_ctf_field_class_type type;
+
+ /* Weak */
+ const bt_field_class *ir_fc;
+
+ unsigned int alignment;
+
+ /* Index of the field class within its own parent */
+ uint64_t index_in_parent;
+};
+
+struct fs_sink_ctf_field_class_bit_array {
+ struct fs_sink_ctf_field_class base;
+ unsigned int size;
+};
+
+struct fs_sink_ctf_field_class_int {
+ struct fs_sink_ctf_field_class_bit_array base;
+ bool is_signed;
+};
+
+struct fs_sink_ctf_field_class_float {
+ struct fs_sink_ctf_field_class_bit_array base;
+};
+
+struct fs_sink_ctf_field_class_string {
+ struct fs_sink_ctf_field_class base;
+};
+
+struct fs_sink_ctf_named_field_class {
+ GString *name;
+
+ /* Owned by this */
+ struct fs_sink_ctf_field_class *fc;
+};
+
+struct fs_sink_ctf_field_class_struct {
+ struct fs_sink_ctf_field_class base;
+
+ /* Array of `struct fs_sink_ctf_named_field_class` */
+ GArray *members;
+};
+
+struct fs_sink_ctf_field_class_variant {
+ struct fs_sink_ctf_field_class base;
+ GString *tag_ref;
+ bool tag_is_before;
+
+ /* Array of `struct fs_sink_ctf_named_field_class` */
+ GArray *options;
+};
+
+struct fs_sink_ctf_field_class_array_base {
+ struct fs_sink_ctf_field_class base;
+ struct fs_sink_ctf_field_class *elem_fc;
+};
+
+struct fs_sink_ctf_field_class_array {
+ struct fs_sink_ctf_field_class_array_base base;
+ uint64_t length;
+};
+
+struct fs_sink_ctf_field_class_sequence {
+ struct fs_sink_ctf_field_class_array_base base;
+ GString *length_ref;
+ bool length_is_before;
+};
+
+struct fs_sink_ctf_stream_class;
+
+struct fs_sink_ctf_event_class {
+ /* Weak */
+ const bt_event_class *ir_ec;
+
+ /* Weak */
+ struct fs_sink_ctf_stream_class *sc;
+
+ /* Owned by this */
+ struct fs_sink_ctf_field_class *spec_context_fc;
+
+ /* Owned by this */
+ struct fs_sink_ctf_field_class *payload_fc;
+};
+
+struct fs_sink_ctf_trace_class;
+
+struct fs_sink_ctf_stream_class {
+ /* Weak */
+ struct fs_sink_ctf_trace_class *tc;
+
+ /* Weak */
+ const bt_stream_class *ir_sc;
+
+ /* Weak */
+ const bt_clock_class *default_clock_class;
+
+ GString *default_clock_class_name;
+
+ /* Owned by this */
+ struct fs_sink_ctf_field_class *packet_context_fc;
+
+ /* Owned by this */
+ struct fs_sink_ctf_field_class *event_common_context_fc;
+
+ /* Array of `struct fs_sink_ctf_event_class *` (owned by this) */
+ GPtrArray *event_classes;
+
+ /*
+ * `const bt_event_class *` (weak) ->
+ * `struct fs_sink_ctf_event_class *` (weak)
+ */
+ GHashTable *event_classes_from_ir;
+};
+
+struct fs_sink_ctf_trace_class {
+ /* Weak */
+ const bt_trace_class *ir_tc;
+
+ unsigned char uuid[BABELTRACE_UUID_LEN];
+
+ /* Array of `struct fs_sink_ctf_stream_class *` (owned by this) */
+ GPtrArray *stream_classes;
+};
+
+static inline
+void fs_sink_ctf_field_class_destroy(struct fs_sink_ctf_field_class *fc);
+
+static inline
+void _fs_sink_ctf_field_class_init(struct fs_sink_ctf_field_class *fc,
+ enum fs_sink_ctf_field_class_type type,
+ const bt_field_class *ir_fc, unsigned int alignment,
+ uint64_t index_in_parent)
+{
+ BT_ASSERT(fc);
+ fc->type = type;
+ fc->ir_fc = ir_fc;
+ fc->alignment = alignment;
+ fc->index_in_parent = index_in_parent;
+}
+
+static inline
+void _fs_sink_ctf_field_class_bit_array_init(
+ struct fs_sink_ctf_field_class_bit_array *fc,
+ enum fs_sink_ctf_field_class_type type,
+ const bt_field_class *ir_fc, unsigned int size,
+ uint64_t index_in_parent)
+{
+ _fs_sink_ctf_field_class_init((void *) fc, type, ir_fc,
+ size % 8 == 0 ? 8 : 1, index_in_parent);
+ fc->size = size;
+}
+
+static inline
+void _fs_sink_ctf_field_class_int_init(struct fs_sink_ctf_field_class_int *fc,
+ enum fs_sink_ctf_field_class_type type,
+ const bt_field_class *ir_fc, uint64_t index_in_parent)
+{
+ bt_field_class_type ir_fc_type = bt_field_class_get_type(ir_fc);
+
+ _fs_sink_ctf_field_class_bit_array_init((void *) fc, type, ir_fc,
+ (unsigned int) bt_field_class_integer_get_field_value_range(
+ ir_fc),
+ index_in_parent);
+ fc->is_signed = (ir_fc_type == BT_FIELD_CLASS_TYPE_SIGNED_INTEGER ||
+ ir_fc_type == BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION);
+}
+
+static inline
+void _fs_sink_ctf_named_field_class_init(
+ struct fs_sink_ctf_named_field_class *named_fc)
+{
+ BT_ASSERT(named_fc);
+ named_fc->name = g_string_new(NULL);
+ BT_ASSERT(named_fc->name);
+}
+
+static inline
+void _fs_sink_ctf_named_field_class_fini(
+ struct fs_sink_ctf_named_field_class *named_fc)
+{
+ BT_ASSERT(named_fc);
+
+ if (named_fc->name) {
+ g_string_free(named_fc->name, TRUE);
+ named_fc->name = NULL;
+ }
+
+ fs_sink_ctf_field_class_destroy(named_fc->fc);
+ named_fc->fc = NULL;
+}
+
+static inline
+struct fs_sink_ctf_field_class_int *fs_sink_ctf_field_class_int_create(
+ const bt_field_class *ir_fc, uint64_t index_in_parent)
+{
+ struct fs_sink_ctf_field_class_int *fc =
+ g_new0(struct fs_sink_ctf_field_class_int, 1);
+
+ BT_ASSERT(fc);
+ _fs_sink_ctf_field_class_int_init(fc, FS_SINK_CTF_FIELD_CLASS_TYPE_INT,
+ ir_fc, index_in_parent);
+ return fc;
+}
+
+static inline
+struct fs_sink_ctf_field_class_float *fs_sink_ctf_field_class_float_create(
+ const bt_field_class *ir_fc, uint64_t index_in_parent)
+{
+ struct fs_sink_ctf_field_class_float *fc =
+ g_new0(struct fs_sink_ctf_field_class_float, 1);
+
+ BT_ASSERT(fc);
+ _fs_sink_ctf_field_class_bit_array_init((void *) fc,
+ FS_SINK_CTF_FIELD_CLASS_TYPE_FLOAT,
+ ir_fc, bt_field_class_real_is_single_precision(ir_fc) ? 32 : 64,
+ index_in_parent);
+ return fc;
+}
+
+static inline
+struct fs_sink_ctf_field_class_string *fs_sink_ctf_field_class_string_create(
+ const bt_field_class *ir_fc, uint64_t index_in_parent)
+{
+ struct fs_sink_ctf_field_class_string *fc =
+ g_new0(struct fs_sink_ctf_field_class_string, 1);
+
+ BT_ASSERT(fc);
+ _fs_sink_ctf_field_class_init((void *) fc,
+ FS_SINK_CTF_FIELD_CLASS_TYPE_STRING, ir_fc,
+ 8, index_in_parent);
+ return fc;
+}
+
+static inline
+struct fs_sink_ctf_field_class_struct *fs_sink_ctf_field_class_struct_create_empty(
+ const bt_field_class *ir_fc, uint64_t index_in_parent)
+{
+ struct fs_sink_ctf_field_class_struct *fc =
+ g_new0(struct fs_sink_ctf_field_class_struct, 1);
+
+ BT_ASSERT(fc);
+ _fs_sink_ctf_field_class_init((void *) fc,
+ FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT, ir_fc, 1, index_in_parent);
+ fc->members = g_array_new(FALSE, TRUE,
+ sizeof(struct fs_sink_ctf_named_field_class));
+ BT_ASSERT(fc->members);
+ return fc;
+}
+
+static inline
+struct fs_sink_ctf_field_class_variant *fs_sink_ctf_field_class_variant_create_empty(
+ const bt_field_class *ir_fc, uint64_t index_in_parent)
+{
+ struct fs_sink_ctf_field_class_variant *fc =
+ g_new0(struct fs_sink_ctf_field_class_variant, 1);
+
+ BT_ASSERT(fc);
+ _fs_sink_ctf_field_class_init((void *) fc,
+ FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT, ir_fc,
+ 1, index_in_parent);
+ fc->options = g_array_new(FALSE, TRUE,
+ sizeof(struct fs_sink_ctf_named_field_class));
+ BT_ASSERT(fc->options);
+ fc->tag_ref = g_string_new(NULL);
+ BT_ASSERT(fc->tag_ref);
+ fc->tag_is_before =
+ bt_field_class_variant_borrow_selector_field_path_const(ir_fc) ==
+ NULL;
+ return fc;
+}
+
+static inline
+struct fs_sink_ctf_field_class_array *fs_sink_ctf_field_class_array_create_empty(
+ const bt_field_class *ir_fc, uint64_t index_in_parent)
+{
+ struct fs_sink_ctf_field_class_array *fc =
+ g_new0(struct fs_sink_ctf_field_class_array, 1);
+
+ BT_ASSERT(fc);
+ _fs_sink_ctf_field_class_init((void *) fc,
+ FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY, ir_fc,
+ 1, index_in_parent);
+ fc->length = bt_field_class_static_array_get_length(ir_fc);
+ return fc;
+}
+
+static inline
+struct fs_sink_ctf_field_class_sequence *fs_sink_ctf_field_class_sequence_create_empty(
+ const bt_field_class *ir_fc, uint64_t index_in_parent)
+{
+ struct fs_sink_ctf_field_class_sequence *fc =
+ g_new0(struct fs_sink_ctf_field_class_sequence, 1);
+
+ BT_ASSERT(fc);
+ _fs_sink_ctf_field_class_init((void *) fc,
+ FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE,
+ ir_fc, 1, index_in_parent);
+ fc->length_ref = g_string_new(NULL);
+ BT_ASSERT(fc->length_ref);
+ fc->length_is_before =
+ bt_field_class_dynamic_array_borrow_length_field_path_const(ir_fc) ==
+ NULL;
+ return fc;
+}
+
+static inline
+struct fs_sink_ctf_named_field_class *
+fs_sink_ctf_field_class_struct_borrow_member_by_index(
+ struct fs_sink_ctf_field_class_struct *fc, uint64_t index);
+
+static inline
+struct fs_sink_ctf_named_field_class *
+fs_sink_ctf_field_class_variant_borrow_option_by_index(
+ struct fs_sink_ctf_field_class_variant *fc, uint64_t index);
+
+static inline
+void _fs_sink_ctf_field_class_fini(struct fs_sink_ctf_field_class *fc)
+{
+ BT_ASSERT(fc);
+}
+
+static inline
+void _fs_sink_ctf_field_class_int_destroy(
+ struct fs_sink_ctf_field_class_int *fc)
+{
+ BT_ASSERT(fc);
+ _fs_sink_ctf_field_class_fini((void *) fc);
+ g_free(fc);
+}
+
+static inline
+void _fs_sink_ctf_field_class_float_destroy(
+ struct fs_sink_ctf_field_class_float *fc)
+{
+ BT_ASSERT(fc);
+ _fs_sink_ctf_field_class_fini((void *) fc);
+ g_free(fc);
+}
+
+static inline
+void _fs_sink_ctf_field_class_string_destroy(
+ struct fs_sink_ctf_field_class_string *fc)
+{
+ BT_ASSERT(fc);
+ _fs_sink_ctf_field_class_fini((void *) fc);
+ g_free(fc);
+}
+
+static inline
+void _fs_sink_ctf_field_class_struct_destroy(
+ struct fs_sink_ctf_field_class_struct *fc)
+{
+ BT_ASSERT(fc);
+ _fs_sink_ctf_field_class_fini((void *) fc);
+
+ if (fc->members) {
+ uint64_t i;
+
+ for (i = 0; i < fc->members->len; i++) {
+ struct fs_sink_ctf_named_field_class *named_fc =
+ fs_sink_ctf_field_class_struct_borrow_member_by_index(
+ fc, i);
+
+ _fs_sink_ctf_named_field_class_fini(named_fc);
+ }
+
+ g_array_free(fc->members, TRUE);
+ fc->members = NULL;
+ }
+
+ g_free(fc);
+}
+
+static inline
+void _fs_sink_ctf_field_class_array_base_fini(
+ struct fs_sink_ctf_field_class_array_base *fc)
+{
+ BT_ASSERT(fc);
+ _fs_sink_ctf_field_class_fini((void *) fc);
+ fs_sink_ctf_field_class_destroy(fc->elem_fc);
+ fc->elem_fc = NULL;
+}
+
+static inline
+void _fs_sink_ctf_field_class_array_destroy(
+ struct fs_sink_ctf_field_class_array *fc)
+{
+ BT_ASSERT(fc);
+ _fs_sink_ctf_field_class_array_base_fini((void *) fc);
+ g_free(fc);
+}
+
+static inline
+void _fs_sink_ctf_field_class_sequence_destroy(
+ struct fs_sink_ctf_field_class_sequence *fc)
+{
+ BT_ASSERT(fc);
+ _fs_sink_ctf_field_class_array_base_fini((void *) fc);
+
+ if (fc->length_ref) {
+ g_string_free(fc->length_ref, TRUE);
+ fc->length_ref = NULL;
+ }
+
+ g_free(fc);
+}
+
+static inline
+void _fs_sink_ctf_field_class_variant_destroy(
+ struct fs_sink_ctf_field_class_variant *fc)
+{
+ BT_ASSERT(fc);
+ _fs_sink_ctf_field_class_fini((void *) fc);
+
+ if (fc->options) {
+ uint64_t i;
+
+ for (i = 0; i < fc->options->len; i++) {
+ struct fs_sink_ctf_named_field_class *named_fc =
+ fs_sink_ctf_field_class_variant_borrow_option_by_index(
+ fc, i);
+
+ _fs_sink_ctf_named_field_class_fini(named_fc);
+ }
+
+ g_array_free(fc->options, TRUE);
+ fc->options = NULL;
+ }
+
+ if (fc->tag_ref) {
+ g_string_free(fc->tag_ref, TRUE);
+ fc->tag_ref = NULL;
+ }
+
+ g_free(fc);
+}
+
+static inline
+void fs_sink_ctf_field_class_destroy(struct fs_sink_ctf_field_class *fc)
+{
+ if (!fc) {
+ return;
+ }
+
+ switch (fc->type) {
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_INT:
+ _fs_sink_ctf_field_class_int_destroy((void *) fc);
+ break;
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_FLOAT:
+ _fs_sink_ctf_field_class_float_destroy((void *) fc);
+ break;
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_STRING:
+ _fs_sink_ctf_field_class_string_destroy((void *) fc);
+ break;
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT:
+ _fs_sink_ctf_field_class_struct_destroy((void *) fc);
+ break;
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY:
+ _fs_sink_ctf_field_class_array_destroy((void *) fc);
+ break;
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE:
+ _fs_sink_ctf_field_class_sequence_destroy((void *) fc);
+ break;
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT:
+ _fs_sink_ctf_field_class_variant_destroy((void *) fc);
+ break;
+ default:
+ abort();
+ }
+}
+
+static inline
+struct fs_sink_ctf_named_field_class *
+fs_sink_ctf_field_class_struct_borrow_member_by_index(
+ struct fs_sink_ctf_field_class_struct *fc, uint64_t index)
+{
+ BT_ASSERT(fc);
+ BT_ASSERT(index < fc->members->len);
+ return &g_array_index(fc->members, struct fs_sink_ctf_named_field_class,
+ index);
+}
+
+static inline
+struct fs_sink_ctf_named_field_class *
+fs_sink_ctf_field_class_struct_borrow_member_by_name(
+ struct fs_sink_ctf_field_class_struct *fc, const char *name)
+{
+ uint64_t i;
+ struct fs_sink_ctf_named_field_class *ret_named_fc = NULL;
+
+ BT_ASSERT(fc);
+ BT_ASSERT(name);
+
+ for (i = 0; i < fc->members->len; i++) {
+ struct fs_sink_ctf_named_field_class *named_fc =
+ fs_sink_ctf_field_class_struct_borrow_member_by_index(
+ fc, i);
+
+ if (strcmp(name, named_fc->name->str) == 0) {
+ ret_named_fc = named_fc;
+ goto end;
+ }
+ }
+
+end:
+ return ret_named_fc;
+}
+
+static inline
+struct fs_sink_ctf_field_class *
+fs_sink_ctf_field_class_struct_borrow_member_field_class_by_name(
+ struct fs_sink_ctf_field_class_struct *struct_fc, const char *name)
+{
+ struct fs_sink_ctf_named_field_class *named_fc = NULL;
+ struct fs_sink_ctf_field_class *fc = NULL;
+
+ if (!struct_fc) {
+ goto end;
+ }
+
+ named_fc = fs_sink_ctf_field_class_struct_borrow_member_by_name(
+ struct_fc, name);
+ if (!named_fc) {
+ goto end;
+ }
+
+ fc = named_fc->fc;
+
+end:
+ return fc;
+}
+
+static inline
+struct fs_sink_ctf_field_class_int *
+fs_sink_ctf_field_class_struct_borrow_member_int_field_class_by_name(
+ struct fs_sink_ctf_field_class_struct *struct_fc,
+ const char *name)
+{
+ struct fs_sink_ctf_field_class_int *int_fc = NULL;
+
+ int_fc = (void *)
+ fs_sink_ctf_field_class_struct_borrow_member_field_class_by_name(
+ struct_fc, name);
+ if (!int_fc) {
+ goto end;
+ }
+
+ if (int_fc->base.base.type != FS_SINK_CTF_FIELD_CLASS_TYPE_INT) {
+ int_fc = NULL;
+ goto end;
+ }
+
+end:
+ return int_fc;
+}
+
+static inline
+void fs_sink_ctf_field_class_struct_align_at_least(
+ struct fs_sink_ctf_field_class_struct *fc,
+ unsigned int alignment)
+{
+ if (alignment > fc->base.alignment) {
+ fc->base.alignment = alignment;
+ }
+}
+
+static inline
+void fs_sink_ctf_field_class_struct_append_member(
+ struct fs_sink_ctf_field_class_struct *fc,
+ const char *name, struct fs_sink_ctf_field_class *member_fc)
+{
+ struct fs_sink_ctf_named_field_class *named_fc;
+
+ BT_ASSERT(fc);
+ BT_ASSERT(name);
+ g_array_set_size(fc->members, fc->members->len + 1);
+
+ named_fc = &g_array_index(fc->members,
+ struct fs_sink_ctf_named_field_class, fc->members->len - 1);
+ _fs_sink_ctf_named_field_class_init(named_fc);
+ g_string_assign(named_fc->name, name);
+ named_fc->fc = member_fc;
+ fs_sink_ctf_field_class_struct_align_at_least(fc, member_fc->alignment);
+}
+
+static inline
+struct fs_sink_ctf_named_field_class *
+fs_sink_ctf_field_class_variant_borrow_option_by_index(
+ struct fs_sink_ctf_field_class_variant *fc, uint64_t index)
+{
+ BT_ASSERT(fc);
+ BT_ASSERT(index < fc->options->len);
+ return &g_array_index(fc->options, struct fs_sink_ctf_named_field_class,
+ index);
+}
+
+static inline
+struct fs_sink_ctf_named_field_class *
+fs_sink_ctf_field_class_variant_borrow_option_by_name(
+ struct fs_sink_ctf_field_class_variant *fc, const char *name)
+{
+ uint64_t i;
+ struct fs_sink_ctf_named_field_class *ret_named_fc = NULL;
+
+ BT_ASSERT(fc);
+ BT_ASSERT(name);
+
+ for (i = 0; i < fc->options->len; i++) {
+ struct fs_sink_ctf_named_field_class *named_fc =
+ fs_sink_ctf_field_class_variant_borrow_option_by_index(
+ fc, i);
+
+ if (strcmp(name, named_fc->name->str) == 0) {
+ ret_named_fc = named_fc;
+ goto end;
+ }
+ }
+
+end:
+ return ret_named_fc;
+}
+
+static inline
+void fs_sink_ctf_field_class_variant_append_option(
+ struct fs_sink_ctf_field_class_variant *fc,
+ const char *name, struct fs_sink_ctf_field_class *option_fc)
+{
+ struct fs_sink_ctf_named_field_class *named_fc;
+
+ BT_ASSERT(fc);
+ BT_ASSERT(name);
+ g_array_set_size(fc->options, fc->options->len + 1);
+
+ named_fc = &g_array_index(fc->options,
+ struct fs_sink_ctf_named_field_class, fc->options->len - 1);
+ _fs_sink_ctf_named_field_class_init(named_fc);
+ g_string_assign(named_fc->name, name);
+ named_fc->fc = option_fc;
+}
+
+static inline
+struct fs_sink_ctf_event_class *fs_sink_ctf_event_class_create(
+ struct fs_sink_ctf_stream_class *sc,
+ const bt_event_class *ir_ec)
+{
+ struct fs_sink_ctf_event_class *ec =
+ g_new0(struct fs_sink_ctf_event_class, 1);
+
+ BT_ASSERT(sc);
+ BT_ASSERT(ir_ec);
+ BT_ASSERT(ec);
+ ec->ir_ec = ir_ec;
+ ec->sc = sc;
+ g_ptr_array_add(sc->event_classes, ec);
+ g_hash_table_insert(sc->event_classes_from_ir, (gpointer) ir_ec, ec);
+ return ec;
+}
+
+static inline
+void fs_sink_ctf_event_class_destroy(struct fs_sink_ctf_event_class *ec)
+{
+ if (!ec) {
+ return;
+ }
+
+ fs_sink_ctf_field_class_destroy(ec->spec_context_fc);
+ ec->spec_context_fc = NULL;
+ fs_sink_ctf_field_class_destroy(ec->payload_fc);
+ ec->payload_fc = NULL;
+ g_free(ec);
+}
+
+static inline
+struct fs_sink_ctf_stream_class *fs_sink_ctf_stream_class_create(
+ struct fs_sink_ctf_trace_class *tc,
+ const bt_stream_class *ir_sc)
+{
+ struct fs_sink_ctf_stream_class *sc =
+ g_new0(struct fs_sink_ctf_stream_class, 1);
+
+ BT_ASSERT(tc);
+ BT_ASSERT(ir_sc);
+ BT_ASSERT(sc);
+ sc->tc = tc;
+ sc->ir_sc = ir_sc;
+ sc->default_clock_class =
+ bt_stream_class_borrow_default_clock_class_const(ir_sc);
+ sc->default_clock_class_name = g_string_new(NULL);
+ BT_ASSERT(sc->default_clock_class_name);
+ sc->event_classes = g_ptr_array_new_with_free_func(
+ (GDestroyNotify) fs_sink_ctf_event_class_destroy);
+ BT_ASSERT(sc->event_classes);
+ sc->event_classes_from_ir = g_hash_table_new(g_direct_hash,
+ g_direct_equal);
+ BT_ASSERT(sc->event_classes_from_ir);
+ g_ptr_array_add(tc->stream_classes, sc);
+ return sc;
+}
+
+static inline
+void fs_sink_ctf_stream_class_destroy(struct fs_sink_ctf_stream_class *sc)
+{
+ if (!sc) {
+ return;
+ }
+
+ if (sc->default_clock_class_name) {
+ g_string_free(sc->default_clock_class_name, TRUE);
+ sc->default_clock_class_name = NULL;
+ }
+
+ if (sc->event_classes) {
+ g_ptr_array_free(sc->event_classes, TRUE);
+ sc->event_classes = NULL;
+ }
+
+ if (sc->event_classes_from_ir) {
+ g_hash_table_destroy(sc->event_classes_from_ir);
+ sc->event_classes_from_ir = NULL;
+ }
+
+ fs_sink_ctf_field_class_destroy(sc->packet_context_fc);
+ sc->packet_context_fc = NULL;
+ fs_sink_ctf_field_class_destroy(sc->event_common_context_fc);
+ sc->event_common_context_fc = NULL;
+ g_free(sc);
+}
+
+static inline
+void fs_sink_ctf_stream_class_append_event_class(
+ struct fs_sink_ctf_stream_class *sc,
+ struct fs_sink_ctf_event_class *ec)
+{
+ g_ptr_array_add(sc->event_classes, ec);
+}
+
+static inline
+void fs_sink_ctf_trace_class_destroy(struct fs_sink_ctf_trace_class *tc)
+{
+ if (!tc) {
+ return;
+ }
+
+ if (tc->stream_classes) {
+ g_ptr_array_free(tc->stream_classes, TRUE);
+ tc->stream_classes = NULL;
+ }
+
+ g_free(tc);
+}
+
+static inline
+struct fs_sink_ctf_trace_class *fs_sink_ctf_trace_class_create(
+ const bt_trace_class *ir_tc)
+{
+ struct fs_sink_ctf_trace_class *tc =
+ g_new0(struct fs_sink_ctf_trace_class, 1);
+
+ BT_ASSERT(tc);
+
+ if (bt_uuid_generate(tc->uuid)) {
+ fs_sink_ctf_trace_class_destroy(tc);
+ tc = NULL;
+ goto end;
+ }
+
+ tc->ir_tc = ir_tc;
+ tc->stream_classes = g_ptr_array_new_with_free_func(
+ (GDestroyNotify) fs_sink_ctf_stream_class_destroy);
+ BT_ASSERT(tc->stream_classes);
+
+end:
+ return tc;
+}
+
+static inline
+bool fs_sink_ctf_ist_valid_identifier(const char *name)
+{
+ const char *at;
+ uint64_t i;
+ bool ist_valid = true;
+ static const char *reserved_keywords[] = {
+ "align",
+ "callsite",
+ "const",
+ "char",
+ "clock",
+ "double",
+ "enum",
+ "env",
+ "event",
+ "floating_point",
+ "float",
+ "integer",
+ "int",
+ "long",
+ "short",
+ "signed",
+ "stream",
+ "string",
+ "struct",
+ "trace",
+ "typealias",
+ "typedef",
+ "unsigned",
+ "variant",
+ "void",
+ "_Bool",
+ "_Complex",
+ "_Imaginary",
+ };
+
+ /* Make sure the name is not a reserved keyword */
+ for (i = 0; i < sizeof(reserved_keywords) / sizeof(*reserved_keywords);
+ i++) {
+ if (strcmp(name, reserved_keywords[i]) == 0) {
+ ist_valid = false;
+ goto end;
+ }
+ }
+
+ /* Make sure the name is not an empty string */
+ if (strlen(name) == 0) {
+ ist_valid = false;
+ goto end;
+ }
+
+ /* Make sure the name starts with a letter or `_` */
+ if (!isalpha(name[0]) && name[0] != '_') {
+ ist_valid = false;
+ goto end;
+ }
+
+ /* Make sure the name only contains letters, digits, and `_` */
+ for (at = name; *at != '\0'; at++) {
+ if (!isalnum(*at) && *at != '_') {
+ ist_valid = false;
+ goto end;
+ }
+ }
+
+end:
+ return ist_valid;
+}
+
+static inline
+int fs_sink_ctf_protect_name(GString *name)
+{
+ int ret = 0;
+
+ if (!fs_sink_ctf_ist_valid_identifier(name->str)) {
+ ret = -1;
+ goto end;
+ }
+
+ /* Prepend `_` to protect it */
+ g_string_prepend_c(name, '_');
+
+end:
+ return ret;
+}
+
+#endif /* BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_CTF_META_H */
--- /dev/null
+/*
+ * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#define BT_LOG_TAG "PLUGIN-CTF-FS-SINK-STREAM"
+#include "logging.h"
+
+#include <babeltrace/babeltrace.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <glib.h>
+#include <babeltrace/assert-internal.h>
+#include <babeltrace/ctfser-internal.h>
+#include <babeltrace/endian-internal.h>
+
+#include "fs-sink-trace.h"
+#include "fs-sink-stream.h"
+#include "translate-trace-ir-to-ctf-ir.h"
+
+BT_HIDDEN
+void fs_sink_stream_destroy(struct fs_sink_stream *stream)
+{
+ if (!stream) {
+ goto end;
+ }
+
+ bt_ctfser_fini(&stream->ctfser);
+
+ if (stream->file_name) {
+ g_string_free(stream->file_name, TRUE);
+ stream->file_name = NULL;
+ }
+
+ bt_packet_put_ref(stream->packet_state.packet);
+ g_free(stream);
+
+end:
+ return;
+}
+
+static
+bool stream_file_name_exists(struct fs_sink_trace *trace, const char *name)
+{
+ bool exists = false;
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_hash_table_iter_init(&iter, trace->streams);
+
+ while (g_hash_table_iter_next(&iter, &key, &value)) {
+ struct fs_sink_stream *stream = value;
+
+ if (strcmp(name, stream->file_name->str) == 0) {
+ exists = true;
+ goto end;
+ }
+ }
+
+end:
+ return exists;
+}
+
+static
+GString *sanitize_stream_file_name(const char *file_name)
+{
+ GString *san_file_name = g_string_new(NULL);
+ const char *ch;
+ gchar *basename;
+
+ BT_ASSERT(san_file_name);
+ BT_ASSERT(file_name);
+ basename = g_path_get_basename(file_name);
+
+ for (ch = basename; *ch != '\0'; ch++) {
+ if (*ch == '/') {
+ g_string_append_c(san_file_name, '_');
+ } else {
+ g_string_append_c(san_file_name, *ch);
+ }
+ }
+
+ /* Do not allow `.` and `..` either */
+ if (strcmp(san_file_name->str, ".") == 0 ||
+ strcmp(san_file_name->str, "..") == 0) {
+ g_string_assign(san_file_name, "stream");
+ }
+
+ g_free(basename);
+ return san_file_name;
+}
+
+static
+GString *make_unique_stream_file_name(struct fs_sink_trace *trace,
+ const char *base)
+{
+ GString *san_base = sanitize_stream_file_name(base);
+ GString *name = g_string_new(san_base->str);
+ unsigned int suffix = 0;
+
+ BT_ASSERT(name);
+
+ while (stream_file_name_exists(trace, name->str) &&
+ strcmp(name->str, "metadata") == 0) {
+ g_string_assign(name, san_base->str);
+ g_string_append_printf(name, "%u", suffix);
+ suffix++;
+ }
+
+ g_string_free(san_base, TRUE);
+ return name;
+}
+
+static
+void set_stream_file_name(struct fs_sink_stream *stream)
+{
+ const char *base_name = bt_stream_get_name(stream->ir_stream);
+
+ if (!base_name) {
+ base_name = "stream";
+ }
+
+ BT_ASSERT(!stream->file_name);
+ stream->file_name = make_unique_stream_file_name(stream->trace,
+ base_name);
+}
+
+BT_HIDDEN
+struct fs_sink_stream *fs_sink_stream_create(struct fs_sink_trace *trace,
+ const bt_stream *ir_stream)
+{
+ struct fs_sink_stream *stream = g_new0(struct fs_sink_stream, 1);
+ int ret;
+ GString *path = g_string_new(trace->path->str);
+
+ if (!stream) {
+ goto end;
+ }
+
+ stream->trace = trace;
+ stream->ir_stream = ir_stream;
+ stream->packet_state.beginning_cs = UINT64_C(-1);
+ stream->packet_state.end_cs = UINT64_C(-1);
+ stream->prev_packet_state.end_cs = UINT64_C(-1);
+ stream->prev_packet_state.discarded_events_counter = UINT64_C(-1);
+ stream->prev_packet_state.seq_num = UINT64_C(-1);
+ ret = try_translate_stream_class_trace_ir_to_ctf_ir(trace->tc,
+ bt_stream_borrow_class_const(ir_stream), &stream->sc);
+ if (ret) {
+ goto error;
+ }
+
+ set_stream_file_name(stream);
+ g_string_append_printf(path, "/%s", stream->file_name->str);
+ ret = bt_ctfser_init(&stream->ctfser, path->str);
+ if (ret) {
+ goto error;
+ }
+
+ g_hash_table_insert(trace->streams, (gpointer) ir_stream, stream);
+ goto end;
+
+error:
+ fs_sink_stream_destroy(stream);
+ stream = NULL;
+
+end:
+ if (path) {
+ g_string_free(path, TRUE);
+ }
+
+ return stream;
+}
+
+static
+int write_field(struct fs_sink_stream *stream,
+ struct fs_sink_ctf_field_class *fc, const bt_field *field);
+
+static inline
+int write_int_field(struct fs_sink_stream *stream,
+ struct fs_sink_ctf_field_class_int *fc, const bt_field *field)
+{
+ int ret;
+
+ if (fc->is_signed) {
+ ret = bt_ctfser_write_signed_int(&stream->ctfser,
+ bt_field_signed_integer_get_value(field),
+ fc->base.base.alignment, fc->base.size, BYTE_ORDER);
+ } else {
+ ret = bt_ctfser_write_unsigned_int(&stream->ctfser,
+ bt_field_unsigned_integer_get_value(field),
+ fc->base.base.alignment, fc->base.size, BYTE_ORDER);
+ }
+
+ return ret;
+}
+
+static inline
+int write_float_field(struct fs_sink_stream *stream,
+ struct fs_sink_ctf_field_class_float *fc, const bt_field *field)
+{
+ int ret;
+ double val = bt_field_real_get_value(field);
+
+ if (fc->base.size == 32) {
+ ret = bt_ctfser_write_float32(&stream->ctfser, val,
+ fc->base.base.alignment, BYTE_ORDER);
+ } else {
+ ret = bt_ctfser_write_float32(&stream->ctfser, val,
+ fc->base.base.alignment, BYTE_ORDER);
+ }
+
+ return ret;
+}
+
+static inline
+int write_string_field(struct fs_sink_stream *stream,
+ struct fs_sink_ctf_field_class_string *fc, const bt_field *field)
+{
+ return bt_ctfser_write_string(&stream->ctfser,
+ bt_field_string_get_value(field));
+}
+
+static inline
+int write_array_field_elements(struct fs_sink_stream *stream,
+ struct fs_sink_ctf_field_class_array_base *fc,
+ const bt_field *field)
+{
+ uint64_t i;
+ uint64_t len = bt_field_array_get_length(field);
+ int ret = 0;
+
+ for (i = 0; i < len; i++) {
+ const bt_field *elem_field =
+ bt_field_array_borrow_element_field_by_index_const(
+ field, i);
+ ret = write_field(stream, fc->elem_fc, elem_field);
+ if (unlikely(ret)) {
+ goto end;
+ }
+ }
+
+end:
+ return ret;
+}
+
+static inline
+int write_sequence_field(struct fs_sink_stream *stream,
+ struct fs_sink_ctf_field_class_sequence *fc,
+ const bt_field *field)
+{
+ int ret;
+
+ if (fc->length_is_before) {
+ ret = bt_ctfser_write_unsigned_int(&stream->ctfser,
+ bt_field_array_get_length(field), 8, 32, BYTE_ORDER);
+ if (unlikely(ret)) {
+ goto end;
+ }
+ }
+
+ ret = write_array_field_elements(stream, (void *) fc, field);
+
+end:
+ return ret;
+}
+
+static inline
+int write_struct_field(struct fs_sink_stream *stream,
+ struct fs_sink_ctf_field_class_struct *fc,
+ const bt_field *field, bool align_struct)
+{
+ int ret = 0;
+ uint64_t i;
+
+ if (likely(align_struct)) {
+ ret = bt_ctfser_align_offset_in_current_packet(&stream->ctfser,
+ fc->base.alignment);
+ if (unlikely(ret)) {
+ goto end;
+ }
+ }
+
+ for (i = 0; i < fc->members->len; i++) {
+ const bt_field *memb_field =
+ bt_field_structure_borrow_member_field_by_index_const(
+ field, i);
+ struct fs_sink_ctf_field_class *member_fc =
+ fs_sink_ctf_field_class_struct_borrow_member_by_index(
+ fc, i)->fc;
+
+ ret = write_field(stream, member_fc, memb_field);
+ if (unlikely(ret)) {
+ goto end;
+ }
+ }
+
+end:
+ return ret;
+}
+
+static inline
+int write_variant_field(struct fs_sink_stream *stream,
+ struct fs_sink_ctf_field_class_variant *fc,
+ const bt_field *field)
+{
+ uint64_t opt_index =
+ bt_field_variant_get_selected_option_field_index(field);
+ int ret;
+
+ if (fc->tag_is_before) {
+ ret = bt_ctfser_write_unsigned_int(&stream->ctfser,
+ opt_index, 8, 16, BYTE_ORDER);
+ if (unlikely(ret)) {
+ goto end;
+ }
+ }
+
+ ret = write_field(stream,
+ fs_sink_ctf_field_class_variant_borrow_option_by_index(fc,
+ opt_index)->fc,
+ bt_field_variant_borrow_selected_option_field_const(field));
+
+end:
+ return ret;
+}
+
+static
+int write_field(struct fs_sink_stream *stream,
+ struct fs_sink_ctf_field_class *fc, const bt_field *field)
+{
+ int ret;
+
+ switch (fc->type) {
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_INT:
+ ret = write_int_field(stream, (void *) fc, field);
+ break;
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_FLOAT:
+ ret = write_float_field(stream, (void *) fc, field);
+ break;
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_STRING:
+ ret = write_string_field(stream, (void *) fc, field);
+ break;
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT:
+ ret = write_struct_field(stream, (void *) fc, field, true);
+ break;
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY:
+ ret = write_array_field_elements(stream, (void *) fc, field);
+ break;
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE:
+ ret = write_sequence_field(stream, (void *) fc, field);
+ break;
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT:
+ ret = write_variant_field(stream, (void *) fc, field);
+ break;
+ default:
+ abort();
+ }
+
+ return ret;
+}
+
+static inline
+int write_event_header(struct fs_sink_stream *stream,
+ const bt_clock_snapshot *cs, struct fs_sink_ctf_event_class *ec)
+{
+ int ret;
+
+ /* Event class ID */
+ ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser,
+ bt_event_class_get_id(ec->ir_ec), 8, 64, BYTE_ORDER);
+ if (unlikely(ret)) {
+ goto end;
+ }
+
+ /* Time */
+ if (stream->sc->default_clock_class) {
+ BT_ASSERT(cs);
+ ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser,
+ bt_clock_snapshot_get_value(cs), 8, 64, BYTE_ORDER);
+ if (unlikely(ret)) {
+ goto end;
+ }
+ }
+
+end:
+ return ret;
+}
+
+BT_HIDDEN
+int fs_sink_stream_write_event(struct fs_sink_stream *stream,
+ const bt_clock_snapshot *cs, const bt_event *event,
+ struct fs_sink_ctf_event_class *ec)
+{
+ int ret;
+ const bt_field *field;
+
+ /* Header */
+ ret = write_event_header(stream, cs, ec);
+ if (unlikely(ret)) {
+ goto end;
+ }
+
+ /* Common context */
+ if (stream->sc->event_common_context_fc) {
+ field = bt_event_borrow_common_context_field_const(event);
+ BT_ASSERT(field);
+ ret = write_struct_field(stream,
+ (void *) stream->sc->event_common_context_fc,
+ field, true);
+ if (unlikely(ret)) {
+ goto end;
+ }
+ }
+
+ /* Specific context */
+ if (ec->spec_context_fc) {
+ field = bt_event_borrow_specific_context_field_const(event);
+ BT_ASSERT(field);
+ ret = write_struct_field(stream, (void *) ec->spec_context_fc,
+ field, true);
+ if (unlikely(ret)) {
+ goto end;
+ }
+ }
+
+ /* Specific context */
+ if (ec->payload_fc) {
+ field = bt_event_borrow_payload_field_const(event);
+ BT_ASSERT(field);
+ ret = write_struct_field(stream, (void *) ec->payload_fc,
+ field, true);
+ if (unlikely(ret)) {
+ goto end;
+ }
+ }
+
+end:
+ return ret;
+}
+
+static
+int write_packet_context(struct fs_sink_stream *stream)
+{
+ int ret;
+
+ /* Packet total size */
+ ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser,
+ stream->packet_state.total_size, 8, 64, BYTE_ORDER);
+ if (ret) {
+ goto end;
+ }
+
+ /* Packet content size */
+ ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser,
+ stream->packet_state.content_size, 8, 64, BYTE_ORDER);
+ if (ret) {
+ goto end;
+ }
+
+ if (stream->sc->default_clock_class) {
+ /* Beginning time */
+ ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser,
+ stream->packet_state.beginning_cs, 8, 64, BYTE_ORDER);
+ if (ret) {
+ goto end;
+ }
+
+ /* End time */
+ ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser,
+ stream->packet_state.end_cs, 8, 64, BYTE_ORDER);
+ if (ret) {
+ goto end;
+ }
+ }
+
+ /* Discarded event counter */
+ ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser,
+ stream->packet_state.discarded_events_counter, 8, 64,
+ BYTE_ORDER);
+ if (ret) {
+ goto end;
+ }
+
+ /* Sequence number */
+ ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser,
+ stream->packet_state.seq_num, 8, 64, BYTE_ORDER);
+ if (ret) {
+ goto end;
+ }
+
+ /* Other members */
+ if (stream->sc->packet_context_fc) {
+ const bt_field *packet_context_field =
+ bt_packet_borrow_context_field_const(
+ stream->packet_state.packet);
+
+ BT_ASSERT(packet_context_field);
+ ret = write_struct_field(stream,
+ (void *) stream->sc->packet_context_fc,
+ packet_context_field, false);
+ if (ret) {
+ goto end;
+ }
+ }
+
+end:
+ return ret;
+}
+
+BT_HIDDEN
+int fs_sink_stream_open_packet(struct fs_sink_stream *stream,
+ const bt_clock_snapshot *cs, const bt_packet *packet)
+{
+ int ret;
+ uint64_t i;
+
+ BT_ASSERT(!stream->packet_state.is_open);
+ bt_packet_put_ref(stream->packet_state.packet);
+ stream->packet_state.packet = packet;
+ bt_packet_get_ref(stream->packet_state.packet);
+ if (cs) {
+ stream->packet_state.beginning_cs =
+ bt_clock_snapshot_get_value(cs);
+ }
+
+ /* Open packet */
+ ret = bt_ctfser_open_packet(&stream->ctfser);
+ if (ret) {
+ /* bt_ctfser_open_packet() logs errors */
+ goto end;
+ }
+
+ /* Packet header: magic */
+ bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser,
+ UINT64_C(0xc1fc1fc1), 8, 32, BYTE_ORDER);
+
+ /* Packet header: UUID */
+ for (i = 0; i < BABELTRACE_UUID_LEN; i++) {
+ bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser,
+ (uint64_t) stream->sc->tc->uuid[i], 8, 8, BYTE_ORDER);
+ }
+
+ /* Packet header: stream class ID */
+ bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser,
+ bt_stream_class_get_id(stream->sc->ir_sc), 8, 64, BYTE_ORDER);
+
+ /* Packet header: stream ID */
+ bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser,
+ bt_stream_get_id(stream->ir_stream), 8, 64, BYTE_ORDER);
+
+ /* Save packet context's offset to rewrite it later */
+ stream->packet_state.context_offset_bits =
+ bt_ctfser_get_offset_in_current_packet_bits(&stream->ctfser);
+
+ /* Write packet context just to advance to content (first event) */
+ ret = write_packet_context(stream);
+ if (ret) {
+ goto end;
+ }
+
+ stream->packet_state.is_open = true;
+
+end:
+ return ret;
+}
+
+BT_HIDDEN
+int fs_sink_stream_close_packet(struct fs_sink_stream *stream,
+ const bt_clock_snapshot *cs)
+{
+ int ret;
+
+ BT_ASSERT(stream->packet_state.is_open);
+
+ if (cs) {
+ stream->packet_state.end_cs = bt_clock_snapshot_get_value(cs);
+ }
+
+ stream->packet_state.content_size =
+ bt_ctfser_get_offset_in_current_packet_bits(&stream->ctfser);
+ stream->packet_state.total_size =
+ (stream->packet_state.content_size + 7) & ~UINT64_C(7);
+
+ /* Rewrite packet context */
+ bt_ctfser_set_offset_in_current_packet_bits(&stream->ctfser,
+ stream->packet_state.context_offset_bits);
+ ret = write_packet_context(stream);
+ if (ret) {
+ goto end;
+ }
+
+ /* Close packet */
+ bt_ctfser_close_current_packet(&stream->ctfser,
+ stream->packet_state.total_size / 8);
+
+ /* Partially copy current packet state to previous packet state */
+ stream->prev_packet_state.end_cs = stream->packet_state.end_cs;
+ stream->prev_packet_state.discarded_events_counter =
+ stream->packet_state.discarded_events_counter;
+ stream->prev_packet_state.seq_num =
+ stream->packet_state.seq_num;
+
+ /* Reset current packet state */
+ stream->packet_state.beginning_cs = UINT64_C(-1);
+ stream->packet_state.end_cs = UINT64_C(-1);
+ stream->packet_state.content_size = 0;
+ stream->packet_state.total_size = 0;
+ stream->packet_state.seq_num += 1;
+ stream->packet_state.context_offset_bits = 0;
+ stream->packet_state.is_open = false;
+ BT_PACKET_PUT_REF_AND_RESET(stream->packet_state.packet);
+
+end:
+ return ret;
+}
--- /dev/null
+#ifndef BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_STREAM_H
+#define BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_STREAM_H
+
+/*
+ * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <babeltrace/babeltrace-internal.h>
+#include <babeltrace/babeltrace.h>
+#include <babeltrace/ctfser-internal.h>
+#include <glib.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "fs-sink-ctf-meta.h"
+
+struct fs_sink_trace;
+
+struct fs_sink_stream {
+ struct fs_sink_trace *trace;
+ struct bt_ctfser ctfser;
+
+ /* Stream's file name */
+ GString *file_name;
+
+ /* Weak */
+ const bt_stream *ir_stream;
+
+ struct fs_sink_ctf_stream_class *sc;
+
+ struct {
+ bool is_open;
+ uint64_t beginning_cs;
+ uint64_t end_cs;
+ uint64_t content_size;
+ uint64_t total_size;
+ uint64_t discarded_events_counter;
+ uint64_t seq_num;
+ uint64_t context_offset_bits;
+
+ /* Owned by this */
+ const bt_packet *packet;
+ } packet_state;
+
+ struct {
+ uint64_t end_cs;
+ uint64_t discarded_events_counter;
+ uint64_t seq_num;
+ } prev_packet_state;
+
+ struct {
+ bool in_range;
+ uint64_t beginning_cs;
+ uint64_t end_cs;
+ } discarded_events_state;
+
+ struct {
+ bool in_range;
+ uint64_t beginning_cs;
+ uint64_t end_cs;
+ } discarded_packets_state;
+
+ bool in_discarded_events_range;
+};
+
+BT_HIDDEN
+struct fs_sink_stream *fs_sink_stream_create(struct fs_sink_trace *trace,
+ const bt_stream *ir_stream);
+
+BT_HIDDEN
+void fs_sink_stream_destroy(struct fs_sink_stream *stream);
+
+BT_HIDDEN
+int fs_sink_stream_write_event(struct fs_sink_stream *stream,
+ const bt_clock_snapshot *cs, const bt_event *event,
+ struct fs_sink_ctf_event_class *ec);
+
+BT_HIDDEN
+int fs_sink_stream_open_packet(struct fs_sink_stream *stream,
+ const bt_clock_snapshot *cs, const bt_packet *packet);
+
+BT_HIDDEN
+int fs_sink_stream_close_packet(struct fs_sink_stream *stream,
+ const bt_clock_snapshot *cs);
+
+#endif /* BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_STREAM_H */
--- /dev/null
+/*
+ * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#define BT_LOG_TAG "PLUGIN-CTF-FS-SINK-TRACE"
+#include "logging.h"
+
+#include <babeltrace/babeltrace.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <glib.h>
+#include <babeltrace/assert-internal.h>
+#include <babeltrace/ctfser-internal.h>
+
+#include "translate-trace-ir-to-ctf-ir.h"
+#include "translate-ctf-ir-to-tsdl.h"
+#include "fs-sink.h"
+#include "fs-sink-trace.h"
+#include "fs-sink-stream.h"
+
+/*
+ * Sanitizes `path` so as to:
+ *
+ * * Replace `.` subdirectories with `_`.
+ * * Replace `..` subdirectories with `__`.
+ * * Remove trailing slashes.
+ */
+static
+GString *sanitize_trace_path(const char *path)
+{
+ GString *san_path = g_string_new(NULL);
+ const char *ch = path;
+ bool dir_start = true;
+
+ BT_ASSERT(san_path);
+ BT_ASSERT(path);
+
+ while (*ch != '\0') {
+ switch (*ch) {
+ case '/':
+ /* Start of directory */
+ dir_start = true;
+ g_string_append_c(san_path, *ch);
+ ch++;
+ continue;
+ case '.':
+ if (dir_start) {
+ switch (ch[1]) {
+ case '\0':
+ case '/':
+ /* `.` -> `_` */
+ g_string_append_c(san_path, '_');
+ ch++;
+ continue;
+ case '.':
+ switch (ch[2]) {
+ case '\0':
+ case '/':
+ /* `..` -> `__` */
+ g_string_append(san_path, "__");
+ ch += 2;
+ continue;
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ default:
+ break;
+ }
+
+ /* Not a special character */
+ g_string_append_c(san_path, *ch);
+ ch++;
+ dir_start = false;
+ }
+
+ /* Remove trailing slashes */
+ while (san_path->len > 0 &&
+ san_path->str[san_path->len - 1] == '/') {
+ /* Remove trailing slash */
+ g_string_set_size(san_path, san_path->len - 1);
+ }
+
+ if (san_path->len == 0) {
+ /* Looks like there's nothing left: just use `trace` */
+ g_string_assign(san_path, "trace");
+ }
+
+ return san_path;
+}
+
+static
+GString *make_unique_trace_path(struct fs_sink_comp *fs_sink,
+ const char *output_dir_path, const char *base)
+{
+ GString *path = g_string_new(output_dir_path);
+ GString *san_base = NULL;
+ unsigned int suffix = 0;
+
+ if (fs_sink->assume_single_trace) {
+ /* Use output directory directly */
+ goto end;
+ }
+
+ san_base = sanitize_trace_path(base);
+ g_string_append_printf(path, "/%s", san_base->str);
+
+ while (g_file_test(path->str, G_FILE_TEST_IS_DIR)) {
+ g_string_assign(path, output_dir_path);
+ g_string_append_printf(path, "/%s%u", san_base->str, suffix);
+ suffix++;
+ }
+
+end:
+ if (san_base) {
+ g_string_free(san_base, TRUE);
+ }
+
+ return path;
+}
+
+BT_HIDDEN
+void fs_sink_trace_destroy(struct fs_sink_trace *trace)
+{
+ GString *tsdl = NULL;
+ FILE *fh = NULL;
+ size_t len;
+
+ if (!trace) {
+ goto end;
+ }
+
+ if (trace->ir_trace_destruction_listener_id != UINT64_C(-1)) {
+ /*
+ * Remove the destruction listener, otherwise it could
+ * be called in the future, and its private data is this
+ * CTF FS sink trace object which won't exist anymore.
+ */
+ (void) bt_trace_remove_destruction_listener(trace->ir_trace,
+ trace->ir_trace_destruction_listener_id);
+ trace->ir_trace_destruction_listener_id = UINT64_C(-1);
+ }
+
+ if (trace->streams) {
+ g_hash_table_destroy(trace->streams);
+ trace->streams = NULL;
+ }
+
+ tsdl = g_string_new(NULL);
+ BT_ASSERT(tsdl);
+ translate_trace_class_ctf_ir_to_tsdl(trace->tc, tsdl);
+ fh = fopen(trace->metadata_path->str, "wb");
+ if (!fh) {
+ BT_LOGF_ERRNO("In trace destruction listener: "
+ "cannot open metadata file for writing: ",
+ ": path=\"%s\"", trace->metadata_path->str);
+ abort();
+ }
+
+ len = fwrite(tsdl->str, sizeof(*tsdl->str), tsdl->len, fh);
+ if (len != tsdl->len) {
+ BT_LOGF_ERRNO("In trace destruction listener: "
+ "cannot write metadata file: ",
+ ": path=\"%s\"", trace->metadata_path->str);
+ abort();
+ }
+
+ if (!trace->fs_sink->quiet) {
+ printf("Created CTF trace `%s`.\n", trace->path->str);
+ }
+
+ if (trace->path) {
+ g_string_free(trace->path, TRUE);
+ trace->path = NULL;
+ }
+
+ if (trace->metadata_path) {
+ g_string_free(trace->metadata_path, TRUE);
+ trace->metadata_path = NULL;
+ }
+
+ fs_sink_ctf_trace_class_destroy(trace->tc);
+ trace->tc = NULL;
+ g_free(trace);
+
+end:
+ if (fh) {
+ int ret = fclose(fh);
+
+ if (ret != 0) {
+ BT_LOGW_ERRNO("In trace destruction listener: "
+ "cannot close metadata file: ",
+ ": path=\"%s\"", trace->metadata_path->str);
+ }
+ }
+
+ if (tsdl) {
+ g_string_free(tsdl, TRUE);
+ }
+
+ return;
+}
+
+static
+void ir_trace_destruction_listener(const bt_trace *ir_trace, void *data)
+{
+ struct fs_sink_trace *trace = data;
+
+ /*
+ * Prevent bt_trace_remove_destruction_listener() from being
+ * called in fs_sink_trace_destroy(), which is called by
+ * g_hash_table_remove() below.
+ */
+ trace->ir_trace_destruction_listener_id = UINT64_C(-1);
+ g_hash_table_remove(trace->fs_sink->traces, ir_trace);
+}
+
+BT_HIDDEN
+struct fs_sink_trace *fs_sink_trace_create(struct fs_sink_comp *fs_sink,
+ const bt_trace *ir_trace)
+{
+ int ret;
+ struct fs_sink_trace *trace = g_new0(struct fs_sink_trace, 1);
+ const char *trace_name = bt_trace_get_name(ir_trace);
+ bt_trace_status trace_status;
+
+ if (!trace) {
+ goto end;
+ }
+
+ if (!trace_name) {
+ trace_name = "trace";
+ }
+
+ trace->fs_sink = fs_sink;
+ trace->ir_trace = ir_trace;
+ trace->ir_trace_destruction_listener_id = UINT64_C(-1);
+ trace->tc = translate_trace_class_trace_ir_to_ctf_ir(
+ bt_trace_borrow_class_const(ir_trace));
+ if (!trace->tc) {
+ goto error;
+ }
+
+ trace->path = make_unique_trace_path(fs_sink,
+ fs_sink->output_dir_path->str, trace_name);
+ BT_ASSERT(trace->path);
+ ret = g_mkdir_with_parents(trace->path->str, 0755);
+ if (ret) {
+ BT_LOGE_ERRNO("Cannot create directories for trace directory",
+ ": path=\"%s\"", trace->path->str);
+ goto error;
+ }
+
+ trace->metadata_path = g_string_new(trace->path->str);
+ BT_ASSERT(trace->metadata_path);
+ g_string_append(trace->metadata_path, "/metadata");
+ trace->streams = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+ NULL, (GDestroyNotify) fs_sink_stream_destroy);
+ BT_ASSERT(trace->streams);
+ trace_status = bt_trace_add_destruction_listener(ir_trace,
+ ir_trace_destruction_listener, trace,
+ &trace->ir_trace_destruction_listener_id);
+ if (trace_status) {
+ goto error;
+ }
+
+ g_hash_table_insert(fs_sink->traces, (gpointer) ir_trace, trace);
+ goto end;
+
+error:
+ fs_sink_trace_destroy(trace);
+ trace = NULL;
+
+end:
+ return trace;
+}
--- /dev/null
+#ifndef BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_TRACE_H
+#define BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_TRACE_H
+
+/*
+ * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <babeltrace/babeltrace-internal.h>
+#include <babeltrace/babeltrace.h>
+#include <babeltrace/ctfser-internal.h>
+#include <glib.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "fs-sink-ctf-meta.h"
+
+struct fs_sink_comp;
+
+struct fs_sink_trace {
+ struct fs_sink_comp *fs_sink;
+
+ /* Owned by this */
+ struct fs_sink_ctf_trace_class *tc;
+
+ /*
+ * Weak reference: this object does not own it, and `tc` above
+ * does not own its trace IR trace class either. Instead, we add
+ * a "trace destruction" listener (in create_trace()) so that
+ * this object gets destroyed when the trace object is
+ * destroyed.
+ *
+ * Otherwise (with a strong reference), we would keep this trace
+ * object alive until the upstream message iterator ends. This
+ * could "leak" resources (memory, file descriptors) associated
+ * to traces and streams which otherwise would not exist.
+ */
+ const bt_trace *ir_trace;
+
+ uint64_t ir_trace_destruction_listener_id;
+
+ /* Trace's directory */
+ GString *path;
+
+ /* `metadata` file path */
+ GString *metadata_path;
+
+ /*
+ * Hash table of `const bt_stream *` (weak) to
+ * `struct fs_sink_stream *` (owned by hash table).
+ */
+ GHashTable *streams;
+};
+
+BT_HIDDEN
+struct fs_sink_trace *fs_sink_trace_create(struct fs_sink_comp *fs_sink,
+ const bt_trace *ir_trace);
+
+BT_HIDDEN
+void fs_sink_trace_destroy(struct fs_sink_trace *trace);
+
+#endif /* BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_TRACE_H */
--- /dev/null
+/*
+ * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#define BT_LOG_TAG "PLUGIN-CTF-FS-SINK"
+#include "logging.h"
+
+#include <babeltrace/babeltrace.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <glib.h>
+#include <babeltrace/assert-internal.h>
+#include <babeltrace/ctfser-internal.h>
+
+#include "fs-sink.h"
+#include "fs-sink-trace.h"
+#include "fs-sink-stream.h"
+#include "fs-sink-ctf-meta.h"
+#include "translate-trace-ir-to-ctf-ir.h"
+#include "translate-ctf-ir-to-tsdl.h"
+
+static
+const char * const in_port_name = "in";
+
+static
+bt_self_component_status ensure_output_dir_exists(
+ struct fs_sink_comp *fs_sink)
+{
+ bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK;
+ int ret;
+
+ ret = g_mkdir_with_parents(fs_sink->output_dir_path->str, 0755);
+ if (ret) {
+ BT_LOGE_ERRNO("Cannot create directories for output directory",
+ ": output-dir-path=\"%s\"",
+ fs_sink->output_dir_path->str);
+ status = BT_SELF_COMPONENT_STATUS_ERROR;
+ goto end;
+ }
+
+end:
+ return status;
+}
+
+static
+bt_self_component_status configure_component(struct fs_sink_comp *fs_sink,
+ const bt_value *params)
+{
+ bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK;
+ const bt_value *value;
+
+ value = bt_value_map_borrow_entry_value_const(params, "path");
+ if (!value) {
+ BT_LOGE_STR("Missing mandatory `path` parameter.");
+ status = BT_SELF_COMPONENT_STATUS_ERROR;
+ goto end;
+ }
+
+ if (!bt_value_is_string(value)) {
+ BT_LOGE_STR("`path` parameter: expecting a string.");
+ status = BT_SELF_COMPONENT_STATUS_ERROR;
+ goto end;
+ }
+
+ g_string_assign(fs_sink->output_dir_path,
+ bt_value_string_get(value));
+ value = bt_value_map_borrow_entry_value_const(params,
+ "assume-single-trace");
+ if (value) {
+ if (!bt_value_is_bool(value)) {
+ BT_LOGE_STR("`assume-single-trace` parameter: expecting a boolean.");
+ status = BT_SELF_COMPONENT_STATUS_ERROR;
+ goto end;
+ }
+
+ fs_sink->assume_single_trace = (bool) bt_value_bool_get(value);
+ }
+
+ value = bt_value_map_borrow_entry_value_const(params,
+ "ignore-discarded-events");
+ if (value) {
+ if (!bt_value_is_bool(value)) {
+ BT_LOGE_STR("`ignore-discarded-events` parameter: expecting a boolean.");
+ status = BT_SELF_COMPONENT_STATUS_ERROR;
+ goto end;
+ }
+
+ fs_sink->ignore_discarded_events =
+ (bool) bt_value_bool_get(value);
+ }
+
+ value = bt_value_map_borrow_entry_value_const(params,
+ "ignore-discarded-packets");
+ if (value) {
+ if (!bt_value_is_bool(value)) {
+ BT_LOGE_STR("`ignore-discarded-packets` parameter: expecting a boolean.");
+ status = BT_SELF_COMPONENT_STATUS_ERROR;
+ goto end;
+ }
+
+ fs_sink->ignore_discarded_packets =
+ (bool) bt_value_bool_get(value);
+ }
+
+ value = bt_value_map_borrow_entry_value_const(params,
+ "quiet");
+ if (value) {
+ if (!bt_value_is_bool(value)) {
+ BT_LOGE_STR("`quiet` parameter: expecting a boolean.");
+ status = BT_SELF_COMPONENT_STATUS_ERROR;
+ goto end;
+ }
+
+ fs_sink->quiet = (bool) bt_value_bool_get(value);
+ }
+
+end:
+ return status;
+}
+
+static
+void destroy_fs_sink_comp(struct fs_sink_comp *fs_sink)
+{
+ if (!fs_sink) {
+ goto end;
+ }
+
+ if (fs_sink->output_dir_path) {
+ g_string_free(fs_sink->output_dir_path, TRUE);
+ fs_sink->output_dir_path = NULL;
+ }
+
+ if (fs_sink->traces) {
+ g_hash_table_destroy(fs_sink->traces);
+ fs_sink->traces = NULL;
+ }
+
+ BT_SELF_COMPONENT_PORT_INPUT_MESSAGE_ITERATOR_PUT_REF_AND_RESET(
+ fs_sink->upstream_iter);
+ g_free(fs_sink);
+
+end:
+ return;
+}
+
+BT_HIDDEN
+bt_self_component_status ctf_fs_sink_init(
+ bt_self_component_sink *self_comp, const bt_value *params,
+ void *init_method_data)
+{
+ bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK;
+ struct fs_sink_comp *fs_sink = NULL;
+
+ fs_sink = g_new0(struct fs_sink_comp, 1);
+ if (!fs_sink) {
+ BT_LOGE_STR("Failed to allocate one CTF FS sink structure.");
+ status = BT_SELF_COMPONENT_STATUS_NOMEM;
+ goto end;
+ }
+
+ fs_sink->output_dir_path = g_string_new(NULL);
+ fs_sink->self_comp = self_comp;
+ status = configure_component(fs_sink, params);
+ if (status != BT_SELF_COMPONENT_STATUS_OK) {
+ /* configure_component() logs errors */
+ goto end;
+ }
+
+ if (fs_sink->assume_single_trace &&
+ g_file_test(fs_sink->output_dir_path->str,
+ G_FILE_TEST_EXISTS)) {
+ BT_LOGE("Single trace mode, but output path exists: "
+ "output-path=\"%s\"", fs_sink->output_dir_path->str);
+ status = BT_SELF_COMPONENT_STATUS_ERROR;
+ goto end;
+ }
+
+ status = ensure_output_dir_exists(fs_sink);
+ if (status != BT_SELF_COMPONENT_STATUS_OK) {
+ /* ensure_output_dir_exists() logs errors */
+ goto end;
+ }
+
+ fs_sink->traces = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+ NULL, (GDestroyNotify) fs_sink_trace_destroy);
+ if (!fs_sink->traces) {
+ BT_LOGE_STR("Failed to allocate one GHashTable.");
+ status = BT_SELF_COMPONENT_STATUS_NOMEM;
+ goto end;
+ }
+
+ status = bt_self_component_sink_add_input_port(self_comp, in_port_name,
+ NULL, NULL);
+ if (status != BT_SELF_COMPONENT_STATUS_OK) {
+ goto end;
+ }
+
+ bt_self_component_set_data(
+ bt_self_component_sink_as_self_component(self_comp), fs_sink);
+
+end:
+ if (status != BT_SELF_COMPONENT_STATUS_OK) {
+ destroy_fs_sink_comp(fs_sink);
+ }
+
+ return status;
+}
+
+static inline
+struct fs_sink_stream *borrow_stream(struct fs_sink_comp *fs_sink,
+ const bt_stream *ir_stream)
+{
+ const bt_trace *ir_trace = bt_stream_borrow_trace_const(ir_stream);
+ struct fs_sink_trace *trace;
+ struct fs_sink_stream *stream = NULL;
+
+ trace = g_hash_table_lookup(fs_sink->traces, ir_trace);
+ if (unlikely(!trace)) {
+ if (fs_sink->assume_single_trace &&
+ g_hash_table_size(fs_sink->traces) > 0) {
+ BT_LOGE("Single trace mode, but getting more than one trace: "
+ "stream-name=\"%s\"",
+ bt_stream_get_name(ir_stream));
+ goto end;
+ }
+
+ trace = fs_sink_trace_create(fs_sink, ir_trace);
+ if (!trace) {
+ goto end;
+ }
+ }
+
+ stream = g_hash_table_lookup(trace->streams, ir_stream);
+ if (unlikely(!stream)) {
+ stream = fs_sink_stream_create(trace, ir_stream);
+ if (!stream) {
+ goto end;
+ }
+ }
+
+end:
+ return stream;
+}
+
+static inline
+bt_self_component_status handle_event_msg(struct fs_sink_comp *fs_sink,
+ const bt_message *msg)
+{
+ int ret;
+ bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK;
+ const bt_event *ir_event = bt_message_event_borrow_event_const(msg);
+ const bt_stream *ir_stream = bt_event_borrow_stream_const(ir_event);
+ struct fs_sink_stream *stream;
+ struct fs_sink_ctf_event_class *ec = NULL;
+ const bt_clock_snapshot *cs = NULL;
+
+ stream = borrow_stream(fs_sink, ir_stream);
+ if (unlikely(!stream)) {
+ status = BT_SELF_COMPONENT_STATUS_ERROR;
+ goto end;
+ }
+
+ ret = try_translate_event_class_trace_ir_to_ctf_ir(stream->sc,
+ bt_event_borrow_class_const(ir_event), &ec);
+ if (ret) {
+ status = BT_SELF_COMPONENT_STATUS_ERROR;
+ goto end;
+ }
+
+ BT_ASSERT(ec);
+
+ if (stream->sc->default_clock_class) {
+ (void) bt_message_event_borrow_default_clock_snapshot_const(
+ msg, &cs);
+ }
+
+ ret = fs_sink_stream_write_event(stream, cs, ir_event, ec);
+ if (unlikely(ret)) {
+ status = BT_SELF_COMPONENT_STATUS_ERROR;
+ goto end;
+ }
+
+end:
+ return status;
+}
+
+static inline
+bt_self_component_status handle_packet_beginning_msg(
+ struct fs_sink_comp *fs_sink, const bt_message *msg)
+{
+ int ret;
+ bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK;
+ const bt_packet *ir_packet =
+ bt_message_packet_beginning_borrow_packet_const(msg);
+ const bt_stream *ir_stream = bt_packet_borrow_stream_const(ir_packet);
+ struct fs_sink_stream *stream;
+ const bt_clock_snapshot *cs = NULL;
+
+ stream = borrow_stream(fs_sink, ir_stream);
+ if (unlikely(!stream)) {
+ status = BT_SELF_COMPONENT_STATUS_ERROR;
+ goto end;
+ }
+
+ if (stream->sc->default_clock_class) {
+ (void) bt_message_packet_beginning_borrow_default_clock_snapshot_const(
+ msg, &cs);
+ BT_ASSERT(cs);
+ }
+
+ if (stream->discarded_events_state.in_range) {
+ /*
+ * Make sure that the current discarded events range's
+ * beginning time matches what's expected for CTF 1.8.
+ */
+ if (stream->sc->default_clock_class) {
+ uint64_t expected_cs;
+
+ if (stream->prev_packet_state.end_cs == UINT64_C(-1)) {
+ /* We're opening the first packet */
+ expected_cs = bt_clock_snapshot_get_value(cs);
+ } else {
+ expected_cs = stream->prev_packet_state.end_cs;
+ }
+
+ if (stream->discarded_events_state.beginning_cs !=
+ expected_cs) {
+ BT_LOGE("Incompatible discarded events message: "
+ "unexpected beginning time: "
+ "beginning-cs-val=%" PRIu64 ", "
+ "expected-beginning-cs-val=%" PRIu64 ", "
+ "stream-id=%" PRIu64 ", stream-name=\"%s\", "
+ "trace-name=\"%s\", path=\"%s/%s\"",
+ stream->discarded_events_state.beginning_cs,
+ expected_cs,
+ bt_stream_get_id(ir_stream),
+ bt_stream_get_name(ir_stream),
+ bt_trace_get_name(
+ bt_stream_borrow_trace_const(ir_stream)),
+ stream->trace->path->str, stream->file_name->str);
+ status = BT_SELF_COMPONENT_STATUS_ERROR;
+ goto end;
+ }
+ }
+ }
+
+
+ if (stream->discarded_packets_state.in_range) {
+ if (stream->prev_packet_state.end_cs == UINT64_C(-1)) {
+ BT_LOGE("Incompatible discarded packets message "
+ "occuring before the stream's first packet: "
+ "stream-id=%" PRIu64 ", stream-name=\"%s\", "
+ "trace-name=\"%s\", path=\"%s/%s\"",
+ bt_stream_get_id(ir_stream),
+ bt_stream_get_name(ir_stream),
+ bt_trace_get_name(
+ bt_stream_borrow_trace_const(ir_stream)),
+ stream->trace->path->str, stream->file_name->str);
+ status = BT_SELF_COMPONENT_STATUS_ERROR;
+ goto end;
+ }
+
+ /*
+ * Make sure that the current discarded packets range's
+ * beginning and end times match what's expected for CTF
+ * 1.8.
+ */
+ if (stream->sc->default_clock_class) {
+ uint64_t expected_end_cs =
+ bt_clock_snapshot_get_value(cs);
+
+ if (stream->discarded_packets_state.beginning_cs !=
+ stream->prev_packet_state.end_cs) {
+ BT_LOGE("Incompatible discarded packets message: "
+ "unexpected beginning time: "
+ "beginning-cs-val=%" PRIu64 ", "
+ "expected-beginning-cs-val=%" PRIu64 ", "
+ "stream-id=%" PRIu64 ", stream-name=\"%s\", "
+ "trace-name=\"%s\", path=\"%s/%s\"",
+ stream->discarded_packets_state.beginning_cs,
+ stream->prev_packet_state.end_cs,
+ bt_stream_get_id(ir_stream),
+ bt_stream_get_name(ir_stream),
+ bt_trace_get_name(
+ bt_stream_borrow_trace_const(ir_stream)),
+ stream->trace->path->str, stream->file_name->str);
+ status = BT_SELF_COMPONENT_STATUS_ERROR;
+ goto end;
+ }
+
+ if (stream->discarded_packets_state.end_cs !=
+ expected_end_cs) {
+ BT_LOGE("Incompatible discarded packets message: "
+ "unexpected end time: "
+ "end-cs-val=%" PRIu64 ", "
+ "expected-end-cs-val=%" PRIu64 ", "
+ "stream-id=%" PRIu64 ", stream-name=\"%s\", "
+ "trace-name=\"%s\", path=\"%s/%s\"",
+ stream->discarded_packets_state.beginning_cs,
+ expected_end_cs,
+ bt_stream_get_id(ir_stream),
+ bt_stream_get_name(ir_stream),
+ bt_trace_get_name(
+ bt_stream_borrow_trace_const(ir_stream)),
+ stream->trace->path->str, stream->file_name->str);
+ status = BT_SELF_COMPONENT_STATUS_ERROR;
+ goto end;
+ }
+ }
+ }
+
+ stream->discarded_packets_state.in_range = false;
+ ret = fs_sink_stream_open_packet(stream, cs, ir_packet);
+ if (ret) {
+ status = BT_SELF_COMPONENT_STATUS_ERROR;
+ goto end;
+ }
+
+end:
+ return status;
+}
+
+static inline
+bt_self_component_status handle_packet_end_msg(
+ struct fs_sink_comp *fs_sink, const bt_message *msg)
+{
+ int ret;
+ bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK;
+ const bt_packet *ir_packet =
+ bt_message_packet_end_borrow_packet_const(msg);
+ const bt_stream *ir_stream = bt_packet_borrow_stream_const(ir_packet);
+ struct fs_sink_stream *stream;
+ const bt_clock_snapshot *cs = NULL;
+
+ stream = borrow_stream(fs_sink, ir_stream);
+ if (unlikely(!stream)) {
+ status = BT_SELF_COMPONENT_STATUS_ERROR;
+ goto end;
+ }
+
+ if (stream->sc->default_clock_class) {
+ (void) bt_message_packet_end_borrow_default_clock_snapshot_const(
+ msg, &cs);
+ BT_ASSERT(cs);
+ }
+
+ if (stream->sc->default_clock_class) {
+ (void) bt_message_packet_end_borrow_default_clock_snapshot_const(
+ msg, &cs);
+ BT_ASSERT(cs);
+ }
+
+ if (stream->discarded_events_state.in_range) {
+ /*
+ * Make sure that the current discarded events range's
+ * end time matches what's expected for CTF 1.8.
+ */
+ if (stream->sc->default_clock_class) {
+ uint64_t expected_cs = bt_clock_snapshot_get_value(cs);
+
+ if (stream->discarded_events_state.end_cs !=
+ expected_cs) {
+ BT_LOGE("Incompatible discarded events message: "
+ "unexpected end time: "
+ "end-cs-val=%" PRIu64 ", "
+ "expected-end-cs-val=%" PRIu64 ", "
+ "stream-id=%" PRIu64 ", stream-name=\"%s\", "
+ "trace-name=\"%s\", path=\"%s/%s\"",
+ stream->discarded_events_state.end_cs,
+ expected_cs,
+ bt_stream_get_id(ir_stream),
+ bt_stream_get_name(ir_stream),
+ bt_trace_get_name(
+ bt_stream_borrow_trace_const(ir_stream)),
+ stream->trace->path->str, stream->file_name->str);
+ status = BT_SELF_COMPONENT_STATUS_ERROR;
+ goto end;
+ }
+ }
+ }
+
+ ret = fs_sink_stream_close_packet(stream, cs);
+ if (ret) {
+ status = BT_SELF_COMPONENT_STATUS_ERROR;
+ goto end;
+ }
+
+ stream->discarded_events_state.in_range = false;
+
+end:
+ return status;
+}
+
+static inline
+bt_self_component_status handle_stream_beginning_msg(
+ struct fs_sink_comp *fs_sink, const bt_message *msg)
+{
+ bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK;
+ const bt_stream *ir_stream =
+ bt_message_stream_beginning_borrow_stream_const(msg);
+ struct fs_sink_stream *stream;
+
+ stream = borrow_stream(fs_sink, ir_stream);
+ if (!stream) {
+ status = BT_SELF_COMPONENT_STATUS_ERROR;
+ goto end;
+ }
+
+ BT_LOGI("Created new, empty stream file: "
+ "stream-id=%" PRIu64 ", stream-name=\"%s\", "
+ "trace-name=\"%s\", path=\"%s/%s\"",
+ bt_stream_get_id(ir_stream), bt_stream_get_name(ir_stream),
+ bt_trace_get_name(bt_stream_borrow_trace_const(ir_stream)),
+ stream->trace->path->str, stream->file_name->str);
+
+end:
+ return status;
+}
+
+static inline
+bt_self_component_status handle_stream_end_msg(struct fs_sink_comp *fs_sink,
+ const bt_message *msg)
+{
+ bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK;
+ const bt_stream *ir_stream =
+ bt_message_stream_end_borrow_stream_const(msg);
+ struct fs_sink_stream *stream;
+
+ stream = borrow_stream(fs_sink, ir_stream);
+ if (!stream) {
+ status = BT_SELF_COMPONENT_STATUS_ERROR;
+ goto end;
+ }
+
+ BT_LOGI("Closing stream file: "
+ "stream-id=%" PRIu64 ", stream-name=\"%s\", "
+ "trace-name=\"%s\", path=\"%s/%s\"",
+ bt_stream_get_id(ir_stream), bt_stream_get_name(ir_stream),
+ bt_trace_get_name(bt_stream_borrow_trace_const(ir_stream)),
+ stream->trace->path->str, stream->file_name->str);
+
+ /*
+ * This destroys the stream object and frees all its resources,
+ * closing the stream file.
+ */
+ g_hash_table_remove(stream->trace->streams, ir_stream);
+
+end:
+ return status;
+}
+
+static inline
+bt_self_component_status handle_discarded_events_msg(
+ struct fs_sink_comp *fs_sink, const bt_message *msg)
+{
+ bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK;
+ const bt_stream *ir_stream =
+ bt_message_discarded_events_borrow_stream_const(msg);
+ struct fs_sink_stream *stream;
+ const bt_clock_snapshot *cs = NULL;
+ bt_property_availability avail;
+ uint64_t count;
+
+ stream = borrow_stream(fs_sink, ir_stream);
+ if (!stream) {
+ status = BT_SELF_COMPONENT_STATUS_ERROR;
+ goto end;
+ }
+
+ if (fs_sink->ignore_discarded_events) {
+ BT_LOGI("Ignoring discarded events message: "
+ "stream-id=%" PRIu64 ", stream-name=\"%s\", "
+ "trace-name=\"%s\", path=\"%s/%s\"",
+ bt_stream_get_id(ir_stream),
+ bt_stream_get_name(ir_stream),
+ bt_trace_get_name(
+ bt_stream_borrow_trace_const(ir_stream)),
+ stream->trace->path->str, stream->file_name->str);
+ goto end;
+ }
+
+ if (stream->discarded_events_state.in_range) {
+ BT_LOGE("Unsupported contiguous discarded events message: "
+ "stream-id=%" PRIu64 ", stream-name=\"%s\", "
+ "trace-name=\"%s\", path=\"%s/%s\"",
+ bt_stream_get_id(ir_stream),
+ bt_stream_get_name(ir_stream),
+ bt_trace_get_name(
+ bt_stream_borrow_trace_const(ir_stream)),
+ stream->trace->path->str, stream->file_name->str);
+ status = BT_SELF_COMPONENT_STATUS_ERROR;
+ goto end;
+ }
+
+ if (stream->packet_state.is_open) {
+ BT_LOGE("Unsupported discarded events message occuring "
+ "within a packet: "
+ "stream-id=%" PRIu64 ", stream-name=\"%s\", "
+ "trace-name=\"%s\", path=\"%s/%s\"",
+ bt_stream_get_id(ir_stream),
+ bt_stream_get_name(ir_stream),
+ bt_trace_get_name(
+ bt_stream_borrow_trace_const(ir_stream)),
+ stream->trace->path->str, stream->file_name->str);
+ status = BT_SELF_COMPONENT_STATUS_ERROR;
+ goto end;
+ }
+
+ stream->discarded_events_state.in_range = true;
+
+ if (stream->sc->default_clock_class) {
+ /*
+ * The clock snapshot values will be validated when
+ * handling the next "packet beginning" message.
+ */
+ (void) bt_message_discarded_events_borrow_default_beginning_clock_snapshot_const(
+ msg, &cs);
+ BT_ASSERT(cs);
+ stream->discarded_events_state.beginning_cs =
+ bt_clock_snapshot_get_value(cs);
+ cs = NULL;
+ (void) bt_message_discarded_events_borrow_default_end_clock_snapshot_const(
+ msg, &cs);
+ BT_ASSERT(cs);
+ stream->discarded_events_state.end_cs =
+ bt_clock_snapshot_get_value(cs);
+ } else {
+ stream->discarded_events_state.beginning_cs = UINT64_C(-1);
+ stream->discarded_events_state.end_cs = UINT64_C(-1);
+ }
+
+ avail = bt_message_discarded_events_get_count(msg, &count);
+ if (avail != BT_PROPERTY_AVAILABILITY_AVAILABLE) {
+ count = 1;
+ }
+
+ stream->packet_state.discarded_events_counter += count;
+
+end:
+ return status;
+}
+
+static inline
+bt_self_component_status handle_discarded_packets_msg(
+ struct fs_sink_comp *fs_sink, const bt_message *msg)
+{
+ bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK;
+ const bt_stream *ir_stream =
+ bt_message_discarded_packets_borrow_stream_const(msg);
+ struct fs_sink_stream *stream;
+ const bt_clock_snapshot *cs = NULL;
+ bt_property_availability avail;
+ uint64_t count;
+
+ stream = borrow_stream(fs_sink, ir_stream);
+ if (!stream) {
+ status = BT_SELF_COMPONENT_STATUS_ERROR;
+ goto end;
+ }
+
+ if (fs_sink->ignore_discarded_packets) {
+ BT_LOGI("Ignoring discarded packets message: "
+ "stream-id=%" PRIu64 ", stream-name=\"%s\", "
+ "trace-name=\"%s\", path=\"%s/%s\"",
+ bt_stream_get_id(ir_stream),
+ bt_stream_get_name(ir_stream),
+ bt_trace_get_name(
+ bt_stream_borrow_trace_const(ir_stream)),
+ stream->trace->path->str, stream->file_name->str);
+ goto end;
+ }
+
+ if (stream->discarded_packets_state.in_range) {
+ BT_LOGE("Unsupported contiguous discarded packets message: "
+ "stream-id=%" PRIu64 ", stream-name=\"%s\", "
+ "trace-name=\"%s\", path=\"%s/%s\"",
+ bt_stream_get_id(ir_stream),
+ bt_stream_get_name(ir_stream),
+ bt_trace_get_name(
+ bt_stream_borrow_trace_const(ir_stream)),
+ stream->trace->path->str, stream->file_name->str);
+ status = BT_SELF_COMPONENT_STATUS_ERROR;
+ goto end;
+ }
+
+ if (stream->packet_state.is_open) {
+ BT_LOGE("Unsupported discarded packets message occuring "
+ "within a packet: "
+ "stream-id=%" PRIu64 ", stream-name=\"%s\", "
+ "trace-name=\"%s\", path=\"%s/%s\"",
+ bt_stream_get_id(ir_stream),
+ bt_stream_get_name(ir_stream),
+ bt_trace_get_name(
+ bt_stream_borrow_trace_const(ir_stream)),
+ stream->trace->path->str, stream->file_name->str);
+ status = BT_SELF_COMPONENT_STATUS_ERROR;
+ goto end;
+ }
+
+ stream->discarded_packets_state.in_range = true;
+
+ if (stream->sc->default_clock_class) {
+ /*
+ * The clock snapshot values will be validated when
+ * handling the next "packet beginning" message.
+ */
+ (void) bt_message_discarded_packets_borrow_default_beginning_clock_snapshot_const(
+ msg, &cs);
+ BT_ASSERT(cs);
+ stream->discarded_packets_state.beginning_cs =
+ bt_clock_snapshot_get_value(cs);
+ cs = NULL;
+ (void) bt_message_discarded_packets_borrow_default_end_clock_snapshot_const(
+ msg, &cs);
+ BT_ASSERT(cs);
+ stream->discarded_packets_state.end_cs =
+ bt_clock_snapshot_get_value(cs);
+ } else {
+ stream->discarded_packets_state.beginning_cs = UINT64_C(-1);
+ stream->discarded_packets_state.end_cs = UINT64_C(-1);
+ }
+
+ avail = bt_message_discarded_packets_get_count(msg, &count);
+ if (avail != BT_PROPERTY_AVAILABILITY_AVAILABLE) {
+ count = 1;
+ }
+
+ stream->packet_state.seq_num += count;
+
+end:
+ return status;
+}
+
+static inline
+void put_messages(bt_message_array_const msgs, uint64_t count)
+{
+ uint64_t i;
+
+ for (i = 0; i < count; i++) {
+ BT_MESSAGE_PUT_REF_AND_RESET(msgs[i]);
+ }
+}
+
+BT_HIDDEN
+bt_self_component_status ctf_fs_sink_consume(bt_self_component_sink *self_comp)
+{
+ bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK;
+ struct fs_sink_comp *fs_sink;
+ bt_message_iterator_status it_status;
+ uint64_t msg_count = 0;
+ bt_message_array_const msgs;
+
+ fs_sink = bt_self_component_get_data(
+ bt_self_component_sink_as_self_component(self_comp));
+ BT_ASSERT(fs_sink);
+ BT_ASSERT(fs_sink->upstream_iter);
+
+ /* Consume messages */
+ it_status = bt_self_component_port_input_message_iterator_next(
+ fs_sink->upstream_iter, &msgs, &msg_count);
+ if (it_status < 0) {
+ status = BT_SELF_COMPONENT_STATUS_ERROR;
+ goto end;
+ }
+
+ switch (it_status) {
+ case BT_MESSAGE_ITERATOR_STATUS_OK:
+ {
+ uint64_t i;
+
+ for (i = 0; i < msg_count; i++) {
+ const bt_message *msg = msgs[i];
+
+ BT_ASSERT(msg);
+
+ switch (bt_message_get_type(msg)) {
+ case BT_MESSAGE_TYPE_EVENT:
+ status = handle_event_msg(fs_sink, msg);
+ break;
+ case BT_MESSAGE_TYPE_PACKET_BEGINNING:
+ status = handle_packet_beginning_msg(
+ fs_sink, msg);
+ break;
+ case BT_MESSAGE_TYPE_PACKET_END:
+ status = handle_packet_end_msg(
+ fs_sink, msg);
+ break;
+ case BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY:
+ /* Ignore */
+ BT_LOGD_STR("Ignoring message iterator inactivity message.");
+ break;
+ case BT_MESSAGE_TYPE_STREAM_BEGINNING:
+ status = handle_stream_beginning_msg(
+ fs_sink, msg);
+ break;
+ case BT_MESSAGE_TYPE_STREAM_END:
+ status = handle_stream_end_msg(
+ fs_sink, msg);
+ break;
+ case BT_MESSAGE_TYPE_STREAM_ACTIVITY_BEGINNING:
+ case BT_MESSAGE_TYPE_STREAM_ACTIVITY_END:
+ /* Not supported by CTF 1.8 */
+ BT_LOGD_STR("Ignoring stream activity message.");
+ break;
+ case BT_MESSAGE_TYPE_DISCARDED_EVENTS:
+ status = handle_discarded_events_msg(
+ fs_sink, msg);
+ break;
+ case BT_MESSAGE_TYPE_DISCARDED_PACKETS:
+ status = handle_discarded_packets_msg(
+ fs_sink, msg);
+ break;
+ default:
+ abort();
+ }
+
+ BT_MESSAGE_PUT_REF_AND_RESET(msgs[i]);
+
+ if (status != BT_SELF_COMPONENT_STATUS_OK) {
+ BT_LOGE("Failed to handle message: "
+ "generated CTF traces could be incomplete: "
+ "output-dir-path=\"%s\"",
+ fs_sink->output_dir_path->str);
+ goto error;
+ }
+ }
+
+ break;
+ }
+ case BT_MESSAGE_ITERATOR_STATUS_AGAIN:
+ status = BT_SELF_COMPONENT_STATUS_AGAIN;
+ break;
+ case BT_MESSAGE_ITERATOR_STATUS_END:
+ /* TODO: Finalize all traces (should already be done?) */
+ status = BT_SELF_COMPONENT_STATUS_END;
+ break;
+ case BT_MESSAGE_ITERATOR_STATUS_NOMEM:
+ status = BT_SELF_COMPONENT_STATUS_NOMEM;
+ break;
+ case BT_MESSAGE_ITERATOR_STATUS_ERROR:
+ status = BT_SELF_COMPONENT_STATUS_NOMEM;
+ break;
+ default:
+ break;
+ }
+
+ goto end;
+
+error:
+ BT_ASSERT(status != BT_SELF_COMPONENT_STATUS_OK);
+ put_messages(msgs, msg_count);
+
+end:
+ return status;
+}
+
+BT_HIDDEN
+bt_self_component_status ctf_fs_sink_graph_is_configured(
+ bt_self_component_sink *self_comp)
+{
+ bt_self_component_status status = BT_SELF_COMPONENT_STATUS_OK;
+ struct fs_sink_comp *fs_sink = bt_self_component_get_data(
+ bt_self_component_sink_as_self_component(self_comp));
+
+ fs_sink->upstream_iter =
+ bt_self_component_port_input_message_iterator_create(
+ bt_self_component_sink_borrow_input_port_by_name(
+ self_comp, in_port_name));
+ if (!fs_sink->upstream_iter) {
+ status = BT_SELF_COMPONENT_STATUS_NOMEM;
+ goto end;
+ }
+
+end:
+ return status;
+}
+
+BT_HIDDEN
+void ctf_fs_sink_finalize(bt_self_component_sink *self_comp)
+{
+ struct fs_sink_comp *fs_sink = bt_self_component_get_data(
+ bt_self_component_sink_as_self_component(self_comp));
+
+ destroy_fs_sink_comp(fs_sink);
+}
--- /dev/null
+#ifndef BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_H
+#define BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_H
+
+/*
+ * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <babeltrace/babeltrace-internal.h>
+#include <babeltrace/babeltrace.h>
+#include <stdbool.h>
+#include <glib.h>
+
+struct fs_sink_comp {
+ bt_self_component_sink *self_comp;
+
+ /* Owned by this */
+ bt_self_component_port_input_message_iterator *upstream_iter;
+
+ /* Base output directory path */
+ GString *output_dir_path;
+
+ bool assume_single_trace;
+ bool ignore_discarded_events;
+ bool ignore_discarded_packets;
+ bool quiet;
+
+ /*
+ * Hash table of `const bt_trace *` (weak) to
+ * `struct fs_sink_trace *` (owned by hash table).
+ */
+ GHashTable *traces;
+};
+
+BT_HIDDEN
+bt_self_component_status ctf_fs_sink_init(
+ bt_self_component_sink *component,
+ const bt_value *params,
+ void *init_method_data);
+
+BT_HIDDEN
+bt_self_component_status ctf_fs_sink_consume(
+ bt_self_component_sink *component);
+
+BT_HIDDEN
+bt_self_component_status ctf_fs_sink_graph_is_configured(
+ bt_self_component_sink *component);
+
+BT_HIDDEN
+void ctf_fs_sink_finalize(bt_self_component_sink *component);
+
+#endif /* BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_H */
--- /dev/null
+/*
+ * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#define BT_LOG_TAG "PLUGIN-CTF-FS-SINK-TRANSLATE-CTF-IR-TO-TSDL"
+#include "logging.h"
+
+#include <babeltrace/babeltrace.h>
+#include <babeltrace/babeltrace-internal.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <glib.h>
+#include <babeltrace/assert-internal.h>
+#include <babeltrace/endian-internal.h>
+
+#include "fs-sink-ctf-meta.h"
+
+struct ctx {
+ unsigned int indent_level;
+ GString *tsdl;
+};
+
+static inline
+void append_indent(struct ctx *ctx)
+{
+ unsigned int i;
+
+ for (i = 0; i < ctx->indent_level; i++) {
+ g_string_append_c(ctx->tsdl, '\t');
+ }
+}
+
+static
+void append_uuid(struct ctx *ctx, bt_uuid uuid)
+{
+ g_string_append_printf(ctx->tsdl,
+ "\"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\"",
+ (unsigned int) uuid[0],
+ (unsigned int) uuid[1],
+ (unsigned int) uuid[2],
+ (unsigned int) uuid[3],
+ (unsigned int) uuid[4],
+ (unsigned int) uuid[5],
+ (unsigned int) uuid[6],
+ (unsigned int) uuid[7],
+ (unsigned int) uuid[8],
+ (unsigned int) uuid[9],
+ (unsigned int) uuid[10],
+ (unsigned int) uuid[11],
+ (unsigned int) uuid[12],
+ (unsigned int) uuid[13],
+ (unsigned int) uuid[14],
+ (unsigned int) uuid[15]);
+}
+
+static
+void append_quoted_string_content(struct ctx *ctx, const char *str)
+{
+ const char *ch;
+
+ for (ch = str; *ch != '\0'; ch++) {
+ unsigned char uch = (unsigned char) *ch;
+
+ if (uch < 32 || uch >= 127) {
+ switch (*ch) {
+ case '\a':
+ g_string_append(ctx->tsdl, "\\a");
+ break;
+ case '\b':
+ g_string_append(ctx->tsdl, "\\b");
+ break;
+ case '\f':
+ g_string_append(ctx->tsdl, "\\f");
+ break;
+ case '\n':
+ g_string_append(ctx->tsdl, "\\n");
+ break;
+ case '\r':
+ g_string_append(ctx->tsdl, "\\r");
+ break;
+ case '\t':
+ g_string_append(ctx->tsdl, "\\t");
+ break;
+ case '\v':
+ g_string_append(ctx->tsdl, "\\v");
+ break;
+ default:
+ g_string_append_printf(ctx->tsdl, "\\x%02x",
+ (unsigned int) uch);
+ break;
+ }
+ } else if (*ch == '"' || *ch == '\\') {
+ g_string_append_c(ctx->tsdl, '\\');
+ g_string_append_c(ctx->tsdl, *ch);
+ } else {
+ g_string_append_c(ctx->tsdl, *ch);
+ }
+ }
+}
+
+static
+void append_quoted_string(struct ctx *ctx, const char *str)
+{
+ g_string_append_c(ctx->tsdl, '"');
+ append_quoted_string_content(ctx, str);
+ g_string_append_c(ctx->tsdl, '"');
+}
+
+static
+void append_integer_field_class_from_props(struct ctx *ctx, unsigned int size,
+ unsigned int alignment, bool is_signed,
+ bt_field_class_integer_preferred_display_base disp_base,
+ const char *mapped_clock_class_name, const char *field_name,
+ bool end)
+{
+ g_string_append_printf(ctx->tsdl,
+ "integer { size = %u; align = %u;",
+ size, alignment);
+
+ if (is_signed) {
+ g_string_append(ctx->tsdl, " signed = true;");
+ }
+
+ if (disp_base != BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL) {
+ g_string_append(ctx->tsdl, " base = ");
+
+ switch (disp_base) {
+ case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_BINARY:
+ g_string_append(ctx->tsdl, "b");
+ break;
+ case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL:
+ g_string_append(ctx->tsdl, "o");
+ break;
+ case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL:
+ g_string_append(ctx->tsdl, "x");
+ break;
+ default:
+ abort();
+ }
+
+ g_string_append_c(ctx->tsdl, ';');
+ }
+
+ if (mapped_clock_class_name) {
+ g_string_append_printf(ctx->tsdl, " map = clock.%s.value;",
+ mapped_clock_class_name);
+ }
+
+ g_string_append(ctx->tsdl, " }");
+
+ if (field_name) {
+ g_string_append_printf(ctx->tsdl, " %s", field_name);
+ }
+
+ if (end) {
+ g_string_append(ctx->tsdl, ";\n");
+ }
+}
+
+static
+void append_end_block(struct ctx *ctx)
+{
+ ctx->indent_level--;
+ append_indent(ctx);
+ g_string_append(ctx->tsdl, "}");
+}
+
+static
+void append_end_block_semi_nl(struct ctx *ctx)
+{
+ ctx->indent_level--;
+ append_indent(ctx);
+ g_string_append(ctx->tsdl, "};\n");
+}
+
+static
+void append_end_block_semi_nl_nl(struct ctx *ctx)
+{
+ append_end_block_semi_nl(ctx);
+ g_string_append_c(ctx->tsdl, '\n');
+}
+
+static
+void append_integer_field_class(struct ctx *ctx,
+ struct fs_sink_ctf_field_class_int *fc)
+{
+ const bt_field_class *ir_fc = fc->base.base.ir_fc;
+ bt_field_class_type type = bt_field_class_get_type(ir_fc);
+ bool is_signed = type == BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION ||
+ type == BT_FIELD_CLASS_TYPE_SIGNED_INTEGER;
+
+ if (type == BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION ||
+ type == BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION) {
+ g_string_append(ctx->tsdl, "enum : ");
+ }
+
+ append_integer_field_class_from_props(ctx, fc->base.size,
+ fc->base.base.alignment, is_signed,
+ bt_field_class_integer_get_preferred_display_base(ir_fc),
+ NULL, NULL, false);
+
+ if (type == BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION ||
+ type == BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION) {
+ uint64_t i;
+
+ g_string_append(ctx->tsdl, " {\n");
+ ctx->indent_level++;
+
+ for (i = 0; i < bt_field_class_enumeration_get_mapping_count(ir_fc); i++) {
+ const char *label;
+ const bt_field_class_unsigned_enumeration_mapping_ranges *u_ranges;
+ const bt_field_class_signed_enumeration_mapping_ranges *i_ranges;
+ uint64_t range_count;
+ uint64_t range_i;
+
+ if (is_signed) {
+ bt_field_class_signed_enumeration_borrow_mapping_by_index_const(
+ ir_fc, i, &label, &i_ranges);
+ range_count = bt_field_class_signed_enumeration_mapping_ranges_get_range_count(
+ i_ranges);
+ } else {
+ bt_field_class_unsigned_enumeration_borrow_mapping_by_index_const(
+ ir_fc, i, &label, &u_ranges);
+ range_count = bt_field_class_unsigned_enumeration_mapping_ranges_get_range_count(
+ u_ranges);
+ }
+
+ for (range_i = 0; range_i < range_count; range_i++) {
+ append_indent(ctx);
+
+ /*
+ * Systematically prepend `_` to the
+ * mapping's label as this could be used
+ * as the tag of a subsequent variant
+ * field class and variant FC option
+ * names are systematically protected
+ * with a leading `_`.
+ *
+ * FIXME: This is temporary as the
+ * library's API should change to
+ * decouple variant FC option names from
+ * selector FC labels. The current
+ * drawback is that an original label
+ * `HELLO` becomes `_HELLO` in the
+ * generated metadata, therefore tools
+ * expecting `HELLO` could fail.
+ */
+ g_string_append(ctx->tsdl, "\"_");
+ append_quoted_string_content(ctx, label);
+ g_string_append(ctx->tsdl, "\" = ");
+
+ if (is_signed) {
+ int64_t lower, upper;
+
+ bt_field_class_signed_enumeration_mapping_ranges_get_range_by_index(
+ i_ranges, range_i,
+ &lower, &upper);
+
+ if (lower == upper) {
+ g_string_append_printf(
+ ctx->tsdl, "%" PRId64,
+ lower);
+ } else {
+ g_string_append_printf(
+ ctx->tsdl, "%" PRId64 " ... %" PRId64,
+ lower, upper);
+ }
+ } else {
+ uint64_t lower, upper;
+
+ bt_field_class_unsigned_enumeration_mapping_ranges_get_range_by_index(
+ u_ranges, range_i,
+ &lower, &upper);
+
+ if (lower == upper) {
+ g_string_append_printf(
+ ctx->tsdl, "%" PRIu64,
+ lower);
+ } else {
+ g_string_append_printf(
+ ctx->tsdl, "%" PRIu64 " ... %" PRIu64,
+ lower, upper);
+ }
+ }
+
+ g_string_append(ctx->tsdl, ",\n");
+ }
+ }
+
+ append_end_block(ctx);
+ }
+}
+
+static
+void append_float_field_class(struct ctx *ctx,
+ struct fs_sink_ctf_field_class_float *fc)
+{
+ unsigned int mant_dig, exp_dig;
+
+ if (bt_field_class_real_is_single_precision(fc->base.base.ir_fc)) {
+ mant_dig = 24;
+ exp_dig = 8;
+ } else {
+ mant_dig = 53;
+ exp_dig = 11;
+ }
+
+ g_string_append_printf(ctx->tsdl,
+ "floating_point { mant_dig = %u; exp_dig = %u; align = %u; }",
+ mant_dig, exp_dig, fc->base.base.alignment);
+}
+
+static
+void append_string_field_class(struct ctx *ctx,
+ struct fs_sink_ctf_field_class_float *fc)
+{
+ g_string_append(ctx->tsdl, "string { encoding = UTF8; }");
+}
+
+static
+void append_field_class(struct ctx *ctx, struct fs_sink_ctf_field_class *fc);
+
+static
+void append_member(struct ctx *ctx, const char *name,
+ struct fs_sink_ctf_field_class *fc)
+{
+ GString *lengths = NULL;
+ const char *lengths_str = "";
+
+ while (fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY ||
+ fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE) {
+ if (!lengths) {
+ lengths = g_string_new(NULL);
+ BT_ASSERT(lengths);
+ }
+
+ if (fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY) {
+ struct fs_sink_ctf_field_class_array *array_fc =
+ (void *) fc;
+
+ g_string_append_printf(lengths, "[%" PRIu64 "]",
+ array_fc->length);
+ fc = array_fc->base.elem_fc;
+ } else {
+ struct fs_sink_ctf_field_class_sequence *seq_fc =
+ (void *) fc;
+
+ g_string_append_printf(lengths, "[%s]",
+ seq_fc->length_ref->str);
+ fc = seq_fc->base.elem_fc;
+ }
+ }
+
+ append_field_class(ctx, fc);
+
+ if (lengths) {
+ lengths_str = lengths->str;
+ }
+
+ g_string_append_printf(ctx->tsdl, " %s%s;\n", name, lengths_str);
+
+ if (lengths) {
+ g_string_free(lengths, TRUE);
+ }
+}
+
+static
+void append_struct_field_class_members(struct ctx *ctx,
+ struct fs_sink_ctf_field_class_struct *struct_fc)
+{
+ uint64_t i;
+
+ for (i = 0; i < struct_fc->members->len; i++) {
+ struct fs_sink_ctf_named_field_class *named_fc =
+ fs_sink_ctf_field_class_struct_borrow_member_by_index(
+ struct_fc, i);
+ struct fs_sink_ctf_field_class *fc = named_fc->fc;
+
+ if (fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE) {
+ struct fs_sink_ctf_field_class_sequence *seq_fc =
+ (void *) fc;
+
+ if (seq_fc->length_is_before) {
+ append_indent(ctx);
+ append_integer_field_class_from_props(ctx,
+ 32, 8, false,
+ BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL,
+ NULL, seq_fc->length_ref->str, true);
+ }
+ } else if (fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT) {
+ struct fs_sink_ctf_field_class_variant *var_fc =
+ (void *) fc;
+
+ if (var_fc->tag_is_before) {
+ append_indent(ctx);
+ g_string_append(ctx->tsdl, "enum : ");
+ append_integer_field_class_from_props(ctx,
+ 16, 8, false,
+ BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL,
+ NULL, NULL, false);
+ g_string_append(ctx->tsdl, " {\n");
+ ctx->indent_level++;
+
+ for (i = 0; i < var_fc->options->len; i++) {
+ struct fs_sink_ctf_named_field_class *named_fc =
+ fs_sink_ctf_field_class_variant_borrow_option_by_index(
+ var_fc, i);
+
+ append_indent(ctx);
+ g_string_append_printf(ctx->tsdl,
+ "\"%s\" = %" PRIu64 ",\n",
+ named_fc->name->str, i);
+ }
+
+ append_end_block(ctx);
+ g_string_append_printf(ctx->tsdl, " %s;\n",
+ var_fc->tag_ref->str);
+ }
+ }
+
+ append_indent(ctx);
+ append_member(ctx, named_fc->name->str, fc);
+ }
+}
+
+static
+void append_struct_field_class(struct ctx *ctx,
+ struct fs_sink_ctf_field_class_struct *fc)
+{
+ g_string_append(ctx->tsdl, "struct {\n");
+ ctx->indent_level++;
+ append_struct_field_class_members(ctx, fc);
+ append_end_block(ctx);
+ g_string_append_printf(ctx->tsdl, " align(%u)",
+ fc->base.alignment);
+}
+
+static
+void append_variant_field_class(struct ctx *ctx,
+ struct fs_sink_ctf_field_class_variant *var_fc)
+{
+ uint64_t i;
+
+ g_string_append_printf(ctx->tsdl, "variant <%s> {\n",
+ var_fc->tag_ref->str);
+ ctx->indent_level++;
+
+ for (i = 0; i < var_fc->options->len; i++) {
+ struct fs_sink_ctf_named_field_class *named_fc =
+ fs_sink_ctf_field_class_variant_borrow_option_by_index(
+ var_fc, i);
+
+ append_indent(ctx);
+ append_member(ctx, named_fc->name->str, named_fc->fc);
+ }
+
+ append_end_block(ctx);
+}
+
+static
+void append_field_class(struct ctx *ctx, struct fs_sink_ctf_field_class *fc)
+{
+ switch (fc->type) {
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_INT:
+ append_integer_field_class(ctx, (void *) fc);
+ break;
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_FLOAT:
+ append_float_field_class(ctx, (void *) fc);
+ break;
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_STRING:
+ append_string_field_class(ctx, (void *) fc);
+ break;
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT:
+ append_struct_field_class(ctx, (void *) fc);
+ break;
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT:
+ append_variant_field_class(ctx, (void *) fc);
+ break;
+ default:
+ abort();
+ }
+}
+
+static
+void append_event_class(struct ctx *ctx, struct fs_sink_ctf_event_class *ec)
+{
+ const char *str;
+ bt_event_class_log_level log_level;
+
+ /* Event class */
+ append_indent(ctx);
+ g_string_append(ctx->tsdl, "event {\n");
+ ctx->indent_level++;
+
+ /* Event class properties */
+ append_indent(ctx);
+ g_string_append(ctx->tsdl, "name = ");
+ str = bt_event_class_get_name(ec->ir_ec);
+ if (!str) {
+ str = "unknown";
+ }
+
+ append_quoted_string(ctx, str);
+ g_string_append(ctx->tsdl, ";\n");
+ append_indent(ctx);
+ g_string_append_printf(ctx->tsdl, "stream_id = %" PRIu64 ";\n",
+ bt_stream_class_get_id(ec->sc->ir_sc));
+ append_indent(ctx);
+ g_string_append_printf(ctx->tsdl, "id = %" PRIu64 ";\n",
+ bt_event_class_get_id(ec->ir_ec));
+
+ str = bt_event_class_get_emf_uri(ec->ir_ec);
+ if (str) {
+ append_indent(ctx);
+ g_string_append(ctx->tsdl, "model.emf.uri = ");
+ append_quoted_string(ctx, str);
+ g_string_append(ctx->tsdl, ";\n");
+ }
+
+ if (bt_event_class_get_log_level(ec->ir_ec, &log_level) ==
+ BT_PROPERTY_AVAILABILITY_AVAILABLE) {
+ unsigned int level;
+
+ append_indent(ctx);
+ g_string_append(ctx->tsdl, "loglevel = ");
+
+ switch (log_level) {
+ case BT_EVENT_CLASS_LOG_LEVEL_EMERGENCY:
+ level = 0;
+ break;
+ case BT_EVENT_CLASS_LOG_LEVEL_ALERT:
+ level = 1;
+ break;
+ case BT_EVENT_CLASS_LOG_LEVEL_CRITICAL:
+ level = 2;
+ break;
+ case BT_EVENT_CLASS_LOG_LEVEL_ERROR:
+ level = 3;
+ break;
+ case BT_EVENT_CLASS_LOG_LEVEL_WARNING:
+ level = 4;
+ break;
+ case BT_EVENT_CLASS_LOG_LEVEL_NOTICE:
+ level = 5;
+ break;
+ case BT_EVENT_CLASS_LOG_LEVEL_INFO:
+ level = 6;
+ break;
+ case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM:
+ level = 7;
+ break;
+ case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM:
+ level = 8;
+ break;
+ case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS:
+ level = 9;
+ break;
+ case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE:
+ level = 10;
+ break;
+ case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT:
+ level = 11;
+ break;
+ case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION:
+ level = 12;
+ break;
+ case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_LINE:
+ level = 13;
+ break;
+ case BT_EVENT_CLASS_LOG_LEVEL_DEBUG:
+ level = 14;
+ break;
+ default:
+ abort();
+ }
+
+ g_string_append_printf(ctx->tsdl, "%u;\n", level);
+ }
+
+ /* Event specific context field class */
+ if (ec->spec_context_fc) {
+ append_indent(ctx);
+ g_string_append(ctx->tsdl, "context := ");
+ append_field_class(ctx, ec->spec_context_fc);
+ g_string_append(ctx->tsdl, ";\n");
+ }
+
+ /* Event payload field class */
+ if (ec->payload_fc) {
+ append_indent(ctx);
+ g_string_append(ctx->tsdl, "fields := ");
+ append_field_class(ctx, ec->payload_fc);
+ g_string_append(ctx->tsdl, ";\n");
+ }
+
+ append_end_block_semi_nl_nl(ctx);
+}
+
+static
+void append_stream_class(struct ctx *ctx,
+ struct fs_sink_ctf_stream_class *sc)
+{
+ uint64_t i;
+
+ /* Default clock class */
+ if (sc->default_clock_class) {
+ const char *descr;
+ int64_t offset_seconds;
+ uint64_t offset_cycles;
+ bt_uuid uuid;
+
+ append_indent(ctx);
+ g_string_append(ctx->tsdl, "clock {\n");
+ ctx->indent_level++;
+ BT_ASSERT(sc->default_clock_class_name->len > 0);
+ append_indent(ctx);
+ g_string_append_printf(ctx->tsdl, "name = %s;\n",
+ sc->default_clock_class_name->str);
+ descr = bt_clock_class_get_description(sc->default_clock_class);
+ if (descr) {
+ append_indent(ctx);
+ g_string_append(ctx->tsdl, "description = ");
+ append_quoted_string(ctx, descr);
+ g_string_append(ctx->tsdl, ";\n");
+ }
+
+ append_indent(ctx);
+ g_string_append_printf(ctx->tsdl, "freq = %" PRIu64 ";\n",
+ bt_clock_class_get_frequency(sc->default_clock_class));
+ append_indent(ctx);
+ g_string_append_printf(ctx->tsdl, "precision = %" PRIu64 ";\n",
+ bt_clock_class_get_precision(sc->default_clock_class));
+ bt_clock_class_get_offset(sc->default_clock_class,
+ &offset_seconds, &offset_cycles);
+ append_indent(ctx);
+ g_string_append_printf(ctx->tsdl, "offset_s = %" PRId64 ";\n",
+ offset_seconds);
+ append_indent(ctx);
+ g_string_append_printf(ctx->tsdl, "offset = %" PRIu64 ";\n",
+ offset_cycles);
+ append_indent(ctx);
+ g_string_append(ctx->tsdl, "absolute = ");
+
+ if (bt_clock_class_origin_is_unix_epoch(
+ sc->default_clock_class)) {
+ g_string_append(ctx->tsdl, "true");
+ } else {
+ g_string_append(ctx->tsdl, "false");
+ }
+
+ g_string_append(ctx->tsdl, ";\n");
+ uuid = bt_clock_class_get_uuid(sc->default_clock_class);
+ if (uuid) {
+ append_indent(ctx);
+ g_string_append(ctx->tsdl, "uuid = ");
+ append_uuid(ctx, uuid);
+ g_string_append(ctx->tsdl, ";\n");
+ }
+
+ /* End clock class */
+ append_end_block_semi_nl_nl(ctx);
+ }
+
+ /* Stream class */
+ append_indent(ctx);
+ g_string_append(ctx->tsdl, "stream {\n");
+ ctx->indent_level++;
+
+ /* Stream class properties */
+ append_indent(ctx);
+ g_string_append_printf(ctx->tsdl, "id = %" PRIu64 ";\n",
+ bt_stream_class_get_id(sc->ir_sc));
+
+ /* Packet context field class */
+ append_indent(ctx);
+ g_string_append(ctx->tsdl, "packet.context := struct {\n");
+ ctx->indent_level++;
+ append_indent(ctx);
+ append_integer_field_class_from_props(ctx, 64, 8, false,
+ BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL,
+ NULL, "packet_size", true);
+ append_indent(ctx);
+ append_integer_field_class_from_props(ctx, 64, 8, false,
+ BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL,
+ NULL, "content_size", true);
+
+ if (sc->default_clock_class) {
+ append_indent(ctx);
+ append_integer_field_class_from_props(ctx, 64, 8, false,
+ BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL,
+ sc->default_clock_class_name->str,
+ "timestamp_begin", true);
+ append_indent(ctx);
+ append_integer_field_class_from_props(ctx, 64, 8, false,
+ BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL,
+ sc->default_clock_class_name->str,
+ "timestamp_end", true);
+ }
+
+ append_indent(ctx);
+ append_integer_field_class_from_props(ctx, 64, 8, false,
+ BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL,
+ NULL, "events_discarded", true);
+ append_indent(ctx);
+ append_integer_field_class_from_props(ctx, 64, 8, false,
+ BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL,
+ NULL, "packet_seq_num", true);
+
+ if (sc->packet_context_fc) {
+ append_struct_field_class_members(ctx,
+ (void *) sc->packet_context_fc);
+ fs_sink_ctf_field_class_struct_align_at_least(
+ (void *) sc->packet_context_fc, 8);
+ }
+
+ /* End packet context field class */
+ append_end_block(ctx);
+ g_string_append_printf(ctx->tsdl, " align(%u);\n\n",
+ sc->packet_context_fc ? sc->packet_context_fc->alignment : 8);
+
+ /* Event header field class */
+ append_indent(ctx);
+ g_string_append(ctx->tsdl, "event.header := struct {\n");
+ ctx->indent_level++;
+ append_indent(ctx);
+ append_integer_field_class_from_props(ctx, 64, 8, false,
+ BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL,
+ NULL, "id", true);
+
+ if (sc->default_clock_class) {
+ append_indent(ctx);
+ append_integer_field_class_from_props(ctx, 64, 8, false,
+ BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL,
+ sc->default_clock_class_name->str,
+ "timestamp", true);
+ }
+
+ /* End event header field class */
+ append_end_block(ctx);
+ g_string_append(ctx->tsdl, " align(8);\n");
+
+ /* Event common context field class */
+ if (sc->event_common_context_fc) {
+ append_indent(ctx);
+ g_string_append(ctx->tsdl, "event.context := ");
+ append_field_class(ctx,
+ (void *) sc->event_common_context_fc);
+ g_string_append(ctx->tsdl, ";\n");
+ }
+
+ /* End stream class */
+ append_end_block_semi_nl_nl(ctx);
+
+ /* Event classes */
+ for (i = 0; i < sc->event_classes->len; i++) {
+ append_event_class(ctx, sc->event_classes->pdata[i]);
+ }
+}
+
+BT_HIDDEN
+void translate_trace_class_ctf_ir_to_tsdl(struct fs_sink_ctf_trace_class *tc,
+ GString *tsdl)
+{
+ struct ctx ctx = {
+ .indent_level = 0,
+ .tsdl = tsdl,
+ };
+ uint64_t i;
+ uint64_t count;
+
+ g_string_assign(tsdl, "/* CTF 1.8 */\n\n");
+ g_string_append(tsdl, "/* This was generated by a Babeltrace `sink.ctf.fs` component. */\n\n");
+
+ /* Trace class */
+ append_indent(&ctx);
+ g_string_append(tsdl, "trace {\n");
+ ctx.indent_level++;
+
+ /* Trace class properties */
+ append_indent(&ctx);
+ g_string_append(tsdl, "major = 1;\n");
+ append_indent(&ctx);
+ g_string_append(tsdl, "minor = 8;\n");
+ append_indent(&ctx);
+ g_string_append(tsdl, "uuid = ");
+ append_uuid(&ctx, tc->uuid);
+ g_string_append(tsdl, ";\n");
+ append_indent(&ctx);
+ g_string_append(tsdl, "byte_order = ");
+
+ if (BYTE_ORDER == LITTLE_ENDIAN) {
+ g_string_append(tsdl, "le");
+ } else {
+ g_string_append(tsdl, "be");
+ }
+
+ g_string_append(tsdl, ";\n");
+
+ /* Packet header field class */
+ append_indent(&ctx);
+ g_string_append(tsdl, "packet.header := struct {\n");
+ ctx.indent_level++;
+ append_indent(&ctx);
+ append_integer_field_class_from_props(&ctx, 32, 8, false,
+ BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL,
+ NULL, "magic", true);
+ append_indent(&ctx);
+ append_integer_field_class_from_props(&ctx, 8, 8, false,
+ BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL,
+ NULL, "uuid[16]", true);
+ append_indent(&ctx);
+ append_integer_field_class_from_props(&ctx, 64, 8, false,
+ BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL,
+ NULL, "stream_id", true);
+ append_indent(&ctx);
+ append_integer_field_class_from_props(&ctx, 64, 8, false,
+ BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL,
+ NULL, "stream_instance_id", true);
+
+ /* End packet header field class */
+ append_end_block(&ctx);
+ g_string_append(ctx.tsdl, " align(8);\n");
+
+ /* End trace class */
+ append_end_block_semi_nl_nl(&ctx);
+
+ /* Trace class environment */
+ count = bt_trace_class_get_environment_entry_count(tc->ir_tc);
+ if (count > 0) {
+ append_indent(&ctx);
+ g_string_append(tsdl, "env {\n");
+ ctx.indent_level++;
+
+ for (i = 0; i < count; i++) {
+ const char *name;
+ const bt_value *val;
+
+ bt_trace_class_borrow_environment_entry_by_index_const(
+ tc->ir_tc, i, &name, &val);
+ append_indent(&ctx);
+ g_string_append_printf(tsdl, "%s = ", name);
+
+ switch (bt_value_get_type(val)) {
+ case BT_VALUE_TYPE_INTEGER:
+ g_string_append_printf(tsdl, "%" PRId64,
+ bt_value_integer_get(val));
+ break;
+ case BT_VALUE_TYPE_STRING:
+ append_quoted_string(&ctx, bt_value_string_get(val));
+ break;
+ default:
+ /*
+ * This is checked in
+ * translate_trace_class_trace_ir_to_ctf_ir().
+ */
+ abort();
+ }
+
+ g_string_append(tsdl, ";\n");
+ }
+
+ /* End trace class environment */
+ append_end_block_semi_nl_nl(&ctx);
+ }
+
+ /* Stream classes and their event classes */
+ for (i = 0; i < tc->stream_classes->len; i++) {
+ append_stream_class(&ctx, tc->stream_classes->pdata[i]);
+ }
+}
--- /dev/null
+#ifndef BABELTRACE_PLUGIN_CTF_FS_SINK_TRANSLATE_CTF_IR_TO_TSDL_H
+#define BABELTRACE_PLUGIN_CTF_FS_SINK_TRANSLATE_CTF_IR_TO_TSDL_H
+
+/*
+ * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <glib.h>
+
+#include "fs-sink-ctf-meta.h"
+
+BT_HIDDEN
+void translate_trace_class_ctf_ir_to_tsdl(struct fs_sink_ctf_trace_class *tc,
+ GString *tsdl);
+
+#endif /* BABELTRACE_PLUGIN_CTF_FS_SINK_TRANSLATE_CTF_IR_TO_TSDL_H */
--- /dev/null
+/*
+ * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#define BT_LOG_TAG "PLUGIN-CTF-FS-SINK-TRANSLATE-TRACE-IR-TO-CTF-IR"
+#include "logging.h"
+
+#include <babeltrace/babeltrace.h>
+#include <babeltrace/babeltrace-internal.h>
+#include <babeltrace/common-internal.h>
+#include <babeltrace/assert-internal.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <glib.h>
+
+#include "fs-sink-ctf-meta.h"
+
+struct field_path_elem {
+ uint64_t index_in_parent;
+ GString *name;
+
+ /* Weak */
+ const bt_field_class *ir_fc;
+
+ /* Weak */
+ struct fs_sink_ctf_field_class *parent_fc;
+};
+
+struct ctx {
+ /* Weak */
+ struct fs_sink_ctf_stream_class *cur_sc;
+
+ /* Weak */
+ struct fs_sink_ctf_event_class *cur_ec;
+
+ bt_scope cur_scope;
+
+ /*
+ * Array of `struct field_path_elem` */
+ GArray *cur_path;
+};
+
+static inline
+struct field_path_elem *cur_path_stack_at(struct ctx *ctx, uint64_t i)
+{
+ BT_ASSERT(i < ctx->cur_path->len);
+ return &g_array_index(ctx->cur_path, struct field_path_elem, i);
+}
+
+static inline
+struct field_path_elem *cur_path_stack_top(struct ctx *ctx)
+{
+ BT_ASSERT(ctx->cur_path->len > 0);
+ return cur_path_stack_at(ctx, ctx->cur_path->len - 1);
+}
+
+static inline
+bool is_reserved_member_name(const char *name, const char *reserved_name)
+{
+ bool is_reserved = false;
+
+ if (strcmp(name, reserved_name) == 0) {
+ is_reserved = true;
+ goto end;
+ }
+
+ if (name[0] == '_' && strcmp(&name[1], reserved_name) == 0) {
+ is_reserved = true;
+ goto end;
+ }
+
+end:
+ return is_reserved;
+}
+
+static inline
+int cur_path_stack_push(struct ctx *ctx,
+ uint64_t index_in_parent, const char *ir_name,
+ const bt_field_class *ir_fc,
+ struct fs_sink_ctf_field_class *parent_fc)
+{
+ int ret = 0;
+ struct field_path_elem *field_path_elem;
+
+ g_array_set_size(ctx->cur_path, ctx->cur_path->len + 1);
+ field_path_elem = cur_path_stack_top(ctx);
+ field_path_elem->index_in_parent = index_in_parent;
+ field_path_elem->name = g_string_new(ir_name);
+
+ if (ir_name) {
+ if (ctx->cur_scope == BT_SCOPE_PACKET_CONTEXT) {
+ if (is_reserved_member_name(ir_name, "packet_size") ||
+ is_reserved_member_name(ir_name, "content_size") ||
+ is_reserved_member_name(ir_name, "timestamp_begin") ||
+ is_reserved_member_name(ir_name, "timestamp_end") ||
+ is_reserved_member_name(ir_name, "events_discarded") ||
+ is_reserved_member_name(ir_name, "packet_seq_num")) {
+ BT_LOGE("Unsupported reserved TSDL structure field class member "
+ "or variant field class option name: name=\"%s\"",
+ ir_name);
+ ret = -1;
+ goto end;
+ }
+ }
+
+ ret = fs_sink_ctf_protect_name(field_path_elem->name);
+ if (ret) {
+ BT_LOGE("Unsupported non-TSDL structure field class member "
+ "or variant field class option name: name=\"%s\"",
+ ir_name);
+ goto end;
+ }
+ }
+
+ field_path_elem->ir_fc = ir_fc;
+ field_path_elem->parent_fc = parent_fc;
+
+end:
+ return ret;
+}
+
+static inline
+void cur_path_stack_pop(struct ctx *ctx)
+{
+ struct field_path_elem *field_path_elem;
+
+ BT_ASSERT(ctx->cur_path->len > 0);
+ field_path_elem = cur_path_stack_top(ctx);
+
+ if (field_path_elem->name) {
+ g_string_free(field_path_elem->name, TRUE);
+ field_path_elem->name = NULL;
+ }
+
+ g_array_set_size(ctx->cur_path, ctx->cur_path->len - 1);
+}
+
+static inline
+struct field_path_elem *get_field_path_elem(
+ GArray *full_field_path, uint64_t i)
+{
+ return &g_array_index(full_field_path, struct field_path_elem,
+ i);
+}
+
+
+/*
+ * Creates a relative field ref (a single name) from IR field path
+ * `tgt_ir_field_path`.
+ *
+ * This function tries to locate the target field class recursively from
+ * the top to the bottom of the context's current path using only the
+ * target field class's own name. This is because many CTF reading tools
+ * do not support a relative field ref with more than one element, for
+ * example `prev_struct.len`.
+ *
+ * Returns a negative value if this resolving operation failed.
+ */
+static
+int create_relative_field_ref(struct ctx *ctx,
+ const bt_field_path *tgt_ir_field_path, GString *tgt_field_ref)
+{
+ int ret = 0;
+ struct fs_sink_ctf_field_class *tgt_fc = NULL;
+ uint64_t i;
+ int64_t si;
+ const char *tgt_fc_name;
+ struct field_path_elem *field_path_elem;
+
+ /* Get target field class's name */
+ switch (bt_field_path_get_root_scope(tgt_ir_field_path)) {
+ case BT_SCOPE_PACKET_CONTEXT:
+ BT_ASSERT(ctx->cur_sc);
+ tgt_fc = ctx->cur_sc->packet_context_fc;
+ break;
+ case BT_SCOPE_EVENT_COMMON_CONTEXT:
+ BT_ASSERT(ctx->cur_sc);
+ tgt_fc = ctx->cur_sc->event_common_context_fc;
+ break;
+ case BT_SCOPE_EVENT_SPECIFIC_CONTEXT:
+ BT_ASSERT(ctx->cur_ec);
+ tgt_fc = ctx->cur_ec->spec_context_fc;
+ break;
+ case BT_SCOPE_EVENT_PAYLOAD:
+ BT_ASSERT(ctx->cur_ec);
+ tgt_fc = ctx->cur_ec->payload_fc;
+ break;
+ default:
+ abort();
+ }
+
+ i = 0;
+
+ while (i < bt_field_path_get_index_count(tgt_ir_field_path)) {
+ uint64_t index = bt_field_path_get_index_by_index(
+ tgt_ir_field_path, i);
+ struct fs_sink_ctf_named_field_class *named_fc = NULL;
+
+ BT_ASSERT(tgt_fc);
+
+ switch (tgt_fc->type) {
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT:
+ named_fc = fs_sink_ctf_field_class_struct_borrow_member_by_index(
+ (void *) tgt_fc, index);
+ break;
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT:
+ named_fc = fs_sink_ctf_field_class_variant_borrow_option_by_index(
+ (void *) tgt_fc, index);
+ break;
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY:
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE:
+ {
+ struct fs_sink_ctf_field_class_array_base *array_base_fc =
+ (void *) tgt_fc;
+
+ tgt_fc = array_base_fc->elem_fc;
+ break;
+ }
+ default:
+ abort();
+ }
+
+ if (named_fc) {
+ tgt_fc = named_fc->fc;
+ tgt_fc_name = named_fc->name->str;
+ i++;
+ }
+ }
+
+ BT_ASSERT(tgt_fc);
+ BT_ASSERT(tgt_fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_INT);
+ BT_ASSERT(tgt_fc_name);
+
+ /* Find target field class having this name in current context */
+ for (si = ctx->cur_path->len - 1; si >= 0; si--) {
+ struct fs_sink_ctf_field_class *fc;
+ struct fs_sink_ctf_field_class_struct *struct_fc;
+ struct fs_sink_ctf_field_class_variant *var_fc;
+ struct fs_sink_ctf_named_field_class *named_fc;
+ uint64_t len;
+
+ field_path_elem = cur_path_stack_at(ctx, (uint64_t) si);
+ fc = field_path_elem->parent_fc;
+ if (!fc) {
+ /* Reached stack's bottom */
+ ret = -1;
+ goto end;
+ }
+
+ switch (fc->type) {
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT:
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT:
+ break;
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY:
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE:
+ continue;
+ default:
+ /* Not supported by TSDL 1.8 */
+ ret = -1;
+ goto end;
+ }
+
+ if (fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT) {
+ struct_fc = (void *) fc;
+ len = struct_fc->members->len;
+ } else {
+ var_fc = (void *) fc;
+ len = var_fc->options->len;
+ }
+
+ for (i = 0; i < len; i++) {
+ if (fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT) {
+ named_fc = fs_sink_ctf_field_class_struct_borrow_member_by_index(
+ struct_fc, i);
+ } else {
+ named_fc = fs_sink_ctf_field_class_variant_borrow_option_by_index(
+ var_fc, i);
+ }
+
+ if (strcmp(named_fc->name->str, tgt_fc_name) == 0) {
+ if (named_fc->fc == tgt_fc) {
+ g_string_assign(tgt_field_ref,
+ tgt_fc_name);
+ } else {
+ /*
+ * Using only the target field
+ * class's name, we're not
+ * reaching the target field
+ * class. This is not supported
+ * by TSDL 1.8.
+ */
+ ret = -1;
+ }
+
+ goto end;
+ }
+ }
+ }
+
+end:
+ return ret;
+}
+
+/*
+ * Creates an absolute field ref from IR field path `tgt_ir_field_path`.
+ *
+ * Returns a negative value if this resolving operation failed.
+ */
+static
+int create_absolute_field_ref(struct ctx *ctx,
+ const bt_field_path *tgt_ir_field_path, GString *tgt_field_ref)
+{
+ int ret = 0;
+ struct fs_sink_ctf_field_class *fc = NULL;
+ uint64_t i;
+
+ switch (bt_field_path_get_root_scope(tgt_ir_field_path)) {
+ case BT_SCOPE_PACKET_CONTEXT:
+ BT_ASSERT(ctx->cur_sc);
+ fc = ctx->cur_sc->packet_context_fc;
+ g_string_assign(tgt_field_ref, "stream.packet.context");
+ break;
+ case BT_SCOPE_EVENT_COMMON_CONTEXT:
+ BT_ASSERT(ctx->cur_sc);
+ fc = ctx->cur_sc->event_common_context_fc;
+ g_string_assign(tgt_field_ref, "stream.event.context");
+ break;
+ case BT_SCOPE_EVENT_SPECIFIC_CONTEXT:
+ BT_ASSERT(ctx->cur_ec);
+ fc = ctx->cur_ec->spec_context_fc;
+ g_string_assign(tgt_field_ref, "event.context");
+ break;
+ case BT_SCOPE_EVENT_PAYLOAD:
+ BT_ASSERT(ctx->cur_ec);
+ fc = ctx->cur_ec->payload_fc;
+ g_string_assign(tgt_field_ref, "event.fields");
+ break;
+ default:
+ abort();
+ }
+
+ BT_ASSERT(fc);
+
+ for (i = 0; i < bt_field_path_get_index_count(tgt_ir_field_path); i++) {
+ uint64_t index = bt_field_path_get_index_by_index(
+ tgt_ir_field_path, i);
+ struct fs_sink_ctf_named_field_class *named_fc = NULL;
+
+ switch (fc->type) {
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT:
+ named_fc = fs_sink_ctf_field_class_struct_borrow_member_by_index(
+ (void *) fc, index);
+ break;
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT:
+ named_fc = fs_sink_ctf_field_class_variant_borrow_option_by_index(
+ (void *) fc, index);
+ break;
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY:
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE:
+ /* Not supported by TSDL 1.8 */
+ ret = -1;
+ goto end;
+ default:
+ abort();
+ }
+
+ BT_ASSERT(named_fc);
+ g_string_append_c(tgt_field_ref, '.');
+ g_string_append(tgt_field_ref, named_fc->name->str);
+ fc = named_fc->fc;
+ }
+
+end:
+ return ret;
+}
+
+/*
+ * Resolves a target field class located at `tgt_ir_field_path`, writing
+ * the resolved field ref to `tgt_field_ref` and setting
+ * `*create_before` according to whether or not the target field must be
+ * created immediately before (in which case `tgt_field_ref` is
+ * irrelevant).
+ */
+static
+void resolve_field_class(struct ctx *ctx,
+ const bt_field_path *tgt_ir_field_path,
+ GString *tgt_field_ref, bool *create_before)
+{
+ int ret;
+ bt_scope tgt_scope;
+
+ *create_before = false;
+
+ if (!tgt_ir_field_path) {
+ *create_before = true;
+ goto end;
+ }
+
+ tgt_scope = bt_field_path_get_root_scope(tgt_ir_field_path);
+
+ if (tgt_scope == ctx->cur_scope) {
+ /*
+ * Try, in this order:
+ *
+ * 1. Use a relative path, using only the target field
+ * class's name. This is what is the most commonly
+ * supported by popular CTF reading tools.
+ *
+ * 2. Use an absolute path. This could fail if there's
+ * an array field class from the current root's field
+ * class to the target field class.
+ *
+ * 3. Create the target field class before the
+ * requesting field class (fallback).
+ */
+ ret = create_relative_field_ref(ctx, tgt_ir_field_path,
+ tgt_field_ref);
+ if (ret) {
+ ret = create_absolute_field_ref(ctx, tgt_ir_field_path,
+ tgt_field_ref);
+ if (ret) {
+ *create_before = true;
+ ret = 0;
+ goto end;
+ }
+ }
+ } else {
+ ret = create_absolute_field_ref(ctx, tgt_ir_field_path,
+ tgt_field_ref);
+
+ /* It must always work in previous scopes */
+ BT_ASSERT(ret == 0);
+ }
+
+end:
+ return;
+}
+
+static
+int translate_field_class(struct ctx *ctx);
+
+static inline
+void append_to_parent_field_class(struct ctx *ctx,
+ struct fs_sink_ctf_field_class *fc)
+{
+ struct fs_sink_ctf_field_class *parent_fc =
+ cur_path_stack_top(ctx)->parent_fc;
+
+ switch (parent_fc->type) {
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT:
+ fs_sink_ctf_field_class_struct_append_member((void *) parent_fc,
+ cur_path_stack_top(ctx)->name->str, fc);
+ break;
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT:
+ fs_sink_ctf_field_class_variant_append_option((void *) parent_fc,
+ cur_path_stack_top(ctx)->name->str, fc);
+ break;
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY:
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE:
+ {
+ struct fs_sink_ctf_field_class_array_base *array_base_fc =
+ (void *) parent_fc;
+
+ BT_ASSERT(!array_base_fc->elem_fc);
+ array_base_fc->elem_fc = fc;
+ array_base_fc->base.alignment = fc->alignment;
+ break;
+ }
+ default:
+ abort();
+ }
+}
+
+static inline
+void update_parent_field_class_alignment(struct ctx *ctx,
+ unsigned int alignment)
+{
+ struct fs_sink_ctf_field_class *parent_fc =
+ cur_path_stack_top(ctx)->parent_fc;
+
+ switch (parent_fc->type) {
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT:
+ fs_sink_ctf_field_class_struct_align_at_least(
+ (void *) parent_fc, alignment);
+ break;
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY:
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE:
+ {
+ struct fs_sink_ctf_field_class_array_base *array_base_fc =
+ (void *) parent_fc;
+
+ array_base_fc->base.alignment = alignment;
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static inline
+int translate_structure_field_class_members(struct ctx *ctx,
+ struct fs_sink_ctf_field_class_struct *struct_fc,
+ const bt_field_class *ir_fc)
+{
+ int ret = 0;
+ uint64_t i;
+
+ for (i = 0; i < bt_field_class_structure_get_member_count(ir_fc); i++) {
+ const bt_field_class_structure_member *member;
+ const char *name;
+ const bt_field_class *memb_ir_fc;
+
+ member =
+ bt_field_class_structure_borrow_member_by_index_const(
+ ir_fc, i);
+ name = bt_field_class_structure_member_get_name(member);
+ memb_ir_fc = bt_field_class_structure_member_borrow_field_class_const(
+ member);
+ ret = cur_path_stack_push(ctx, i, name, memb_ir_fc,
+ (void *) struct_fc);
+ if (ret) {
+ BT_LOGE("Cannot translate structure field class member: "
+ "name=\"%s\"", name);
+ goto end;
+ }
+
+ ret = translate_field_class(ctx);
+ if (ret) {
+ BT_LOGE("Cannot translate structure field class member: "
+ "name=\"%s\"", name);
+ goto end;
+ }
+
+ cur_path_stack_pop(ctx);
+ }
+
+end:
+ return ret;
+}
+
+static inline
+int translate_structure_field_class(struct ctx *ctx)
+{
+ int ret;
+ struct fs_sink_ctf_field_class_struct *fc =
+ fs_sink_ctf_field_class_struct_create_empty(
+ cur_path_stack_top(ctx)->ir_fc,
+ cur_path_stack_top(ctx)->index_in_parent);
+
+ BT_ASSERT(fc);
+ append_to_parent_field_class(ctx, (void *) fc);
+ ret = translate_structure_field_class_members(ctx, fc, fc->base.ir_fc);
+ if (ret) {
+ goto end;
+ }
+
+ update_parent_field_class_alignment(ctx, fc->base.alignment);
+
+end:
+ return ret;
+}
+
+static inline
+int translate_variant_field_class(struct ctx *ctx)
+{
+ int ret = 0;
+ uint64_t i;
+ struct fs_sink_ctf_field_class_variant *fc =
+ fs_sink_ctf_field_class_variant_create_empty(
+ cur_path_stack_top(ctx)->ir_fc,
+ cur_path_stack_top(ctx)->index_in_parent);
+
+ BT_ASSERT(fc);
+
+ /* Resolve tag field class before appending to parent */
+ resolve_field_class(ctx,
+ bt_field_class_variant_borrow_selector_field_path_const(
+ fc->base.ir_fc), fc->tag_ref, &fc->tag_is_before);
+
+ append_to_parent_field_class(ctx, (void *) fc);
+
+ for (i = 0; i < bt_field_class_variant_get_option_count(fc->base.ir_fc);
+ i++) {
+ const bt_field_class_variant_option *opt;
+ const char *name;
+ const bt_field_class *opt_ir_fc;
+
+ opt = bt_field_class_variant_borrow_option_by_index_const(
+ fc->base.ir_fc, i);
+ name = bt_field_class_variant_option_get_name(opt);
+ opt_ir_fc = bt_field_class_variant_option_borrow_field_class_const(
+ opt);
+ ret = cur_path_stack_push(ctx, i, name, opt_ir_fc, (void *) fc);
+ if (ret) {
+ BT_LOGE("Cannot translate variant field class option: "
+ "name=\"%s\"", name);
+ goto end;
+ }
+
+ ret = translate_field_class(ctx);
+ if (ret) {
+ BT_LOGE("Cannot translate variant field class option: "
+ "name=\"%s\"", name);
+ goto end;
+ }
+
+ cur_path_stack_pop(ctx);
+ }
+
+end:
+ return ret;
+}
+
+static inline
+int translate_static_array_field_class(struct ctx *ctx)
+{
+ struct fs_sink_ctf_field_class_array *fc =
+ fs_sink_ctf_field_class_array_create_empty(
+ cur_path_stack_top(ctx)->ir_fc,
+ cur_path_stack_top(ctx)->index_in_parent);
+ const bt_field_class *elem_ir_fc =
+ bt_field_class_array_borrow_element_field_class_const(
+ fc->base.base.ir_fc);
+ int ret;
+
+ BT_ASSERT(fc);
+ append_to_parent_field_class(ctx, (void *) fc);
+ ret = cur_path_stack_push(ctx, UINT64_C(-1), NULL, elem_ir_fc,
+ (void *) fc);
+ if (ret) {
+ BT_LOGE_STR("Cannot translate static array field class element.");
+ goto end;
+ }
+
+ ret = translate_field_class(ctx);
+ if (ret) {
+ BT_LOGE_STR("Cannot translate static array field class element.");
+ goto end;
+ }
+
+ cur_path_stack_pop(ctx);
+ update_parent_field_class_alignment(ctx, fc->base.base.alignment);
+
+end:
+ return ret;
+}
+
+static inline
+int translate_dynamic_array_field_class(struct ctx *ctx)
+{
+ struct fs_sink_ctf_field_class_sequence *fc =
+ fs_sink_ctf_field_class_sequence_create_empty(
+ cur_path_stack_top(ctx)->ir_fc,
+ cur_path_stack_top(ctx)->index_in_parent);
+ const bt_field_class *elem_ir_fc =
+ bt_field_class_array_borrow_element_field_class_const(
+ fc->base.base.ir_fc);
+ int ret;
+
+ BT_ASSERT(fc);
+
+ /* Resolve length field class before appending to parent */
+ resolve_field_class(ctx,
+ bt_field_class_dynamic_array_borrow_length_field_path_const(
+ fc->base.base.ir_fc),
+ fc->length_ref, &fc->length_is_before);
+
+ append_to_parent_field_class(ctx, (void *) fc);
+ ret = cur_path_stack_push(ctx, UINT64_C(-1), NULL, elem_ir_fc,
+ (void *) fc);
+ if (ret) {
+ BT_LOGE_STR("Cannot translate dynamic array field class element.");
+ goto end;
+ }
+
+ ret = translate_field_class(ctx);
+ if (ret) {
+ BT_LOGE_STR("Cannot translate dynamic array field class element.");
+ goto end;
+ }
+
+ cur_path_stack_pop(ctx);
+ update_parent_field_class_alignment(ctx, fc->base.base.alignment);
+
+end:
+ return ret;
+}
+
+static inline
+int translate_integer_field_class(struct ctx *ctx)
+{
+ struct fs_sink_ctf_field_class_int *fc =
+ fs_sink_ctf_field_class_int_create(
+ cur_path_stack_top(ctx)->ir_fc,
+ cur_path_stack_top(ctx)->index_in_parent);
+
+ BT_ASSERT(fc);
+ append_to_parent_field_class(ctx, (void *) fc);
+ return 0;
+}
+
+static inline
+int translate_real_field_class(struct ctx *ctx)
+{
+ struct fs_sink_ctf_field_class_float *fc =
+ fs_sink_ctf_field_class_float_create(
+ cur_path_stack_top(ctx)->ir_fc,
+ cur_path_stack_top(ctx)->index_in_parent);
+
+ BT_ASSERT(fc);
+ append_to_parent_field_class(ctx, (void *) fc);
+ return 0;
+}
+
+static inline
+int translate_string_field_class(struct ctx *ctx)
+{
+ struct fs_sink_ctf_field_class_string *fc =
+ fs_sink_ctf_field_class_string_create(
+ cur_path_stack_top(ctx)->ir_fc,
+ cur_path_stack_top(ctx)->index_in_parent);
+
+ BT_ASSERT(fc);
+ append_to_parent_field_class(ctx, (void *) fc);
+ return 0;
+}
+
+/*
+ * Translates a field class, recursively.
+ *
+ * The field class's IR field class, parent field class, and index
+ * within its parent are in the context's current path's top element
+ * (cur_path_stack_top()).
+ */
+static
+int translate_field_class(struct ctx *ctx)
+{
+ int ret;
+
+ switch (bt_field_class_get_type(cur_path_stack_top(ctx)->ir_fc)) {
+ case BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER:
+ case BT_FIELD_CLASS_TYPE_SIGNED_INTEGER:
+ case BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION:
+ case BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION:
+ ret = translate_integer_field_class(ctx);
+ break;
+ case BT_FIELD_CLASS_TYPE_REAL:
+ ret = translate_real_field_class(ctx);
+ break;
+ case BT_FIELD_CLASS_TYPE_STRING:
+ ret = translate_string_field_class(ctx);
+ break;
+ case BT_FIELD_CLASS_TYPE_STRUCTURE:
+ ret = translate_structure_field_class(ctx);
+ break;
+ case BT_FIELD_CLASS_TYPE_STATIC_ARRAY:
+ ret = translate_static_array_field_class(ctx);
+ break;
+ case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY:
+ ret = translate_dynamic_array_field_class(ctx);
+ break;
+ case BT_FIELD_CLASS_TYPE_VARIANT:
+ ret = translate_variant_field_class(ctx);
+ break;
+ default:
+ abort();
+ }
+
+ return ret;
+}
+
+static
+int set_field_ref(struct fs_sink_ctf_field_class *fc, const char *fc_name,
+ struct fs_sink_ctf_field_class *parent_fc)
+{
+ int ret = 0;
+ GString *field_ref = NULL;
+ bool is_before;
+ const char *tgt_type;
+ struct fs_sink_ctf_field_class_struct *parent_struct_fc =
+ (void *) parent_fc;
+ uint64_t i;
+ unsigned int suffix = 0;
+
+ if (!fc_name || !parent_fc ||
+ parent_fc->type != FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT) {
+ /* Not supported */
+ ret = -1;
+ goto end;
+ }
+
+ switch (fc->type) {
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE:
+ {
+ struct fs_sink_ctf_field_class_sequence *seq_fc = (void *) fc;
+
+ field_ref = seq_fc->length_ref;
+ is_before = seq_fc->length_is_before;
+ tgt_type = "len";
+ break;
+ }
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT:
+ {
+ struct fs_sink_ctf_field_class_variant *var_fc = (void *) fc;
+
+ field_ref = var_fc->tag_ref;
+ is_before = var_fc->tag_is_before;
+ tgt_type = "tag";
+ break;
+ }
+ default:
+ abort();
+ }
+
+ BT_ASSERT(field_ref);
+
+ if (!is_before) {
+ goto end;
+ }
+
+ /* Initial field ref */
+ g_string_printf(field_ref, "__%s_%s", fc_name, tgt_type);
+
+ /*
+ * Make sure field ref does not clash with an existing field
+ * class name within the same parent structure field class.
+ */
+ while (true) {
+ bool name_ok = true;
+
+ for (i = 0; i < parent_struct_fc->members->len; i++) {
+ struct fs_sink_ctf_named_field_class *named_fc =
+ fs_sink_ctf_field_class_struct_borrow_member_by_index(
+ parent_struct_fc, i);
+
+ if (strcmp(field_ref->str, named_fc->name->str) == 0) {
+ /* Name clash */
+ name_ok = false;
+ break;
+ }
+ }
+
+ if (name_ok) {
+ /* No clash: we're done */
+ break;
+ }
+
+ /* Append suffix and try again */
+ g_string_printf(field_ref, "__%s_%s_%u", fc_name, tgt_type,
+ suffix);
+ suffix++;
+ }
+
+end:
+ return ret;
+}
+
+/*
+ * This function recursively sets field refs of sequence and variant
+ * field classes when they are immediately before, avoiding name clashes
+ * with existing field class names.
+ *
+ * It can fail at this point if, for example, a sequence field class of
+ * which to set the length's field ref has something else than a
+ * structure field class as its parent: in this case, there's no
+ * location to place the length field class immediately before the
+ * sequence field class.
+ */
+static
+int set_field_refs(struct fs_sink_ctf_field_class *fc, const char *fc_name,
+ struct fs_sink_ctf_field_class *parent_fc)
+{
+ int ret = 0;
+ BT_ASSERT(fc);
+
+ switch (fc->type) {
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT:
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT:
+ {
+ uint64_t i;
+ uint64_t len;
+ struct fs_sink_ctf_field_class_struct *struct_fc;
+ struct fs_sink_ctf_field_class_variant *var_fc;
+ struct fs_sink_ctf_named_field_class *named_fc;
+
+ if (fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT) {
+ struct_fc = (void *) fc;
+ len = struct_fc->members->len;
+ } else {
+ var_fc = (void *) fc;
+ len = var_fc->options->len;
+ ret = set_field_ref(fc, fc_name, parent_fc);
+ if (ret) {
+ goto end;
+ }
+ }
+
+ for (i = 0; i < len; i++) {
+ if (fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT) {
+ named_fc = fs_sink_ctf_field_class_struct_borrow_member_by_index(
+ struct_fc, i);
+ } else {
+ named_fc = fs_sink_ctf_field_class_variant_borrow_option_by_index(
+ var_fc, i);
+ }
+
+ ret = set_field_refs(named_fc->fc, named_fc->name->str,
+ fc);
+ if (ret) {
+ goto end;
+ }
+ }
+
+ break;
+ }
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY:
+ case FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE:
+ {
+ struct fs_sink_ctf_field_class_array_base *array_base_fc =
+ (void *) fc;
+
+ if (fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE) {
+ ret = set_field_ref(fc, fc_name, parent_fc);
+ if (ret) {
+ goto end;
+ }
+ }
+
+ ret = set_field_refs(array_base_fc->elem_fc, NULL, fc);
+ if (ret) {
+ goto end;
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+
+end:
+ return ret;
+}
+
+/*
+ * This function translates a root scope trace IR field class to
+ * a CTF IR field class.
+ *
+ * The resulting CTF IR field class is written to `*fc` so that it
+ * exists as the parent object's (stream class or event class) true root
+ * field class during the recursive translation for resolving purposes.
+ * This is also why this function creates the empty structure field
+ * class and then calls translate_structure_field_class_members() to
+ * fill it.
+ */
+static
+int translate_scope_field_class(struct ctx *ctx, bt_scope scope,
+ struct fs_sink_ctf_field_class **fc,
+ const bt_field_class *ir_fc)
+{
+ int ret = 0;
+
+ if (!ir_fc) {
+ goto end;
+ }
+
+ BT_ASSERT(bt_field_class_get_type(ir_fc) ==
+ BT_FIELD_CLASS_TYPE_STRUCTURE);
+ BT_ASSERT(fc);
+ *fc = (void *) fs_sink_ctf_field_class_struct_create_empty(
+ ir_fc, UINT64_C(-1));
+ BT_ASSERT(*fc);
+ ctx->cur_scope = scope;
+ BT_ASSERT(ctx->cur_path->len == 0);
+ ret = cur_path_stack_push(ctx, UINT64_C(-1), NULL, ir_fc, NULL);
+ if (ret) {
+ BT_LOGE("Cannot translate scope structure field class: "
+ "scope=%d", scope);
+ goto end;
+ }
+
+ ret = translate_structure_field_class_members(ctx, (void *) *fc, ir_fc);
+ if (ret) {
+ BT_LOGE("Cannot translate scope structure field class: "
+ "scope=%d", scope);
+ goto end;
+ }
+
+ cur_path_stack_pop(ctx);
+
+ /* Set field refs for preceding targets */
+ ret = set_field_refs(*fc, NULL, NULL);
+
+end:
+ return ret;
+}
+
+static inline
+void ctx_init(struct ctx *ctx)
+{
+ memset(ctx, 0, sizeof(struct ctx));
+ ctx->cur_path = g_array_new(FALSE, TRUE,
+ sizeof(struct field_path_elem));
+ BT_ASSERT(ctx->cur_path);
+}
+
+static inline
+void ctx_fini(struct ctx *ctx)
+{
+ if (ctx->cur_path) {
+ g_array_free(ctx->cur_path, TRUE);
+ ctx->cur_path = NULL;
+ }
+}
+
+static
+int translate_event_class(struct fs_sink_ctf_stream_class *sc,
+ const bt_event_class *ir_ec,
+ struct fs_sink_ctf_event_class **out_ec)
+{
+ int ret = 0;
+ struct ctx ctx;
+ struct fs_sink_ctf_event_class *ec;
+
+ BT_ASSERT(sc);
+ BT_ASSERT(ir_ec);
+
+ ctx_init(&ctx);
+ ec = fs_sink_ctf_event_class_create(sc, ir_ec);
+ BT_ASSERT(ec);
+ ctx.cur_sc = sc;
+ ctx.cur_ec = ec;
+ ret = translate_scope_field_class(&ctx, BT_SCOPE_EVENT_SPECIFIC_CONTEXT,
+ &ec->spec_context_fc,
+ bt_event_class_borrow_specific_context_field_class_const(
+ ir_ec));
+ if (ret) {
+ goto end;
+ }
+
+ ret = translate_scope_field_class(&ctx, BT_SCOPE_EVENT_PAYLOAD,
+ &ec->payload_fc,
+ bt_event_class_borrow_payload_field_class_const(ir_ec));
+ if (ret) {
+ goto end;
+ }
+
+end:
+ ctx_fini(&ctx);
+ *out_ec = ec;
+ return ret;
+}
+
+BT_HIDDEN
+int try_translate_event_class_trace_ir_to_ctf_ir(
+ struct fs_sink_ctf_stream_class *sc,
+ const bt_event_class *ir_ec,
+ struct fs_sink_ctf_event_class **out_ec)
+{
+ int ret = 0;
+
+ BT_ASSERT(sc);
+ BT_ASSERT(ir_ec);
+
+ /* Check in hash table first */
+ *out_ec = g_hash_table_lookup(sc->event_classes_from_ir, ir_ec);
+ if (likely(*out_ec)) {
+ goto end;
+ }
+
+ ret = translate_event_class(sc, ir_ec, out_ec);
+
+end:
+ return ret;
+}
+
+bool default_clock_class_name_exists(struct fs_sink_ctf_trace_class *tc,
+ const char *name)
+{
+ bool exists = false;
+ uint64_t i;
+
+ for (i = 0; i < tc->stream_classes->len; i++) {
+ struct fs_sink_ctf_stream_class *sc =
+ tc->stream_classes->pdata[i];
+
+ if (sc->default_clock_class_name->len == 0) {
+ /* No default clock class */
+ continue;
+ }
+
+ if (strcmp(sc->default_clock_class_name->str, name) == 0) {
+ exists = true;
+ goto end;
+ }
+ }
+
+end:
+ return exists;
+}
+
+static
+void make_unique_default_clock_class_name(struct fs_sink_ctf_stream_class *sc)
+{
+ unsigned int suffix = 0;
+ char buf[16];
+
+ g_string_assign(sc->default_clock_class_name, "");
+ sprintf(buf, "default");
+
+ while (default_clock_class_name_exists(sc->tc, buf)) {
+ sprintf(buf, "default%u", suffix);
+ suffix++;
+ }
+
+ g_string_assign(sc->default_clock_class_name, buf);
+}
+
+static
+int translate_stream_class(struct fs_sink_ctf_trace_class *tc,
+ const bt_stream_class *ir_sc,
+ struct fs_sink_ctf_stream_class **out_sc)
+{
+ int ret = 0;
+ struct ctx ctx;
+
+ BT_ASSERT(tc);
+ BT_ASSERT(ir_sc);
+ ctx_init(&ctx);
+ *out_sc = fs_sink_ctf_stream_class_create(tc, ir_sc);
+ BT_ASSERT(*out_sc);
+
+ /* Set default clock class's protected name, if any */
+ if ((*out_sc)->default_clock_class) {
+ const char *name = bt_clock_class_get_name(
+ (*out_sc)->default_clock_class);
+
+ if (!bt_stream_class_default_clock_is_always_known(ir_sc)) {
+ BT_LOGE("Unsupported stream clock which can have an unknown value: "
+ "sc-name=\"%s\"",
+ bt_stream_class_get_name(ir_sc));
+ goto error;
+ }
+
+ if (name) {
+ /* Try original name, protected */
+ g_string_assign((*out_sc)->default_clock_class_name,
+ name);
+ ret = fs_sink_ctf_protect_name(
+ (*out_sc)->default_clock_class_name);
+ if (ret) {
+ /* Invalid: create a new name */
+ make_unique_default_clock_class_name(*out_sc);
+ ret = 0;
+ }
+ } else {
+ /* No name: create a name */
+ make_unique_default_clock_class_name(*out_sc);
+ }
+ }
+
+ ctx.cur_sc = *out_sc;
+ ret = translate_scope_field_class(&ctx, BT_SCOPE_PACKET_CONTEXT,
+ &(*out_sc)->packet_context_fc,
+ bt_stream_class_borrow_packet_context_field_class_const(ir_sc));
+ if (ret) {
+ goto error;
+ }
+
+ if ((*out_sc)->packet_context_fc) {
+ /*
+ * Make sure the structure field class's alignment is
+ * enough: 8 is what we use for our own special members
+ * in the packet context.
+ */
+ fs_sink_ctf_field_class_struct_align_at_least(
+ (void *) (*out_sc)->packet_context_fc, 8);
+ }
+
+ ret = translate_scope_field_class(&ctx, BT_SCOPE_EVENT_COMMON_CONTEXT,
+ &(*out_sc)->event_common_context_fc,
+ bt_stream_class_borrow_event_common_context_field_class_const(
+ ir_sc));
+ if (ret) {
+ goto error;
+ }
+
+ goto end;
+
+error:
+ fs_sink_ctf_stream_class_destroy(*out_sc);
+ *out_sc = NULL;
+
+end:
+ ctx_fini(&ctx);
+ return ret;
+}
+
+BT_HIDDEN
+int try_translate_stream_class_trace_ir_to_ctf_ir(
+ struct fs_sink_ctf_trace_class *tc,
+ const bt_stream_class *ir_sc,
+ struct fs_sink_ctf_stream_class **out_sc)
+{
+ int ret = 0;
+ uint64_t i;
+
+ BT_ASSERT(tc);
+ BT_ASSERT(ir_sc);
+
+ for (i = 0; i < tc->stream_classes->len; i++) {
+ *out_sc = tc->stream_classes->pdata[i];
+
+ if ((*out_sc)->ir_sc == ir_sc) {
+ goto end;
+ }
+ }
+
+ ret = translate_stream_class(tc, ir_sc, out_sc);
+
+end:
+ return ret;
+}
+
+BT_HIDDEN
+struct fs_sink_ctf_trace_class *translate_trace_class_trace_ir_to_ctf_ir(
+ const bt_trace_class *ir_tc)
+{
+ uint64_t count;
+ uint64_t i;
+ struct fs_sink_ctf_trace_class *tc = NULL;
+
+ /* Check that trace class's environment is TSDL-compatible */
+ count = bt_trace_class_get_environment_entry_count(ir_tc);
+ for (i = 0; i < count; i++) {
+ const char *name;
+ const bt_value *val;
+
+ bt_trace_class_borrow_environment_entry_by_index_const(
+ ir_tc, i, &name, &val);
+
+ if (!fs_sink_ctf_ist_valid_identifier(name)) {
+ BT_LOGE("Unsupported trace class's environment entry name: "
+ "name=\"%s\"", name);
+ goto end;
+ }
+
+ switch (bt_value_get_type(val)) {
+ case BT_VALUE_TYPE_INTEGER:
+ case BT_VALUE_TYPE_STRING:
+ break;
+ default:
+ BT_LOGE("Unsupported trace class's environment entry value type: "
+ "type=%s",
+ bt_common_value_type_string(
+ bt_value_get_type(val)));
+ goto end;
+ }
+ }
+
+ tc = fs_sink_ctf_trace_class_create(ir_tc);
+ BT_ASSERT(tc);
+
+end:
+ return tc;
+}
--- /dev/null
+#ifndef BABELTRACE_PLUGIN_CTF_FS_SINK_TRANSLATE_TRACE_IR_TO_CTF_IR_H
+#define BABELTRACE_PLUGIN_CTF_FS_SINK_TRANSLATE_TRACE_IR_TO_CTF_IR_H
+
+/*
+ * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <babeltrace/babeltrace-internal.h>
+#include <babeltrace/babeltrace.h>
+
+#include "fs-sink-ctf-meta.h"
+
+BT_HIDDEN
+int try_translate_event_class_trace_ir_to_ctf_ir(
+ struct fs_sink_ctf_stream_class *sc,
+ const bt_event_class *ir_ec,
+ struct fs_sink_ctf_event_class **out_ec);
+
+BT_HIDDEN
+int try_translate_stream_class_trace_ir_to_ctf_ir(
+ struct fs_sink_ctf_trace_class *tc,
+ const bt_stream_class *ir_sc,
+ struct fs_sink_ctf_stream_class **out_sc);
+
+BT_HIDDEN
+struct fs_sink_ctf_trace_class *translate_trace_class_trace_ir_to_ctf_ir(
+ const bt_trace_class *ir_tc);
+
+#endif /* BABELTRACE_PLUGIN_CTF_FS_SINK_TRANSLATE_TRACE_IR_TO_CTF_IR_H */
+++ /dev/null
-/*
- * writer.c
- *
- * Babeltrace CTF Writer Output Plugin Event Handling
- *
- * Copyright 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * Author: Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-#define BT_LOG_TAG "PLUGIN-CTF-FS-SINK-WRITE"
-#include "logging.h"
-
-#include <babeltrace/babeltrace.h>
-#include <babeltrace/assert-internal.h>
-#include <glib.h>
-
-#include <ctfcopytrace.h>
-
-#include "writer.h"
-
-static
-void unref_stream_class(const bt_stream_class *writer_stream_class)
-{
- bt_stream_class_put_ref(writer_stream_class);
-}
-
-static
-void unref_stream(const bt_stream_class *writer_stream)
-{
- bt_stream_class_put_ref(writer_stream);
-}
-
-static
-gboolean empty_ht(gpointer key, gpointer value, gpointer user_data)
-{
- return TRUE;
-}
-
-static
-gboolean empty_streams_ht(gpointer key, gpointer value, gpointer user_data)
-{
- int ret;
- const bt_stream *writer_stream = value;
-
- ret = bt_stream_flush(writer_stream);
- if (ret) {
- BT_LOGD_STR("Failed to flush stream while emptying hash table.");
- }
- return TRUE;
-}
-
-static
-void destroy_stream_state_key(gpointer key)
-{
- g_free((enum fs_writer_stream_state *) key);
-}
-
-static
-void check_completed_trace(gpointer key, gpointer value, gpointer user_data)
-{
- enum fs_writer_stream_state *state = value;
- int *trace_completed = user_data;
-
- if (*state != FS_WRITER_COMPLETED_STREAM) {
- *trace_completed = 0;
- }
-}
-
-static
-void trace_is_static_listener(const bt_trace *trace, void *data)
-{
- struct fs_writer *fs_writer = data;
- int trace_completed = 1;
-
- fs_writer->trace_static = 1;
-
- g_hash_table_foreach(fs_writer->stream_states,
- check_completed_trace, &trace_completed);
- if (trace_completed) {
- writer_close(fs_writer->writer_component, fs_writer);
- g_hash_table_remove(fs_writer->writer_component->trace_map,
- fs_writer->trace);
- }
-}
-
-static
-const bt_stream_class *insert_new_stream_class(
- struct writer_component *writer_component,
- struct fs_writer *fs_writer,
- const bt_stream_class *stream_class)
-{
- const bt_stream_class *writer_stream_class = NULL;
- const bt_trace *trace = NULL, *writer_trace = NULL;
- struct bt_ctf_writer *ctf_writer = fs_writer->writer;
- bt_component_status ret;
-
- trace = bt_stream_class_get_trace(stream_class);
- BT_ASSERT(trace);
-
- writer_trace = bt_ctf_writer_get_trace(ctf_writer);
- BT_ASSERT(writer_trace);
-
- ret = ctf_copy_clock_classes(writer_component->err, writer_trace,
- writer_stream_class, trace);
- if (ret != BT_COMPONENT_STATUS_OK) {
- BT_LOGE_STR("Failed to copy clock classes.");
- goto error;
- }
-
- writer_stream_class = ctf_copy_stream_class(writer_component->err,
- stream_class, writer_trace, true);
- if (!writer_stream_class) {
- BT_LOGE_STR("Failed to copy stream class.");
- goto error;
- }
-
- ret = bt_trace_add_stream_class(writer_trace, writer_stream_class);
- if (ret) {
- BT_LOGE_STR("Failed to add stream_class.");
- goto error;
- }
-
- g_hash_table_insert(fs_writer->stream_class_map,
- (gpointer) stream_class, writer_stream_class);
-
- goto end;
-
-error:
- BT_STREAM_CLASS_PUT_REF_AND_RESET(writer_stream_class);
-end:
- bt_trace_put_ref(writer_trace);
- bt_trace_put_ref(trace);
- return writer_stream_class;
-}
-
-static
-enum fs_writer_stream_state *insert_new_stream_state(
- struct writer_component *writer_component,
- struct fs_writer *fs_writer, const bt_stream *stream)
-{
- enum fs_writer_stream_state *v = NULL;
-
- v = g_new0(enum fs_writer_stream_state, 1);
- if (!v) {
- BT_LOGE_STR("Failed to allocate fs_writer_stream_state.");
- goto end;
- }
- *v = FS_WRITER_UNKNOWN_STREAM;
-
- g_hash_table_insert(fs_writer->stream_states, stream, v);
-end:
- return v;
-}
-
-/*
- * Make sure the output path is valid for a single trace: either it does
- * not exists or it is empty.
- *
- * Return 0 if the path is valid, -1 otherwise.
- */
-static
-bool valid_single_trace_path(const char *path)
-{
- GError *error = NULL;
- GDir *dir = NULL;
- int ret = 0;
-
- dir = g_dir_open(path, 0, &error);
-
- /* Non-existent directory. */
- if (!dir) {
- /* For any other error, return an error */
- if (error->code != G_FILE_ERROR_NOENT) {
- ret = -1;
- }
- goto end;
- }
-
- /* g_dir_read_name skips "." and "..", error out on first result */
- while (g_dir_read_name(dir) != NULL) {
- ret = -1;
- break;
- }
-
-end:
- if (dir) {
- g_dir_close(dir);
- }
- if (error) {
- g_error_free(error);
- }
-
- return ret;
-}
-
-static
-int make_trace_path(struct writer_component *writer_component,
- const bt_trace *trace, char *trace_path)
-{
- int ret;
- const char *trace_name;
-
- if (writer_component->single_trace) {
- trace_name = "\0";
- } else {
- trace_name = bt_trace_get_name(trace);
- if (!trace_name) {
- trace_name = writer_component->trace_name_base->str;
- }
- }
-
- /* Sanitize the trace name. */
- if (strlen(trace_name) == 2 && !strcmp(trace_name, "..")) {
- BT_LOGE_STR("Trace name cannot be \"..\".");
- goto error;
- }
-
- if (strstr(trace_name, "../")) {
- BT_LOGE_STR("Trace name cannot contain \"../\".");
- goto error;
-
- }
-
- snprintf(trace_path, PATH_MAX, "%s" G_DIR_SEPARATOR_S "%s",
- writer_component->base_path->str,
- trace_name);
- /*
- * Append a suffix if the trace_path exists and we are not in
- * single-trace mode.
- */
- if (writer_component->single_trace) {
- if (valid_single_trace_path(trace_path) != 0) {
- BT_LOGE_STR("Invalid output directory.");
- goto error;
- }
- } else {
- if (g_file_test(trace_path, G_FILE_TEST_EXISTS)) {
- int i = 0;
-
- do {
- snprintf(trace_path, PATH_MAX, "%s" G_DIR_SEPARATOR_S "%s-%d",
- writer_component->base_path->str,
- trace_name, ++i);
- } while (g_file_test(trace_path, G_FILE_TEST_EXISTS) && i < INT_MAX);
- if (i == INT_MAX) {
- BT_LOGE_STR("Unable to find a unique trace path.");
- goto error;
- }
- }
- }
-
- ret = 0;
- goto end;
-
-error:
- ret = -1;
-end:
- return ret;
-}
-
-static
-struct fs_writer *insert_new_writer(
- struct writer_component *writer_component,
- const bt_trace *trace)
-{
- struct bt_ctf_writer *ctf_writer = NULL;
- const bt_trace *writer_trace = NULL;
- char trace_path[PATH_MAX];
- bt_component_status ret;
- const bt_stream *stream = NULL;
- struct fs_writer *fs_writer = NULL;
- int nr_stream, i;
-
- if (writer_component->single_trace && writer_component->nr_traces > 0) {
- BT_LOGE_STR("Trying to process more than one trace but single trace mode enabled.");
- goto error;
- }
-
- ret = make_trace_path(writer_component, trace, trace_path);
- if (ret) {
- BT_LOGE_STR("Failed to make trace path.");
- goto error;
- }
-
- printf("ctf.fs sink creating trace in %s\n", trace_path);
-
- ctf_writer = bt_ctf_writer_create(trace_path);
- if (!ctf_writer) {
- BT_LOGE_STR("Failed to create CTF writer.");
- goto error;
- }
-
- writer_trace = bt_ctf_writer_get_trace(ctf_writer);
- BT_ASSERT(writer_trace);
-
- ret = ctf_copy_trace(writer_component->err, trace, writer_trace);
- if (ret != BT_COMPONENT_STATUS_OK) {
- BT_LOGE_STR("Failed to copy trace.");
- BT_OBJECT_PUT_REF_AND_RESET(ctf_writer);
- goto error;
- }
-
- fs_writer = g_new0(struct fs_writer, 1);
- if (!fs_writer) {
- BT_LOGE_STR("Failed to allocate fs_writer.");
- goto error;
- }
- fs_writer->writer = ctf_writer;
- fs_writer->trace = trace;
- fs_writer->writer_trace = writer_trace;
- fs_writer->writer_component = writer_component;
- BT_TRACE_PUT_REF_AND_RESET(writer_trace);
- fs_writer->stream_class_map = g_hash_table_new_full(g_direct_hash,
- g_direct_equal, NULL, (GDestroyNotify) unref_stream_class);
- fs_writer->stream_map = g_hash_table_new_full(g_direct_hash,
- g_direct_equal, NULL, (GDestroyNotify) unref_stream);
- fs_writer->stream_states = g_hash_table_new_full(g_direct_hash,
- g_direct_equal, NULL, destroy_stream_state_key);
-
- /* Set all the existing streams in the unknown state. */
- nr_stream = bt_trace_get_stream_count(trace);
- for (i = 0; i < nr_stream; i++) {
- stream = bt_trace_get_stream_by_index(trace, i);
- BT_ASSERT(stream);
-
- insert_new_stream_state(writer_component, fs_writer, stream);
- BT_STREAM_PUT_REF_AND_RESET(stream);
- }
-
- /* Check if the trace is already static or register a listener. */
- if (bt_trace_is_static(trace)) {
- fs_writer->trace_static = 1;
- fs_writer->static_listener_id = -1;
- } else {
- ret = bt_trace_add_is_static_listener(trace,
- trace_is_static_listener, NULL, fs_writer);
- BT_ASSERT(ret >= 0);
- fs_writer->static_listener_id = ret;
- }
-
- writer_component->nr_traces++;
- g_hash_table_insert(writer_component->trace_map, (gpointer) trace,
- fs_writer);
-
- goto end;
-
-error:
- g_free(fs_writer);
- fs_writer = NULL;
- bt_trace_put_ref(writer_trace);
- bt_stream_put_ref(stream);
- BT_OBJECT_PUT_REF_AND_RESET(ctf_writer);
-end:
- return fs_writer;
-}
-
-static
-struct fs_writer *get_fs_writer(struct writer_component *writer_component,
- const bt_stream_class *stream_class)
-{
- const bt_trace *trace = NULL;
- struct fs_writer *fs_writer;
-
- trace = bt_stream_class_get_trace(stream_class);
- BT_ASSERT(trace);
-
- fs_writer = g_hash_table_lookup(writer_component->trace_map,
- (gpointer) trace);
- if (!fs_writer) {
- fs_writer = insert_new_writer(writer_component, trace);
- }
- BT_TRACE_PUT_REF_AND_RESET(trace);
-
- return fs_writer;
-}
-
-static
-struct fs_writer *get_fs_writer_from_stream(
- struct writer_component *writer_component,
- const bt_stream *stream)
-{
- const bt_stream_class *stream_class = NULL;
- struct fs_writer *fs_writer;
-
- stream_class = bt_stream_get_class(stream);
- BT_ASSERT(stream_class);
-
- fs_writer = get_fs_writer(writer_component, stream_class);
-
- bt_stream_class_put_ref(stream_class);
- return fs_writer;
-}
-
-static
-const bt_stream_class *lookup_stream_class(
- struct writer_component *writer_component,
- const bt_stream_class *stream_class)
-{
- struct fs_writer *fs_writer = get_fs_writer(
- writer_component, stream_class);
- BT_ASSERT(fs_writer);
- return (const bt_stream_class *) g_hash_table_lookup(
- fs_writer->stream_class_map, (gpointer) stream_class);
-}
-
-static
-const bt_stream *lookup_stream(struct writer_component *writer_component,
- const bt_stream *stream)
-{
- struct fs_writer *fs_writer = get_fs_writer_from_stream(
- writer_component, stream);
- BT_ASSERT(fs_writer);
- return (const bt_stream *) g_hash_table_lookup(
- fs_writer->stream_map, (gpointer) stream);
-}
-
-static
-const bt_stream *insert_new_stream(
- struct writer_component *writer_component,
- struct fs_writer *fs_writer,
- const bt_stream_class *stream_class,
- const bt_stream *stream)
-{
- const bt_stream *writer_stream = NULL;
- const bt_stream_class *writer_stream_class = NULL;
- struct bt_ctf_writer *ctf_writer = bt_object_get_ref(fs_writer->writer);
-
- writer_stream_class = lookup_stream_class(writer_component,
- stream_class);
- if (!writer_stream_class) {
- writer_stream_class = insert_new_stream_class(
- writer_component, fs_writer, stream_class);
- if (!writer_stream_class) {
- BT_LOGE_STR("Failed to insert a new stream_class.");
- goto error;
- }
- }
- bt_stream_class_get_ref(writer_stream_class);
-
- writer_stream = bt_stream_create(writer_stream_class,
- bt_stream_get_name(stream));
- BT_ASSERT(writer_stream);
-
- g_hash_table_insert(fs_writer->stream_map, (gpointer) stream,
- writer_stream);
-
- goto end;
-
-error:
- BT_STREAM_PUT_REF_AND_RESET(writer_stream);
-end:
- bt_object_put_ref(ctf_writer);
- bt_stream_class_put_ref(writer_stream_class);
- return writer_stream;
-}
-
-static
-const bt_event_class *get_event_class(struct writer_component *writer_component,
- const bt_stream_class *writer_stream_class,
- const bt_event_class *event_class)
-{
- return bt_stream_class_get_event_class_by_id(writer_stream_class,
- bt_event_class_get_id(event_class));
-}
-
-static
-const bt_stream *get_writer_stream(
- struct writer_component *writer_component,
- const bt_packet *packet, const bt_stream *stream)
-{
- const bt_stream *writer_stream = NULL;
-
- writer_stream = lookup_stream(writer_component, stream);
- if (!writer_stream) {
- BT_LOGE_STR("Failed to find existing stream.");
- goto error;
- }
- bt_stream_get_ref(writer_stream);
-
- goto end;
-
-error:
- BT_STREAM_PUT_REF_AND_RESET(writer_stream);
-end:
- return writer_stream;
-}
-
-BT_HIDDEN
-void writer_close(struct writer_component *writer_component,
- struct fs_writer *fs_writer)
-{
- if (fs_writer->static_listener_id >= 0) {
- bt_trace_remove_is_static_listener(fs_writer->trace,
- fs_writer->static_listener_id);
- }
-
- /* Empty the stream class HT. */
- g_hash_table_foreach_remove(fs_writer->stream_class_map,
- empty_ht, NULL);
- g_hash_table_destroy(fs_writer->stream_class_map);
-
- /* Empty the stream HT. */
- g_hash_table_foreach_remove(fs_writer->stream_map,
- empty_streams_ht, NULL);
- g_hash_table_destroy(fs_writer->stream_map);
-
- /* Empty the stream state HT. */
- g_hash_table_foreach_remove(fs_writer->stream_states,
- empty_ht, NULL);
- g_hash_table_destroy(fs_writer->stream_states);
-}
-
-BT_HIDDEN
-bt_component_status writer_stream_begin(
- struct writer_component *writer_component,
- const bt_stream *stream)
-{
- const bt_stream_class *stream_class = NULL;
- struct fs_writer *fs_writer;
- const bt_stream *writer_stream = NULL;
- bt_component_status ret = BT_COMPONENT_STATUS_OK;
- enum fs_writer_stream_state *state;
-
- stream_class = bt_stream_get_class(stream);
- BT_ASSERT(stream_class);
-
- fs_writer = get_fs_writer(writer_component, stream_class);
- if (!fs_writer) {
- BT_LOGE_STR("Failed to get fs_writer.");
- goto error;
- }
-
- /* Set the stream as active */
- state = g_hash_table_lookup(fs_writer->stream_states, stream);
- if (!state) {
- if (fs_writer->trace_static) {
- BT_LOGE_STR("Cannot add new stream on a static trace.");
- goto error;
- }
- state = insert_new_stream_state(writer_component, fs_writer,
- stream);
- }
- if (*state != FS_WRITER_UNKNOWN_STREAM) {
- BT_LOGE("Unexpected stream state: state=%d", *state);
- goto error;
- }
- *state = FS_WRITER_ACTIVE_STREAM;
-
- writer_stream = insert_new_stream(writer_component, fs_writer,
- stream_class, stream);
- if (!writer_stream) {
- BT_LOGE_STR("Failed to insert new stream.");
- goto error;
- }
-
- goto end;
-
-error:
- ret = BT_COMPONENT_STATUS_ERROR;
-end:
- bt_object_put_ref(stream_class);
- return ret;
-}
-
-BT_HIDDEN
-bt_component_status writer_stream_end(
- struct writer_component *writer_component,
- const bt_stream *stream)
-{
- const bt_stream_class *stream_class = NULL;
- struct fs_writer *fs_writer;
- const bt_trace *trace = NULL;
- bt_component_status ret = BT_COMPONENT_STATUS_OK;
- enum fs_writer_stream_state *state;
-
- stream_class = bt_stream_get_class(stream);
- BT_ASSERT(stream_class);
-
- fs_writer = get_fs_writer(writer_component, stream_class);
- if (!fs_writer) {
- BT_LOGE_STR("Failed to get fs_writer.");
- goto error;
- }
-
- state = g_hash_table_lookup(fs_writer->stream_states, stream);
- if (*state != FS_WRITER_ACTIVE_STREAM) {
- BT_LOGE("Unexpected stream state: state=%d", *state);
- goto error;
- }
- *state = FS_WRITER_COMPLETED_STREAM;
-
- g_hash_table_remove(fs_writer->stream_map, stream);
-
- if (fs_writer->trace_static) {
- int trace_completed = 1;
-
- g_hash_table_foreach(fs_writer->stream_states,
- check_completed_trace, &trace_completed);
- if (trace_completed) {
- writer_close(writer_component, fs_writer);
- g_hash_table_remove(writer_component->trace_map,
- fs_writer->trace);
- }
- }
-
- goto end;
-
-error:
- ret = BT_COMPONENT_STATUS_ERROR;
-end:
- BT_OBJECT_PUT_REF_AND_RESET(trace);
- BT_OBJECT_PUT_REF_AND_RESET(stream_class);
- return ret;
-}
-
-BT_HIDDEN
-bt_component_status writer_new_packet(
- struct writer_component *writer_component,
- const bt_packet *packet)
-{
- const bt_stream *stream = NULL, *writer_stream = NULL;
- bt_component_status ret = BT_COMPONENT_STATUS_OK;
- int int_ret;
-
- stream = bt_packet_get_stream(packet);
- BT_ASSERT(stream);
-
- writer_stream = get_writer_stream(writer_component, packet, stream);
- if (!writer_stream) {
- BT_LOGE_STR("Failed to get writer_stream.");
- goto error;
- }
- BT_OBJECT_PUT_REF_AND_RESET(stream);
-
- int_ret = ctf_stream_copy_packet_context(
- writer_component->err, packet, writer_stream);
- if (int_ret < 0) {
- BT_LOGE_STR("Failed to copy packet_context.");
- goto error;
- }
-
- ret = ctf_stream_copy_packet_header(writer_component->err,
- packet, writer_stream);
- if (ret != 0) {
- BT_LOGE_STR("Failed to copy packet_header.");
- goto error;
- }
-
- goto end;
-
-error:
- ret = BT_COMPONENT_STATUS_ERROR;
-end:
- bt_object_put_ref(writer_stream);
- bt_object_put_ref(stream);
- return ret;
-}
-
-BT_HIDDEN
-bt_component_status writer_close_packet(
- struct writer_component *writer_component,
- const bt_packet *packet)
-{
- const bt_stream *stream = NULL, *writer_stream = NULL;
- bt_component_status ret;
-
- stream = bt_packet_get_stream(packet);
- BT_ASSERT(stream);
-
- writer_stream = lookup_stream(writer_component, stream);
- if (!writer_stream) {
- BT_LOGE_STR("Failed to find existing stream.");
- goto error;
- }
- BT_OBJECT_PUT_REF_AND_RESET(stream);
-
- bt_object_get_ref(writer_stream);
-
- ret = bt_stream_flush(writer_stream);
- if (ret) {
- BT_LOGE_STR("Failed to flush stream.");
- goto error;
- }
-
- BT_OBJECT_PUT_REF_AND_RESET(writer_stream);
-
- ret = BT_COMPONENT_STATUS_OK;
- goto end;
-
-error:
- ret = BT_COMPONENT_STATUS_ERROR;
-end:
- bt_object_put_ref(writer_stream);
- bt_object_put_ref(stream);
- return ret;
-}
-
-BT_HIDDEN
-bt_component_status writer_output_event(
- struct writer_component *writer_component,
- const bt_event *event)
-{
- bt_component_status ret;
- const bt_event_class *event_class = NULL, *writer_event_class = NULL;
- const bt_stream *stream = NULL, *writer_stream = NULL;
- const bt_stream_class *stream_class = NULL, *writer_stream_class = NULL;
- const bt_event *writer_event = NULL;
- int int_ret;
- const bt_trace *writer_trace = NULL;
-
- event_class = bt_event_get_class(event);
- BT_ASSERT(event_class);
-
- stream = bt_event_get_stream(event);
- BT_ASSERT(stream);
-
- writer_stream = lookup_stream(writer_component, stream);
- if (!writer_stream || !bt_object_get_ref(writer_stream)) {
- BT_LOGE_STR("Failed for find existing stream.");
- goto error;
- }
-
- stream_class = bt_event_class_get_stream_class(event_class);
- BT_ASSERT(stream_class);
-
- writer_stream_class = lookup_stream_class(writer_component, stream_class);
- if (!writer_stream_class || !bt_object_get_ref(writer_stream_class)) {
- BT_LOGE_STR("Failed to find existing stream_class.");
- goto error;
- }
-
- writer_trace = bt_stream_class_get_trace(writer_stream_class);
- BT_ASSERT(writer_trace);
-
- writer_event_class = get_event_class(writer_component,
- writer_stream_class, event_class);
- if (!writer_event_class) {
- writer_event_class = ctf_copy_event_class(writer_component->err,
- writer_trace, event_class);
- if (!writer_event_class) {
- BT_LOGE_STR("Failed to copy event_class.");
- goto error;
- }
- int_ret = bt_stream_class_add_event_class(
- writer_stream_class, writer_event_class);
- if (int_ret) {
- BT_LOGE("Failed to add event_class: event_name=\"%s\"",
- bt_event_class_get_name(event_class));
- goto error;
- }
- }
-
- writer_event = ctf_copy_event(writer_component->err, event,
- writer_event_class, true);
- if (!writer_event) {
- BT_LOGE("Failed to copy event: event_class=\"%s\"",
- bt_event_class_get_name(writer_event_class));
- goto error;
- }
-
- int_ret = bt_stream_append_event(writer_stream, writer_event);
- if (int_ret < 0) {
- BT_LOGE("Failed to append event: event_class=\"%s\"",
- bt_event_class_get_name(writer_event_class));
- goto error;
- }
-
- ret = BT_COMPONENT_STATUS_OK;
- goto end;
-
-error:
- ret = BT_COMPONENT_STATUS_ERROR;
-end:
- bt_object_put_ref(writer_trace);
- bt_object_put_ref(writer_event);
- bt_object_put_ref(writer_event_class);
- bt_object_put_ref(writer_stream_class);
- bt_object_put_ref(stream_class);
- bt_object_put_ref(writer_stream);
- bt_object_put_ref(stream);
- bt_object_put_ref(event_class);
- return ret;
-}
+++ /dev/null
-/*
- * writer.c
- *
- * Babeltrace CTF Writer Output Plugin
- *
- * Copyright 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * Author: Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-#define BT_LOG_TAG "PLUGIN-CTF-FS-SINK-WRITER"
-#include "logging.h"
-
-#include <babeltrace/babeltrace.h>
-#include <plugins-common.h>
-#include <stdio.h>
-#include <stdbool.h>
-#include <glib.h>
-#include "writer.h"
-#include <babeltrace/assert-internal.h>
-
-static
-gboolean empty_trace_map(gpointer key, gpointer value, gpointer user_data)
-{
- struct fs_writer *fs_writer = value;
- struct writer_component *writer_component = user_data;
-
- fs_writer->trace_static = 1;
- writer_close(writer_component, fs_writer);
-
- return TRUE;
-}
-
-static
-void destroy_writer_component_data(struct writer_component *writer_component)
-{
- bt_object_put_ref(writer_component->input_iterator);
-
- g_hash_table_foreach_remove(writer_component->trace_map,
- empty_trace_map, writer_component);
- g_hash_table_destroy(writer_component->trace_map);
-
- g_string_free(writer_component->base_path, true);
- g_string_free(writer_component->trace_name_base, true);
-}
-
-BT_HIDDEN
-void writer_component_finalize(bt_self_component *component)
-{
- struct writer_component *writer_component = (struct writer_component *)
- bt_self_component_get_user_data(component);
-
- destroy_writer_component_data(writer_component);
- g_free(writer_component);
-}
-
-static
-void free_fs_writer(struct fs_writer *fs_writer)
-{
- bt_object_put_ref(fs_writer->writer);
- g_free(fs_writer);
-}
-
-static
-struct writer_component *create_writer_component(void)
-{
- struct writer_component *writer_component;
-
- writer_component = g_new0(struct writer_component, 1);
- if (!writer_component) {
- goto end;
- }
-
- writer_component->err = stderr;
- writer_component->trace_id = 0;
- writer_component->trace_name_base = g_string_new("trace");
- if (!writer_component->trace_name_base) {
- g_free(writer_component);
- writer_component = NULL;
- goto end;
- }
-
- /*
- * Reader to writer corresponding structures.
- */
- writer_component->trace_map = g_hash_table_new_full(g_direct_hash,
- g_direct_equal, NULL, (GDestroyNotify) free_fs_writer);
-
-end:
- return writer_component;
-}
-
-static
-bt_component_status handle_message(
- struct writer_component *writer_component,
- const bt_message *message)
-{
- bt_component_status ret = BT_COMPONENT_STATUS_OK;
-
- if (!writer_component) {
- ret = BT_COMPONENT_STATUS_ERROR;
- goto end;
- }
-
- switch (bt_message_get_type(message)) {
- case BT_MESSAGE_TYPE_PACKET_BEGINNING:
- {
- const bt_packet *packet =
- bt_message_packet_beginning_get_packet(message);
-
- if (!packet) {
- ret = BT_COMPONENT_STATUS_ERROR;
- goto end;
- }
-
- ret = writer_new_packet(writer_component, packet);
- bt_packet_put_ref(packet);
- break;
- }
- case BT_MESSAGE_TYPE_PACKET_END:
- {
- const bt_packet *packet =
- bt_message_packet_end_get_packet(message);
-
- if (!packet) {
- ret = BT_COMPONENT_STATUS_ERROR;
- goto end;
- }
- ret = writer_close_packet(writer_component, packet);
- bt_packet_put_ref(packet);
- break;
- }
- case BT_MESSAGE_TYPE_EVENT:
- {
- const bt_event *event = bt_message_event_get_event(
- message);
-
- if (!event) {
- ret = BT_COMPONENT_STATUS_ERROR;
- goto end;
- }
- ret = writer_output_event(writer_component, event);
- bt_object_put_ref(event);
- if (ret != BT_COMPONENT_STATUS_OK) {
- goto end;
- }
- break;
- }
- case BT_MESSAGE_TYPE_STREAM_BEGINNING:
- {
- const bt_stream *stream =
- bt_message_stream_beginning_get_stream(message);
-
- if (!stream) {
- ret = BT_COMPONENT_STATUS_ERROR;
- goto end;
- }
- ret = writer_stream_begin(writer_component, stream);
- bt_stream_put_ref(stream);
- break;
- }
- case BT_MESSAGE_TYPE_STREAM_END:
- {
- const bt_stream *stream =
- bt_message_stream_end_get_stream(message);
-
- if (!stream) {
- ret = BT_COMPONENT_STATUS_ERROR;
- goto end;
- }
- ret = writer_stream_end(writer_component, stream);
- bt_stream_put_ref(stream);
- break;
- }
- default:
- break;
- }
-end:
- return ret;
-}
-
-BT_HIDDEN
-void writer_component_port_connected(
- bt_self_component *component,
- struct bt_private_port *self_port,
- const bt_port *other_port)
-{
- struct bt_private_connection *connection;
- struct writer_component *writer;
- bt_connection_status conn_status;
-
- writer = bt_self_component_get_user_data(component);
- BT_ASSERT(writer);
- BT_ASSERT(!writer->input_iterator);
- connection = bt_private_port_get_connection(self_port);
- BT_ASSERT(connection);
- conn_status = bt_private_connection_create_message_iterator(
- connection, &writer->input_iterator);
- if (conn_status != BT_CONNECTION_STATUS_OK) {
- writer->error = true;
- }
-
- bt_object_put_ref(connection);
-}
-
-BT_HIDDEN
-bt_component_status writer_run(bt_self_component *component)
-{
- bt_component_status ret;
- const bt_message *message = NULL;
- bt_message_iterator *it;
- struct writer_component *writer_component =
- bt_self_component_get_user_data(component);
- bt_message_iterator_status it_ret;
-
- if (unlikely(writer_component->error)) {
- ret = BT_COMPONENT_STATUS_ERROR;
- goto end;
- }
-
- it = writer_component->input_iterator;
- BT_ASSERT(it);
- it_ret = bt_message_iterator_next(it);
-
- switch (it_ret) {
- case BT_MESSAGE_ITERATOR_STATUS_END:
- ret = BT_COMPONENT_STATUS_END;
- BT_OBJECT_PUT_REF_AND_RESET(writer_component->input_iterator);
- goto end;
- case BT_MESSAGE_ITERATOR_STATUS_AGAIN:
- ret = BT_COMPONENT_STATUS_AGAIN;
- goto end;
- case BT_MESSAGE_ITERATOR_STATUS_OK:
- break;
- default:
- ret = BT_COMPONENT_STATUS_ERROR;
- goto end;
- }
-
- message = bt_message_iterator_get_message(it);
- BT_ASSERT(message);
- ret = handle_message(writer_component, message);
-end:
- bt_object_put_ref(message);
- return ret;
-}
-
-static
-bt_component_status apply_one_bool(const char *key,
- bt_value *params,
- bool *option,
- bool *found)
-{
- bt_component_status ret = BT_COMPONENT_STATUS_OK;
- bt_value *value = NULL;
- bt_value_status status;
- bt_bool bool_val;
-
- value = bt_value_map_get(params, key);
- if (!value) {
- goto end;
- }
- bool_val = bt_value_bool_get(value);
-
- *option = (bool) bool_val;
- if (found) {
- *found = true;
- }
-end:
- bt_value_put_ref(value);
- return ret;
-}
-
-BT_HIDDEN
-bt_component_status writer_component_init(
- bt_self_component *component, bt_value *params,
- UNUSED_VAR void *init_method_data)
-{
- bt_component_status ret;
- bt_value_status value_ret;
- struct writer_component *writer_component = create_writer_component();
- bt_value *value = NULL;
- const char *path;
-
- if (!writer_component) {
- ret = BT_COMPONENT_STATUS_NOMEM;
- goto end;
- }
-
- ret = bt_self_component_sink_add_input_port(component,
- "in", NULL, NULL);
- if (ret != BT_COMPONENT_STATUS_OK) {
- goto end;
- }
-
- value = bt_value_map_get(params, "path");
- if (!value || bt_value_is_null(value) || !bt_value_is_string(value)) {
- BT_LOGE_STR("Missing mandatory \"path\" parameter.");
- ret = BT_COMPONENT_STATUS_INVALID;
- goto error;
- }
-
- value_ret = bt_value_string_get(value, &path);
- if (value_ret != BT_VALUE_STATUS_OK) {
- ret = BT_COMPONENT_STATUS_INVALID;
- goto error;
- }
- bt_object_put_ref(value);
-
- writer_component->base_path = g_string_new(path);
- if (!writer_component->base_path) {
- ret = BT_COMPONENT_STATUS_ERROR;
- goto error;
- }
-
- writer_component->single_trace = false;
- ret = apply_one_bool("single-trace", params,
- &writer_component->single_trace, NULL);
- if (ret != BT_COMPONENT_STATUS_OK) {
- goto end;
- }
-
- ret = bt_self_component_set_user_data(component, writer_component);
- if (ret != BT_COMPONENT_STATUS_OK) {
- goto error;
- }
-
-end:
- return ret;
-error:
- destroy_writer_component_data(writer_component);
- g_free(writer_component);
- return ret;
-}
+++ /dev/null
-#ifndef BABELTRACE_PLUGIN_WRITER_H
-#define BABELTRACE_PLUGIN_WRITER_H
-
-/*
- * BabelTrace - CTF Writer Output Plug-in
- *
- * Copyright 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * Author: Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-#include <stdbool.h>
-#include <babeltrace/babeltrace-internal.h>
-#include <babeltrace/babeltrace.h>
-
-struct writer_component {
- GString *base_path;
- GString *trace_name_base;
- /* For the directory name suffix. */
- int trace_id;
- /* Map between bt_trace and struct fs_writer. */
- GHashTable *trace_map;
- FILE *err;
- bt_message_iterator *input_iterator;
- bool error;
- bool single_trace;
- unsigned int nr_traces;
-};
-
-enum fs_writer_stream_state {
- /*
- * We know the stream exists but we have never received a
- * stream_begin message for it.
- */
- FS_WRITER_UNKNOWN_STREAM,
- /* We know this stream is active (between stream_begin and _end). */
- FS_WRITER_ACTIVE_STREAM,
- /* We have received a stream_end for this stream. */
- FS_WRITER_COMPLETED_STREAM,
-};
-
-struct fs_writer {
- struct bt_ctf_writer *writer;
- const bt_trace *trace;
- const bt_trace *writer_trace;
- struct writer_component *writer_component;
- int static_listener_id;
- int trace_static;
- /* Map between reader and writer stream. */
- GHashTable *stream_map;
- /* Map between reader and writer stream class. */
- GHashTable *stream_class_map;
- GHashTable *stream_states;
-};
-
-BT_HIDDEN
-void writer_close(struct writer_component *writer_component,
- struct fs_writer *fs_writer);
-BT_HIDDEN
-bt_component_status writer_output_event(struct writer_component *writer,
- const bt_event *event);
-BT_HIDDEN
-bt_component_status writer_new_packet(struct writer_component *writer,
- const bt_packet *packet);
-BT_HIDDEN
-bt_component_status writer_close_packet(struct writer_component *writer,
- const bt_packet *packet);
-BT_HIDDEN
-bt_component_status writer_stream_begin(struct writer_component *writer,
- const bt_stream *stream);
-BT_HIDDEN
-bt_component_status writer_stream_end(struct writer_component *writer,
- const bt_stream *stream);
-
-BT_HIDDEN
-bt_component_status writer_component_init(
- bt_self_component *component, bt_value *params,
- void *init_method_data);
-
-BT_HIDDEN
-bt_component_status writer_run(bt_self_component *component);
-
-BT_HIDDEN
-void writer_component_port_connected(
- bt_self_component *component,
- struct bt_private_port *self_port,
- const bt_port *other_port);
-
-BT_HIDDEN
-void writer_component_finalize(bt_self_component *component);
-
-#endif /* BABELTRACE_PLUGIN_WRITER_H */
AM_CPPFLAGS += -I$(top_srcdir)/plugins
-noinst_LTLIBRARIES = libbabeltrace-plugin-ctf-fs.la
+noinst_LTLIBRARIES = libbabeltrace-plugin-ctf-fs-src.la
-libbabeltrace_plugin_ctf_fs_la_SOURCES = \
+libbabeltrace_plugin_ctf_fs_src_la_SOURCES = \
data-stream-file.c \
data-stream-file.h \
file.c \
*/
#include <babeltrace/babeltrace.h>
+
#include "fs-src/fs.h"
+#include "fs-sink/fs-sink.h"
#ifndef BT_BUILT_IN_PLUGINS
BT_PLUGIN_MODULE();
BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_SEEK_BEGINNING_METHOD(fs,
ctf_fs_iterator_seek_beginning);
-#if 0
/* ctf.fs sink */
-BT_PLUGIN_SINK_COMPONENT_CLASS(fs, writer_run);
-BT_PLUGIN_SINK_COMPONENT_CLASS_INIT_METHOD(fs, writer_component_init);
-BT_PLUGIN_SINK_COMPONENT_CLASS_PORT_CONNECTED_METHOD(fs,
- writer_component_port_connected);
-BT_PLUGIN_SINK_COMPONENT_CLASS_FINALIZE_METHOD(fs, writer_component_finalize);
+BT_PLUGIN_SINK_COMPONENT_CLASS(fs, ctf_fs_sink_consume);
+BT_PLUGIN_SINK_COMPONENT_CLASS_INIT_METHOD(fs, ctf_fs_sink_init);
+BT_PLUGIN_SINK_COMPONENT_CLASS_FINALIZE_METHOD(fs, ctf_fs_sink_finalize);
+BT_PLUGIN_SINK_COMPONENT_CLASS_GRAPH_IS_CONFIGURED_METHOD(fs,
+ ctf_fs_sink_graph_is_configured);
BT_PLUGIN_SINK_COMPONENT_CLASS_DESCRIPTION(fs, "Write CTF traces to the file system.");
+#if 0
/* ctf.lttng-live source */
BT_PLUGIN_SOURCE_COMPONENT_CLASS_WITH_ID(auto, lttng_live, "lttng-live",
lttng_live_iterator_next);