ctf: compile plugin as C++
authorSimon Marchi <simon.marchi@efficios.com>
Wed, 19 Jan 2022 18:32:03 +0000 (13:32 -0500)
committerPhilippe Proulx <eeppeliteloop@gmail.com>
Fri, 28 Jan 2022 16:22:26 +0000 (11:22 -0500)
Rename files under src/plugins/ctf:

 - .c to .cpp
 - .h to .hpp
 - .y to .ypp
 - .l to .lpp

Do the necessary adjustements to get it to compile as C++.  Most changes
are not too surprising, just typical adjustments to casts and things
like that.  But there a few things worthy of mention:

  * g++ 7 doesn't accept non-trivial static initializers.  This means
    that parameter validation descriptors don't compile, and we need
    another approach.  Add constructors (only if __cplusplus is
    defined) to bt_param_validation_value_descr to allow constructing a
    descriptor of the various types.  This allows retaining a syntax
    very close to the existing one.

  * Similary, g++ 7 doesn't like the existing initialization of
    node_error, in parser.ypp.  Explicitly initialize the fields before
    `type` to work around that.

  * About the class hierarchies of ctf_field_class and
    fs_sink_ctf_field_class.  Casting to sub- or super-class is
    currently done by casting to `void *`, which doesn't work in C++.
    Replace that with:

     * to cast to a super-class, get the pointer to the right base, like
     `&fc->base.base`.  It might be a bit ugly, but it's shorter than
     casting and type-safe, so why not.

     * to cast to a sub-class, add some free functions such as
     `ctf_field_class_as_string` that check the field class' type, make
     sure they are indeed an instance of the requested type, and do the
     cast.  Note that this cast still assumes that bases are the first
     member in each sub-class.

  * For the rest, if you are wondering why some change is necessary, try
    reverting and compiling (with a recent gcc and clang), the compiler
    will tell you.

Change-Id: I05b12b906c5df0d89fdc2a70ce4a3b6810b48d41
Signed-off-by: Simon Marchi <simon.marchi@efficios.com>
Reviewed-on: https://review.lttng.org/c/babeltrace/+/7096
Reviewed-by: Philippe Proulx <eeppeliteloop@gmail.com>
155 files changed:
src/common/macros.h
src/common/mmap-align.h
src/compat/bitfield.h
src/compat/memstream.h
src/compat/mman.h
src/compat/socket.h
src/ctfser/ctfser.h
src/plugins/common/muxing/muxing.h
src/plugins/common/param-validation/param-validation.h
src/plugins/ctf/Makefile.am
src/plugins/ctf/common/Makefile.am
src/plugins/ctf/common/bfcr/Makefile.am
src/plugins/ctf/common/bfcr/bfcr.c [deleted file]
src/plugins/ctf/common/bfcr/bfcr.cpp [new file with mode: 0644]
src/plugins/ctf/common/bfcr/bfcr.h [deleted file]
src/plugins/ctf/common/bfcr/bfcr.hpp [new file with mode: 0644]
src/plugins/ctf/common/metadata/Makefile.am
src/plugins/ctf/common/metadata/ast.h [deleted file]
src/plugins/ctf/common/metadata/ast.hpp [new file with mode: 0644]
src/plugins/ctf/common/metadata/ctf-meta-configure-ir-trace.c [deleted file]
src/plugins/ctf/common/metadata/ctf-meta-configure-ir-trace.cpp [new file with mode: 0644]
src/plugins/ctf/common/metadata/ctf-meta-configure-ir-trace.h [deleted file]
src/plugins/ctf/common/metadata/ctf-meta-configure-ir-trace.hpp [new file with mode: 0644]
src/plugins/ctf/common/metadata/ctf-meta-resolve.c [deleted file]
src/plugins/ctf/common/metadata/ctf-meta-resolve.cpp [new file with mode: 0644]
src/plugins/ctf/common/metadata/ctf-meta-translate.c [deleted file]
src/plugins/ctf/common/metadata/ctf-meta-translate.cpp [new file with mode: 0644]
src/plugins/ctf/common/metadata/ctf-meta-update-alignments.c [deleted file]
src/plugins/ctf/common/metadata/ctf-meta-update-alignments.cpp [new file with mode: 0644]
src/plugins/ctf/common/metadata/ctf-meta-update-default-clock-classes.c [deleted file]
src/plugins/ctf/common/metadata/ctf-meta-update-default-clock-classes.cpp [new file with mode: 0644]
src/plugins/ctf/common/metadata/ctf-meta-update-in-ir.c [deleted file]
src/plugins/ctf/common/metadata/ctf-meta-update-in-ir.cpp [new file with mode: 0644]
src/plugins/ctf/common/metadata/ctf-meta-update-meanings.c [deleted file]
src/plugins/ctf/common/metadata/ctf-meta-update-meanings.cpp [new file with mode: 0644]
src/plugins/ctf/common/metadata/ctf-meta-update-stream-class-config.c [deleted file]
src/plugins/ctf/common/metadata/ctf-meta-update-stream-class-config.cpp [new file with mode: 0644]
src/plugins/ctf/common/metadata/ctf-meta-update-text-array-sequence.c [deleted file]
src/plugins/ctf/common/metadata/ctf-meta-update-text-array-sequence.cpp [new file with mode: 0644]
src/plugins/ctf/common/metadata/ctf-meta-update-value-storing-indexes.c [deleted file]
src/plugins/ctf/common/metadata/ctf-meta-update-value-storing-indexes.cpp [new file with mode: 0644]
src/plugins/ctf/common/metadata/ctf-meta-validate.c [deleted file]
src/plugins/ctf/common/metadata/ctf-meta-validate.cpp [new file with mode: 0644]
src/plugins/ctf/common/metadata/ctf-meta-visitors.h [deleted file]
src/plugins/ctf/common/metadata/ctf-meta-visitors.hpp [new file with mode: 0644]
src/plugins/ctf/common/metadata/ctf-meta-warn-meaningless-header-fields.c [deleted file]
src/plugins/ctf/common/metadata/ctf-meta-warn-meaningless-header-fields.cpp [new file with mode: 0644]
src/plugins/ctf/common/metadata/ctf-meta.h [deleted file]
src/plugins/ctf/common/metadata/ctf-meta.hpp [new file with mode: 0644]
src/plugins/ctf/common/metadata/decoder-packetized-file-stream-to-buf.c [deleted file]
src/plugins/ctf/common/metadata/decoder-packetized-file-stream-to-buf.cpp [new file with mode: 0644]
src/plugins/ctf/common/metadata/decoder-packetized-file-stream-to-buf.h [deleted file]
src/plugins/ctf/common/metadata/decoder-packetized-file-stream-to-buf.hpp [new file with mode: 0644]
src/plugins/ctf/common/metadata/decoder.c [deleted file]
src/plugins/ctf/common/metadata/decoder.cpp [new file with mode: 0644]
src/plugins/ctf/common/metadata/decoder.h [deleted file]
src/plugins/ctf/common/metadata/decoder.hpp [new file with mode: 0644]
src/plugins/ctf/common/metadata/lexer.l [deleted file]
src/plugins/ctf/common/metadata/lexer.lpp [new file with mode: 0644]
src/plugins/ctf/common/metadata/logging.c [deleted file]
src/plugins/ctf/common/metadata/logging.cpp [new file with mode: 0644]
src/plugins/ctf/common/metadata/logging.h [deleted file]
src/plugins/ctf/common/metadata/logging.hpp [new file with mode: 0644]
src/plugins/ctf/common/metadata/objstack.c [deleted file]
src/plugins/ctf/common/metadata/objstack.cpp [new file with mode: 0644]
src/plugins/ctf/common/metadata/objstack.h [deleted file]
src/plugins/ctf/common/metadata/objstack.hpp [new file with mode: 0644]
src/plugins/ctf/common/metadata/parser-wrap.h [deleted file]
src/plugins/ctf/common/metadata/parser-wrap.hpp [new file with mode: 0644]
src/plugins/ctf/common/metadata/parser.y [deleted file]
src/plugins/ctf/common/metadata/parser.ypp [new file with mode: 0644]
src/plugins/ctf/common/metadata/scanner-symbols.h [deleted file]
src/plugins/ctf/common/metadata/scanner-symbols.hpp [new file with mode: 0644]
src/plugins/ctf/common/metadata/scanner.h [deleted file]
src/plugins/ctf/common/metadata/scanner.hpp [new file with mode: 0644]
src/plugins/ctf/common/metadata/visitor-generate-ir.c [deleted file]
src/plugins/ctf/common/metadata/visitor-generate-ir.cpp [new file with mode: 0644]
src/plugins/ctf/common/metadata/visitor-parent-links.c [deleted file]
src/plugins/ctf/common/metadata/visitor-parent-links.cpp [new file with mode: 0644]
src/plugins/ctf/common/metadata/visitor-semantic-validator.c [deleted file]
src/plugins/ctf/common/metadata/visitor-semantic-validator.cpp [new file with mode: 0644]
src/plugins/ctf/common/msg-iter/Makefile.am
src/plugins/ctf/common/msg-iter/msg-iter.c [deleted file]
src/plugins/ctf/common/msg-iter/msg-iter.cpp [new file with mode: 0644]
src/plugins/ctf/common/msg-iter/msg-iter.h [deleted file]
src/plugins/ctf/common/msg-iter/msg-iter.hpp [new file with mode: 0644]
src/plugins/ctf/common/print.h [deleted file]
src/plugins/ctf/common/print.hpp [new file with mode: 0644]
src/plugins/ctf/fs-sink/Makefile.am
src/plugins/ctf/fs-sink/fs-sink-ctf-meta.h [deleted file]
src/plugins/ctf/fs-sink/fs-sink-ctf-meta.hpp [new file with mode: 0644]
src/plugins/ctf/fs-sink/fs-sink-stream.c [deleted file]
src/plugins/ctf/fs-sink/fs-sink-stream.cpp [new file with mode: 0644]
src/plugins/ctf/fs-sink/fs-sink-stream.h [deleted file]
src/plugins/ctf/fs-sink/fs-sink-stream.hpp [new file with mode: 0644]
src/plugins/ctf/fs-sink/fs-sink-trace.c [deleted file]
src/plugins/ctf/fs-sink/fs-sink-trace.cpp [new file with mode: 0644]
src/plugins/ctf/fs-sink/fs-sink-trace.h [deleted file]
src/plugins/ctf/fs-sink/fs-sink-trace.hpp [new file with mode: 0644]
src/plugins/ctf/fs-sink/fs-sink.c [deleted file]
src/plugins/ctf/fs-sink/fs-sink.cpp [new file with mode: 0644]
src/plugins/ctf/fs-sink/fs-sink.h [deleted file]
src/plugins/ctf/fs-sink/fs-sink.hpp [new file with mode: 0644]
src/plugins/ctf/fs-sink/translate-ctf-ir-to-tsdl.c [deleted file]
src/plugins/ctf/fs-sink/translate-ctf-ir-to-tsdl.cpp [new file with mode: 0644]
src/plugins/ctf/fs-sink/translate-ctf-ir-to-tsdl.h [deleted file]
src/plugins/ctf/fs-sink/translate-ctf-ir-to-tsdl.hpp [new file with mode: 0644]
src/plugins/ctf/fs-sink/translate-trace-ir-to-ctf-ir.c [deleted file]
src/plugins/ctf/fs-sink/translate-trace-ir-to-ctf-ir.cpp [new file with mode: 0644]
src/plugins/ctf/fs-sink/translate-trace-ir-to-ctf-ir.h [deleted file]
src/plugins/ctf/fs-sink/translate-trace-ir-to-ctf-ir.hpp [new file with mode: 0644]
src/plugins/ctf/fs-src/Makefile.am
src/plugins/ctf/fs-src/data-stream-file.c [deleted file]
src/plugins/ctf/fs-src/data-stream-file.cpp [new file with mode: 0644]
src/plugins/ctf/fs-src/data-stream-file.h [deleted file]
src/plugins/ctf/fs-src/data-stream-file.hpp [new file with mode: 0644]
src/plugins/ctf/fs-src/file.c [deleted file]
src/plugins/ctf/fs-src/file.cpp [new file with mode: 0644]
src/plugins/ctf/fs-src/file.h [deleted file]
src/plugins/ctf/fs-src/file.hpp [new file with mode: 0644]
src/plugins/ctf/fs-src/fs.c [deleted file]
src/plugins/ctf/fs-src/fs.cpp [new file with mode: 0644]
src/plugins/ctf/fs-src/fs.h [deleted file]
src/plugins/ctf/fs-src/fs.hpp [new file with mode: 0644]
src/plugins/ctf/fs-src/lttng-index.h [deleted file]
src/plugins/ctf/fs-src/lttng-index.hpp [new file with mode: 0644]
src/plugins/ctf/fs-src/metadata.c [deleted file]
src/plugins/ctf/fs-src/metadata.cpp [new file with mode: 0644]
src/plugins/ctf/fs-src/metadata.h [deleted file]
src/plugins/ctf/fs-src/metadata.hpp [new file with mode: 0644]
src/plugins/ctf/fs-src/query.c [deleted file]
src/plugins/ctf/fs-src/query.cpp [new file with mode: 0644]
src/plugins/ctf/fs-src/query.h [deleted file]
src/plugins/ctf/fs-src/query.hpp [new file with mode: 0644]
src/plugins/ctf/lttng-live/Makefile.am
src/plugins/ctf/lttng-live/data-stream.c [deleted file]
src/plugins/ctf/lttng-live/data-stream.cpp [new file with mode: 0644]
src/plugins/ctf/lttng-live/data-stream.h [deleted file]
src/plugins/ctf/lttng-live/data-stream.hpp [new file with mode: 0644]
src/plugins/ctf/lttng-live/lttng-live.c [deleted file]
src/plugins/ctf/lttng-live/lttng-live.cpp [new file with mode: 0644]
src/plugins/ctf/lttng-live/lttng-live.h [deleted file]
src/plugins/ctf/lttng-live/lttng-live.hpp [new file with mode: 0644]
src/plugins/ctf/lttng-live/lttng-viewer-abi.h [deleted file]
src/plugins/ctf/lttng-live/lttng-viewer-abi.hpp [new file with mode: 0644]
src/plugins/ctf/lttng-live/metadata.c [deleted file]
src/plugins/ctf/lttng-live/metadata.cpp [new file with mode: 0644]
src/plugins/ctf/lttng-live/metadata.h [deleted file]
src/plugins/ctf/lttng-live/metadata.hpp [new file with mode: 0644]
src/plugins/ctf/lttng-live/viewer-connection.c [deleted file]
src/plugins/ctf/lttng-live/viewer-connection.cpp [new file with mode: 0644]
src/plugins/ctf/lttng-live/viewer-connection.h [deleted file]
src/plugins/ctf/lttng-live/viewer-connection.hpp [new file with mode: 0644]
src/plugins/ctf/plugin.c [deleted file]
src/plugins/ctf/plugin.cpp [new file with mode: 0644]

index 5549b8576fe251bd196a79b361bc054019d76239..34ac393f76bda366a8ad142ff4d568ba3465bb25 100644 (file)
 extern "C" {
 #endif
 
+#ifdef __cplusplus
+#define BT_EXTERN_C extern "C"
+#else
+#define BT_EXTERN_C
+#endif
+
 #define bt_max_t(type, a, b)   \
        ((type) (a) > (type) (b) ? (type) (a) : (type) (b))
 
index d275024116a0a13f7a4d60c5ce20bc0f99e0725f..7c16493530eef9043d82ac1744141b7eba0ad085 100644 (file)
@@ -46,9 +46,9 @@ struct mmap_align_data *mmap_align(size_t length, int prot,
 
        page_size = bt_common_get_page_size(log_level);
 
-       mma = malloc(sizeof(*mma));
+       mma = (struct mmap_align_data *) malloc(sizeof(*mma));
        if (!mma)
-               return MAP_FAILED;
+               return (struct mmap_align_data *) MAP_FAILED;
        mma->length = length;
        page_aligned_offset = get_page_aligned_offset(offset, log_level);
        /*
@@ -62,7 +62,7 @@ struct mmap_align_data *mmap_align(size_t length, int prot,
                prot, flags, fd, page_aligned_offset, log_level);
        if (mma->page_aligned_addr == MAP_FAILED) {
                free(mma);
-               return MAP_FAILED;
+               return (struct mmap_align_data *) MAP_FAILED;
        }
        mma->addr = ((uint8_t *) mma->page_aligned_addr) + (offset - page_aligned_offset);
        return mma;
index e0a3885cb28d36996558529dfc76e88b63785a9e..df67401d5e1c56d59d793907edb649f20727add9 100644 (file)
@@ -189,7 +189,7 @@ do {                                                                        \
 #define _bt_bitfield_write_le(ptr, type, start, length, v)             \
 do {                                                                   \
        __typeof__(v) _v = (v);                                 \
-       type *_ptr = (void *) (ptr);                                    \
+       type *_ptr = (type *) (ptr);                                    \
        unsigned long _start = (start), _length = (length);             \
        type _mask, _cmask;                                             \
        unsigned long _ts = sizeof(type) * CHAR_BIT; /* type size */    \
@@ -248,7 +248,7 @@ do {                                                                        \
 #define _bt_bitfield_write_be(ptr, type, start, length, v)             \
 do {                                                                   \
        __typeof__(v) _v = (v);                                         \
-       type *_ptr = (void *) (ptr);                                    \
+       type *_ptr = (type *) (ptr);                                    \
        unsigned long _start = (start), _length = (length);             \
        type _mask, _cmask;                                             \
        unsigned long _ts = sizeof(type) * CHAR_BIT; /* type size */    \
@@ -409,7 +409,7 @@ do {                                                                        \
 do {                                                                   \
        __typeof__(*(vptr)) *_vptr = (vptr);                            \
        __typeof__(*_vptr) _v;                                          \
-       type *_ptr = (void *) (ptr);                                    \
+       type *_ptr = (type *) (ptr);                                    \
        unsigned long _start = (start), _length = (length);             \
        type _mask, _cmask;                                             \
        unsigned long _ts = sizeof(type) * CHAR_BIT; /* type size */    \
index 30d974ca4aaf748fba791d7f19c476f0b6721e66..dcadf048828daefc57ec61ee02ba442d71f456c0 100644 (file)
@@ -299,7 +299,7 @@ int bt_close_memstream(char **buf, size_t *size, FILE *fp)
        }
        *size = pos;
        /* add final \0 */
-       *buf = calloc(pos + 1, sizeof(char));
+       *buf = (char *) calloc(pos + 1, sizeof(char));
        if (!*buf) {
                return -ENOMEM;
        }
index 3f03d4900d4f2b8c0d962240cd38001435067c3a..a9568c6bc99729ddf831a20660ba24777beea2b4 100644 (file)
  * Note that some platforms (e.g. Windows) do not allow read-only
  * mappings to exceed the file's size (even within a page).
  */
-void *bt_mmap(void *addr, size_t length, int prot, int flags, int fd,
+BT_EXTERN_C void *bt_mmap(void *addr, size_t length, int prot, int flags, int fd,
        off_t offset, int log_level);
 
-int bt_munmap(void *addr, size_t length);
+BT_EXTERN_C int bt_munmap(void *addr, size_t length);
 
 /*
  * On Windows the memory mapping offset must be aligned to the memory
  * allocator allocation granularity and not the page size.
  */
-size_t bt_mmap_get_offset_align_size(int log_level);
+BT_EXTERN_C size_t bt_mmap_get_offset_align_size(int log_level);
 
 #else /* __MINGW32__ */
 
index f5c59b4bb1e53133ff4757e0c32a8721067ce3d7..7a57678b377e1995b147ce72d9f7aaaf144e0283 100644 (file)
@@ -59,13 +59,13 @@ int bt_socket_fini(void)
 static inline
 int bt_socket_send(int sockfd, const void *buf, size_t len, int flags)
 {
-       return send(sockfd, buf, len, flags);
+       return send(sockfd, (const char *) buf, len, flags);
 }
 
 static inline
 int bt_socket_recv(int sockfd, void *buf, size_t len, int flags)
 {
-       return recv(sockfd, buf, len, flags);
+       return recv(sockfd, (char *) buf, len, flags);
 }
 
 static inline
index 2abe65a3ae18c2c265266d4c7ea4ba336cc85456..d2fb0b9126cc0fe68706065efbe981e7db9ec2b9 100644 (file)
@@ -67,7 +67,7 @@ struct bt_ctfser {
  *
  * This function opens the file `path` for writing.
  */
-BT_HIDDEN
+BT_EXTERN_C BT_HIDDEN
 int bt_ctfser_init(struct bt_ctfser *ctfser, const char *path,
                int log_level);
 
@@ -77,7 +77,7 @@ int bt_ctfser_init(struct bt_ctfser *ctfser, const char *path,
  * This function truncates the stream file so that there's no extra
  * padding after the last packet, and then closes the file.
  */
-BT_HIDDEN
+BT_EXTERN_C BT_HIDDEN
 int bt_ctfser_fini(struct bt_ctfser *ctfser);
 
 /*
@@ -85,17 +85,17 @@ int bt_ctfser_fini(struct bt_ctfser *ctfser);
  *
  * All the next writing functions are performed within this new packet.
  */
-BT_HIDDEN
+BT_EXTERN_C BT_HIDDEN
 int bt_ctfser_open_packet(struct bt_ctfser *ctfser);
 
 /*
  * Closes the current packet, making its size `packet_size_bytes`.
  */
-BT_HIDDEN
+BT_EXTERN_C BT_HIDDEN
 void bt_ctfser_close_current_packet(struct bt_ctfser *ctfser,
                uint64_t packet_size_bytes);
 
-BT_HIDDEN
+BT_EXTERN_C BT_HIDDEN
 int _bt_ctfser_increase_cur_packet_size(struct bt_ctfser *ctfser);
 
 static inline
@@ -404,11 +404,11 @@ int bt_ctfser_write_unsigned_int(struct bt_ctfser *ctfser, uint64_t value,
        }
 
        if (byte_order == LITTLE_ENDIAN) {
-               bt_bitfield_write_le(mmap_align_addr(ctfser->base_mma) +
+               bt_bitfield_write_le((uint8_t *) mmap_align_addr(ctfser->base_mma) +
                        ctfser->mmap_base_offset, uint8_t,
                        ctfser->offset_in_cur_packet_bits, size_bits, value);
        } else {
-               bt_bitfield_write_be(mmap_align_addr(ctfser->base_mma) +
+               bt_bitfield_write_be((uint8_t *) mmap_align_addr(ctfser->base_mma) +
                        ctfser->mmap_base_offset, uint8_t,
                        ctfser->offset_in_cur_packet_bits, size_bits, value);
        }
@@ -449,11 +449,11 @@ int bt_ctfser_write_signed_int(struct bt_ctfser *ctfser, int64_t value,
        }
 
        if (byte_order == LITTLE_ENDIAN) {
-               bt_bitfield_write_le(mmap_align_addr(ctfser->base_mma) +
+               bt_bitfield_write_le((uint8_t *) mmap_align_addr(ctfser->base_mma) +
                        ctfser->mmap_base_offset, uint8_t,
                        ctfser->offset_in_cur_packet_bits, size_bits, value);
        } else {
-               bt_bitfield_write_be(mmap_align_addr(ctfser->base_mma) +
+               bt_bitfield_write_be((uint8_t *) mmap_align_addr(ctfser->base_mma) +
                        ctfser->mmap_base_offset, uint8_t,
                        ctfser->offset_in_cur_packet_bits, size_bits, value);
        }
index e34f1efa21a50b57431e425ff036b61f854d8a66..07232ee2a445da28d38de209f972671eedecfd32 100644 (file)
@@ -10,7 +10,7 @@
 #include <babeltrace2/babeltrace.h>
 #include "common/macros.h"
 
-BT_HIDDEN
+BT_EXTERN_C BT_HIDDEN
 int common_muxing_compare_messages(const bt_message *left_msg,
                const bt_message *right_msg);
 
index f89ae34971720b971addae5d4204d0f17d0d133d..0daee1ff9040e0c3498126e153a59d47b7d77df4 100644 (file)
 struct bt_param_validation_context;
 struct bt_param_validation_value_descr;
 
+#if defined(__cplusplus)
+#define BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END \
+       { bt_param_validation_map_value_entry_descr::end }
+#else
 #define BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END { NULL, 0, {} }
+#endif
 
 struct bt_param_validation_map_value_descr {
        const struct bt_param_validation_map_value_entry_descr *entries;
@@ -47,6 +52,32 @@ typedef enum bt_param_validation_status
                        struct bt_param_validation_context *);
 
 struct bt_param_validation_value_descr {
+#if defined(__cplusplus)
+       static struct {} array_t;
+       static struct {} string_t;
+       static struct {} signed_integer_t;
+       static struct {} bool_t;
+
+       bt_param_validation_value_descr(decltype(array_t),
+                       uint64_t min_length, uint64_t max_length,
+                       const bt_param_validation_value_descr &element_type)
+               : type{BT_VALUE_TYPE_ARRAY}, array{min_length, max_length, &element_type}
+       {}
+
+       bt_param_validation_value_descr(decltype(string_t),
+                       const char **choices = nullptr)
+               : type{BT_VALUE_TYPE_STRING}, string{choices}
+        {}
+
+       bt_param_validation_value_descr(decltype(signed_integer_t))
+               : type{BT_VALUE_TYPE_SIGNED_INTEGER}
+       {}
+
+       bt_param_validation_value_descr(decltype(bool_t))
+               : type{BT_VALUE_TYPE_BOOL}
+       {}
+#endif
+
        bt_value_type type;
 
        /* Additional checks dependent on the type. */
@@ -64,26 +95,45 @@ struct bt_param_validation_value_descr {
         * `bt_param_validation_error` with the provided context
         * to set the error string.
         */
-       bt_param_validation_func *validation_func;
+       bt_param_validation_func *validation_func
+#if defined(__cplusplus)
+               = nullptr
+#endif
+         ;
 };
 
 #define BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL true
 #define BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY false
 
 struct bt_param_validation_map_value_entry_descr {
+#if defined(__cplusplus)
+       static struct {} end;
+
+       bt_param_validation_map_value_entry_descr(decltype(end))
+               : key{nullptr},
+               /* These values are not important. */
+               is_optional{false}, value_descr(bt_param_validation_value_descr::bool_t)
+       {}
+
+       bt_param_validation_map_value_entry_descr(const char *key_, bool is_optional_,
+                       bt_param_validation_value_descr value_descr_)
+               : key(key_), is_optional(is_optional_), value_descr(value_descr_)
+       {}
+
+#endif
+       /* If NULL/nullptr, this entry represents the end of the list. */
        const char *key;
        bool is_optional;
-
        const struct bt_param_validation_value_descr value_descr;
 };
 
-BT_HIDDEN
+BT_EXTERN_C BT_HIDDEN
 enum bt_param_validation_status bt_param_validation_validate(
                const bt_value *params,
                const struct bt_param_validation_map_value_entry_descr *entries,
                gchar **error);
 
-BT_HIDDEN __BT_ATTR_FORMAT_PRINTF(2, 3)
+BT_EXTERN_C BT_HIDDEN __BT_ATTR_FORMAT_PRINTF(2, 3)
 enum bt_param_validation_status bt_param_validation_error(
                struct bt_param_validation_context *ctx,
                const char *format, ...);
index 24a434011501ac47dfbf4ba5b053be65e46a9b22..b5050b8d280595f99b083235134807bd6c0b4b5f 100644 (file)
@@ -9,7 +9,7 @@ plugindir = "$(BABELTRACE_PLUGINS_DIR)"
 plugin_LTLIBRARIES = babeltrace-plugin-ctf.la
 
 # ctf plugin
-babeltrace_plugin_ctf_la_SOURCES = plugin.c
+babeltrace_plugin_ctf_la_SOURCES = plugin.cpp
 
 babeltrace_plugin_ctf_la_LDFLAGS = \
        $(AM_LDFLAGS) \
index 3f7458bf0f84775a1c86bb95d6a2d56096b6f4da..78b76fe287c10eba739b231847de8d0f01959701 100644 (file)
@@ -3,7 +3,7 @@
 SUBDIRS = metadata bfcr msg-iter
 
 noinst_LTLIBRARIES = libbabeltrace2-plugin-ctf-common.la
-libbabeltrace2_plugin_ctf_common_la_SOURCES = print.h
+libbabeltrace2_plugin_ctf_common_la_SOURCES = print.hpp
 libbabeltrace2_plugin_ctf_common_la_LIBADD =           \
        $(builddir)/metadata/libctf-parser.la           \
        $(builddir)/metadata/libctf-ast.la              \
index e4c2d2e8fcf64ac62650ca0a057bf5b66b72b1a7..17b7705d0bf13271e11ea2439afaffcd8155a127 100644 (file)
@@ -2,5 +2,5 @@
 
 noinst_LTLIBRARIES = libctf-bfcr.la
 libctf_bfcr_la_SOURCES = \
-       bfcr.c \
-       bfcr.h
+       bfcr.cpp \
+       bfcr.hpp
diff --git a/src/plugins/ctf/common/bfcr/bfcr.c b/src/plugins/ctf/common/bfcr/bfcr.c
deleted file mode 100644 (file)
index eba591c..0000000
+++ /dev/null
@@ -1,1353 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright (c) 2015-2016 EfficiOS Inc. and Linux Foundation
- * Copyright (c) 2015-2016 Philippe Proulx <pproulx@efficios.com>
- *
- * Babeltrace - CTF binary field class reader (BFCR)
- */
-
-#define BT_COMP_LOG_SELF_COMP (bfcr->self_comp)
-#define BT_LOG_OUTPUT_LEVEL (bfcr->log_level)
-#define BT_LOG_TAG "PLUGIN/CTF/BFCR"
-#include "logging/comp-logging.h"
-
-#include <stdlib.h>
-#include <stdint.h>
-#include <inttypes.h>
-#include <stdio.h>
-#include <stddef.h>
-#include <stdbool.h>
-#include "common/assert.h"
-#include <string.h>
-#include "compat/bitfield.h"
-#include "common/common.h"
-#include <babeltrace2/babeltrace.h>
-#include "common/align.h"
-#include <glib.h>
-
-#include "bfcr.h"
-#include "../metadata/ctf-meta.h"
-
-#define DIV8(_x)                       ((_x) >> 3)
-#define BYTES_TO_BITS(_x)              ((_x) * 8)
-#define BITS_TO_BYTES_FLOOR(_x)                DIV8(_x)
-#define BITS_TO_BYTES_CEIL(_x)         DIV8((_x) + 7)
-#define IN_BYTE_OFFSET(_at)            ((_at) & 7)
-
-/* A visit stack entry */
-struct stack_entry {
-       /*
-        * Current class of base field, one of:
-        *
-        *   * Structure
-        *   * Array
-        *   * Sequence
-        *   * Variant
-        */
-       struct ctf_field_class *base_class;
-
-       /* Length of base field (always 1 for a variant class) */
-       int64_t base_len;
-
-       /* Index of next field to read */
-       int64_t index;
-};
-
-struct bt_bfcr;
-
-/* Visit stack */
-struct stack {
-       struct bt_bfcr *bfcr;
-
-       /* Entries (struct stack_entry) */
-       GArray *entries;
-
-       /* Number of active entries */
-       size_t size;
-};
-
-/* Reading states */
-enum bfcr_state {
-       BFCR_STATE_NEXT_FIELD,
-       BFCR_STATE_ALIGN_BASIC,
-       BFCR_STATE_ALIGN_COMPOUND,
-       BFCR_STATE_READ_BASIC_BEGIN,
-       BFCR_STATE_READ_BASIC_CONTINUE,
-       BFCR_STATE_DONE,
-};
-
-/* Binary class reader */
-struct bt_bfcr {
-       bt_logging_level log_level;
-
-       /* Weak */
-       bt_self_component *self_comp;
-
-       /* BFCR stack */
-       struct stack *stack;
-
-       /* Current basic field class */
-       struct ctf_field_class *cur_basic_field_class;
-
-       /* Current state */
-       enum bfcr_state state;
-
-       /*
-        * Last basic field class's byte order.
-        *
-        * This is used to detect errors since two contiguous basic
-        * classes for which the common boundary is not the boundary of
-        * a byte cannot have different byte orders.
-        *
-        * This is set to CTF_BYTE_ORDER_UNKNOWN on reset and when the last
-        * basic field class was a string class.
-        */
-       enum ctf_byte_order last_bo;
-
-       /* Current byte order (copied to last_bo after a successful read) */
-       enum ctf_byte_order cur_bo;
-
-       /* Stitch buffer infos */
-       struct {
-               /* Stitch buffer */
-               uint8_t buf[16];
-
-               /* Offset, within stitch buffer, of first bit */
-               size_t offset;
-
-               /* Length (bits) of data in stitch buffer from offset */
-               size_t at;
-       } stitch;
-
-       /* User buffer infos */
-       struct {
-               /* Address */
-               const uint8_t *addr;
-
-               /* Offset of data from address (bits) */
-               size_t offset;
-
-               /* Current position from offset (bits) */
-               size_t at;
-
-               /* Offset of offset within whole packet (bits) */
-               size_t packet_offset;
-
-               /* Data size in buffer (bits) */
-               size_t sz;
-
-               /* Buffer size (bytes) */
-               size_t buf_sz;
-       } buf;
-
-       /* User stuff */
-       struct {
-               /* Callback functions */
-               struct bt_bfcr_cbs cbs;
-
-               /* Private data */
-               void *data;
-       } user;
-};
-
-static inline
-const char *bfcr_state_string(enum bfcr_state state)
-{
-       switch (state) {
-       case BFCR_STATE_NEXT_FIELD:
-               return "NEXT_FIELD";
-       case BFCR_STATE_ALIGN_BASIC:
-               return "ALIGN_BASIC";
-       case BFCR_STATE_ALIGN_COMPOUND:
-               return "ALIGN_COMPOUND";
-       case BFCR_STATE_READ_BASIC_BEGIN:
-               return "READ_BASIC_BEGIN";
-       case BFCR_STATE_READ_BASIC_CONTINUE:
-               return "READ_BASIC_CONTINUE";
-       case BFCR_STATE_DONE:
-               return "DONE";
-       }
-
-       bt_common_abort();
-}
-
-static
-struct stack *stack_new(struct bt_bfcr *bfcr)
-{
-       struct stack *stack = NULL;
-
-       stack = g_new0(struct stack, 1);
-       if (!stack) {
-               BT_COMP_LOGE_STR("Failed to allocate one stack.");
-               goto error;
-       }
-
-       stack->bfcr = bfcr;
-       stack->entries = g_array_new(FALSE, TRUE, sizeof(struct stack_entry));
-       if (!stack->entries) {
-               BT_COMP_LOGE_STR("Failed to allocate a GArray.");
-               goto error;
-       }
-
-       BT_COMP_LOGD("Created stack: addr=%p", stack);
-       return stack;
-
-error:
-       g_free(stack);
-       return NULL;
-}
-
-static
-void stack_destroy(struct stack *stack)
-{
-       struct bt_bfcr *bfcr;
-
-       if (!stack) {
-               return;
-       }
-
-       bfcr = stack->bfcr;
-       BT_COMP_LOGD("Destroying stack: addr=%p", stack);
-
-       if (stack->entries) {
-               g_array_free(stack->entries, TRUE);
-       }
-
-       g_free(stack);
-}
-
-static
-int stack_push(struct stack *stack, struct ctf_field_class *base_class,
-       size_t base_len)
-{
-       struct stack_entry *entry;
-       struct bt_bfcr *bfcr;
-
-       BT_ASSERT_DBG(stack);
-       BT_ASSERT_DBG(base_class);
-       bfcr = stack->bfcr;
-       BT_COMP_LOGT("Pushing field class on stack: stack-addr=%p, "
-               "fc-addr=%p, fc-type=%d, base-length=%zu, "
-               "stack-size-before=%zu, stack-size-after=%zu",
-               stack, base_class, base_class->type,
-               base_len, stack->size, stack->size + 1);
-
-       if (stack->entries->len == stack->size) {
-               g_array_set_size(stack->entries, stack->size + 1);
-       }
-
-       entry = &g_array_index(stack->entries, struct stack_entry, stack->size);
-       entry->base_class = base_class;
-       entry->base_len = base_len;
-       entry->index = 0;
-       stack->size++;
-       return 0;
-}
-
-static inline
-int64_t get_compound_field_class_length(struct bt_bfcr *bfcr,
-               struct ctf_field_class *fc)
-{
-       int64_t length;
-
-       switch (fc->type) {
-       case CTF_FIELD_CLASS_TYPE_STRUCT:
-       {
-               struct ctf_field_class_struct *struct_fc = (void *) fc;
-
-               length = (int64_t) struct_fc->members->len;
-               break;
-       }
-       case CTF_FIELD_CLASS_TYPE_VARIANT:
-       {
-               /* Variant field classes always "contain" a single class */
-               length = 1;
-               break;
-       }
-       case CTF_FIELD_CLASS_TYPE_ARRAY:
-       {
-               struct ctf_field_class_array *array_fc = (void *) fc;
-
-               length = (int64_t) array_fc->length;
-               break;
-       }
-       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-               length = bfcr->user.cbs.query.get_sequence_length(fc,
-                       bfcr->user.data);
-               break;
-       default:
-               bt_common_abort();
-       }
-
-       return length;
-}
-
-static
-int stack_push_with_len(struct bt_bfcr *bfcr, struct ctf_field_class *base_class)
-{
-       int ret;
-       int64_t length = get_compound_field_class_length(bfcr, base_class);
-
-       if (length < 0) {
-               BT_COMP_LOGW("Cannot get compound field class's field count: "
-                       "bfcr-addr=%p, fc-addr=%p, fc-type=%d",
-                       bfcr, base_class, base_class->type);
-               ret = BT_BFCR_STATUS_ERROR;
-               goto end;
-       }
-
-       ret = stack_push(bfcr->stack, base_class, (size_t) length);
-
-end:
-       return ret;
-}
-
-static inline
-unsigned int stack_size(struct stack *stack)
-{
-       BT_ASSERT_DBG(stack);
-       return stack->size;
-}
-
-static
-void stack_pop(struct stack *stack)
-{
-       struct bt_bfcr *bfcr;
-
-       BT_ASSERT_DBG(stack);
-       BT_ASSERT_DBG(stack_size(stack));
-       bfcr = stack->bfcr;
-       BT_COMP_LOGT("Popping from stack: "
-               "stack-addr=%p, stack-size-before=%u, stack-size-after=%u",
-               stack, stack->entries->len, stack->entries->len - 1);
-       stack->size--;
-}
-
-static inline
-bool stack_empty(struct stack *stack)
-{
-       return stack_size(stack) == 0;
-}
-
-static
-void stack_clear(struct stack *stack)
-{
-       BT_ASSERT_DBG(stack);
-       stack->size = 0;
-}
-
-static inline
-struct stack_entry *stack_top(struct stack *stack)
-{
-       BT_ASSERT_DBG(stack);
-       BT_ASSERT_DBG(stack_size(stack));
-       return &g_array_index(stack->entries, struct stack_entry,
-               stack->size - 1);
-}
-
-static inline
-size_t available_bits(struct bt_bfcr *bfcr)
-{
-       return bfcr->buf.sz - bfcr->buf.at;
-}
-
-static inline
-void consume_bits(struct bt_bfcr *bfcr, size_t incr)
-{
-       BT_COMP_LOGT("Advancing cursor: bfcr-addr=%p, cur-before=%zu, cur-after=%zu",
-               bfcr, bfcr->buf.at, bfcr->buf.at + incr);
-       bfcr->buf.at += incr;
-}
-
-static inline
-bool has_enough_bits(struct bt_bfcr *bfcr, size_t sz)
-{
-       return available_bits(bfcr) >= sz;
-}
-
-static inline
-bool at_least_one_bit_left(struct bt_bfcr *bfcr)
-{
-       return has_enough_bits(bfcr, 1);
-}
-
-static inline
-size_t packet_at(struct bt_bfcr *bfcr)
-{
-       return bfcr->buf.packet_offset + bfcr->buf.at;
-}
-
-static inline
-size_t buf_at_from_addr(struct bt_bfcr *bfcr)
-{
-       /*
-        * Considering this:
-        *
-        *     ====== offset ===== (17)
-        *
-        *     xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
-        *     ^
-        *     addr (0)           ==== at ==== (12)
-        *
-        * We want this:
-        *
-        *     =============================== (29)
-        */
-       return bfcr->buf.offset + bfcr->buf.at;
-}
-
-static
-void stitch_reset(struct bt_bfcr *bfcr)
-{
-       bfcr->stitch.offset = 0;
-       bfcr->stitch.at = 0;
-}
-
-static inline
-size_t stitch_at_from_addr(struct bt_bfcr *bfcr)
-{
-       return bfcr->stitch.offset + bfcr->stitch.at;
-}
-
-static
-void stitch_append_from_buf(struct bt_bfcr *bfcr, size_t sz)
-{
-       size_t stitch_byte_at;
-       size_t buf_byte_at;
-       size_t nb_bytes;
-
-       if (sz == 0) {
-               return;
-       }
-
-       stitch_byte_at =
-               BITS_TO_BYTES_FLOOR(stitch_at_from_addr(bfcr));
-       buf_byte_at = BITS_TO_BYTES_FLOOR(buf_at_from_addr(bfcr));
-       nb_bytes = BITS_TO_BYTES_CEIL(sz);
-       BT_ASSERT(nb_bytes > 0);
-       BT_ASSERT(bfcr->buf.addr);
-       memcpy(&bfcr->stitch.buf[stitch_byte_at], &bfcr->buf.addr[buf_byte_at],
-               nb_bytes);
-       bfcr->stitch.at += sz;
-       consume_bits(bfcr, sz);
-}
-
-static
-void stitch_append_from_remaining_buf(struct bt_bfcr *bfcr)
-{
-       stitch_append_from_buf(bfcr, available_bits(bfcr));
-}
-
-static
-void stitch_set_from_remaining_buf(struct bt_bfcr *bfcr)
-{
-       stitch_reset(bfcr);
-       bfcr->stitch.offset = IN_BYTE_OFFSET(buf_at_from_addr(bfcr));
-       stitch_append_from_remaining_buf(bfcr);
-}
-
-static inline
-void read_unsigned_bitfield(struct bt_bfcr *bfcr, const uint8_t *buf, size_t at,
-               unsigned int field_size, enum ctf_byte_order bo,
-               uint64_t *v)
-{
-       switch (bo) {
-       case CTF_BYTE_ORDER_BIG:
-               bt_bitfield_read_be(buf, uint8_t, at, field_size, v);
-               break;
-       case CTF_BYTE_ORDER_LITTLE:
-               bt_bitfield_read_le(buf, uint8_t, at, field_size, v);
-               break;
-       default:
-               bt_common_abort();
-       }
-
-       BT_COMP_LOGT("Read unsigned bit array: cur=%zu, size=%u, "
-               "bo=%d, val=%" PRIu64, at, field_size, bo, *v);
-}
-
-static inline
-void read_signed_bitfield(struct bt_bfcr *bfcr, const uint8_t *buf, size_t at,
-               unsigned int field_size, enum ctf_byte_order bo, int64_t *v)
-{
-       switch (bo) {
-       case CTF_BYTE_ORDER_BIG:
-               bt_bitfield_read_be(buf, uint8_t, at, field_size, v);
-               break;
-       case CTF_BYTE_ORDER_LITTLE:
-               bt_bitfield_read_le(buf, uint8_t, at, field_size, v);
-               break;
-       default:
-               bt_common_abort();
-       }
-
-       BT_COMP_LOGT("Read signed bit array: cur=%zu, size=%u, "
-               "bo=%d, val=%" PRId64, at, field_size, bo, *v);
-}
-
-typedef enum bt_bfcr_status (* read_basic_and_call_cb_t)(struct bt_bfcr *,
-               const uint8_t *, size_t);
-
-static inline
-enum bt_bfcr_status validate_contiguous_bo(struct bt_bfcr *bfcr,
-               enum ctf_byte_order next_bo)
-{
-       enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
-
-       /* Always valid when at a byte boundary */
-       if (packet_at(bfcr) % 8 == 0) {
-               goto end;
-       }
-
-       /* Always valid if last byte order is unknown */
-       if (bfcr->last_bo == CTF_BYTE_ORDER_UNKNOWN) {
-               goto end;
-       }
-
-       /* Always valid if next byte order is unknown */
-       if (next_bo == CTF_BYTE_ORDER_UNKNOWN) {
-               goto end;
-       }
-
-       /* Make sure last byte order is compatible with the next byte order */
-       switch (bfcr->last_bo) {
-       case CTF_BYTE_ORDER_BIG:
-               if (next_bo != CTF_BYTE_ORDER_BIG) {
-                       status = BT_BFCR_STATUS_ERROR;
-               }
-               break;
-       case CTF_BYTE_ORDER_LITTLE:
-               if (next_bo != CTF_BYTE_ORDER_LITTLE) {
-                       status = BT_BFCR_STATUS_ERROR;
-               }
-               break;
-       default:
-               status = BT_BFCR_STATUS_ERROR;
-       }
-
-end:
-       if (status < 0) {
-               BT_COMP_LOGW("Cannot read bit array: two different byte orders not at a byte boundary: "
-                       "bfcr-addr=%p, last-bo=%d, next-bo=%d",
-                       bfcr, bfcr->last_bo, next_bo);
-       }
-
-       return status;
-}
-
-static
-enum bt_bfcr_status read_basic_float_and_call_cb(struct bt_bfcr *bfcr,
-               const uint8_t *buf, size_t at)
-{
-       double dblval;
-       unsigned int field_size;
-       enum ctf_byte_order bo;
-       enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
-       struct ctf_field_class_float *fc = (void *) bfcr->cur_basic_field_class;
-
-       BT_ASSERT_DBG(fc);
-       field_size = fc->base.size;
-       bo = fc->base.byte_order;
-       bfcr->cur_bo = bo;
-
-       switch (field_size) {
-       case 32:
-       {
-               uint64_t v;
-               union {
-                       uint32_t u;
-                       float f;
-               } f32;
-
-               read_unsigned_bitfield(bfcr, buf, at, field_size, bo, &v);
-               f32.u = (uint32_t) v;
-               dblval = (double) f32.f;
-               break;
-       }
-       case 64:
-       {
-               union {
-                       uint64_t u;
-                       double d;
-               } f64;
-
-               read_unsigned_bitfield(bfcr, buf, at, field_size, bo, &f64.u);
-               dblval = f64.d;
-               break;
-       }
-       default:
-               /* Only 32-bit and 64-bit fields are supported currently */
-               bt_common_abort();
-       }
-
-       BT_COMP_LOGT("Read floating point number value: bfcr=%p, cur=%zu, val=%f",
-               bfcr, at, dblval);
-
-       if (bfcr->user.cbs.classes.floating_point) {
-               BT_COMP_LOGT("Calling user function (floating point number).");
-               status = bfcr->user.cbs.classes.floating_point(dblval,
-                       bfcr->cur_basic_field_class, bfcr->user.data);
-               BT_COMP_LOGT("User function returned: status=%s",
-                       bt_bfcr_status_string(status));
-               if (status != BT_BFCR_STATUS_OK) {
-                       BT_COMP_LOGW("User function failed: bfcr-addr=%p, status=%s",
-                               bfcr, bt_bfcr_status_string(status));
-               }
-       }
-
-       return status;
-}
-
-static inline
-enum bt_bfcr_status read_basic_int_and_call_cb(struct bt_bfcr *bfcr,
-               const uint8_t *buf, size_t at)
-{
-       unsigned int field_size;
-       enum ctf_byte_order bo;
-       enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
-       struct ctf_field_class_int *fc = (void *) bfcr->cur_basic_field_class;
-
-       field_size = fc->base.size;
-       bo = fc->base.byte_order;
-
-       /*
-        * Update current byte order now because we could be reading
-        * the integer value of an enumeration class, and thus we know
-        * here the actual supporting integer class's byte order.
-        */
-       bfcr->cur_bo = bo;
-
-       if (fc->is_signed) {
-               int64_t v;
-
-               read_signed_bitfield(bfcr, buf, at, field_size, bo, &v);
-
-               if (bfcr->user.cbs.classes.signed_int) {
-                       BT_COMP_LOGT("Calling user function (signed integer).");
-                       status = bfcr->user.cbs.classes.signed_int(v,
-                               bfcr->cur_basic_field_class, bfcr->user.data);
-                       BT_COMP_LOGT("User function returned: status=%s",
-                               bt_bfcr_status_string(status));
-                       if (status != BT_BFCR_STATUS_OK) {
-                               BT_COMP_LOGW("User function failed: "
-                                       "bfcr-addr=%p, status=%s",
-                                       bfcr, bt_bfcr_status_string(status));
-                       }
-               }
-       } else {
-               uint64_t v;
-
-               read_unsigned_bitfield(bfcr, buf, at, field_size, bo, &v);
-
-               if (bfcr->user.cbs.classes.unsigned_int) {
-                       BT_COMP_LOGT("Calling user function (unsigned integer).");
-                       status = bfcr->user.cbs.classes.unsigned_int(v,
-                               bfcr->cur_basic_field_class, bfcr->user.data);
-                       BT_COMP_LOGT("User function returned: status=%s",
-                               bt_bfcr_status_string(status));
-                       if (status != BT_BFCR_STATUS_OK) {
-                               BT_COMP_LOGW("User function failed: "
-                                       "bfcr-addr=%p, status=%s",
-                                       bfcr, bt_bfcr_status_string(status));
-                       }
-               }
-       }
-
-       return status;
-}
-
-static inline
-enum bt_bfcr_status read_bit_array_class_and_call_continue(struct bt_bfcr *bfcr,
-               read_basic_and_call_cb_t read_basic_and_call_cb)
-{
-       size_t available;
-       size_t needed_bits;
-       enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
-       struct ctf_field_class_bit_array *fc =
-               (void *) bfcr->cur_basic_field_class;
-
-       if (!at_least_one_bit_left(bfcr)) {
-               BT_COMP_LOGT("Reached end of data: bfcr-addr=%p", bfcr);
-               status = BT_BFCR_STATUS_EOF;
-               goto end;
-       }
-
-       available = available_bits(bfcr);
-       needed_bits = fc->size - bfcr->stitch.at;
-       BT_COMP_LOGT("Continuing basic field decoding: "
-               "bfcr-addr=%p, field-size=%u, needed-size=%zu, "
-               "available-size=%zu",
-               bfcr, fc->size, needed_bits, available);
-       if (needed_bits <= available) {
-               /* We have all the bits; append to stitch, then decode */
-               stitch_append_from_buf(bfcr, needed_bits);
-               status = read_basic_and_call_cb(bfcr, bfcr->stitch.buf,
-                       bfcr->stitch.offset);
-               if (status != BT_BFCR_STATUS_OK) {
-                       BT_COMP_LOGW("Cannot read basic field: "
-                               "bfcr-addr=%p, fc-addr=%p, status=%s",
-                               bfcr, bfcr->cur_basic_field_class,
-                               bt_bfcr_status_string(status));
-                       goto end;
-               }
-
-               if (stack_empty(bfcr->stack)) {
-                       /* Root is a basic class */
-                       bfcr->state = BFCR_STATE_DONE;
-               } else {
-                       /* Go to next field */
-                       stack_top(bfcr->stack)->index++;
-                       bfcr->state = BFCR_STATE_NEXT_FIELD;
-                       bfcr->last_bo = bfcr->cur_bo;
-               }
-               goto end;
-       }
-
-       /* We are here; it means we don't have enough data to decode this */
-       BT_COMP_LOGT_STR("Not enough data to read the next basic field: appending to stitch buffer.");
-       stitch_append_from_remaining_buf(bfcr);
-       status = BT_BFCR_STATUS_EOF;
-
-end:
-       return status;
-}
-
-static inline
-enum bt_bfcr_status read_bit_array_class_and_call_begin(struct bt_bfcr *bfcr,
-               read_basic_and_call_cb_t read_basic_and_call_cb)
-{
-       size_t available;
-       enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
-       struct ctf_field_class_bit_array *fc =
-               (void *) bfcr->cur_basic_field_class;
-
-       if (!at_least_one_bit_left(bfcr)) {
-               BT_COMP_LOGT("Reached end of data: bfcr-addr=%p", bfcr);
-               status = BT_BFCR_STATUS_EOF;
-               goto end;
-       }
-
-       status = validate_contiguous_bo(bfcr, fc->byte_order);
-       if (status != BT_BFCR_STATUS_OK) {
-               /* validate_contiguous_bo() logs errors */
-               goto end;
-       }
-
-       available = available_bits(bfcr);
-
-       if (fc->size <= available) {
-               /* We have all the bits; decode and set now */
-               BT_ASSERT_DBG(bfcr->buf.addr);
-               status = read_basic_and_call_cb(bfcr, bfcr->buf.addr,
-                       buf_at_from_addr(bfcr));
-               if (status != BT_BFCR_STATUS_OK) {
-                       BT_COMP_LOGW("Cannot read basic field: "
-                               "bfcr-addr=%p, fc-addr=%p, status=%s",
-                               bfcr, bfcr->cur_basic_field_class,
-                               bt_bfcr_status_string(status));
-                       goto end;
-               }
-
-               consume_bits(bfcr, fc->size);
-
-               if (stack_empty(bfcr->stack)) {
-                       /* Root is a basic class */
-                       bfcr->state = BFCR_STATE_DONE;
-               } else {
-                       /* Go to next field */
-                       stack_top(bfcr->stack)->index++;
-                       bfcr->state = BFCR_STATE_NEXT_FIELD;
-                       bfcr->last_bo = bfcr->cur_bo;
-               }
-
-               goto end;
-       }
-
-       /* We are here; it means we don't have enough data to decode this */
-       BT_COMP_LOGT_STR("Not enough data to read the next basic field: setting stitch buffer.");
-       stitch_set_from_remaining_buf(bfcr);
-       bfcr->state = BFCR_STATE_READ_BASIC_CONTINUE;
-       status = BT_BFCR_STATUS_EOF;
-
-end:
-       return status;
-}
-
-static inline
-enum bt_bfcr_status read_basic_int_class_and_call_begin(
-               struct bt_bfcr *bfcr)
-{
-       return read_bit_array_class_and_call_begin(bfcr, read_basic_int_and_call_cb);
-}
-
-static inline
-enum bt_bfcr_status read_basic_int_class_and_call_continue(
-               struct bt_bfcr *bfcr)
-{
-       return read_bit_array_class_and_call_continue(bfcr,
-               read_basic_int_and_call_cb);
-}
-
-static inline
-enum bt_bfcr_status read_basic_float_class_and_call_begin(
-               struct bt_bfcr *bfcr)
-{
-       return read_bit_array_class_and_call_begin(bfcr,
-               read_basic_float_and_call_cb);
-}
-
-static inline
-enum bt_bfcr_status read_basic_float_class_and_call_continue(
-               struct bt_bfcr *bfcr)
-{
-       return read_bit_array_class_and_call_continue(bfcr,
-               read_basic_float_and_call_cb);
-}
-
-static inline
-enum bt_bfcr_status read_basic_string_class_and_call(
-               struct bt_bfcr *bfcr, bool begin)
-{
-       size_t buf_at_bytes;
-       const uint8_t *result;
-       size_t available_bytes;
-       const uint8_t *first_chr;
-       enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
-
-       if (!at_least_one_bit_left(bfcr)) {
-               BT_COMP_LOGT("Reached end of data: bfcr-addr=%p", bfcr);
-               status = BT_BFCR_STATUS_EOF;
-               goto end;
-       }
-
-       BT_ASSERT_DBG(buf_at_from_addr(bfcr) % 8 == 0);
-       available_bytes = BITS_TO_BYTES_FLOOR(available_bits(bfcr));
-       buf_at_bytes = BITS_TO_BYTES_FLOOR(buf_at_from_addr(bfcr));
-       BT_ASSERT_DBG(bfcr->buf.addr);
-       first_chr = &bfcr->buf.addr[buf_at_bytes];
-       result = memchr(first_chr, '\0', available_bytes);
-
-       if (begin && bfcr->user.cbs.classes.string_begin) {
-               BT_COMP_LOGT("Calling user function (string, beginning).");
-               status = bfcr->user.cbs.classes.string_begin(
-                       bfcr->cur_basic_field_class, bfcr->user.data);
-               BT_COMP_LOGT("User function returned: status=%s",
-                       bt_bfcr_status_string(status));
-               if (status != BT_BFCR_STATUS_OK) {
-                       BT_COMP_LOGW("User function failed: bfcr-addr=%p, status=%s",
-                               bfcr, bt_bfcr_status_string(status));
-                       goto end;
-               }
-       }
-
-       if (!result) {
-               /* No null character yet */
-               if (bfcr->user.cbs.classes.string) {
-                       BT_COMP_LOGT("Calling user function (substring).");
-                       status = bfcr->user.cbs.classes.string(
-                               (const char *) first_chr,
-                               available_bytes, bfcr->cur_basic_field_class,
-                               bfcr->user.data);
-                       BT_COMP_LOGT("User function returned: status=%s",
-                               bt_bfcr_status_string(status));
-                       if (status != BT_BFCR_STATUS_OK) {
-                               BT_COMP_LOGW("User function failed: "
-                                       "bfcr-addr=%p, status=%s",
-                                       bfcr, bt_bfcr_status_string(status));
-                               goto end;
-                       }
-               }
-
-               consume_bits(bfcr, BYTES_TO_BITS(available_bytes));
-               bfcr->state = BFCR_STATE_READ_BASIC_CONTINUE;
-               status = BT_BFCR_STATUS_EOF;
-       } else {
-               /* Found the null character */
-               size_t result_len = (size_t) (result - first_chr);
-
-               if (bfcr->user.cbs.classes.string && result_len) {
-                       BT_COMP_LOGT("Calling user function (substring).");
-                       status = bfcr->user.cbs.classes.string(
-                               (const char *) first_chr,
-                               result_len, bfcr->cur_basic_field_class,
-                               bfcr->user.data);
-                       BT_COMP_LOGT("User function returned: status=%s",
-                               bt_bfcr_status_string(status));
-                       if (status != BT_BFCR_STATUS_OK) {
-                               BT_COMP_LOGW("User function failed: "
-                                       "bfcr-addr=%p, status=%s",
-                                       bfcr, bt_bfcr_status_string(status));
-                               goto end;
-                       }
-               }
-
-               if (bfcr->user.cbs.classes.string_end) {
-                       BT_COMP_LOGT("Calling user function (string, end).");
-                       status = bfcr->user.cbs.classes.string_end(
-                               bfcr->cur_basic_field_class, bfcr->user.data);
-                       BT_COMP_LOGT("User function returned: status=%s",
-                               bt_bfcr_status_string(status));
-                       if (status != BT_BFCR_STATUS_OK) {
-                               BT_COMP_LOGW("User function failed: "
-                                       "bfcr-addr=%p, status=%s",
-                                       bfcr, bt_bfcr_status_string(status));
-                               goto end;
-                       }
-               }
-
-               consume_bits(bfcr, BYTES_TO_BITS(result_len + 1));
-
-               if (stack_empty(bfcr->stack)) {
-                       /* Root is a basic class */
-                       bfcr->state = BFCR_STATE_DONE;
-               } else {
-                       /* Go to next field */
-                       stack_top(bfcr->stack)->index++;
-                       bfcr->state = BFCR_STATE_NEXT_FIELD;
-                       bfcr->last_bo = bfcr->cur_bo;
-               }
-       }
-
-end:
-       return status;
-}
-
-static inline
-enum bt_bfcr_status read_basic_begin_state(struct bt_bfcr *bfcr)
-{
-       enum bt_bfcr_status status;
-
-       BT_ASSERT_DBG(bfcr->cur_basic_field_class);
-
-       switch (bfcr->cur_basic_field_class->type) {
-       case CTF_FIELD_CLASS_TYPE_INT:
-       case CTF_FIELD_CLASS_TYPE_ENUM:
-               status = read_basic_int_class_and_call_begin(bfcr);
-               break;
-       case CTF_FIELD_CLASS_TYPE_FLOAT:
-               status = read_basic_float_class_and_call_begin(bfcr);
-               break;
-       case CTF_FIELD_CLASS_TYPE_STRING:
-               status = read_basic_string_class_and_call(bfcr, true);
-               break;
-       default:
-               bt_common_abort();
-       }
-
-       return status;
-}
-
-static inline
-enum bt_bfcr_status read_basic_continue_state(struct bt_bfcr *bfcr)
-{
-       enum bt_bfcr_status status;
-
-       BT_ASSERT_DBG(bfcr->cur_basic_field_class);
-
-       switch (bfcr->cur_basic_field_class->type) {
-       case CTF_FIELD_CLASS_TYPE_INT:
-       case CTF_FIELD_CLASS_TYPE_ENUM:
-               status = read_basic_int_class_and_call_continue(bfcr);
-               break;
-       case CTF_FIELD_CLASS_TYPE_FLOAT:
-               status = read_basic_float_class_and_call_continue(bfcr);
-               break;
-       case CTF_FIELD_CLASS_TYPE_STRING:
-               status = read_basic_string_class_and_call(bfcr, false);
-               break;
-       default:
-               bt_common_abort();
-       }
-
-       return status;
-}
-
-static inline
-size_t bits_to_skip_to_align_to(struct bt_bfcr *bfcr, size_t align)
-{
-       size_t aligned_packet_at;
-
-       aligned_packet_at = BT_ALIGN(packet_at(bfcr), align);
-       return aligned_packet_at - packet_at(bfcr);
-}
-
-static inline
-enum bt_bfcr_status align_class_state(struct bt_bfcr *bfcr,
-               struct ctf_field_class *field_class, enum bfcr_state next_state)
-{
-       unsigned int field_alignment;
-       size_t skip_bits;
-       enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
-
-       /* Get field's alignment */
-       field_alignment = field_class->alignment;
-
-       /*
-        * 0 means "undefined" for variants; what we really want is 1
-        * (always aligned)
-        */
-       BT_ASSERT_DBG(field_alignment >= 1);
-
-       /* Compute how many bits we need to skip */
-       skip_bits = bits_to_skip_to_align_to(bfcr, (size_t) field_alignment);
-
-       /* Nothing to skip? aligned */
-       if (skip_bits == 0) {
-               bfcr->state = next_state;
-               goto end;
-       }
-
-       /* Make sure there's at least one bit left */
-       if (!at_least_one_bit_left(bfcr)) {
-               status = BT_BFCR_STATUS_EOF;
-               goto end;
-       }
-
-       /* Consume as many bits as possible in what's left */
-       consume_bits(bfcr, MIN(available_bits(bfcr), skip_bits));
-
-       /* Are we done now? */
-       skip_bits = bits_to_skip_to_align_to(bfcr, field_alignment);
-       if (skip_bits == 0) {
-               /* Yes: go to next state */
-               bfcr->state = next_state;
-               goto end;
-       } else {
-               /* No: need more data */
-               BT_COMP_LOGT("Reached end of data when aligning: bfcr-addr=%p", bfcr);
-               status = BT_BFCR_STATUS_EOF;
-       }
-
-end:
-       return status;
-}
-
-static inline
-enum bt_bfcr_status next_field_state(struct bt_bfcr *bfcr)
-{
-       int ret;
-       struct stack_entry *top;
-       struct ctf_field_class *next_field_class = NULL;
-       enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
-
-       if (stack_empty(bfcr->stack)) {
-               goto end;
-       }
-
-       top = stack_top(bfcr->stack);
-
-       /* Are we done with this base class? */
-       while (top->index == top->base_len) {
-               if (bfcr->user.cbs.classes.compound_end) {
-                       BT_COMP_LOGT("Calling user function (compound, end).");
-                       status = bfcr->user.cbs.classes.compound_end(
-                               top->base_class, bfcr->user.data);
-                       BT_COMP_LOGT("User function returned: status=%s",
-                               bt_bfcr_status_string(status));
-                       if (status != BT_BFCR_STATUS_OK) {
-                               BT_COMP_LOGW("User function failed: bfcr-addr=%p, status=%s",
-                                       bfcr, bt_bfcr_status_string(status));
-                               goto end;
-                       }
-               }
-
-               stack_pop(bfcr->stack);
-
-               /* Are we done with the root class? */
-               if (stack_empty(bfcr->stack)) {
-                       bfcr->state = BFCR_STATE_DONE;
-                       goto end;
-               }
-
-               top = stack_top(bfcr->stack);
-               top->index++;
-       }
-
-       /* Get next field's class */
-       switch (top->base_class->type) {
-       case CTF_FIELD_CLASS_TYPE_STRUCT:
-               next_field_class = ctf_field_class_struct_borrow_member_by_index(
-                       (void *) top->base_class, (uint64_t) top->index)->fc;
-               break;
-       case CTF_FIELD_CLASS_TYPE_ARRAY:
-       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-       {
-               struct ctf_field_class_array_base *array_fc =
-                       (void *) top->base_class;
-
-               next_field_class = array_fc->elem_fc;
-               break;
-       }
-       case CTF_FIELD_CLASS_TYPE_VARIANT:
-               /* Variant classes are dynamic: the user should know! */
-               next_field_class =
-                       bfcr->user.cbs.query.borrow_variant_selected_field_class(
-                               top->base_class, bfcr->user.data);
-               break;
-       default:
-               break;
-       }
-
-       if (!next_field_class) {
-               BT_COMP_LOGW("Cannot get the field class of the next field: "
-                       "bfcr-addr=%p, base-fc-addr=%p, base-fc-type=%d, "
-                       "index=%" PRId64,
-                       bfcr, top->base_class, top->base_class->type,
-                       top->index);
-               status = BT_BFCR_STATUS_ERROR;
-               goto end;
-       }
-
-       if (next_field_class->is_compound) {
-               if (bfcr->user.cbs.classes.compound_begin) {
-                       BT_COMP_LOGT("Calling user function (compound, begin).");
-                       status = bfcr->user.cbs.classes.compound_begin(
-                               next_field_class, bfcr->user.data);
-                       BT_COMP_LOGT("User function returned: status=%s",
-                               bt_bfcr_status_string(status));
-                       if (status != BT_BFCR_STATUS_OK) {
-                               BT_COMP_LOGW("User function failed: bfcr-addr=%p, status=%s",
-                                       bfcr, bt_bfcr_status_string(status));
-                               goto end;
-                       }
-               }
-
-               ret = stack_push_with_len(bfcr, next_field_class);
-               if (ret) {
-                       /* stack_push_with_len() logs errors */
-                       status = BT_BFCR_STATUS_ERROR;
-                       goto end;
-               }
-
-               /* Next state: align a compound class */
-               bfcr->state = BFCR_STATE_ALIGN_COMPOUND;
-       } else {
-               /* Replace current basic field class */
-               BT_COMP_LOGT("Replacing current basic field class: "
-                       "bfcr-addr=%p, cur-basic-fc-addr=%p, "
-                       "next-basic-fc-addr=%p",
-                       bfcr, bfcr->cur_basic_field_class, next_field_class);
-               bfcr->cur_basic_field_class = next_field_class;
-
-               /* Next state: align a basic class */
-               bfcr->state = BFCR_STATE_ALIGN_BASIC;
-       }
-
-end:
-       return status;
-}
-
-static inline
-enum bt_bfcr_status handle_state(struct bt_bfcr *bfcr)
-{
-       enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
-
-       BT_COMP_LOGT("Handling state: bfcr-addr=%p, state=%s",
-               bfcr, bfcr_state_string(bfcr->state));
-
-       switch (bfcr->state) {
-       case BFCR_STATE_NEXT_FIELD:
-               status = next_field_state(bfcr);
-               break;
-       case BFCR_STATE_ALIGN_BASIC:
-               status = align_class_state(bfcr, bfcr->cur_basic_field_class,
-                       BFCR_STATE_READ_BASIC_BEGIN);
-               break;
-       case BFCR_STATE_ALIGN_COMPOUND:
-               status = align_class_state(bfcr, stack_top(bfcr->stack)->base_class,
-                       BFCR_STATE_NEXT_FIELD);
-               break;
-       case BFCR_STATE_READ_BASIC_BEGIN:
-               status = read_basic_begin_state(bfcr);
-               break;
-       case BFCR_STATE_READ_BASIC_CONTINUE:
-               status = read_basic_continue_state(bfcr);
-               break;
-       case BFCR_STATE_DONE:
-               break;
-       }
-
-       BT_COMP_LOGT("Handled state: bfcr-addr=%p, status=%s",
-               bfcr, bt_bfcr_status_string(status));
-       return status;
-}
-
-BT_HIDDEN
-struct bt_bfcr *bt_bfcr_create(struct bt_bfcr_cbs cbs, void *data,
-               bt_logging_level log_level, bt_self_component *self_comp)
-{
-       struct bt_bfcr *bfcr;
-
-       BT_COMP_LOG_CUR_LVL(BT_LOG_DEBUG, log_level, self_comp,
-               "Creating binary field class reader (BFCR).");
-       bfcr = g_new0(struct bt_bfcr, 1);
-       if (!bfcr) {
-               BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, log_level, self_comp,
-                       "Failed to allocate one binary class reader.");
-               goto end;
-       }
-
-       bfcr->log_level = log_level;
-       bfcr->self_comp = self_comp;
-       bfcr->stack = stack_new(bfcr);
-       if (!bfcr->stack) {
-               BT_COMP_LOGE_STR("Cannot create BFCR's stack.");
-               bt_bfcr_destroy(bfcr);
-               bfcr = NULL;
-               goto end;
-       }
-
-       bfcr->state = BFCR_STATE_NEXT_FIELD;
-       bfcr->user.cbs = cbs;
-       bfcr->user.data = data;
-       BT_COMP_LOGD("Created BFCR: addr=%p", bfcr);
-
-end:
-       return bfcr;
-}
-
-BT_HIDDEN
-void bt_bfcr_destroy(struct bt_bfcr *bfcr)
-{
-       if (bfcr->stack) {
-               stack_destroy(bfcr->stack);
-       }
-
-       BT_COMP_LOGD("Destroying BFCR: addr=%p", bfcr);
-       g_free(bfcr);
-}
-
-static
-void reset(struct bt_bfcr *bfcr)
-{
-       BT_COMP_LOGD("Resetting BFCR: addr=%p", bfcr);
-       stack_clear(bfcr->stack);
-       stitch_reset(bfcr);
-       bfcr->buf.addr = NULL;
-       bfcr->last_bo = CTF_BYTE_ORDER_UNKNOWN;
-}
-
-static
-void update_packet_offset(struct bt_bfcr *bfcr)
-{
-       BT_COMP_LOGT("Updating packet offset for next call: "
-               "bfcr-addr=%p, cur-packet-offset=%zu, next-packet-offset=%zu",
-               bfcr, bfcr->buf.packet_offset,
-               bfcr->buf.packet_offset + bfcr->buf.at);
-       bfcr->buf.packet_offset += bfcr->buf.at;
-}
-
-BT_HIDDEN
-size_t bt_bfcr_start(struct bt_bfcr *bfcr,
-       struct ctf_field_class *cls, const uint8_t *buf,
-       size_t offset, size_t packet_offset, size_t sz,
-       enum bt_bfcr_status *status)
-{
-       BT_ASSERT_DBG(bfcr);
-       BT_ASSERT_DBG(BYTES_TO_BITS(sz) >= offset);
-       reset(bfcr);
-       bfcr->buf.addr = buf;
-       bfcr->buf.offset = offset;
-       bfcr->buf.at = 0;
-       bfcr->buf.packet_offset = packet_offset;
-       bfcr->buf.buf_sz = sz;
-       bfcr->buf.sz = BYTES_TO_BITS(sz) - offset;
-       *status = BT_BFCR_STATUS_OK;
-
-       BT_COMP_LOGT("Starting decoding: bfcr-addr=%p, fc-addr=%p, "
-               "buf-addr=%p, buf-size=%zu, offset=%zu, "
-               "packet-offset=%zu",
-               bfcr, cls, buf, sz, offset, packet_offset);
-
-       /* Set root class */
-       if (cls->is_compound) {
-               /* Compound class: push on visit stack */
-               int stack_ret;
-
-               if (bfcr->user.cbs.classes.compound_begin) {
-                       BT_COMP_LOGT("Calling user function (compound, begin).");
-                       *status = bfcr->user.cbs.classes.compound_begin(
-                               cls, bfcr->user.data);
-                       BT_COMP_LOGT("User function returned: status=%s",
-                               bt_bfcr_status_string(*status));
-                       if (*status != BT_BFCR_STATUS_OK) {
-                               BT_COMP_LOGW("User function failed: bfcr-addr=%p, status=%s",
-                                       bfcr, bt_bfcr_status_string(*status));
-                               goto end;
-                       }
-               }
-
-               stack_ret = stack_push_with_len(bfcr, cls);
-               if (stack_ret) {
-                       /* stack_push_with_len() logs errors */
-                       *status = BT_BFCR_STATUS_ERROR;
-                       goto end;
-               }
-
-               bfcr->state = BFCR_STATE_ALIGN_COMPOUND;
-       } else {
-               /* Basic class: set as current basic class */
-               bfcr->cur_basic_field_class = cls;
-               bfcr->state = BFCR_STATE_ALIGN_BASIC;
-       }
-
-       /* Run the machine! */
-       BT_COMP_LOGT_STR("Running the state machine.");
-
-       while (true) {
-               *status = handle_state(bfcr);
-               if (*status != BT_BFCR_STATUS_OK ||
-                               bfcr->state == BFCR_STATE_DONE) {
-                       break;
-               }
-       }
-
-       /* Update packet offset for next time */
-       update_packet_offset(bfcr);
-
-end:
-       return bfcr->buf.at;
-}
-
-BT_HIDDEN
-size_t bt_bfcr_continue(struct bt_bfcr *bfcr, const uint8_t *buf, size_t sz,
-               enum bt_bfcr_status *status)
-{
-       BT_ASSERT_DBG(bfcr);
-       BT_ASSERT_DBG(buf);
-       BT_ASSERT_DBG(sz > 0);
-       bfcr->buf.addr = buf;
-       bfcr->buf.offset = 0;
-       bfcr->buf.at = 0;
-       bfcr->buf.buf_sz = sz;
-       bfcr->buf.sz = BYTES_TO_BITS(sz);
-       *status = BT_BFCR_STATUS_OK;
-
-       BT_COMP_LOGT("Continuing decoding: bfcr-addr=%p, buf-addr=%p, buf-size=%zu",
-               bfcr, buf, sz);
-
-       /* Continue running the machine */
-       BT_COMP_LOGT_STR("Running the state machine.");
-
-       while (true) {
-               *status = handle_state(bfcr);
-               if (*status != BT_BFCR_STATUS_OK ||
-                               bfcr->state == BFCR_STATE_DONE) {
-                       break;
-               }
-       }
-
-       /* Update packet offset for next time */
-       update_packet_offset(bfcr);
-       return bfcr->buf.at;
-}
-
-BT_HIDDEN
-void bt_bfcr_set_unsigned_int_cb(struct bt_bfcr *bfcr,
-               bt_bfcr_unsigned_int_cb_func cb)
-{
-       BT_ASSERT_DBG(bfcr);
-       BT_ASSERT_DBG(cb);
-       bfcr->user.cbs.classes.unsigned_int = cb;
-}
diff --git a/src/plugins/ctf/common/bfcr/bfcr.cpp b/src/plugins/ctf/common/bfcr/bfcr.cpp
new file mode 100644 (file)
index 0000000..b745dd0
--- /dev/null
@@ -0,0 +1,1351 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright (c) 2015-2016 EfficiOS Inc. and Linux Foundation
+ * Copyright (c) 2015-2016 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Babeltrace - CTF binary field class reader (BFCR)
+ */
+
+#define BT_COMP_LOG_SELF_COMP (bfcr->self_comp)
+#define BT_LOG_OUTPUT_LEVEL (bfcr->log_level)
+#define BT_LOG_TAG "PLUGIN/CTF/BFCR"
+#include "logging/comp-logging.h"
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include "common/assert.h"
+#include <string.h>
+#include "compat/bitfield.h"
+#include "common/common.h"
+#include <babeltrace2/babeltrace.h>
+#include "common/align.h"
+#include <glib.h>
+
+#include "bfcr.hpp"
+#include "../metadata/ctf-meta.hpp"
+
+#define DIV8(_x)                       ((_x) >> 3)
+#define BYTES_TO_BITS(_x)              ((_x) * 8)
+#define BITS_TO_BYTES_FLOOR(_x)                DIV8(_x)
+#define BITS_TO_BYTES_CEIL(_x)         DIV8((_x) + 7)
+#define IN_BYTE_OFFSET(_at)            ((_at) & 7)
+
+/* A visit stack entry */
+struct stack_entry {
+       /*
+        * Current class of base field, one of:
+        *
+        *   * Structure
+        *   * Array
+        *   * Sequence
+        *   * Variant
+        */
+       struct ctf_field_class *base_class;
+
+       /* Length of base field (always 1 for a variant class) */
+       int64_t base_len;
+
+       /* Index of next field to read */
+       int64_t index;
+};
+
+struct bt_bfcr;
+
+/* Visit stack */
+struct stack {
+       struct bt_bfcr *bfcr;
+
+       /* Entries (struct stack_entry) */
+       GArray *entries;
+
+       /* Number of active entries */
+       size_t size;
+};
+
+/* Reading states */
+enum bfcr_state {
+       BFCR_STATE_NEXT_FIELD,
+       BFCR_STATE_ALIGN_BASIC,
+       BFCR_STATE_ALIGN_COMPOUND,
+       BFCR_STATE_READ_BASIC_BEGIN,
+       BFCR_STATE_READ_BASIC_CONTINUE,
+       BFCR_STATE_DONE,
+};
+
+/* Binary class reader */
+struct bt_bfcr {
+       bt_logging_level log_level;
+
+       /* Weak */
+       bt_self_component *self_comp;
+
+       /* BFCR stack */
+       struct stack *stack;
+
+       /* Current basic field class */
+       struct ctf_field_class *cur_basic_field_class;
+
+       /* Current state */
+       enum bfcr_state state;
+
+       /*
+        * Last basic field class's byte order.
+        *
+        * This is used to detect errors since two contiguous basic
+        * classes for which the common boundary is not the boundary of
+        * a byte cannot have different byte orders.
+        *
+        * This is set to CTF_BYTE_ORDER_UNKNOWN on reset and when the last
+        * basic field class was a string class.
+        */
+       enum ctf_byte_order last_bo;
+
+       /* Current byte order (copied to last_bo after a successful read) */
+       enum ctf_byte_order cur_bo;
+
+       /* Stitch buffer infos */
+       struct {
+               /* Stitch buffer */
+               uint8_t buf[16];
+
+               /* Offset, within stitch buffer, of first bit */
+               size_t offset;
+
+               /* Length (bits) of data in stitch buffer from offset */
+               size_t at;
+       } stitch;
+
+       /* User buffer infos */
+       struct {
+               /* Address */
+               const uint8_t *addr;
+
+               /* Offset of data from address (bits) */
+               size_t offset;
+
+               /* Current position from offset (bits) */
+               size_t at;
+
+               /* Offset of offset within whole packet (bits) */
+               size_t packet_offset;
+
+               /* Data size in buffer (bits) */
+               size_t sz;
+
+               /* Buffer size (bytes) */
+               size_t buf_sz;
+       } buf;
+
+       /* User stuff */
+       struct {
+               /* Callback functions */
+               struct bt_bfcr_cbs cbs;
+
+               /* Private data */
+               void *data;
+       } user;
+};
+
+static inline
+const char *bfcr_state_string(enum bfcr_state state)
+{
+       switch (state) {
+       case BFCR_STATE_NEXT_FIELD:
+               return "NEXT_FIELD";
+       case BFCR_STATE_ALIGN_BASIC:
+               return "ALIGN_BASIC";
+       case BFCR_STATE_ALIGN_COMPOUND:
+               return "ALIGN_COMPOUND";
+       case BFCR_STATE_READ_BASIC_BEGIN:
+               return "READ_BASIC_BEGIN";
+       case BFCR_STATE_READ_BASIC_CONTINUE:
+               return "READ_BASIC_CONTINUE";
+       case BFCR_STATE_DONE:
+               return "DONE";
+       }
+
+       bt_common_abort();
+}
+
+static
+struct stack *stack_new(struct bt_bfcr *bfcr)
+{
+       struct stack *stack = NULL;
+
+       stack = g_new0(struct stack, 1);
+       if (!stack) {
+               BT_COMP_LOGE_STR("Failed to allocate one stack.");
+               goto error;
+       }
+
+       stack->bfcr = bfcr;
+       stack->entries = g_array_new(FALSE, TRUE, sizeof(struct stack_entry));
+       if (!stack->entries) {
+               BT_COMP_LOGE_STR("Failed to allocate a GArray.");
+               goto error;
+       }
+
+       BT_COMP_LOGD("Created stack: addr=%p", stack);
+       return stack;
+
+error:
+       g_free(stack);
+       return NULL;
+}
+
+static
+void stack_destroy(struct stack *stack)
+{
+       struct bt_bfcr *bfcr;
+
+       if (!stack) {
+               return;
+       }
+
+       bfcr = stack->bfcr;
+       BT_COMP_LOGD("Destroying stack: addr=%p", stack);
+
+       if (stack->entries) {
+               g_array_free(stack->entries, TRUE);
+       }
+
+       g_free(stack);
+}
+
+static
+int stack_push(struct stack *stack, struct ctf_field_class *base_class,
+       size_t base_len)
+{
+       struct stack_entry *entry;
+       struct bt_bfcr *bfcr;
+
+       BT_ASSERT_DBG(stack);
+       BT_ASSERT_DBG(base_class);
+       bfcr = stack->bfcr;
+       BT_COMP_LOGT("Pushing field class on stack: stack-addr=%p, "
+               "fc-addr=%p, fc-type=%d, base-length=%zu, "
+               "stack-size-before=%zu, stack-size-after=%zu",
+               stack, base_class, base_class->type,
+               base_len, stack->size, stack->size + 1);
+
+       if (stack->entries->len == stack->size) {
+               g_array_set_size(stack->entries, stack->size + 1);
+       }
+
+       entry = &g_array_index(stack->entries, struct stack_entry, stack->size);
+       entry->base_class = base_class;
+       entry->base_len = base_len;
+       entry->index = 0;
+       stack->size++;
+       return 0;
+}
+
+static inline
+int64_t get_compound_field_class_length(struct bt_bfcr *bfcr,
+               struct ctf_field_class *fc)
+{
+       int64_t length;
+
+       switch (fc->type) {
+       case CTF_FIELD_CLASS_TYPE_STRUCT:
+       {
+               ctf_field_class_struct *struct_fc = ctf_field_class_as_struct(fc);
+
+               length = (int64_t) struct_fc->members->len;
+               break;
+       }
+       case CTF_FIELD_CLASS_TYPE_VARIANT:
+       {
+               /* Variant field classes always "contain" a single class */
+               length = 1;
+               break;
+       }
+       case CTF_FIELD_CLASS_TYPE_ARRAY:
+       {
+               struct ctf_field_class_array *array_fc = ctf_field_class_as_array(fc);
+
+               length = (int64_t) array_fc->length;
+               break;
+       }
+       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+               length = bfcr->user.cbs.query.get_sequence_length(fc,
+                       bfcr->user.data);
+               break;
+       default:
+               bt_common_abort();
+       }
+
+       return length;
+}
+
+static
+int stack_push_with_len(struct bt_bfcr *bfcr, struct ctf_field_class *base_class)
+{
+       int ret;
+       int64_t length = get_compound_field_class_length(bfcr, base_class);
+
+       if (length < 0) {
+               BT_COMP_LOGW("Cannot get compound field class's field count: "
+                       "bfcr-addr=%p, fc-addr=%p, fc-type=%d",
+                       bfcr, base_class, base_class->type);
+               ret = BT_BFCR_STATUS_ERROR;
+               goto end;
+       }
+
+       ret = stack_push(bfcr->stack, base_class, (size_t) length);
+
+end:
+       return ret;
+}
+
+static inline
+unsigned int stack_size(struct stack *stack)
+{
+       BT_ASSERT_DBG(stack);
+       return stack->size;
+}
+
+static
+void stack_pop(struct stack *stack)
+{
+       struct bt_bfcr *bfcr;
+
+       BT_ASSERT_DBG(stack);
+       BT_ASSERT_DBG(stack_size(stack));
+       bfcr = stack->bfcr;
+       BT_COMP_LOGT("Popping from stack: "
+               "stack-addr=%p, stack-size-before=%u, stack-size-after=%u",
+               stack, stack->entries->len, stack->entries->len - 1);
+       stack->size--;
+}
+
+static inline
+bool stack_empty(struct stack *stack)
+{
+       return stack_size(stack) == 0;
+}
+
+static
+void stack_clear(struct stack *stack)
+{
+       BT_ASSERT_DBG(stack);
+       stack->size = 0;
+}
+
+static inline
+struct stack_entry *stack_top(struct stack *stack)
+{
+       BT_ASSERT_DBG(stack);
+       BT_ASSERT_DBG(stack_size(stack));
+       return &g_array_index(stack->entries, struct stack_entry,
+               stack->size - 1);
+}
+
+static inline
+size_t available_bits(struct bt_bfcr *bfcr)
+{
+       return bfcr->buf.sz - bfcr->buf.at;
+}
+
+static inline
+void consume_bits(struct bt_bfcr *bfcr, size_t incr)
+{
+       BT_COMP_LOGT("Advancing cursor: bfcr-addr=%p, cur-before=%zu, cur-after=%zu",
+               bfcr, bfcr->buf.at, bfcr->buf.at + incr);
+       bfcr->buf.at += incr;
+}
+
+static inline
+bool has_enough_bits(struct bt_bfcr *bfcr, size_t sz)
+{
+       return available_bits(bfcr) >= sz;
+}
+
+static inline
+bool at_least_one_bit_left(struct bt_bfcr *bfcr)
+{
+       return has_enough_bits(bfcr, 1);
+}
+
+static inline
+size_t packet_at(struct bt_bfcr *bfcr)
+{
+       return bfcr->buf.packet_offset + bfcr->buf.at;
+}
+
+static inline
+size_t buf_at_from_addr(struct bt_bfcr *bfcr)
+{
+       /*
+        * Considering this:
+        *
+        *     ====== offset ===== (17)
+        *
+        *     xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
+        *     ^
+        *     addr (0)           ==== at ==== (12)
+        *
+        * We want this:
+        *
+        *     =============================== (29)
+        */
+       return bfcr->buf.offset + bfcr->buf.at;
+}
+
+static
+void stitch_reset(struct bt_bfcr *bfcr)
+{
+       bfcr->stitch.offset = 0;
+       bfcr->stitch.at = 0;
+}
+
+static inline
+size_t stitch_at_from_addr(struct bt_bfcr *bfcr)
+{
+       return bfcr->stitch.offset + bfcr->stitch.at;
+}
+
+static
+void stitch_append_from_buf(struct bt_bfcr *bfcr, size_t sz)
+{
+       size_t stitch_byte_at;
+       size_t buf_byte_at;
+       size_t nb_bytes;
+
+       if (sz == 0) {
+               return;
+       }
+
+       stitch_byte_at =
+               BITS_TO_BYTES_FLOOR(stitch_at_from_addr(bfcr));
+       buf_byte_at = BITS_TO_BYTES_FLOOR(buf_at_from_addr(bfcr));
+       nb_bytes = BITS_TO_BYTES_CEIL(sz);
+       BT_ASSERT(nb_bytes > 0);
+       BT_ASSERT(bfcr->buf.addr);
+       memcpy(&bfcr->stitch.buf[stitch_byte_at], &bfcr->buf.addr[buf_byte_at],
+               nb_bytes);
+       bfcr->stitch.at += sz;
+       consume_bits(bfcr, sz);
+}
+
+static
+void stitch_append_from_remaining_buf(struct bt_bfcr *bfcr)
+{
+       stitch_append_from_buf(bfcr, available_bits(bfcr));
+}
+
+static
+void stitch_set_from_remaining_buf(struct bt_bfcr *bfcr)
+{
+       stitch_reset(bfcr);
+       bfcr->stitch.offset = IN_BYTE_OFFSET(buf_at_from_addr(bfcr));
+       stitch_append_from_remaining_buf(bfcr);
+}
+
+static inline
+void read_unsigned_bitfield(struct bt_bfcr *bfcr, const uint8_t *buf, size_t at,
+               unsigned int field_size, enum ctf_byte_order bo,
+               uint64_t *v)
+{
+       switch (bo) {
+       case CTF_BYTE_ORDER_BIG:
+               bt_bitfield_read_be(buf, uint8_t, at, field_size, v);
+               break;
+       case CTF_BYTE_ORDER_LITTLE:
+               bt_bitfield_read_le(buf, uint8_t, at, field_size, v);
+               break;
+       default:
+               bt_common_abort();
+       }
+
+       BT_COMP_LOGT("Read unsigned bit array: cur=%zu, size=%u, "
+               "bo=%d, val=%" PRIu64, at, field_size, bo, *v);
+}
+
+static inline
+void read_signed_bitfield(struct bt_bfcr *bfcr, const uint8_t *buf, size_t at,
+               unsigned int field_size, enum ctf_byte_order bo, int64_t *v)
+{
+       switch (bo) {
+       case CTF_BYTE_ORDER_BIG:
+               bt_bitfield_read_be(buf, uint8_t, at, field_size, v);
+               break;
+       case CTF_BYTE_ORDER_LITTLE:
+               bt_bitfield_read_le(buf, uint8_t, at, field_size, v);
+               break;
+       default:
+               bt_common_abort();
+       }
+
+       BT_COMP_LOGT("Read signed bit array: cur=%zu, size=%u, "
+               "bo=%d, val=%" PRId64, at, field_size, bo, *v);
+}
+
+typedef enum bt_bfcr_status (* read_basic_and_call_cb_t)(struct bt_bfcr *,
+               const uint8_t *, size_t);
+
+static inline
+enum bt_bfcr_status validate_contiguous_bo(struct bt_bfcr *bfcr,
+               enum ctf_byte_order next_bo)
+{
+       enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
+
+       /* Always valid when at a byte boundary */
+       if (packet_at(bfcr) % 8 == 0) {
+               goto end;
+       }
+
+       /* Always valid if last byte order is unknown */
+       if (bfcr->last_bo == CTF_BYTE_ORDER_UNKNOWN) {
+               goto end;
+       }
+
+       /* Always valid if next byte order is unknown */
+       if (next_bo == CTF_BYTE_ORDER_UNKNOWN) {
+               goto end;
+       }
+
+       /* Make sure last byte order is compatible with the next byte order */
+       switch (bfcr->last_bo) {
+       case CTF_BYTE_ORDER_BIG:
+               if (next_bo != CTF_BYTE_ORDER_BIG) {
+                       status = BT_BFCR_STATUS_ERROR;
+               }
+               break;
+       case CTF_BYTE_ORDER_LITTLE:
+               if (next_bo != CTF_BYTE_ORDER_LITTLE) {
+                       status = BT_BFCR_STATUS_ERROR;
+               }
+               break;
+       default:
+               status = BT_BFCR_STATUS_ERROR;
+       }
+
+end:
+       if (status < 0) {
+               BT_COMP_LOGW("Cannot read bit array: two different byte orders not at a byte boundary: "
+                       "bfcr-addr=%p, last-bo=%d, next-bo=%d",
+                       bfcr, bfcr->last_bo, next_bo);
+       }
+
+       return status;
+}
+
+static
+enum bt_bfcr_status read_basic_float_and_call_cb(struct bt_bfcr *bfcr,
+               const uint8_t *buf, size_t at)
+{
+       double dblval;
+       unsigned int field_size;
+       enum ctf_byte_order bo;
+       enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
+       ctf_field_class_float *fc = ctf_field_class_as_float(bfcr->cur_basic_field_class);
+
+       BT_ASSERT_DBG(fc);
+       field_size = fc->base.size;
+       bo = fc->base.byte_order;
+       bfcr->cur_bo = bo;
+
+       switch (field_size) {
+       case 32:
+       {
+               uint64_t v;
+               union {
+                       uint32_t u;
+                       float f;
+               } f32;
+
+               read_unsigned_bitfield(bfcr, buf, at, field_size, bo, &v);
+               f32.u = (uint32_t) v;
+               dblval = (double) f32.f;
+               break;
+       }
+       case 64:
+       {
+               union {
+                       uint64_t u;
+                       double d;
+               } f64;
+
+               read_unsigned_bitfield(bfcr, buf, at, field_size, bo, &f64.u);
+               dblval = f64.d;
+               break;
+       }
+       default:
+               /* Only 32-bit and 64-bit fields are supported currently */
+               bt_common_abort();
+       }
+
+       BT_COMP_LOGT("Read floating point number value: bfcr=%p, cur=%zu, val=%f",
+               bfcr, at, dblval);
+
+       if (bfcr->user.cbs.classes.floating_point) {
+               BT_COMP_LOGT("Calling user function (floating point number).");
+               status = bfcr->user.cbs.classes.floating_point(dblval,
+                       bfcr->cur_basic_field_class, bfcr->user.data);
+               BT_COMP_LOGT("User function returned: status=%s",
+                       bt_bfcr_status_string(status));
+               if (status != BT_BFCR_STATUS_OK) {
+                       BT_COMP_LOGW("User function failed: bfcr-addr=%p, status=%s",
+                               bfcr, bt_bfcr_status_string(status));
+               }
+       }
+
+       return status;
+}
+
+static inline
+enum bt_bfcr_status read_basic_int_and_call_cb(struct bt_bfcr *bfcr,
+               const uint8_t *buf, size_t at)
+{
+       unsigned int field_size;
+       enum ctf_byte_order bo;
+       enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
+       ctf_field_class_int *fc = ctf_field_class_as_int(bfcr->cur_basic_field_class);
+
+       field_size = fc->base.size;
+       bo = fc->base.byte_order;
+
+       /*
+        * Update current byte order now because we could be reading
+        * the integer value of an enumeration class, and thus we know
+        * here the actual supporting integer class's byte order.
+        */
+       bfcr->cur_bo = bo;
+
+       if (fc->is_signed) {
+               int64_t v;
+
+               read_signed_bitfield(bfcr, buf, at, field_size, bo, &v);
+
+               if (bfcr->user.cbs.classes.signed_int) {
+                       BT_COMP_LOGT("Calling user function (signed integer).");
+                       status = bfcr->user.cbs.classes.signed_int(v,
+                               bfcr->cur_basic_field_class, bfcr->user.data);
+                       BT_COMP_LOGT("User function returned: status=%s",
+                               bt_bfcr_status_string(status));
+                       if (status != BT_BFCR_STATUS_OK) {
+                               BT_COMP_LOGW("User function failed: "
+                                       "bfcr-addr=%p, status=%s",
+                                       bfcr, bt_bfcr_status_string(status));
+                       }
+               }
+       } else {
+               uint64_t v;
+
+               read_unsigned_bitfield(bfcr, buf, at, field_size, bo, &v);
+
+               if (bfcr->user.cbs.classes.unsigned_int) {
+                       BT_COMP_LOGT("Calling user function (unsigned integer).");
+                       status = bfcr->user.cbs.classes.unsigned_int(v,
+                               bfcr->cur_basic_field_class, bfcr->user.data);
+                       BT_COMP_LOGT("User function returned: status=%s",
+                               bt_bfcr_status_string(status));
+                       if (status != BT_BFCR_STATUS_OK) {
+                               BT_COMP_LOGW("User function failed: "
+                                       "bfcr-addr=%p, status=%s",
+                                       bfcr, bt_bfcr_status_string(status));
+                       }
+               }
+       }
+
+       return status;
+}
+
+static inline
+enum bt_bfcr_status read_bit_array_class_and_call_continue(struct bt_bfcr *bfcr,
+               read_basic_and_call_cb_t read_basic_and_call_cb)
+{
+       size_t available;
+       size_t needed_bits;
+       enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
+       ctf_field_class_bit_array *fc = ctf_field_class_as_bit_array(bfcr->cur_basic_field_class);
+
+       if (!at_least_one_bit_left(bfcr)) {
+               BT_COMP_LOGT("Reached end of data: bfcr-addr=%p", bfcr);
+               status = BT_BFCR_STATUS_EOF;
+               goto end;
+       }
+
+       available = available_bits(bfcr);
+       needed_bits = fc->size - bfcr->stitch.at;
+       BT_COMP_LOGT("Continuing basic field decoding: "
+               "bfcr-addr=%p, field-size=%u, needed-size=%zu, "
+               "available-size=%zu",
+               bfcr, fc->size, needed_bits, available);
+       if (needed_bits <= available) {
+               /* We have all the bits; append to stitch, then decode */
+               stitch_append_from_buf(bfcr, needed_bits);
+               status = read_basic_and_call_cb(bfcr, bfcr->stitch.buf,
+                       bfcr->stitch.offset);
+               if (status != BT_BFCR_STATUS_OK) {
+                       BT_COMP_LOGW("Cannot read basic field: "
+                               "bfcr-addr=%p, fc-addr=%p, status=%s",
+                               bfcr, bfcr->cur_basic_field_class,
+                               bt_bfcr_status_string(status));
+                       goto end;
+               }
+
+               if (stack_empty(bfcr->stack)) {
+                       /* Root is a basic class */
+                       bfcr->state = BFCR_STATE_DONE;
+               } else {
+                       /* Go to next field */
+                       stack_top(bfcr->stack)->index++;
+                       bfcr->state = BFCR_STATE_NEXT_FIELD;
+                       bfcr->last_bo = bfcr->cur_bo;
+               }
+               goto end;
+       }
+
+       /* We are here; it means we don't have enough data to decode this */
+       BT_COMP_LOGT_STR("Not enough data to read the next basic field: appending to stitch buffer.");
+       stitch_append_from_remaining_buf(bfcr);
+       status = BT_BFCR_STATUS_EOF;
+
+end:
+       return status;
+}
+
+static inline
+enum bt_bfcr_status read_bit_array_class_and_call_begin(struct bt_bfcr *bfcr,
+               read_basic_and_call_cb_t read_basic_and_call_cb)
+{
+       size_t available;
+       enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
+       ctf_field_class_bit_array *fc = ctf_field_class_as_bit_array(bfcr->cur_basic_field_class);
+
+       if (!at_least_one_bit_left(bfcr)) {
+               BT_COMP_LOGT("Reached end of data: bfcr-addr=%p", bfcr);
+               status = BT_BFCR_STATUS_EOF;
+               goto end;
+       }
+
+       status = validate_contiguous_bo(bfcr, fc->byte_order);
+       if (status != BT_BFCR_STATUS_OK) {
+               /* validate_contiguous_bo() logs errors */
+               goto end;
+       }
+
+       available = available_bits(bfcr);
+
+       if (fc->size <= available) {
+               /* We have all the bits; decode and set now */
+               BT_ASSERT_DBG(bfcr->buf.addr);
+               status = read_basic_and_call_cb(bfcr, bfcr->buf.addr,
+                       buf_at_from_addr(bfcr));
+               if (status != BT_BFCR_STATUS_OK) {
+                       BT_COMP_LOGW("Cannot read basic field: "
+                               "bfcr-addr=%p, fc-addr=%p, status=%s",
+                               bfcr, bfcr->cur_basic_field_class,
+                               bt_bfcr_status_string(status));
+                       goto end;
+               }
+
+               consume_bits(bfcr, fc->size);
+
+               if (stack_empty(bfcr->stack)) {
+                       /* Root is a basic class */
+                       bfcr->state = BFCR_STATE_DONE;
+               } else {
+                       /* Go to next field */
+                       stack_top(bfcr->stack)->index++;
+                       bfcr->state = BFCR_STATE_NEXT_FIELD;
+                       bfcr->last_bo = bfcr->cur_bo;
+               }
+
+               goto end;
+       }
+
+       /* We are here; it means we don't have enough data to decode this */
+       BT_COMP_LOGT_STR("Not enough data to read the next basic field: setting stitch buffer.");
+       stitch_set_from_remaining_buf(bfcr);
+       bfcr->state = BFCR_STATE_READ_BASIC_CONTINUE;
+       status = BT_BFCR_STATUS_EOF;
+
+end:
+       return status;
+}
+
+static inline
+enum bt_bfcr_status read_basic_int_class_and_call_begin(
+               struct bt_bfcr *bfcr)
+{
+       return read_bit_array_class_and_call_begin(bfcr, read_basic_int_and_call_cb);
+}
+
+static inline
+enum bt_bfcr_status read_basic_int_class_and_call_continue(
+               struct bt_bfcr *bfcr)
+{
+       return read_bit_array_class_and_call_continue(bfcr,
+               read_basic_int_and_call_cb);
+}
+
+static inline
+enum bt_bfcr_status read_basic_float_class_and_call_begin(
+               struct bt_bfcr *bfcr)
+{
+       return read_bit_array_class_and_call_begin(bfcr,
+               read_basic_float_and_call_cb);
+}
+
+static inline
+enum bt_bfcr_status read_basic_float_class_and_call_continue(
+               struct bt_bfcr *bfcr)
+{
+       return read_bit_array_class_and_call_continue(bfcr,
+               read_basic_float_and_call_cb);
+}
+
+static inline
+enum bt_bfcr_status read_basic_string_class_and_call(
+               struct bt_bfcr *bfcr, bool begin)
+{
+       size_t buf_at_bytes;
+       const uint8_t *result;
+       size_t available_bytes;
+       const uint8_t *first_chr;
+       enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
+
+       if (!at_least_one_bit_left(bfcr)) {
+               BT_COMP_LOGT("Reached end of data: bfcr-addr=%p", bfcr);
+               status = BT_BFCR_STATUS_EOF;
+               goto end;
+       }
+
+       BT_ASSERT_DBG(buf_at_from_addr(bfcr) % 8 == 0);
+       available_bytes = BITS_TO_BYTES_FLOOR(available_bits(bfcr));
+       buf_at_bytes = BITS_TO_BYTES_FLOOR(buf_at_from_addr(bfcr));
+       BT_ASSERT_DBG(bfcr->buf.addr);
+       first_chr = &bfcr->buf.addr[buf_at_bytes];
+       result = (const uint8_t *) memchr(first_chr, '\0', available_bytes);
+
+       if (begin && bfcr->user.cbs.classes.string_begin) {
+               BT_COMP_LOGT("Calling user function (string, beginning).");
+               status = bfcr->user.cbs.classes.string_begin(
+                       bfcr->cur_basic_field_class, bfcr->user.data);
+               BT_COMP_LOGT("User function returned: status=%s",
+                       bt_bfcr_status_string(status));
+               if (status != BT_BFCR_STATUS_OK) {
+                       BT_COMP_LOGW("User function failed: bfcr-addr=%p, status=%s",
+                               bfcr, bt_bfcr_status_string(status));
+                       goto end;
+               }
+       }
+
+       if (!result) {
+               /* No null character yet */
+               if (bfcr->user.cbs.classes.string) {
+                       BT_COMP_LOGT("Calling user function (substring).");
+                       status = bfcr->user.cbs.classes.string(
+                               (const char *) first_chr,
+                               available_bytes, bfcr->cur_basic_field_class,
+                               bfcr->user.data);
+                       BT_COMP_LOGT("User function returned: status=%s",
+                               bt_bfcr_status_string(status));
+                       if (status != BT_BFCR_STATUS_OK) {
+                               BT_COMP_LOGW("User function failed: "
+                                       "bfcr-addr=%p, status=%s",
+                                       bfcr, bt_bfcr_status_string(status));
+                               goto end;
+                       }
+               }
+
+               consume_bits(bfcr, BYTES_TO_BITS(available_bytes));
+               bfcr->state = BFCR_STATE_READ_BASIC_CONTINUE;
+               status = BT_BFCR_STATUS_EOF;
+       } else {
+               /* Found the null character */
+               size_t result_len = (size_t) (result - first_chr);
+
+               if (bfcr->user.cbs.classes.string && result_len) {
+                       BT_COMP_LOGT("Calling user function (substring).");
+                       status = bfcr->user.cbs.classes.string(
+                               (const char *) first_chr,
+                               result_len, bfcr->cur_basic_field_class,
+                               bfcr->user.data);
+                       BT_COMP_LOGT("User function returned: status=%s",
+                               bt_bfcr_status_string(status));
+                       if (status != BT_BFCR_STATUS_OK) {
+                               BT_COMP_LOGW("User function failed: "
+                                       "bfcr-addr=%p, status=%s",
+                                       bfcr, bt_bfcr_status_string(status));
+                               goto end;
+                       }
+               }
+
+               if (bfcr->user.cbs.classes.string_end) {
+                       BT_COMP_LOGT("Calling user function (string, end).");
+                       status = bfcr->user.cbs.classes.string_end(
+                               bfcr->cur_basic_field_class, bfcr->user.data);
+                       BT_COMP_LOGT("User function returned: status=%s",
+                               bt_bfcr_status_string(status));
+                       if (status != BT_BFCR_STATUS_OK) {
+                               BT_COMP_LOGW("User function failed: "
+                                       "bfcr-addr=%p, status=%s",
+                                       bfcr, bt_bfcr_status_string(status));
+                               goto end;
+                       }
+               }
+
+               consume_bits(bfcr, BYTES_TO_BITS(result_len + 1));
+
+               if (stack_empty(bfcr->stack)) {
+                       /* Root is a basic class */
+                       bfcr->state = BFCR_STATE_DONE;
+               } else {
+                       /* Go to next field */
+                       stack_top(bfcr->stack)->index++;
+                       bfcr->state = BFCR_STATE_NEXT_FIELD;
+                       bfcr->last_bo = bfcr->cur_bo;
+               }
+       }
+
+end:
+       return status;
+}
+
+static inline
+enum bt_bfcr_status read_basic_begin_state(struct bt_bfcr *bfcr)
+{
+       enum bt_bfcr_status status;
+
+       BT_ASSERT_DBG(bfcr->cur_basic_field_class);
+
+       switch (bfcr->cur_basic_field_class->type) {
+       case CTF_FIELD_CLASS_TYPE_INT:
+       case CTF_FIELD_CLASS_TYPE_ENUM:
+               status = read_basic_int_class_and_call_begin(bfcr);
+               break;
+       case CTF_FIELD_CLASS_TYPE_FLOAT:
+               status = read_basic_float_class_and_call_begin(bfcr);
+               break;
+       case CTF_FIELD_CLASS_TYPE_STRING:
+               status = read_basic_string_class_and_call(bfcr, true);
+               break;
+       default:
+               bt_common_abort();
+       }
+
+       return status;
+}
+
+static inline
+enum bt_bfcr_status read_basic_continue_state(struct bt_bfcr *bfcr)
+{
+       enum bt_bfcr_status status;
+
+       BT_ASSERT_DBG(bfcr->cur_basic_field_class);
+
+       switch (bfcr->cur_basic_field_class->type) {
+       case CTF_FIELD_CLASS_TYPE_INT:
+       case CTF_FIELD_CLASS_TYPE_ENUM:
+               status = read_basic_int_class_and_call_continue(bfcr);
+               break;
+       case CTF_FIELD_CLASS_TYPE_FLOAT:
+               status = read_basic_float_class_and_call_continue(bfcr);
+               break;
+       case CTF_FIELD_CLASS_TYPE_STRING:
+               status = read_basic_string_class_and_call(bfcr, false);
+               break;
+       default:
+               bt_common_abort();
+       }
+
+       return status;
+}
+
+static inline
+size_t bits_to_skip_to_align_to(struct bt_bfcr *bfcr, size_t align)
+{
+       size_t aligned_packet_at;
+
+       aligned_packet_at = BT_ALIGN(packet_at(bfcr), align);
+       return aligned_packet_at - packet_at(bfcr);
+}
+
+static inline
+enum bt_bfcr_status align_class_state(struct bt_bfcr *bfcr,
+               struct ctf_field_class *field_class, enum bfcr_state next_state)
+{
+       unsigned int field_alignment;
+       size_t skip_bits;
+       enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
+
+       /* Get field's alignment */
+       field_alignment = field_class->alignment;
+
+       /*
+        * 0 means "undefined" for variants; what we really want is 1
+        * (always aligned)
+        */
+       BT_ASSERT_DBG(field_alignment >= 1);
+
+       /* Compute how many bits we need to skip */
+       skip_bits = bits_to_skip_to_align_to(bfcr, (size_t) field_alignment);
+
+       /* Nothing to skip? aligned */
+       if (skip_bits == 0) {
+               bfcr->state = next_state;
+               goto end;
+       }
+
+       /* Make sure there's at least one bit left */
+       if (!at_least_one_bit_left(bfcr)) {
+               status = BT_BFCR_STATUS_EOF;
+               goto end;
+       }
+
+       /* Consume as many bits as possible in what's left */
+       consume_bits(bfcr, MIN(available_bits(bfcr), skip_bits));
+
+       /* Are we done now? */
+       skip_bits = bits_to_skip_to_align_to(bfcr, field_alignment);
+       if (skip_bits == 0) {
+               /* Yes: go to next state */
+               bfcr->state = next_state;
+               goto end;
+       } else {
+               /* No: need more data */
+               BT_COMP_LOGT("Reached end of data when aligning: bfcr-addr=%p", bfcr);
+               status = BT_BFCR_STATUS_EOF;
+       }
+
+end:
+       return status;
+}
+
+static inline
+enum bt_bfcr_status next_field_state(struct bt_bfcr *bfcr)
+{
+       int ret;
+       struct stack_entry *top;
+       struct ctf_field_class *next_field_class = NULL;
+       enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
+
+       if (stack_empty(bfcr->stack)) {
+               goto end;
+       }
+
+       top = stack_top(bfcr->stack);
+
+       /* Are we done with this base class? */
+       while (top->index == top->base_len) {
+               if (bfcr->user.cbs.classes.compound_end) {
+                       BT_COMP_LOGT("Calling user function (compound, end).");
+                       status = bfcr->user.cbs.classes.compound_end(
+                               top->base_class, bfcr->user.data);
+                       BT_COMP_LOGT("User function returned: status=%s",
+                               bt_bfcr_status_string(status));
+                       if (status != BT_BFCR_STATUS_OK) {
+                               BT_COMP_LOGW("User function failed: bfcr-addr=%p, status=%s",
+                                       bfcr, bt_bfcr_status_string(status));
+                               goto end;
+                       }
+               }
+
+               stack_pop(bfcr->stack);
+
+               /* Are we done with the root class? */
+               if (stack_empty(bfcr->stack)) {
+                       bfcr->state = BFCR_STATE_DONE;
+                       goto end;
+               }
+
+               top = stack_top(bfcr->stack);
+               top->index++;
+       }
+
+       /* Get next field's class */
+       switch (top->base_class->type) {
+       case CTF_FIELD_CLASS_TYPE_STRUCT:
+               next_field_class = ctf_field_class_struct_borrow_member_by_index(
+                       ctf_field_class_as_struct(top->base_class), (uint64_t) top->index)->fc;
+               break;
+       case CTF_FIELD_CLASS_TYPE_ARRAY:
+       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+       {
+               ctf_field_class_array_base *array_fc =
+                       ctf_field_class_as_array_base(top->base_class);
+
+               next_field_class = array_fc->elem_fc;
+               break;
+       }
+       case CTF_FIELD_CLASS_TYPE_VARIANT:
+               /* Variant classes are dynamic: the user should know! */
+               next_field_class =
+                       bfcr->user.cbs.query.borrow_variant_selected_field_class(
+                               top->base_class, bfcr->user.data);
+               break;
+       default:
+               break;
+       }
+
+       if (!next_field_class) {
+               BT_COMP_LOGW("Cannot get the field class of the next field: "
+                       "bfcr-addr=%p, base-fc-addr=%p, base-fc-type=%d, "
+                       "index=%" PRId64,
+                       bfcr, top->base_class, top->base_class->type,
+                       top->index);
+               status = BT_BFCR_STATUS_ERROR;
+               goto end;
+       }
+
+       if (next_field_class->is_compound) {
+               if (bfcr->user.cbs.classes.compound_begin) {
+                       BT_COMP_LOGT("Calling user function (compound, begin).");
+                       status = bfcr->user.cbs.classes.compound_begin(
+                               next_field_class, bfcr->user.data);
+                       BT_COMP_LOGT("User function returned: status=%s",
+                               bt_bfcr_status_string(status));
+                       if (status != BT_BFCR_STATUS_OK) {
+                               BT_COMP_LOGW("User function failed: bfcr-addr=%p, status=%s",
+                                       bfcr, bt_bfcr_status_string(status));
+                               goto end;
+                       }
+               }
+
+               ret = stack_push_with_len(bfcr, next_field_class);
+               if (ret) {
+                       /* stack_push_with_len() logs errors */
+                       status = BT_BFCR_STATUS_ERROR;
+                       goto end;
+               }
+
+               /* Next state: align a compound class */
+               bfcr->state = BFCR_STATE_ALIGN_COMPOUND;
+       } else {
+               /* Replace current basic field class */
+               BT_COMP_LOGT("Replacing current basic field class: "
+                       "bfcr-addr=%p, cur-basic-fc-addr=%p, "
+                       "next-basic-fc-addr=%p",
+                       bfcr, bfcr->cur_basic_field_class, next_field_class);
+               bfcr->cur_basic_field_class = next_field_class;
+
+               /* Next state: align a basic class */
+               bfcr->state = BFCR_STATE_ALIGN_BASIC;
+       }
+
+end:
+       return status;
+}
+
+static inline
+enum bt_bfcr_status handle_state(struct bt_bfcr *bfcr)
+{
+       enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
+
+       BT_COMP_LOGT("Handling state: bfcr-addr=%p, state=%s",
+               bfcr, bfcr_state_string(bfcr->state));
+
+       switch (bfcr->state) {
+       case BFCR_STATE_NEXT_FIELD:
+               status = next_field_state(bfcr);
+               break;
+       case BFCR_STATE_ALIGN_BASIC:
+               status = align_class_state(bfcr, bfcr->cur_basic_field_class,
+                       BFCR_STATE_READ_BASIC_BEGIN);
+               break;
+       case BFCR_STATE_ALIGN_COMPOUND:
+               status = align_class_state(bfcr, stack_top(bfcr->stack)->base_class,
+                       BFCR_STATE_NEXT_FIELD);
+               break;
+       case BFCR_STATE_READ_BASIC_BEGIN:
+               status = read_basic_begin_state(bfcr);
+               break;
+       case BFCR_STATE_READ_BASIC_CONTINUE:
+               status = read_basic_continue_state(bfcr);
+               break;
+       case BFCR_STATE_DONE:
+               break;
+       }
+
+       BT_COMP_LOGT("Handled state: bfcr-addr=%p, status=%s",
+               bfcr, bt_bfcr_status_string(status));
+       return status;
+}
+
+BT_HIDDEN
+struct bt_bfcr *bt_bfcr_create(struct bt_bfcr_cbs cbs, void *data,
+               bt_logging_level log_level, bt_self_component *self_comp)
+{
+       struct bt_bfcr *bfcr;
+
+       BT_COMP_LOG_CUR_LVL(BT_LOG_DEBUG, log_level, self_comp,
+               "Creating binary field class reader (BFCR).");
+       bfcr = g_new0(struct bt_bfcr, 1);
+       if (!bfcr) {
+               BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, log_level, self_comp,
+                       "Failed to allocate one binary class reader.");
+               goto end;
+       }
+
+       bfcr->log_level = log_level;
+       bfcr->self_comp = self_comp;
+       bfcr->stack = stack_new(bfcr);
+       if (!bfcr->stack) {
+               BT_COMP_LOGE_STR("Cannot create BFCR's stack.");
+               bt_bfcr_destroy(bfcr);
+               bfcr = NULL;
+               goto end;
+       }
+
+       bfcr->state = BFCR_STATE_NEXT_FIELD;
+       bfcr->user.cbs = cbs;
+       bfcr->user.data = data;
+       BT_COMP_LOGD("Created BFCR: addr=%p", bfcr);
+
+end:
+       return bfcr;
+}
+
+BT_HIDDEN
+void bt_bfcr_destroy(struct bt_bfcr *bfcr)
+{
+       if (bfcr->stack) {
+               stack_destroy(bfcr->stack);
+       }
+
+       BT_COMP_LOGD("Destroying BFCR: addr=%p", bfcr);
+       g_free(bfcr);
+}
+
+static
+void reset(struct bt_bfcr *bfcr)
+{
+       BT_COMP_LOGD("Resetting BFCR: addr=%p", bfcr);
+       stack_clear(bfcr->stack);
+       stitch_reset(bfcr);
+       bfcr->buf.addr = NULL;
+       bfcr->last_bo = CTF_BYTE_ORDER_UNKNOWN;
+}
+
+static
+void update_packet_offset(struct bt_bfcr *bfcr)
+{
+       BT_COMP_LOGT("Updating packet offset for next call: "
+               "bfcr-addr=%p, cur-packet-offset=%zu, next-packet-offset=%zu",
+               bfcr, bfcr->buf.packet_offset,
+               bfcr->buf.packet_offset + bfcr->buf.at);
+       bfcr->buf.packet_offset += bfcr->buf.at;
+}
+
+BT_HIDDEN
+size_t bt_bfcr_start(struct bt_bfcr *bfcr,
+       struct ctf_field_class *cls, const uint8_t *buf,
+       size_t offset, size_t packet_offset, size_t sz,
+       enum bt_bfcr_status *status)
+{
+       BT_ASSERT_DBG(bfcr);
+       BT_ASSERT_DBG(BYTES_TO_BITS(sz) >= offset);
+       reset(bfcr);
+       bfcr->buf.addr = buf;
+       bfcr->buf.offset = offset;
+       bfcr->buf.at = 0;
+       bfcr->buf.packet_offset = packet_offset;
+       bfcr->buf.buf_sz = sz;
+       bfcr->buf.sz = BYTES_TO_BITS(sz) - offset;
+       *status = BT_BFCR_STATUS_OK;
+
+       BT_COMP_LOGT("Starting decoding: bfcr-addr=%p, fc-addr=%p, "
+               "buf-addr=%p, buf-size=%zu, offset=%zu, "
+               "packet-offset=%zu",
+               bfcr, cls, buf, sz, offset, packet_offset);
+
+       /* Set root class */
+       if (cls->is_compound) {
+               /* Compound class: push on visit stack */
+               int stack_ret;
+
+               if (bfcr->user.cbs.classes.compound_begin) {
+                       BT_COMP_LOGT("Calling user function (compound, begin).");
+                       *status = bfcr->user.cbs.classes.compound_begin(
+                               cls, bfcr->user.data);
+                       BT_COMP_LOGT("User function returned: status=%s",
+                               bt_bfcr_status_string(*status));
+                       if (*status != BT_BFCR_STATUS_OK) {
+                               BT_COMP_LOGW("User function failed: bfcr-addr=%p, status=%s",
+                                       bfcr, bt_bfcr_status_string(*status));
+                               goto end;
+                       }
+               }
+
+               stack_ret = stack_push_with_len(bfcr, cls);
+               if (stack_ret) {
+                       /* stack_push_with_len() logs errors */
+                       *status = BT_BFCR_STATUS_ERROR;
+                       goto end;
+               }
+
+               bfcr->state = BFCR_STATE_ALIGN_COMPOUND;
+       } else {
+               /* Basic class: set as current basic class */
+               bfcr->cur_basic_field_class = cls;
+               bfcr->state = BFCR_STATE_ALIGN_BASIC;
+       }
+
+       /* Run the machine! */
+       BT_COMP_LOGT_STR("Running the state machine.");
+
+       while (true) {
+               *status = handle_state(bfcr);
+               if (*status != BT_BFCR_STATUS_OK ||
+                               bfcr->state == BFCR_STATE_DONE) {
+                       break;
+               }
+       }
+
+       /* Update packet offset for next time */
+       update_packet_offset(bfcr);
+
+end:
+       return bfcr->buf.at;
+}
+
+BT_HIDDEN
+size_t bt_bfcr_continue(struct bt_bfcr *bfcr, const uint8_t *buf, size_t sz,
+               enum bt_bfcr_status *status)
+{
+       BT_ASSERT_DBG(bfcr);
+       BT_ASSERT_DBG(buf);
+       BT_ASSERT_DBG(sz > 0);
+       bfcr->buf.addr = buf;
+       bfcr->buf.offset = 0;
+       bfcr->buf.at = 0;
+       bfcr->buf.buf_sz = sz;
+       bfcr->buf.sz = BYTES_TO_BITS(sz);
+       *status = BT_BFCR_STATUS_OK;
+
+       BT_COMP_LOGT("Continuing decoding: bfcr-addr=%p, buf-addr=%p, buf-size=%zu",
+               bfcr, buf, sz);
+
+       /* Continue running the machine */
+       BT_COMP_LOGT_STR("Running the state machine.");
+
+       while (true) {
+               *status = handle_state(bfcr);
+               if (*status != BT_BFCR_STATUS_OK ||
+                               bfcr->state == BFCR_STATE_DONE) {
+                       break;
+               }
+       }
+
+       /* Update packet offset for next time */
+       update_packet_offset(bfcr);
+       return bfcr->buf.at;
+}
+
+BT_HIDDEN
+void bt_bfcr_set_unsigned_int_cb(struct bt_bfcr *bfcr,
+               bt_bfcr_unsigned_int_cb_func cb)
+{
+       BT_ASSERT_DBG(bfcr);
+       BT_ASSERT_DBG(cb);
+       bfcr->user.cbs.classes.unsigned_int = cb;
+}
diff --git a/src/plugins/ctf/common/bfcr/bfcr.h b/src/plugins/ctf/common/bfcr/bfcr.h
deleted file mode 100644 (file)
index 8326e31..0000000
+++ /dev/null
@@ -1,363 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright (c) 2015-2016 EfficiOS Inc. and Linux Foundation
- * Copyright (c) 2015-2016 Philippe Proulx <pproulx@efficios.com>
- *
- * Babeltrace - CTF binary field class reader (BFCR)
- */
-
-#ifndef CTF_BFCR_H
-#define CTF_BFCR_H
-
-#include <stdint.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <babeltrace2/babeltrace.h>
-#include "common/macros.h"
-
-#include "../metadata/ctf-meta.h"
-
-/**
- * @file bfcr.h
- *
- * Event-driven CTF binary field class reader (BFCR).
- *
- * This is a common, internal API used by CTF source plugins. It allows
- * a binary CTF IR field class to be decoded from user-provided buffers.
- * As the class is decoded (and, possibly, its nested classes),
- * registered user callback functions are called.
- *
- * This API is only concerned with reading one CTF class at a time from
- * one or more buffer of bytes. It does not know CTF dynamic scopes,
- * events, or streams. Sequence lengths and selected variant classes are
- * requested to the user when needed.
- */
-
-/**
- * Binary class reader API status codes.
- */
-enum bt_bfcr_status {
-       /** Out of memory. */
-       BT_BFCR_STATUS_ENOMEM =         -5,
-       /**
-        * The binary stream reader reached the end of the user-provided
-        * buffer, but data is still needed to finish decoding the
-        * requested class.
-        *
-        * The user needs to call bt_bfcr_continue() as long as
-        * #BT_BFCR_STATUS_EOF is returned to complete the decoding
-        * process of a given class.
-        */
-       BT_BFCR_STATUS_EOF =            1,
-
-       /** Invalid argument. */
-       BT_BFCR_STATUS_INVAL =          -3,
-
-       /** General error. */
-       BT_BFCR_STATUS_ERROR =          -1,
-
-       /** Everything okay. */
-       BT_BFCR_STATUS_OK =             0,
-};
-
-/** Field class reader. */
-struct bt_bfcr;
-
-typedef enum bt_bfcr_status (* bt_bfcr_unsigned_int_cb_func)(uint64_t,
-               struct ctf_field_class *, void *);
-
-/*
- * Field class reader user callback functions.
- */
-struct bt_bfcr_cbs {
-       /**
-        * Field class callback functions.
-        *
-        * This CTF binary class reader is event-driven. The following
-        * functions are called during the decoding process, either when
-        * a compound class begins/ends, or when a basic class is
-        * completely decoded (along with its value).
-        *
-        * Each function also receives the CTF field class associated
-        * with the call, and user data (registered to the class reader
-        * calling them).
-        *
-        * Actual trace IR fields are \em not created here; this would
-        * be the responsibility of a class reader's user (the provider
-        * of those callback functions).
-        *
-        * All the class callback functions return one of the following
-        * values:
-        *
-        *   - <b>#BT_BFCR_STATUS_OK</b>: Everything is okay;
-        *     continue the decoding process.
-        *   - <b>#BT_BFCR_STATUS_ERROR</b>: General error (reported
-        *     to class reader's user).
-        *
-        * Any member of this structure may be set to \c NULL, should
-        * a specific message be not needed.
-        */
-       struct {
-               /**
-                * Called when a signed integer class is completely
-                * decoded. This could also be the supporting signed
-                * integer class of an enumeration class (\p class will
-                * indicate this).
-                *
-                * @param value         Signed integer value
-                * @param class         Integer or enumeration class
-                * @param data          User data
-                * @returns             #BT_BFCR_STATUS_OK or
-                *                      #BT_BFCR_STATUS_ERROR
-                */
-               enum bt_bfcr_status (* signed_int)(int64_t value,
-                               struct ctf_field_class *cls, void *data);
-
-               /**
-                * Called when an unsigned integer class is completely
-                * decoded. This could also be the supporting signed
-                * integer class of an enumeration class (\p class will
-                * indicate this).
-                *
-                * @param value         Unsigned integer value
-                * @param class         Integer or enumeration class
-                * @param data          User data
-                * @returns             #BT_BFCR_STATUS_OK or
-                *                      #BT_BFCR_STATUS_ERROR
-                */
-               bt_bfcr_unsigned_int_cb_func unsigned_int;
-
-               /**
-                * Called when a floating point number class is
-                * completely decoded.
-                *
-                * @param value         Floating point number value
-                * @param class         Floating point number class
-                * @param data          User data
-                * @returns             #BT_BFCR_STATUS_OK or
-                *                      #BT_BFCR_STATUS_ERROR
-                */
-               enum bt_bfcr_status (* floating_point)(double value,
-                               struct ctf_field_class *cls, void *data);
-
-               /**
-                * Called when a string class begins.
-                *
-                * All the following user callback function calls will
-                * be made to bt_bfcr_cbs::classes::string(), each of
-                * them providing one substring of the complete string
-                * class's value.
-                *
-                * @param class         Beginning string class
-                * @param data          User data
-                * @returns             #BT_BFCR_STATUS_OK or
-                *                      #BT_BFCR_STATUS_ERROR
-                */
-               enum bt_bfcr_status (* string_begin)(
-                               struct ctf_field_class *cls, void *data);
-
-               /**
-                * Called when a string class's substring is decoded
-                * (between a call to bt_bfcr_cbs::classes::string_begin()
-                * and a call to bt_bfcr_cbs::classes::string_end()).
-                *
-                * @param value         String value (\em not null-terminated)
-                * @param len           String value length
-                * @param class         String class
-                * @param data          User data
-                * @returns             #BT_BFCR_STATUS_OK or
-                *                      #BT_BFCR_STATUS_ERROR
-                */
-               enum bt_bfcr_status (* string)(const char *value,
-                               size_t len, struct ctf_field_class *cls,
-                               void *data);
-
-               /**
-                * Called when a string class ends.
-                *
-                * @param class         Ending string class
-                * @param data          User data
-                * @returns             #BT_BFCR_STATUS_OK or
-                *                      #BT_BFCR_STATUS_ERROR
-                */
-               enum bt_bfcr_status (* string_end)(
-                               struct ctf_field_class *cls, void *data);
-
-               /**
-                * Called when a compound class begins.
-                *
-                * All the following class callback function calls will
-                * signal sequential elements of this compound class,
-                * until the next corresponding
-                * bt_bfcr_cbs::classes::compound_end() is called.
-                *
-                * If \p class is a variant class, then only one class
-                * callback function call will follow before the call to
-                * bt_bfcr_cbs::classes::compound_end(). This single
-                * call indicates the selected class of this variant
-                * class.
-                *
-                * @param class         Beginning compound class
-                * @param data          User data
-                * @returns             #BT_BFCR_STATUS_OK or
-                *                      #BT_BFCR_STATUS_ERROR
-                */
-               enum bt_bfcr_status (* compound_begin)(
-                               struct ctf_field_class *cls, void *data);
-
-               /**
-                * Called when a compound class ends.
-                *
-                * @param class         Ending compound class
-                * @param data          User data
-                * @returns             #BT_BFCR_STATUS_OK or
-                *                      #BT_BFCR_STATUS_ERROR
-                */
-               enum bt_bfcr_status (* compound_end)(
-                               struct ctf_field_class *cls, void *data);
-       } classes;
-
-       /**
-        * Query callback functions are used when the class reader needs
-        * dynamic information, i.e. a sequence class's current length
-        * or a variant class's current selected class.
-        *
-        * Both functions need to be set unless it is known that no
-        * sequences or variants will have to be decoded.
-        */
-       struct {
-               /**
-                * Called to query the current length of a given sequence
-                * class.
-                *
-                * @param class         Sequence class
-                * @param data          User data
-                * @returns             Sequence length or
-                *                      #BT_BFCR_STATUS_ERROR on error
-                */
-               int64_t (* get_sequence_length)(struct ctf_field_class *cls,
-                               void *data);
-
-               /**
-                * Called to query the current selected class of a given
-                * variant class.
-                *
-                * @param class         Variant class
-                * @param data          User data
-                * @returns             Current selected class (owned by
-                *                      this) or \c NULL on error
-                */
-               struct ctf_field_class * (* borrow_variant_selected_field_class)(
-                               struct ctf_field_class *cls, void *data);
-       } query;
-};
-
-/**
- * Creates a CTF binary class reader.
- *
- * @param cbs          User callback functions
- * @param data         User data (passed to user callback functions)
- * @returns            New binary class reader on success, or \c NULL on error
- */
-BT_HIDDEN
-struct bt_bfcr *bt_bfcr_create(struct bt_bfcr_cbs cbs, void *data,
-               bt_logging_level log_level, bt_self_component *self_comp);
-
-/**
- * Destroys a CTF binary class reader, freeing all internal resources.
- *
- * @param bfcr Binary class reader
- */
-BT_HIDDEN
-void bt_bfcr_destroy(struct bt_bfcr *bfcr);
-
-/**
- * Decodes a given CTF class from a buffer of bytes.
- *
- * The number of \em bits consumed by this function is returned.
- *
- * The \p status output parameter is where a status is written, amongst
- * the following:
- *
- *   - <b>#BT_BFCR_STATUS_OK</b>: Decoding is done.
- *   - <b>#BT_BFCR_STATUS_EOF</b>: The end of the buffer was reached,
- *     but more data is needed to finish the decoding process of the
- *     requested class. The user needs to call bt_bfcr_continue()
- *     as long as #BT_BFCR_STATUS_EOF is returned to complete the
- *     decoding process of the original class.
- *   - <b>#BT_BFCR_STATUS_INVAL</b>: Invalid argument.
- *   - <b>#BT_BFCR_STATUS_ERROR</b>: General error.
- *
- * Calling this function resets the class reader's internal state. If
- * #BT_BFCR_STATUS_EOF is returned, bt_bfcr_continue() needs to
- * be called next, \em not bt_bfcr_decode().
- *
- * @param bfcr                 Binary class reader
- * @param class                        Field class to decode
- * @param buf                  Buffer
- * @param offset               Offset of first bit from \p buf (bits)
- * @param packet_offset                Offset of \p offset within the CTF
- *                             binary packet containing \p class (bits)
- * @param sz                   Size of buffer in bytes (from \p buf)
- * @param status               Returned status (see description above)
- * @returns                    Number of consumed bits
- */
-BT_HIDDEN
-size_t bt_bfcr_start(struct bt_bfcr *bfcr,
-               struct ctf_field_class *cls, const uint8_t *buf,
-               size_t offset, size_t packet_offset, size_t sz,
-               enum bt_bfcr_status *status);
-
-/**
- * Continues the decoding process a given CTF class.
- *
- * The number of bits consumed by this function is returned.
- *
- * The \p status output parameter is where a status is placed, amongst
- * the following:
- *
- *   - <b>#BT_BFCR_STATUS_OK</b>: decoding is done.
- *   - <b>#BT_BFCR_STATUS_EOF</b>: the end of the buffer was reached,
- *     but more data is needed to finish the decoding process of the
- *     requested class. The user needs to call bt_bfcr_continue()
- *     as long as #BT_BFCR_STATUS_EOF is returned to complete the
- *     decoding process of the original class.
- *   - <b>#BT_BFCR_STATUS_INVAL</b>: invalid argument.
- *   - <b>#BT_BFCR_STATUS_ERROR</b>: general error.
- *
- * @param bfcr         Binary class reader
- * @param buf          Buffer
- * @param sz           Size of buffer in bytes (from \p offset)
- * @param status       Returned status (see description above)
- * @returns            Number of consumed bits
- */
-BT_HIDDEN
-size_t bt_bfcr_continue(struct bt_bfcr *bfcr,
-               const uint8_t *buf, size_t sz,
-               enum bt_bfcr_status *status);
-
-BT_HIDDEN
-void bt_bfcr_set_unsigned_int_cb(struct bt_bfcr *bfcr,
-               bt_bfcr_unsigned_int_cb_func cb);
-
-static inline
-const char *bt_bfcr_status_string(enum bt_bfcr_status status)
-{
-       switch (status) {
-       case BT_BFCR_STATUS_ENOMEM:
-               return "ENOMEM";
-       case BT_BFCR_STATUS_EOF:
-               return "EOF";
-       case BT_BFCR_STATUS_INVAL:
-               return "INVAL";
-       case BT_BFCR_STATUS_ERROR:
-               return "ERROR";
-       case BT_BFCR_STATUS_OK:
-               return "OK";
-       }
-
-       bt_common_abort();
-}
-
-#endif /* CTF_BFCR_H */
diff --git a/src/plugins/ctf/common/bfcr/bfcr.hpp b/src/plugins/ctf/common/bfcr/bfcr.hpp
new file mode 100644 (file)
index 0000000..e94c05b
--- /dev/null
@@ -0,0 +1,363 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright (c) 2015-2016 EfficiOS Inc. and Linux Foundation
+ * Copyright (c) 2015-2016 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Babeltrace - CTF binary field class reader (BFCR)
+ */
+
+#ifndef CTF_BFCR_H
+#define CTF_BFCR_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <babeltrace2/babeltrace.h>
+#include "common/macros.h"
+
+#include "../metadata/ctf-meta.hpp"
+
+/**
+ * @file bfcr.h
+ *
+ * Event-driven CTF binary field class reader (BFCR).
+ *
+ * This is a common, internal API used by CTF source plugins. It allows
+ * a binary CTF IR field class to be decoded from user-provided buffers.
+ * As the class is decoded (and, possibly, its nested classes),
+ * registered user callback functions are called.
+ *
+ * This API is only concerned with reading one CTF class at a time from
+ * one or more buffer of bytes. It does not know CTF dynamic scopes,
+ * events, or streams. Sequence lengths and selected variant classes are
+ * requested to the user when needed.
+ */
+
+/**
+ * Binary class reader API status codes.
+ */
+enum bt_bfcr_status {
+       /** Out of memory. */
+       BT_BFCR_STATUS_ENOMEM =         -5,
+       /**
+        * The binary stream reader reached the end of the user-provided
+        * buffer, but data is still needed to finish decoding the
+        * requested class.
+        *
+        * The user needs to call bt_bfcr_continue() as long as
+        * #BT_BFCR_STATUS_EOF is returned to complete the decoding
+        * process of a given class.
+        */
+       BT_BFCR_STATUS_EOF =            1,
+
+       /** Invalid argument. */
+       BT_BFCR_STATUS_INVAL =          -3,
+
+       /** General error. */
+       BT_BFCR_STATUS_ERROR =          -1,
+
+       /** Everything okay. */
+       BT_BFCR_STATUS_OK =             0,
+};
+
+/** Field class reader. */
+struct bt_bfcr;
+
+typedef enum bt_bfcr_status (* bt_bfcr_unsigned_int_cb_func)(uint64_t,
+               struct ctf_field_class *, void *);
+
+/*
+ * Field class reader user callback functions.
+ */
+struct bt_bfcr_cbs {
+       /**
+        * Field class callback functions.
+        *
+        * This CTF binary class reader is event-driven. The following
+        * functions are called during the decoding process, either when
+        * a compound class begins/ends, or when a basic class is
+        * completely decoded (along with its value).
+        *
+        * Each function also receives the CTF field class associated
+        * with the call, and user data (registered to the class reader
+        * calling them).
+        *
+        * Actual trace IR fields are \em not created here; this would
+        * be the responsibility of a class reader's user (the provider
+        * of those callback functions).
+        *
+        * All the class callback functions return one of the following
+        * values:
+        *
+        *   - <b>#BT_BFCR_STATUS_OK</b>: Everything is okay;
+        *     continue the decoding process.
+        *   - <b>#BT_BFCR_STATUS_ERROR</b>: General error (reported
+        *     to class reader's user).
+        *
+        * Any member of this structure may be set to \c NULL, should
+        * a specific message be not needed.
+        */
+       struct {
+               /**
+                * Called when a signed integer class is completely
+                * decoded. This could also be the supporting signed
+                * integer class of an enumeration class (\p class will
+                * indicate this).
+                *
+                * @param value         Signed integer value
+                * @param class         Integer or enumeration class
+                * @param data          User data
+                * @returns             #BT_BFCR_STATUS_OK or
+                *                      #BT_BFCR_STATUS_ERROR
+                */
+               enum bt_bfcr_status (* signed_int)(int64_t value,
+                               struct ctf_field_class *cls, void *data);
+
+               /**
+                * Called when an unsigned integer class is completely
+                * decoded. This could also be the supporting signed
+                * integer class of an enumeration class (\p class will
+                * indicate this).
+                *
+                * @param value         Unsigned integer value
+                * @param class         Integer or enumeration class
+                * @param data          User data
+                * @returns             #BT_BFCR_STATUS_OK or
+                *                      #BT_BFCR_STATUS_ERROR
+                */
+               bt_bfcr_unsigned_int_cb_func unsigned_int;
+
+               /**
+                * Called when a floating point number class is
+                * completely decoded.
+                *
+                * @param value         Floating point number value
+                * @param class         Floating point number class
+                * @param data          User data
+                * @returns             #BT_BFCR_STATUS_OK or
+                *                      #BT_BFCR_STATUS_ERROR
+                */
+               enum bt_bfcr_status (* floating_point)(double value,
+                               struct ctf_field_class *cls, void *data);
+
+               /**
+                * Called when a string class begins.
+                *
+                * All the following user callback function calls will
+                * be made to bt_bfcr_cbs::classes::string(), each of
+                * them providing one substring of the complete string
+                * class's value.
+                *
+                * @param class         Beginning string class
+                * @param data          User data
+                * @returns             #BT_BFCR_STATUS_OK or
+                *                      #BT_BFCR_STATUS_ERROR
+                */
+               enum bt_bfcr_status (* string_begin)(
+                               struct ctf_field_class *cls, void *data);
+
+               /**
+                * Called when a string class's substring is decoded
+                * (between a call to bt_bfcr_cbs::classes::string_begin()
+                * and a call to bt_bfcr_cbs::classes::string_end()).
+                *
+                * @param value         String value (\em not null-terminated)
+                * @param len           String value length
+                * @param class         String class
+                * @param data          User data
+                * @returns             #BT_BFCR_STATUS_OK or
+                *                      #BT_BFCR_STATUS_ERROR
+                */
+               enum bt_bfcr_status (* string)(const char *value,
+                               size_t len, struct ctf_field_class *cls,
+                               void *data);
+
+               /**
+                * Called when a string class ends.
+                *
+                * @param class         Ending string class
+                * @param data          User data
+                * @returns             #BT_BFCR_STATUS_OK or
+                *                      #BT_BFCR_STATUS_ERROR
+                */
+               enum bt_bfcr_status (* string_end)(
+                               struct ctf_field_class *cls, void *data);
+
+               /**
+                * Called when a compound class begins.
+                *
+                * All the following class callback function calls will
+                * signal sequential elements of this compound class,
+                * until the next corresponding
+                * bt_bfcr_cbs::classes::compound_end() is called.
+                *
+                * If \p class is a variant class, then only one class
+                * callback function call will follow before the call to
+                * bt_bfcr_cbs::classes::compound_end(). This single
+                * call indicates the selected class of this variant
+                * class.
+                *
+                * @param class         Beginning compound class
+                * @param data          User data
+                * @returns             #BT_BFCR_STATUS_OK or
+                *                      #BT_BFCR_STATUS_ERROR
+                */
+               enum bt_bfcr_status (* compound_begin)(
+                               struct ctf_field_class *cls, void *data);
+
+               /**
+                * Called when a compound class ends.
+                *
+                * @param class         Ending compound class
+                * @param data          User data
+                * @returns             #BT_BFCR_STATUS_OK or
+                *                      #BT_BFCR_STATUS_ERROR
+                */
+               enum bt_bfcr_status (* compound_end)(
+                               struct ctf_field_class *cls, void *data);
+       } classes;
+
+       /**
+        * Query callback functions are used when the class reader needs
+        * dynamic information, i.e. a sequence class's current length
+        * or a variant class's current selected class.
+        *
+        * Both functions need to be set unless it is known that no
+        * sequences or variants will have to be decoded.
+        */
+       struct {
+               /**
+                * Called to query the current length of a given sequence
+                * class.
+                *
+                * @param class         Sequence class
+                * @param data          User data
+                * @returns             Sequence length or
+                *                      #BT_BFCR_STATUS_ERROR on error
+                */
+               int64_t (* get_sequence_length)(struct ctf_field_class *cls,
+                               void *data);
+
+               /**
+                * Called to query the current selected class of a given
+                * variant class.
+                *
+                * @param class         Variant class
+                * @param data          User data
+                * @returns             Current selected class (owned by
+                *                      this) or \c NULL on error
+                */
+               struct ctf_field_class * (* borrow_variant_selected_field_class)(
+                               struct ctf_field_class *cls, void *data);
+       } query;
+};
+
+/**
+ * Creates a CTF binary class reader.
+ *
+ * @param cbs          User callback functions
+ * @param data         User data (passed to user callback functions)
+ * @returns            New binary class reader on success, or \c NULL on error
+ */
+BT_HIDDEN
+struct bt_bfcr *bt_bfcr_create(struct bt_bfcr_cbs cbs, void *data,
+               bt_logging_level log_level, bt_self_component *self_comp);
+
+/**
+ * Destroys a CTF binary class reader, freeing all internal resources.
+ *
+ * @param bfcr Binary class reader
+ */
+BT_HIDDEN
+void bt_bfcr_destroy(struct bt_bfcr *bfcr);
+
+/**
+ * Decodes a given CTF class from a buffer of bytes.
+ *
+ * The number of \em bits consumed by this function is returned.
+ *
+ * The \p status output parameter is where a status is written, amongst
+ * the following:
+ *
+ *   - <b>#BT_BFCR_STATUS_OK</b>: Decoding is done.
+ *   - <b>#BT_BFCR_STATUS_EOF</b>: The end of the buffer was reached,
+ *     but more data is needed to finish the decoding process of the
+ *     requested class. The user needs to call bt_bfcr_continue()
+ *     as long as #BT_BFCR_STATUS_EOF is returned to complete the
+ *     decoding process of the original class.
+ *   - <b>#BT_BFCR_STATUS_INVAL</b>: Invalid argument.
+ *   - <b>#BT_BFCR_STATUS_ERROR</b>: General error.
+ *
+ * Calling this function resets the class reader's internal state. If
+ * #BT_BFCR_STATUS_EOF is returned, bt_bfcr_continue() needs to
+ * be called next, \em not bt_bfcr_decode().
+ *
+ * @param bfcr                 Binary class reader
+ * @param class                        Field class to decode
+ * @param buf                  Buffer
+ * @param offset               Offset of first bit from \p buf (bits)
+ * @param packet_offset                Offset of \p offset within the CTF
+ *                             binary packet containing \p class (bits)
+ * @param sz                   Size of buffer in bytes (from \p buf)
+ * @param status               Returned status (see description above)
+ * @returns                    Number of consumed bits
+ */
+BT_HIDDEN
+size_t bt_bfcr_start(struct bt_bfcr *bfcr,
+               struct ctf_field_class *cls, const uint8_t *buf,
+               size_t offset, size_t packet_offset, size_t sz,
+               enum bt_bfcr_status *status);
+
+/**
+ * Continues the decoding process a given CTF class.
+ *
+ * The number of bits consumed by this function is returned.
+ *
+ * The \p status output parameter is where a status is placed, amongst
+ * the following:
+ *
+ *   - <b>#BT_BFCR_STATUS_OK</b>: decoding is done.
+ *   - <b>#BT_BFCR_STATUS_EOF</b>: the end of the buffer was reached,
+ *     but more data is needed to finish the decoding process of the
+ *     requested class. The user needs to call bt_bfcr_continue()
+ *     as long as #BT_BFCR_STATUS_EOF is returned to complete the
+ *     decoding process of the original class.
+ *   - <b>#BT_BFCR_STATUS_INVAL</b>: invalid argument.
+ *   - <b>#BT_BFCR_STATUS_ERROR</b>: general error.
+ *
+ * @param bfcr         Binary class reader
+ * @param buf          Buffer
+ * @param sz           Size of buffer in bytes (from \p offset)
+ * @param status       Returned status (see description above)
+ * @returns            Number of consumed bits
+ */
+BT_HIDDEN
+size_t bt_bfcr_continue(struct bt_bfcr *bfcr,
+               const uint8_t *buf, size_t sz,
+               enum bt_bfcr_status *status);
+
+BT_HIDDEN
+void bt_bfcr_set_unsigned_int_cb(struct bt_bfcr *bfcr,
+               bt_bfcr_unsigned_int_cb_func cb);
+
+static inline
+const char *bt_bfcr_status_string(enum bt_bfcr_status status)
+{
+       switch (status) {
+       case BT_BFCR_STATUS_ENOMEM:
+               return "ENOMEM";
+       case BT_BFCR_STATUS_EOF:
+               return "EOF";
+       case BT_BFCR_STATUS_INVAL:
+               return "INVAL";
+       case BT_BFCR_STATUS_ERROR:
+               return "ERROR";
+       case BT_BFCR_STATUS_OK:
+               return "OK";
+       }
+
+       bt_common_abort();
+}
+
+#endif /* CTF_BFCR_H */
index 7fe4ce77d2a5ddf0ad6b4f1451727856e72b1171..7d843d64653ab09f22e8fd1bad058fc1f0d3e891 100644 (file)
@@ -5,51 +5,51 @@ AM_YFLAGS = -t -d -v -Wno-yacc
 
 noinst_LTLIBRARIES = libctf-parser.la libctf-ast.la
 
-BUILT_SOURCES = parser.h
+BUILT_SOURCES = parser.hpp
 
-libctf_parser_la_SOURCES = lexer.l parser.y objstack.c
+libctf_parser_la_SOURCES = lexer.lpp parser.ypp objstack.cpp
 # scanner-symbols.h is included to prefix generated yy_* symbols
 # with bt_.
 libctf_parser_la_CPPFLAGS = $(AM_CPPFLAGS) \
-               -include $(srcdir)/scanner-symbols.h
+               -include $(srcdir)/scanner-symbols.hpp
 
 # This library contains (mostly) generated code, silence some warnings that it
 # produces.
-libctf_parser_la_CFLAGS = $(AM_CFLAGS) \
+libctf_parser_la_CXXFLAGS = $(AM_CXXFLAGS) \
        -Wno-unused-function \
        -Wno-null-dereference
 
 libctf_ast_la_SOURCES = \
-       visitor-generate-ir.c \
-       visitor-semantic-validator.c \
-       visitor-parent-links.c \
-       ast.h \
-       objstack.h \
-       parser.h \
-       parser-wrap.h \
-       scanner.h \
-       scanner-symbols.h \
-       decoder.c \
-       decoder.h \
-       decoder-packetized-file-stream-to-buf.c \
-       decoder-packetized-file-stream-to-buf.h \
-       logging.c \
-       logging.h \
-       ctf-meta.h \
-       ctf-meta-visitors.h \
-       ctf-meta-validate.c \
-       ctf-meta-update-meanings.c \
-       ctf-meta-update-in-ir.c \
-       ctf-meta-update-default-clock-classes.c \
-       ctf-meta-update-text-array-sequence.c \
-       ctf-meta-update-alignments.c \
-       ctf-meta-update-value-storing-indexes.c \
-       ctf-meta-update-stream-class-config.c \
-       ctf-meta-warn-meaningless-header-fields.c \
-       ctf-meta-translate.c \
-       ctf-meta-resolve.c \
-       ctf-meta-configure-ir-trace.c \
-       ctf-meta-configure-ir-trace.h
+       visitor-generate-ir.cpp \
+       visitor-semantic-validator.cpp \
+       visitor-parent-links.cpp \
+       ast.hpp \
+       objstack.hpp \
+       parser.hpp \
+       parser-wrap.hpp \
+       scanner.hpp \
+       scanner-symbols.hpp \
+       decoder.cpp \
+       decoder.hpp \
+       decoder-packetized-file-stream-to-buf.cpp \
+       decoder-packetized-file-stream-to-buf.hpp \
+       logging.cpp \
+       logging.hpp \
+       ctf-meta.hpp \
+       ctf-meta-visitors.hpp \
+       ctf-meta-validate.cpp \
+       ctf-meta-update-meanings.cpp \
+       ctf-meta-update-in-ir.cpp \
+       ctf-meta-update-default-clock-classes.cpp \
+       ctf-meta-update-text-array-sequence.cpp \
+       ctf-meta-update-alignments.cpp \
+       ctf-meta-update-value-storing-indexes.cpp \
+       ctf-meta-update-stream-class-config.cpp \
+       ctf-meta-warn-meaningless-header-fields.cpp \
+       ctf-meta-translate.cpp \
+       ctf-meta-resolve.cpp \
+       ctf-meta-configure-ir-trace.cpp \
+       ctf-meta-configure-ir-trace.hpp
 
 if BABELTRACE_BUILD_WITH_MINGW
 libctf_ast_la_LIBADD = -lintl -liconv -lole32
@@ -60,32 +60,32 @@ CLEANFILES =
 
 if HAVE_BISON
 # we have bison: we can clean the generated parser files
-CLEANFILES += parser.c parser.h parser.output
+CLEANFILES += parser.cpp parser.hpp parser.output
 else # HAVE_BISON
 # create target used to stop the build if we want to build the parser,
 # but we don't have the necessary tool to do so
 ERR_MSG = "Error: Cannot build target because bison is missing."
 ERR_MSG += "Make sure bison is installed and run the configure script again."
 
-parser.c parser.h: parser.y
+parser.cpp parser.hpp: parser.ypp
        @echo $(ERR_MSG)
        @false
 
-all-local: parser.c parser.h
+all-local: parser.cpp parser.hpp
 endif # HAVE_BISON
 
 if HAVE_FLEX
 # we have flex: we can clean the generated lexer files
-CLEANFILES += lexer.c
+CLEANFILES += lexer.cpp
 else # HAVE_FLEX
 # create target used to stop the build if we want to build the lexer,
 # but we don't have the necessary tool to do so
 ERR_MSG = "Error: Cannot build target because flex is missing."
 ERR_MSG += "Make sure flex is installed and run the configure script again."
 
-filter-lexer.c: lexer.l
+filter-lexer.cpp: lexer.lpp
        @echo $(ERR_MSG)
        @false
 
-all-local: lexer.c
+all-local: lexer.cpp
 endif # HAVE_FLEX
diff --git a/src/plugins/ctf/common/metadata/ast.h b/src/plugins/ctf/common/metadata/ast.h
deleted file mode 100644 (file)
index afab409..0000000
+++ /dev/null
@@ -1,428 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2011-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- */
-
-#ifndef _CTF_AST_H
-#define _CTF_AST_H
-
-#include <stdint.h>
-#include <stdio.h>
-#include <glib.h>
-#include "common/list.h"
-#include <babeltrace2/babeltrace.h>
-#include "common/macros.h"
-#include "common/assert.h"
-
-#include "decoder.h"
-#include "ctf-meta.h"
-
-// the parameter name (of the reentrant 'yyparse' function)
-// data is a pointer to a 'SParserParam' structure
-//#define YYPARSE_PARAM        scanner
-
-struct ctf_node;
-struct ctf_parser;
-struct ctf_visitor_generate_ir;
-
-#define EINCOMPLETE    1000
-
-#define FOREACH_CTF_NODES(F) \
-       F(NODE_UNKNOWN) \
-       F(NODE_ROOT) \
-       F(NODE_ERROR) \
-       F(NODE_EVENT) \
-       F(NODE_STREAM) \
-       F(NODE_ENV) \
-       F(NODE_TRACE) \
-       F(NODE_CLOCK) \
-       F(NODE_CALLSITE) \
-       F(NODE_CTF_EXPRESSION) \
-       F(NODE_UNARY_EXPRESSION) \
-       F(NODE_TYPEDEF) \
-       F(NODE_TYPEALIAS_TARGET) \
-       F(NODE_TYPEALIAS_ALIAS) \
-       F(NODE_TYPEALIAS) \
-       F(NODE_TYPE_SPECIFIER) \
-       F(NODE_TYPE_SPECIFIER_LIST) \
-       F(NODE_POINTER) \
-       F(NODE_TYPE_DECLARATOR) \
-       F(NODE_FLOATING_POINT) \
-       F(NODE_INTEGER) \
-       F(NODE_STRING) \
-       F(NODE_ENUMERATOR) \
-       F(NODE_ENUM) \
-       F(NODE_STRUCT_OR_VARIANT_DECLARATION) \
-       F(NODE_VARIANT) \
-       F(NODE_STRUCT)
-
-enum node_type {
-#define ENTRY(S)       S,
-       FOREACH_CTF_NODES(ENTRY)
-#undef ENTRY
-       NR_NODE_TYPES,
-};
-
-struct ctf_node {
-       /*
-        * Parent node is only set on demand by specific visitor.
-        */
-       struct ctf_node *parent;
-       struct bt_list_head siblings;
-       struct bt_list_head tmp_head;
-       unsigned int lineno;
-       /*
-        * We mark nodes visited in the generate-ir phase (last
-        * phase). We only mark the 1-depth level nodes as visited
-        * (never the root node, and not their sub-nodes). This allows
-        * skipping already visited nodes when doing incremental
-        * metadata append.
-        */
-       int visited;
-
-       enum node_type type;
-       union {
-               struct {
-               } unknown;
-               struct {
-                       /*
-                        * Children nodes are ctf_expression, field_class_def,
-                        * field_class_alias and field_class_specifier_list.
-                        */
-                       struct bt_list_head declaration_list;
-                       struct bt_list_head trace;
-                       struct bt_list_head env;
-                       struct bt_list_head stream;
-                       struct bt_list_head event;
-                       struct bt_list_head clock;
-                       struct bt_list_head callsite;
-               } root;
-               struct {
-                       /*
-                        * Children nodes are ctf_expression, field_class_def,
-                        * field_class_alias and field_class_specifier_list.
-                        */
-                       struct bt_list_head declaration_list;
-               } event;
-               struct {
-                       /*
-                        * Children nodes are ctf_expression, field_class_def,
-                        * field_class_alias and field_class_specifier_list.
-                        */
-                       struct bt_list_head declaration_list;
-               } stream;
-               struct {
-                       /*
-                        * Children nodes are ctf_expression, field_class_def,
-                        * field_class_alias and field_class_specifier_list.
-                        */
-                       struct bt_list_head declaration_list;
-               } env;
-               struct {
-                       /*
-                        * Children nodes are ctf_expression, field_class_def,
-                        * field_class_alias and field_class_specifier_list.
-                        */
-                       struct bt_list_head declaration_list;
-               } trace;
-               struct {
-                       /*
-                        * Children nodes are ctf_expression, field_class_def,
-                        * field_class_alias and field_class_specifier_list.
-                        */
-                       struct bt_list_head declaration_list;
-               } clock;
-               struct {
-                       /*
-                        * Children nodes are ctf_expression, field_class_def,
-                        * field_class_alias and field_class_specifier_list.
-                        */
-                       struct bt_list_head declaration_list;
-               } callsite;
-               struct {
-                       struct bt_list_head left;       /* Should be string */
-                       struct bt_list_head right;      /* Unary exp. or type */
-               } ctf_expression;
-               struct {
-                       enum {
-                               UNARY_UNKNOWN = 0,
-                               UNARY_STRING,
-                               UNARY_SIGNED_CONSTANT,
-                               UNARY_UNSIGNED_CONSTANT,
-                               UNARY_SBRAC,
-                       } type;
-                       union {
-                               /*
-                                * string for identifier, id_type, keywords,
-                                * string literals and character constants.
-                                */
-                               char *string;
-                               int64_t signed_constant;
-                               uint64_t unsigned_constant;
-                               struct ctf_node *sbrac_exp;
-                       } u;
-                       enum {
-                               UNARY_LINK_UNKNOWN = 0,
-                               UNARY_DOTLINK,
-                               UNARY_ARROWLINK,
-                               UNARY_DOTDOTDOT,
-                       } link;
-               } unary_expression;
-               struct {
-                       struct ctf_node *field_class_specifier_list;
-                       struct bt_list_head field_class_declarators;
-               } field_class_def;
-               /* new type is "alias", existing type "target" */
-               struct {
-                       struct ctf_node *field_class_specifier_list;
-                       struct bt_list_head field_class_declarators;
-               } field_class_alias_target;
-               struct {
-                       struct ctf_node *field_class_specifier_list;
-                       struct bt_list_head field_class_declarators;
-               } field_class_alias_name;
-               struct {
-                       struct ctf_node *target;
-                       struct ctf_node *alias;
-               } field_class_alias;
-               struct {
-                       enum {
-                               TYPESPEC_UNKNOWN = 0,
-                               TYPESPEC_VOID,
-                               TYPESPEC_CHAR,
-                               TYPESPEC_SHORT,
-                               TYPESPEC_INT,
-                               TYPESPEC_LONG,
-                               TYPESPEC_FLOAT,
-                               TYPESPEC_DOUBLE,
-                               TYPESPEC_SIGNED,
-                               TYPESPEC_UNSIGNED,
-                               TYPESPEC_BOOL,
-                               TYPESPEC_COMPLEX,
-                               TYPESPEC_IMAGINARY,
-                               TYPESPEC_CONST,
-                               TYPESPEC_ID_TYPE,
-                               TYPESPEC_FLOATING_POINT,
-                               TYPESPEC_INTEGER,
-                               TYPESPEC_STRING,
-                               TYPESPEC_STRUCT,
-                               TYPESPEC_VARIANT,
-                               TYPESPEC_ENUM,
-                       } type;
-                       /* For struct, variant and enum */
-                       struct ctf_node *node;
-                       const char *id_type;
-               } field_class_specifier;
-               struct {
-                       /* list of field_class_specifier */
-                       struct bt_list_head head;
-               } field_class_specifier_list;
-               struct {
-                       unsigned int const_qualifier;
-               } pointer;
-               struct {
-                       struct bt_list_head pointers;
-                       enum {
-                               TYPEDEC_UNKNOWN = 0,
-                               TYPEDEC_ID,     /* identifier */
-                               TYPEDEC_NESTED, /* (), array or sequence */
-                       } type;
-                       union {
-                               char *id;
-                               struct {
-                                       /* typedec has no pointer list */
-                                       struct ctf_node *field_class_declarator;
-                                       /*
-                                        * unary expression (value) or
-                                        * field_class_specifier_list.
-                                        */
-                                       struct bt_list_head length;
-                                       /* for abstract type declarator */
-                                       unsigned int abstract_array;
-                               } nested;
-                       } u;
-                       struct ctf_node *bitfield_len;
-               } field_class_declarator;
-               struct {
-                       /* Children nodes are ctf_expression. */
-                       struct bt_list_head expressions;
-               } floating_point;
-               struct {
-                       /* Children nodes are ctf_expression. */
-                       struct bt_list_head expressions;
-               } integer;
-               struct {
-                       /* Children nodes are ctf_expression. */
-                       struct bt_list_head expressions;
-               } string;
-               struct {
-                       char *id;
-                       /*
-                        * Range list or single value node. Contains unary
-                        * expressions.
-                        */
-                       struct bt_list_head values;
-               } enumerator;
-               struct {
-                       char *enum_id;
-                       /*
-                        * Either NULL, or points to unary expression or
-                        * field_class_specifier_list.
-                        */
-                       struct ctf_node *container_field_class;
-                       struct bt_list_head enumerator_list;
-                       int has_body;
-               } _enum;
-               struct {
-                       struct ctf_node *field_class_specifier_list;
-                       struct bt_list_head field_class_declarators;
-               } struct_or_variant_declaration;
-               struct {
-                       char *name;
-                       char *choice;
-                       /*
-                        * list of field_class_def, field_class_alias and
-                        * declarations
-                        */
-                       struct bt_list_head declaration_list;
-                       int has_body;
-               } variant;
-               struct {
-                       char *name;
-                       /*
-                        * list of field_class_def, field_class_alias and
-                        * declarations
-                        */
-                       struct bt_list_head declaration_list;
-                       int has_body;
-                       struct bt_list_head min_align;  /* align() attribute */
-               } _struct;
-       } u;
-};
-
-struct ctf_ast {
-       struct ctf_node root;
-};
-
-const char *node_type(struct ctf_node *node);
-
-struct meta_log_config;
-
-BT_HIDDEN
-struct ctf_visitor_generate_ir *ctf_visitor_generate_ir_create(
-               const struct ctf_metadata_decoder_config *config);
-
-void ctf_visitor_generate_ir_destroy(struct ctf_visitor_generate_ir *visitor);
-
-BT_HIDDEN
-bt_trace_class *ctf_visitor_generate_ir_get_ir_trace_class(
-               struct ctf_visitor_generate_ir *visitor);
-
-BT_HIDDEN
-struct ctf_trace_class *ctf_visitor_generate_ir_borrow_ctf_trace_class(
-               struct ctf_visitor_generate_ir *visitor);
-
-BT_HIDDEN
-int ctf_visitor_generate_ir_visit_node(struct ctf_visitor_generate_ir *visitor,
-               struct ctf_node *node);
-
-BT_HIDDEN
-int ctf_visitor_semantic_check(int depth, struct ctf_node *node,
-               struct meta_log_config *log_cfg);
-
-BT_HIDDEN
-int ctf_visitor_parent_links(int depth, struct ctf_node *node,
-               struct meta_log_config *log_cfg);
-
-static inline
-char *ctf_ast_concatenate_unary_strings(struct bt_list_head *head)
-{
-       int i = 0;
-       GString *str;
-       struct ctf_node *node;
-
-       str = g_string_new(NULL);
-       BT_ASSERT(str);
-
-       bt_list_for_each_entry(node, head, siblings) {
-               char *src_string;
-
-               if (
-                       node->type != NODE_UNARY_EXPRESSION ||
-                       node->u.unary_expression.type != UNARY_STRING ||
-                       !(
-                               (
-                                       node->u.unary_expression.link !=
-                                       UNARY_LINK_UNKNOWN
-                               ) ^ (i == 0)
-                       )
-               ) {
-                       goto error;
-               }
-
-               switch (node->u.unary_expression.link) {
-               case UNARY_DOTLINK:
-                       g_string_append(str, ".");
-                       break;
-               case UNARY_ARROWLINK:
-                       g_string_append(str, "->");
-                       break;
-               case UNARY_DOTDOTDOT:
-                       g_string_append(str, "...");
-                       break;
-               default:
-                       break;
-               }
-
-               src_string = node->u.unary_expression.u.string;
-               g_string_append(str, src_string);
-               i++;
-       }
-
-       /* Destroys the container, returns the underlying string */
-       return g_string_free(str, FALSE);
-
-error:
-       /* This always returns NULL */
-       return g_string_free(str, TRUE);
-}
-
-static inline
-int ctf_ast_get_unary_uuid(struct bt_list_head *head,
-               bt_uuid_t uuid, int log_level, bt_self_component *self_comp)
-{
-       int i = 0;
-       int ret = 0;
-       struct ctf_node *node;
-
-       bt_list_for_each_entry(node, head, siblings) {
-               int uexpr_type = node->u.unary_expression.type;
-               int uexpr_link = node->u.unary_expression.link;
-               const char *src_string;
-
-               if (node->type != NODE_UNARY_EXPRESSION ||
-                               uexpr_type != UNARY_STRING ||
-                               uexpr_link != UNARY_LINK_UNKNOWN ||
-                               i != 0) {
-                       ret = -EINVAL;
-                       goto end;
-               }
-
-               src_string = node->u.unary_expression.u.string;
-               ret = bt_uuid_from_str(src_string, uuid);
-               if (ret) {
-#ifdef BT_COMP_LOG_CUR_LVL
-                       BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, log_level,
-                               self_comp,
-                               "Cannot parse UUID: uuid=\"%s\"", src_string);
-#endif
-                       goto end;
-               }
-       }
-
-end:
-       return ret;
-}
-
-#endif /* _CTF_AST_H */
diff --git a/src/plugins/ctf/common/metadata/ast.hpp b/src/plugins/ctf/common/metadata/ast.hpp
new file mode 100644 (file)
index 0000000..490e4d2
--- /dev/null
@@ -0,0 +1,435 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2011-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ */
+
+#ifndef _CTF_AST_H
+#define _CTF_AST_H
+
+#include <stdint.h>
+#include <stdio.h>
+#include <glib.h>
+#include "common/list.h"
+#include <babeltrace2/babeltrace.h>
+#include "common/macros.h"
+#include "common/assert.h"
+
+#include "decoder.hpp"
+#include "ctf-meta.hpp"
+
+// the parameter name (of the reentrant 'yyparse' function)
+// data is a pointer to a 'SParserParam' structure
+//#define YYPARSE_PARAM        scanner
+
+struct ctf_node;
+struct ctf_parser;
+struct ctf_visitor_generate_ir;
+
+#define EINCOMPLETE    1000
+
+#define FOREACH_CTF_NODES(F) \
+       F(NODE_UNKNOWN) \
+       F(NODE_ROOT) \
+       F(NODE_ERROR) \
+       F(NODE_EVENT) \
+       F(NODE_STREAM) \
+       F(NODE_ENV) \
+       F(NODE_TRACE) \
+       F(NODE_CLOCK) \
+       F(NODE_CALLSITE) \
+       F(NODE_CTF_EXPRESSION) \
+       F(NODE_UNARY_EXPRESSION) \
+       F(NODE_TYPEDEF) \
+       F(NODE_TYPEALIAS_TARGET) \
+       F(NODE_TYPEALIAS_ALIAS) \
+       F(NODE_TYPEALIAS) \
+       F(NODE_TYPE_SPECIFIER) \
+       F(NODE_TYPE_SPECIFIER_LIST) \
+       F(NODE_POINTER) \
+       F(NODE_TYPE_DECLARATOR) \
+       F(NODE_FLOATING_POINT) \
+       F(NODE_INTEGER) \
+       F(NODE_STRING) \
+       F(NODE_ENUMERATOR) \
+       F(NODE_ENUM) \
+       F(NODE_STRUCT_OR_VARIANT_DECLARATION) \
+       F(NODE_VARIANT) \
+       F(NODE_STRUCT)
+
+enum node_type {
+#define ENTRY(S)       S,
+       FOREACH_CTF_NODES(ENTRY)
+#undef ENTRY
+};
+
+enum ctf_unary {
+       UNARY_UNKNOWN = 0,
+       UNARY_STRING,
+       UNARY_SIGNED_CONSTANT,
+       UNARY_UNSIGNED_CONSTANT,
+       UNARY_SBRAC,
+};
+
+enum ctf_unary_link {
+       UNARY_LINK_UNKNOWN = 0,
+       UNARY_DOTLINK,
+       UNARY_ARROWLINK,
+       UNARY_DOTDOTDOT,
+};
+
+enum ctf_typedec {
+       TYPEDEC_UNKNOWN = 0,
+       TYPEDEC_ID,     /* identifier */
+       TYPEDEC_NESTED, /* (), array or sequence */
+};
+
+enum ctf_typespec {
+       TYPESPEC_UNKNOWN = 0,
+       TYPESPEC_VOID,
+       TYPESPEC_CHAR,
+       TYPESPEC_SHORT,
+       TYPESPEC_INT,
+       TYPESPEC_LONG,
+       TYPESPEC_FLOAT,
+       TYPESPEC_DOUBLE,
+       TYPESPEC_SIGNED,
+       TYPESPEC_UNSIGNED,
+       TYPESPEC_BOOL,
+       TYPESPEC_COMPLEX,
+       TYPESPEC_IMAGINARY,
+       TYPESPEC_CONST,
+       TYPESPEC_ID_TYPE,
+       TYPESPEC_FLOATING_POINT,
+       TYPESPEC_INTEGER,
+       TYPESPEC_STRING,
+       TYPESPEC_STRUCT,
+       TYPESPEC_VARIANT,
+       TYPESPEC_ENUM,
+};
+
+struct ctf_node {
+       /*
+        * Parent node is only set on demand by specific visitor.
+        */
+       struct ctf_node *parent;
+       struct bt_list_head siblings;
+       struct bt_list_head tmp_head;
+       unsigned int lineno;
+       /*
+        * We mark nodes visited in the generate-ir phase (last
+        * phase). We only mark the 1-depth level nodes as visited
+        * (never the root node, and not their sub-nodes). This allows
+        * skipping already visited nodes when doing incremental
+        * metadata append.
+        */
+       int visited;
+
+       enum node_type type;
+       union {
+               struct {
+               } unknown;
+               struct {
+                       /*
+                        * Children nodes are ctf_expression, field_class_def,
+                        * field_class_alias and field_class_specifier_list.
+                        */
+                       struct bt_list_head declaration_list;
+                       struct bt_list_head trace;
+                       struct bt_list_head env;
+                       struct bt_list_head stream;
+                       struct bt_list_head event;
+                       struct bt_list_head clock;
+                       struct bt_list_head callsite;
+               } root;
+               struct {
+                       /*
+                        * Children nodes are ctf_expression, field_class_def,
+                        * field_class_alias and field_class_specifier_list.
+                        */
+                       struct bt_list_head declaration_list;
+               } event;
+               struct {
+                       /*
+                        * Children nodes are ctf_expression, field_class_def,
+                        * field_class_alias and field_class_specifier_list.
+                        */
+                       struct bt_list_head declaration_list;
+               } stream;
+               struct {
+                       /*
+                        * Children nodes are ctf_expression, field_class_def,
+                        * field_class_alias and field_class_specifier_list.
+                        */
+                       struct bt_list_head declaration_list;
+               } env;
+               struct {
+                       /*
+                        * Children nodes are ctf_expression, field_class_def,
+                        * field_class_alias and field_class_specifier_list.
+                        */
+                       struct bt_list_head declaration_list;
+               } trace;
+               struct {
+                       /*
+                        * Children nodes are ctf_expression, field_class_def,
+                        * field_class_alias and field_class_specifier_list.
+                        */
+                       struct bt_list_head declaration_list;
+               } clock;
+               struct {
+                       /*
+                        * Children nodes are ctf_expression, field_class_def,
+                        * field_class_alias and field_class_specifier_list.
+                        */
+                       struct bt_list_head declaration_list;
+               } callsite;
+               struct {
+                       struct bt_list_head left;       /* Should be string */
+                       struct bt_list_head right;      /* Unary exp. or type */
+               } ctf_expression;
+               struct {
+                       ctf_unary type;
+                       union {
+                               /*
+                                * string for identifier, id_type, keywords,
+                                * string literals and character constants.
+                                */
+                               char *string;
+                               int64_t signed_constant;
+                               uint64_t unsigned_constant;
+                               struct ctf_node *sbrac_exp;
+                       } u;
+                       ctf_unary_link link;
+               } unary_expression;
+               struct {
+                       struct ctf_node *field_class_specifier_list;
+                       struct bt_list_head field_class_declarators;
+               } field_class_def;
+               /* new type is "alias", existing type "target" */
+               struct {
+                       struct ctf_node *field_class_specifier_list;
+                       struct bt_list_head field_class_declarators;
+               } field_class_alias_target;
+               struct {
+                       struct ctf_node *field_class_specifier_list;
+                       struct bt_list_head field_class_declarators;
+               } field_class_alias_name;
+               struct {
+                       struct ctf_node *target;
+                       struct ctf_node *alias;
+               } field_class_alias;
+               struct {
+                       ctf_typespec type;
+                       /* For struct, variant and enum */
+                       struct ctf_node *node;
+                       const char *id_type;
+               } field_class_specifier;
+               struct {
+                       /* list of field_class_specifier */
+                       struct bt_list_head head;
+               } field_class_specifier_list;
+               struct {
+                       unsigned int const_qualifier;
+               } pointer;
+               struct {
+                       struct bt_list_head pointers;
+                       ctf_typedec type;
+                       union {
+                               char *id;
+                               struct {
+                                       /* typedec has no pointer list */
+                                       struct ctf_node *field_class_declarator;
+                                       /*
+                                        * unary expression (value) or
+                                        * field_class_specifier_list.
+                                        */
+                                       struct bt_list_head length;
+                                       /* for abstract type declarator */
+                                       unsigned int abstract_array;
+                               } nested;
+                       } u;
+                       struct ctf_node *bitfield_len;
+               } field_class_declarator;
+               struct {
+                       /* Children nodes are ctf_expression. */
+                       struct bt_list_head expressions;
+               } floating_point;
+               struct {
+                       /* Children nodes are ctf_expression. */
+                       struct bt_list_head expressions;
+               } integer;
+               struct {
+                       /* Children nodes are ctf_expression. */
+                       struct bt_list_head expressions;
+               } string;
+               struct {
+                       char *id;
+                       /*
+                        * Range list or single value node. Contains unary
+                        * expressions.
+                        */
+                       struct bt_list_head values;
+               } enumerator;
+               struct {
+                       char *enum_id;
+                       /*
+                        * Either NULL, or points to unary expression or
+                        * field_class_specifier_list.
+                        */
+                       struct ctf_node *container_field_class;
+                       struct bt_list_head enumerator_list;
+                       int has_body;
+               } _enum;
+               struct {
+                       struct ctf_node *field_class_specifier_list;
+                       struct bt_list_head field_class_declarators;
+               } struct_or_variant_declaration;
+               struct {
+                       char *name;
+                       char *choice;
+                       /*
+                        * list of field_class_def, field_class_alias and
+                        * declarations
+                        */
+                       struct bt_list_head declaration_list;
+                       int has_body;
+               } variant;
+               struct {
+                       char *name;
+                       /*
+                        * list of field_class_def, field_class_alias and
+                        * declarations
+                        */
+                       struct bt_list_head declaration_list;
+                       int has_body;
+                       struct bt_list_head min_align;  /* align() attribute */
+               } _struct;
+       } u;
+};
+
+struct ctf_ast {
+       struct ctf_node root;
+};
+
+const char *node_type(struct ctf_node *node);
+
+struct meta_log_config;
+
+BT_HIDDEN
+struct ctf_visitor_generate_ir *ctf_visitor_generate_ir_create(
+               const struct ctf_metadata_decoder_config *config);
+
+void ctf_visitor_generate_ir_destroy(struct ctf_visitor_generate_ir *visitor);
+
+BT_HIDDEN
+bt_trace_class *ctf_visitor_generate_ir_get_ir_trace_class(
+               struct ctf_visitor_generate_ir *visitor);
+
+BT_HIDDEN
+struct ctf_trace_class *ctf_visitor_generate_ir_borrow_ctf_trace_class(
+               struct ctf_visitor_generate_ir *visitor);
+
+BT_HIDDEN
+int ctf_visitor_generate_ir_visit_node(struct ctf_visitor_generate_ir *visitor,
+               struct ctf_node *node);
+
+BT_HIDDEN
+int ctf_visitor_semantic_check(int depth, struct ctf_node *node,
+               struct meta_log_config *log_cfg);
+
+BT_HIDDEN
+int ctf_visitor_parent_links(int depth, struct ctf_node *node,
+               struct meta_log_config *log_cfg);
+
+static inline
+char *ctf_ast_concatenate_unary_strings(struct bt_list_head *head)
+{
+       int i = 0;
+       GString *str;
+       struct ctf_node *node;
+
+       str = g_string_new(NULL);
+       BT_ASSERT(str);
+
+       bt_list_for_each_entry(node, head, siblings) {
+               char *src_string;
+
+               if (
+                       node->type != NODE_UNARY_EXPRESSION ||
+                       node->u.unary_expression.type != UNARY_STRING ||
+                       !(
+                               (
+                                       node->u.unary_expression.link !=
+                                       UNARY_LINK_UNKNOWN
+                               ) ^ (i == 0)
+                       )
+               ) {
+                       goto error;
+               }
+
+               switch (node->u.unary_expression.link) {
+               case UNARY_DOTLINK:
+                       g_string_append(str, ".");
+                       break;
+               case UNARY_ARROWLINK:
+                       g_string_append(str, "->");
+                       break;
+               case UNARY_DOTDOTDOT:
+                       g_string_append(str, "...");
+                       break;
+               default:
+                       break;
+               }
+
+               src_string = node->u.unary_expression.u.string;
+               g_string_append(str, src_string);
+               i++;
+       }
+
+       /* Destroys the container, returns the underlying string */
+       return g_string_free(str, FALSE);
+
+error:
+       /* This always returns NULL */
+       return g_string_free(str, TRUE);
+}
+
+static inline
+int ctf_ast_get_unary_uuid(struct bt_list_head *head,
+               bt_uuid_t uuid, int log_level, bt_self_component *self_comp)
+{
+       int i = 0;
+       int ret = 0;
+       struct ctf_node *node;
+
+       bt_list_for_each_entry(node, head, siblings) {
+               int uexpr_type = node->u.unary_expression.type;
+               int uexpr_link = node->u.unary_expression.link;
+               const char *src_string;
+
+               if (node->type != NODE_UNARY_EXPRESSION ||
+                               uexpr_type != UNARY_STRING ||
+                               uexpr_link != UNARY_LINK_UNKNOWN ||
+                               i != 0) {
+                       ret = -EINVAL;
+                       goto end;
+               }
+
+               src_string = node->u.unary_expression.u.string;
+               ret = bt_uuid_from_str(src_string, uuid);
+               if (ret) {
+#ifdef BT_COMP_LOG_CUR_LVL
+                       BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, log_level,
+                               self_comp,
+                               "Cannot parse UUID: uuid=\"%s\"", src_string);
+#endif
+                       goto end;
+               }
+       }
+
+end:
+       return ret;
+}
+
+#endif /* _CTF_AST_H */
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-configure-ir-trace.c b/src/plugins/ctf/common/metadata/ctf-meta-configure-ir-trace.c
deleted file mode 100644 (file)
index 2610575..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
- */
-
-#include <babeltrace2/babeltrace.h>
-
-#include "ctf-meta-configure-ir-trace.h"
-
-BT_HIDDEN
-int ctf_trace_class_configure_ir_trace(struct ctf_trace_class *tc,
-               bt_trace *ir_trace)
-{
-       int ret = 0;
-       uint64_t i;
-
-       BT_ASSERT(tc);
-       BT_ASSERT(ir_trace);
-
-       if (tc->is_uuid_set) {
-               bt_trace_set_uuid(ir_trace, tc->uuid);
-       }
-
-       for (i = 0; i < tc->env_entries->len; i++) {
-               struct ctf_trace_class_env_entry *env_entry =
-                       ctf_trace_class_borrow_env_entry_by_index(tc, i);
-
-               switch (env_entry->type) {
-               case CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT:
-                       ret = bt_trace_set_environment_entry_integer(
-                               ir_trace, env_entry->name->str,
-                               env_entry->value.i);
-                       break;
-               case CTF_TRACE_CLASS_ENV_ENTRY_TYPE_STR:
-                       ret = bt_trace_set_environment_entry_string(
-                               ir_trace, env_entry->name->str,
-                               env_entry->value.str->str);
-                       break;
-               default:
-                       bt_common_abort();
-               }
-
-               if (ret) {
-                       goto end;
-               }
-       }
-
-end:
-       return ret;
-}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-configure-ir-trace.cpp b/src/plugins/ctf/common/metadata/ctf-meta-configure-ir-trace.cpp
new file mode 100644 (file)
index 0000000..e802316
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include <babeltrace2/babeltrace.h>
+
+#include "ctf-meta-configure-ir-trace.hpp"
+
+BT_HIDDEN
+int ctf_trace_class_configure_ir_trace(struct ctf_trace_class *tc,
+               bt_trace *ir_trace)
+{
+       int ret = 0;
+       uint64_t i;
+
+       BT_ASSERT(tc);
+       BT_ASSERT(ir_trace);
+
+       if (tc->is_uuid_set) {
+               bt_trace_set_uuid(ir_trace, tc->uuid);
+       }
+
+       for (i = 0; i < tc->env_entries->len; i++) {
+               struct ctf_trace_class_env_entry *env_entry =
+                       ctf_trace_class_borrow_env_entry_by_index(tc, i);
+
+               switch (env_entry->type) {
+               case CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT:
+                       ret = bt_trace_set_environment_entry_integer(
+                               ir_trace, env_entry->name->str,
+                               env_entry->value.i);
+                       break;
+               case CTF_TRACE_CLASS_ENV_ENTRY_TYPE_STR:
+                       ret = bt_trace_set_environment_entry_string(
+                               ir_trace, env_entry->name->str,
+                               env_entry->value.str->str);
+                       break;
+               default:
+                       bt_common_abort();
+               }
+
+               if (ret) {
+                       goto end;
+               }
+       }
+
+end:
+       return ret;
+}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-configure-ir-trace.h b/src/plugins/ctf/common/metadata/ctf-meta-configure-ir-trace.h
deleted file mode 100644 (file)
index 775b9a0..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
- */
-
-#ifndef _CTF_META_CONFIGURE_IR_TRACE_H
-#define _CTF_META_CONFIGURE_IR_TRACE_H
-
-#include "common/macros.h"
-
-#include "ctf-meta.h"
-
-BT_HIDDEN
-int ctf_trace_class_configure_ir_trace(struct ctf_trace_class *tc,
-               bt_trace *ir_trace);
-
-#endif /* _CTF_META_CONFIGURE_IR_TRACE_H */
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-configure-ir-trace.hpp b/src/plugins/ctf/common/metadata/ctf-meta-configure-ir-trace.hpp
new file mode 100644 (file)
index 0000000..09d5ade
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#ifndef _CTF_META_CONFIGURE_IR_TRACE_H
+#define _CTF_META_CONFIGURE_IR_TRACE_H
+
+#include "common/macros.h"
+
+#include "ctf-meta.hpp"
+
+BT_HIDDEN
+int ctf_trace_class_configure_ir_trace(struct ctf_trace_class *tc,
+               bt_trace *ir_trace);
+
+#endif /* _CTF_META_CONFIGURE_IR_TRACE_H */
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-resolve.c b/src/plugins/ctf/common/metadata/ctf-meta-resolve.c
deleted file mode 100644 (file)
index a8019da..0000000
+++ /dev/null
@@ -1,1319 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2016-2018 Philippe Proulx <pproulx@efficios.com>
- * Copyright 2015 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- */
-
-#define BT_COMP_LOG_SELF_COMP (ctx->self_comp)
-#define BT_COMP_LOG_SELF_COMP_CLASS (ctx->self_comp_class)
-#define BT_LOG_OUTPUT_LEVEL (ctx->log_level)
-#define BT_LOG_TAG "PLUGIN/CTF/META/RESOLVE"
-#include "logging/comp-logging.h"
-
-#include <babeltrace2/babeltrace.h>
-#include "common/macros.h"
-#include "common/assert.h"
-#include "common/common.h"
-#include <glib.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <string.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <glib.h>
-
-#include "ctf-meta-visitors.h"
-#include "logging.h"
-
-typedef GPtrArray field_class_stack;
-
-/*
- * A stack frame.
- *
- * `fc` contains a compound field class (structure, variant, array,
- * or sequence) and `index` indicates the index of the field class in
- * the upper frame (-1 for array and sequence field classes). `name`
- * indicates the name of the field class in the upper frame (empty
- * string for array and sequence field classes).
- */
-struct field_class_stack_frame {
-       struct ctf_field_class *fc;
-       int64_t index;
-};
-
-/*
- * The current context of the resolving engine.
- */
-struct resolve_context {
-       bt_logging_level log_level;
-
-       /* Weak, exactly one of these must be set */
-       bt_self_component *self_comp;
-       bt_self_component_class *self_comp_class;
-
-       struct ctf_trace_class *tc;
-       struct ctf_stream_class *sc;
-       struct ctf_event_class *ec;
-
-       struct {
-               struct ctf_field_class *packet_header;
-               struct ctf_field_class *packet_context;
-               struct ctf_field_class *event_header;
-               struct ctf_field_class *event_common_context;
-               struct ctf_field_class *event_spec_context;
-               struct ctf_field_class *event_payload;
-       } scopes;
-
-       /* Root scope being visited */
-       enum ctf_scope root_scope;
-       field_class_stack *field_class_stack;
-       struct ctf_field_class *cur_fc;
-};
-
-/* TSDL dynamic scope prefixes as defined in CTF Section 7.3.2 */
-static const char * const absolute_path_prefixes[] = {
-       [CTF_SCOPE_PACKET_HEADER]               = "trace.packet.header.",
-       [CTF_SCOPE_PACKET_CONTEXT]              = "stream.packet.context.",
-       [CTF_SCOPE_EVENT_HEADER]                = "stream.event.header.",
-       [CTF_SCOPE_EVENT_COMMON_CONTEXT]        = "stream.event.context.",
-       [CTF_SCOPE_EVENT_SPECIFIC_CONTEXT]      = "event.context.",
-       [CTF_SCOPE_EVENT_PAYLOAD]               = "event.fields.",
-};
-
-/* Number of path tokens used for the absolute prefixes */
-static const uint64_t absolute_path_prefix_ptoken_counts[] = {
-       [CTF_SCOPE_PACKET_HEADER]               = 3,
-       [CTF_SCOPE_PACKET_CONTEXT]              = 3,
-       [CTF_SCOPE_EVENT_HEADER]                = 3,
-       [CTF_SCOPE_EVENT_COMMON_CONTEXT]        = 3,
-       [CTF_SCOPE_EVENT_SPECIFIC_CONTEXT]      = 2,
-       [CTF_SCOPE_EVENT_PAYLOAD]               = 2,
-};
-
-static
-void destroy_field_class_stack_frame(struct field_class_stack_frame *frame)
-{
-       if (!frame) {
-               return;
-       }
-
-       g_free(frame);
-}
-
-/*
- * Creates a class stack.
- */
-static
-field_class_stack *field_class_stack_create(void)
-{
-       return g_ptr_array_new_with_free_func(
-               (GDestroyNotify) destroy_field_class_stack_frame);
-}
-
-/*
- * Destroys a class stack.
- */
-static
-void field_class_stack_destroy(field_class_stack *stack)
-{
-       if (stack) {
-               g_ptr_array_free(stack, TRUE);
-       }
-}
-
-/*
- * Pushes a field class onto a class stack.
- */
-static
-int field_class_stack_push(field_class_stack *stack, struct ctf_field_class *fc,
-               struct resolve_context *ctx)
-{
-       int ret = 0;
-       struct field_class_stack_frame *frame = NULL;
-
-       if (!stack || !fc) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid parameter: stack or field class is `NULL`.");
-               ret = -1;
-               goto end;
-       }
-
-       frame = g_new0(struct field_class_stack_frame, 1);
-       if (!frame) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Failed to allocate one field class stack frame.");
-               ret = -1;
-               goto end;
-       }
-
-       BT_COMP_LOGD("Pushing field class on context's stack: "
-               "fc-addr=%p, stack-size-before=%u", fc, stack->len);
-       frame->fc = fc;
-       g_ptr_array_add(stack, frame);
-
-end:
-       return ret;
-}
-
-/*
- * Checks whether or not `stack` is empty.
- */
-static
-bool field_class_stack_empty(field_class_stack *stack)
-{
-       return stack->len == 0;
-}
-
-/*
- * Returns the number of frames in `stack`.
- */
-static
-size_t field_class_stack_size(field_class_stack *stack)
-{
-       return stack->len;
-}
-
-/*
- * Returns the top frame of `stack`.
- */
-static
-struct field_class_stack_frame *field_class_stack_peek(field_class_stack *stack)
-{
-       BT_ASSERT(stack);
-       BT_ASSERT(!field_class_stack_empty(stack));
-
-       return g_ptr_array_index(stack, stack->len - 1);
-}
-
-/*
- * Returns the frame at index `index` in `stack`.
- */
-static
-struct field_class_stack_frame *field_class_stack_at(field_class_stack *stack,
-               size_t index)
-{
-       BT_ASSERT(stack);
-       BT_ASSERT(index < stack->len);
-
-       return g_ptr_array_index(stack, index);
-}
-
-/*
- * Removes the top frame of `stack`.
- */
-static
-void field_class_stack_pop(field_class_stack *stack,
-               struct resolve_context *ctx)
-{
-       if (!field_class_stack_empty(stack)) {
-               /*
-                * This will call the frame's destructor and free it, as
-                * well as put its contained field class.
-                */
-               BT_COMP_LOGD("Popping context's stack: stack-size-before=%u",
-                       stack->len);
-               g_ptr_array_set_size(stack, stack->len - 1);
-       }
-}
-
-/*
- * Returns the scope field class of `scope` in the context `ctx`.
- */
-static
-struct ctf_field_class *borrow_class_from_ctx(struct resolve_context *ctx,
-               enum ctf_scope scope)
-{
-       switch (scope) {
-       case CTF_SCOPE_PACKET_HEADER:
-               return ctx->scopes.packet_header;
-       case CTF_SCOPE_PACKET_CONTEXT:
-               return ctx->scopes.packet_context;
-       case CTF_SCOPE_EVENT_HEADER:
-               return ctx->scopes.event_header;
-       case CTF_SCOPE_EVENT_COMMON_CONTEXT:
-               return ctx->scopes.event_common_context;
-       case CTF_SCOPE_EVENT_SPECIFIC_CONTEXT:
-               return ctx->scopes.event_spec_context;
-       case CTF_SCOPE_EVENT_PAYLOAD:
-               return ctx->scopes.event_payload;
-       default:
-               bt_common_abort();
-       }
-
-       return NULL;
-}
-
-/*
- * Returns the CTF scope from a path string. May return -1 if the path
- * is found to be relative.
- */
-static
-enum ctf_scope get_root_scope_from_absolute_pathstr(const char *pathstr,
-               struct resolve_context *ctx)
-{
-       enum ctf_scope scope;
-       enum ctf_scope ret = CTF_SCOPE_PACKET_UNKNOWN;
-       const size_t prefixes_count = sizeof(absolute_path_prefixes) /
-               sizeof(*absolute_path_prefixes);
-
-       for (scope = CTF_SCOPE_PACKET_HEADER; scope < CTF_SCOPE_PACKET_HEADER +
-                       prefixes_count; scope++) {
-               /*
-                * Check if path string starts with a known absolute
-                * path prefix.
-                *
-                * Refer to CTF 7.3.2 STATIC AND DYNAMIC SCOPES.
-                */
-               if (strncmp(pathstr, absolute_path_prefixes[scope],
-                               strlen(absolute_path_prefixes[scope]))) {
-                       /* Prefix does not match: try the next one */
-                       BT_COMP_LOGD("Prefix does not match: trying the next one: "
-                               "path=\"%s\", path-prefix=\"%s\", scope=%s",
-                               pathstr, absolute_path_prefixes[scope],
-                               ctf_scope_string(scope));
-                       continue;
-               }
-
-               /* Found it! */
-               ret = scope;
-               BT_COMP_LOGD("Found root scope from absolute path: "
-                       "path=\"%s\", scope=%s", pathstr,
-                       ctf_scope_string(scope));
-               goto end;
-       }
-
-end:
-       return ret;
-}
-
-/*
- * Destroys a path token.
- */
-static
-void ptokens_destroy_func(gpointer ptoken, gpointer data)
-{
-       g_string_free(ptoken, TRUE);
-}
-
-/*
- * Destroys a path token list.
- */
-static
-void ptokens_destroy(GList *ptokens)
-{
-       if (!ptokens) {
-               return;
-       }
-
-       g_list_foreach(ptokens, ptokens_destroy_func, NULL);
-       g_list_free(ptokens);
-}
-
-/*
- * Returns the string contained in a path token.
- */
-static
-const char *ptoken_get_string(GList *ptoken)
-{
-       GString *tokenstr = (GString *) ptoken->data;
-
-       return tokenstr->str;
-}
-
-/*
- * Converts a path string to a path token list, that is, splits the
- * individual words of a path string into a list of individual
- * strings.
- */
-static
-GList *pathstr_to_ptokens(const char *pathstr, struct resolve_context *ctx)
-{
-       const char *at = pathstr;
-       const char *last = at;
-       GList *ptokens = NULL;
-
-       for (;;) {
-               if (*at == '.' || *at == '\0') {
-                       GString *tokenstr;
-
-                       if (at == last) {
-                               /* Error: empty token */
-                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Empty path token: path=\"%s\", pos=%u",
-                                       pathstr, (unsigned int) (at - pathstr));
-                               goto error;
-                       }
-
-                       tokenstr = g_string_new(NULL);
-                       g_string_append_len(tokenstr, last, at - last);
-                       ptokens = g_list_append(ptokens, tokenstr);
-                       last = at + 1;
-               }
-
-               if (*at == '\0') {
-                       break;
-               }
-
-               at++;
-       }
-
-       return ptokens;
-
-error:
-       ptokens_destroy(ptokens);
-       return NULL;
-}
-
-/*
- * Converts a path token list to a field path object. The path token
- * list is relative from `fc`. The index of the source looking for its
- * target within `fc` is indicated by `src_index`. This can be
- * `INT64_MAX` if the source is contained in `fc`.
- *
- * `field_path` is an output parameter owned by the caller that must be
- * filled here.
- */
-static
-int ptokens_to_field_path(GList *ptokens, struct ctf_field_path *field_path,
-               struct ctf_field_class *fc, int64_t src_index,
-               struct resolve_context *ctx)
-{
-       int ret = 0;
-       GList *cur_ptoken = ptokens;
-       bool first_level_done = false;
-
-       /* Locate target */
-       while (cur_ptoken) {
-               int64_t child_index;
-               struct ctf_field_class *child_fc;
-               const char *ft_name = ptoken_get_string(cur_ptoken);
-
-               BT_COMP_LOGD("Current path token: token=\"%s\"", ft_name);
-
-               /* Find to which index corresponds the current path token */
-               if (fc->type == CTF_FIELD_CLASS_TYPE_ARRAY ||
-                               fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE) {
-                       child_index = -1;
-               } else {
-                       child_index =
-                               ctf_field_class_compound_get_field_class_index_from_orig_name(
-                                       fc, ft_name);
-                       if (child_index < 0) {
-                               /*
-                                * Error: field name does not exist or
-                                * wrong current class.
-                                */
-                               BT_COMP_LOGD("Cannot get index of field class: "
-                                       "field-name=\"%s\", "
-                                       "src-index=%" PRId64 ", "
-                                       "child-index=%" PRId64 ", "
-                                       "first-level-done=%d",
-                                       ft_name, src_index, child_index,
-                                       first_level_done);
-                               ret = -1;
-                               goto end;
-                       } else if (child_index > src_index &&
-                                       !first_level_done) {
-                               BT_COMP_LOGD("Child field class is located after source field class: "
-                                       "field-name=\"%s\", "
-                                       "src-index=%" PRId64 ", "
-                                       "child-index=%" PRId64 ", "
-                                       "first-level-done=%d",
-                                       ft_name, src_index, child_index,
-                                       first_level_done);
-                               ret = -1;
-                               goto end;
-                       }
-
-                       /* Next path token */
-                       cur_ptoken = g_list_next(cur_ptoken);
-                       first_level_done = true;
-               }
-
-               /* Create new field path entry */
-               ctf_field_path_append_index(field_path, child_index);
-
-               /* Get child field class */
-               child_fc = ctf_field_class_compound_borrow_field_class_by_index(
-                       fc, child_index);
-               BT_ASSERT(child_fc);
-
-               /* Move child class to current class */
-               fc = child_fc;
-       }
-
-end:
-       return ret;
-}
-
-/*
- * Converts a known absolute path token list to a field path object
- * within the resolving context `ctx`.
- *
- * `field_path` is an output parameter owned by the caller that must be
- * filled here.
- */
-static
-int absolute_ptokens_to_field_path(GList *ptokens,
-               struct ctf_field_path *field_path,
-               struct resolve_context *ctx)
-{
-       int ret = 0;
-       GList *cur_ptoken;
-       struct ctf_field_class *fc;
-
-       /*
-        * Make sure we're not referring to a scope within a translated
-        * object.
-        */
-       switch (field_path->root) {
-       case CTF_SCOPE_PACKET_HEADER:
-               if (ctx->tc->is_translated) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Trace class is already translated: "
-                               "root-scope=%s",
-                               ctf_scope_string(field_path->root));
-                       ret = -1;
-                       goto end;
-               }
-
-               break;
-       case CTF_SCOPE_PACKET_CONTEXT:
-       case CTF_SCOPE_EVENT_HEADER:
-       case CTF_SCOPE_EVENT_COMMON_CONTEXT:
-               if (!ctx->sc) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("No current stream class: "
-                               "root-scope=%s",
-                               ctf_scope_string(field_path->root));
-                       ret = -1;
-                       goto end;
-               }
-
-               if (ctx->sc->is_translated) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Stream class is already translated: "
-                               "root-scope=%s",
-                               ctf_scope_string(field_path->root));
-                       ret = -1;
-                       goto end;
-               }
-
-               break;
-       case CTF_SCOPE_EVENT_SPECIFIC_CONTEXT:
-       case CTF_SCOPE_EVENT_PAYLOAD:
-               if (!ctx->ec) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("No current event class: "
-                               "root-scope=%s",
-                               ctf_scope_string(field_path->root));
-                       ret = -1;
-                       goto end;
-               }
-
-               if (ctx->ec->is_translated) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Event class is already translated: "
-                               "root-scope=%s",
-                               ctf_scope_string(field_path->root));
-                       ret = -1;
-                       goto end;
-               }
-
-               break;
-
-       default:
-               bt_common_abort();
-       }
-
-       /* Skip absolute path tokens */
-       cur_ptoken = g_list_nth(ptokens,
-               absolute_path_prefix_ptoken_counts[field_path->root]);
-
-       /* Start with root class */
-       fc = borrow_class_from_ctx(ctx, field_path->root);
-       if (!fc) {
-               /* Error: root class is not available */
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Root field class is not available: "
-                       "root-scope=%s",
-                       ctf_scope_string(field_path->root));
-               ret = -1;
-               goto end;
-       }
-
-       /* Locate target */
-       ret = ptokens_to_field_path(cur_ptoken, field_path, fc, INT64_MAX, ctx);
-
-end:
-       return ret;
-}
-
-/*
- * Converts a known relative path token list to a field path object
- * within the resolving context `ctx`.
- *
- * `field_path` is an output parameter owned by the caller that must be
- * filled here.
- */
-static
-int relative_ptokens_to_field_path(GList *ptokens,
-               struct ctf_field_path *field_path, struct resolve_context *ctx)
-{
-       int ret = 0;
-       int64_t parent_pos_in_stack;
-       struct ctf_field_path tail_field_path;
-
-       ctf_field_path_init(&tail_field_path);
-       parent_pos_in_stack = field_class_stack_size(ctx->field_class_stack) - 1;
-
-       while (parent_pos_in_stack >= 0) {
-               struct ctf_field_class *parent_class =
-                       field_class_stack_at(ctx->field_class_stack,
-                               parent_pos_in_stack)->fc;
-               int64_t cur_index = field_class_stack_at(ctx->field_class_stack,
-                       parent_pos_in_stack)->index;
-
-               BT_COMP_LOGD("Locating target field class from current parent field class: "
-                       "parent-pos=%" PRId64 ", parent-fc-addr=%p, "
-                       "cur-index=%" PRId64,
-                       parent_pos_in_stack, parent_class, cur_index);
-
-               /* Locate target from current parent class */
-               ret = ptokens_to_field_path(ptokens, &tail_field_path,
-                       parent_class, cur_index, ctx);
-               if (ret) {
-                       /* Not found... yet */
-                       BT_COMP_LOGD_STR("Not found at this point.");
-                       ctf_field_path_clear(&tail_field_path);
-               } else {
-                       /* Found: stitch tail field path to head field path */
-                       uint64_t i = 0;
-                       size_t tail_field_path_len =
-                               tail_field_path.path->len;
-
-                       while (BT_TRUE) {
-                               struct ctf_field_class *cur_class =
-                                       field_class_stack_at(
-                                               ctx->field_class_stack, i)->fc;
-                               int64_t index = field_class_stack_at(
-                                       ctx->field_class_stack, i)->index;
-
-                               if (cur_class == parent_class) {
-                                       break;
-                               }
-
-                               ctf_field_path_append_index(field_path,
-                                       index);
-                               i++;
-                       }
-
-                       for (i = 0; i < tail_field_path_len; i++) {
-                               int64_t index =
-                                       ctf_field_path_borrow_index_by_index(
-                                               &tail_field_path, i);
-
-                               ctf_field_path_append_index(field_path,
-                                       (int64_t) index);
-                       }
-                       break;
-               }
-
-               parent_pos_in_stack--;
-       }
-
-       if (parent_pos_in_stack < 0) {
-               /* Not found */
-               ret = -1;
-       }
-
-       ctf_field_path_fini(&tail_field_path);
-       return ret;
-}
-
-/*
- * Converts a path string to a field path object within the resolving
- * context `ctx`.
- */
-static
-int pathstr_to_field_path(const char *pathstr,
-               struct ctf_field_path *field_path, struct resolve_context *ctx)
-{
-       int ret = 0;
-       enum ctf_scope root_scope;
-       GList *ptokens = NULL;
-
-       /* Convert path string to path tokens */
-       ptokens = pathstr_to_ptokens(pathstr, ctx);
-       if (!ptokens) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot convert path string to path tokens: "
-                       "path=\"%s\"", pathstr);
-               ret = -1;
-               goto end;
-       }
-
-       /* Absolute or relative path? */
-       root_scope = get_root_scope_from_absolute_pathstr(pathstr, ctx);
-
-       if (root_scope == CTF_SCOPE_PACKET_UNKNOWN) {
-               /* Relative path: start with current root scope */
-               field_path->root = ctx->root_scope;
-               BT_COMP_LOGD("Detected relative path: starting with current root scope: "
-                       "scope=%s", ctf_scope_string(field_path->root));
-               ret = relative_ptokens_to_field_path(ptokens, field_path, ctx);
-               if (ret) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot get relative field path of path string: "
-                               "path=\"%s\", start-scope=%s, end-scope=%s",
-                               pathstr, ctf_scope_string(ctx->root_scope),
-                               ctf_scope_string(field_path->root));
-                       goto end;
-               }
-       } else {
-               /* Absolute path: use found root scope */
-               field_path->root = root_scope;
-               BT_COMP_LOGD("Detected absolute path: using root scope: "
-                       "scope=%s", ctf_scope_string(field_path->root));
-               ret = absolute_ptokens_to_field_path(ptokens, field_path, ctx);
-               if (ret) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot get absolute field path of path string: "
-                               "path=\"%s\", root-scope=%s",
-                               pathstr, ctf_scope_string(root_scope));
-                       goto end;
-               }
-       }
-
-       if (BT_LOG_ON_TRACE && ret == 0) {
-               GString *field_path_pretty = ctf_field_path_string(field_path);
-               const char *field_path_pretty_str =
-                       field_path_pretty ? field_path_pretty->str : NULL;
-
-               BT_COMP_LOGD("Found field path: path=\"%s\", field-path=\"%s\"",
-                       pathstr, field_path_pretty_str);
-
-               if (field_path_pretty) {
-                       g_string_free(field_path_pretty, TRUE);
-               }
-       }
-
-end:
-       ptokens_destroy(ptokens);
-       return ret;
-}
-
-/*
- * Retrieves a field class by following the field path `field_path` in
- * the resolving context `ctx`.
- */
-static
-struct ctf_field_class *field_path_to_field_class(
-               struct ctf_field_path *field_path, struct resolve_context *ctx)
-{
-       uint64_t i;
-       struct ctf_field_class *fc;
-
-       /* Start with root class */
-       fc = borrow_class_from_ctx(ctx, field_path->root);
-       if (!fc) {
-               /* Error: root class is not available */
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Root field class is not available: root-scope=%s",
-                       ctf_scope_string(field_path->root));
-               goto end;
-       }
-
-       /* Locate target */
-       for (i = 0; i < field_path->path->len; i++) {
-               struct ctf_field_class *child_fc;
-               int64_t child_index =
-                       ctf_field_path_borrow_index_by_index(field_path, i);
-
-               /* Get child field class */
-               child_fc = ctf_field_class_compound_borrow_field_class_by_index(
-                       fc, child_index);
-               BT_ASSERT(child_fc);
-
-               /* Move child class to current class */
-               fc = child_fc;
-       }
-
-end:
-       return fc;
-}
-
-/*
- * Fills the equivalent field path object of the context class stack.
- */
-static
-void get_ctx_stack_field_path(struct resolve_context *ctx,
-               struct ctf_field_path *field_path)
-{
-       uint64_t i;
-
-       BT_ASSERT(field_path);
-       field_path->root = ctx->root_scope;
-       ctf_field_path_clear(field_path);
-
-       for (i = 0; i < field_class_stack_size(ctx->field_class_stack); i++) {
-               struct field_class_stack_frame *frame =
-                       field_class_stack_at(ctx->field_class_stack, i);
-
-               ctf_field_path_append_index(field_path, frame->index);
-       }
-}
-
-/*
- * Returns the index of the lowest common ancestor of two field path
- * objects having the same root scope.
- */
-static
-int64_t get_field_paths_lca_index(struct ctf_field_path *field_path1,
-               struct ctf_field_path *field_path2,
-               struct resolve_context *ctx)
-{
-       int64_t lca_index = 0;
-       uint64_t field_path1_len, field_path2_len;
-
-       if (BT_LOG_ON_TRACE) {
-               GString *field_path1_pretty =
-                       ctf_field_path_string(field_path1);
-               GString *field_path2_pretty =
-                       ctf_field_path_string(field_path2);
-               const char *field_path1_pretty_str =
-                       field_path1_pretty ? field_path1_pretty->str : NULL;
-               const char *field_path2_pretty_str =
-                       field_path2_pretty ? field_path2_pretty->str : NULL;
-
-               BT_COMP_LOGD("Finding lowest common ancestor (LCA) between two field paths: "
-                       "field-path-1=\"%s\", field-path-2=\"%s\"",
-                       field_path1_pretty_str, field_path2_pretty_str);
-
-               if (field_path1_pretty) {
-                       g_string_free(field_path1_pretty, TRUE);
-               }
-
-               if (field_path2_pretty) {
-                       g_string_free(field_path2_pretty, TRUE);
-               }
-       }
-
-       /*
-        * Start from both roots and find the first mismatch.
-        */
-       BT_ASSERT(field_path1->root == field_path2->root);
-       field_path1_len = field_path1->path->len;
-       field_path2_len = field_path2->path->len;
-
-       while (true) {
-               int64_t target_index, ctx_index;
-
-               if (lca_index == (int64_t) field_path2_len ||
-                               lca_index == (int64_t) field_path1_len) {
-                       /*
-                        * This means that both field paths never split.
-                        * This is invalid because the target cannot be
-                        * an ancestor of the source.
-                        */
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Source field class is an ancestor of target field class or vice versa: "
-                               "lca-index=%" PRId64 ", "
-                               "field-path-1-len=%" PRIu64 ", "
-                               "field-path-2-len=%" PRIu64,
-                               lca_index, field_path1_len, field_path2_len);
-                       lca_index = -1;
-                       break;
-               }
-
-               target_index = ctf_field_path_borrow_index_by_index(field_path1,
-                       lca_index);
-               ctx_index = ctf_field_path_borrow_index_by_index(field_path2,
-                       lca_index);
-
-               if (target_index != ctx_index) {
-                       /* LCA index is the previous */
-                       break;
-               }
-
-               lca_index++;
-       }
-
-       BT_COMP_LOGD("Found LCA: lca-index=%" PRId64, lca_index);
-       return lca_index;
-}
-
-/*
- * Validates a target field path.
- */
-static
-int validate_target_field_path(struct ctf_field_path *target_field_path,
-               struct ctf_field_class *target_fc,
-               struct resolve_context *ctx)
-{
-       int ret = 0;
-       struct ctf_field_path ctx_field_path;
-       uint64_t target_field_path_len = target_field_path->path->len;
-       int64_t lca_index;
-
-       /* Get context field path */
-       ctf_field_path_init(&ctx_field_path);
-       get_ctx_stack_field_path(ctx, &ctx_field_path);
-
-       /*
-        * Make sure the target is not a root.
-        */
-       if (target_field_path_len == 0) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Target field path's length is 0 (targeting the root).");
-               ret = -1;
-               goto end;
-       }
-
-       /*
-        * Make sure the root of the target field path is not located
-        * after the context field path's root.
-        */
-       if (target_field_path->root > ctx_field_path.root) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Target field class is located after source field class: "
-                       "target-root=%s, source-root=%s",
-                       ctf_scope_string(target_field_path->root),
-                       ctf_scope_string(ctx_field_path.root));
-               ret = -1;
-               goto end;
-       }
-
-       if (target_field_path->root == ctx_field_path.root) {
-               int64_t target_index, ctx_index;
-
-               /*
-                * Find the index of the lowest common ancestor of both field
-                * paths.
-                */
-               lca_index = get_field_paths_lca_index(target_field_path,
-                       &ctx_field_path, ctx);
-               if (lca_index < 0) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot get least common ancestor.");
-                       ret = -1;
-                       goto end;
-               }
-
-               /*
-                * Make sure the target field path is located before the
-                * context field path.
-                */
-               target_index = ctf_field_path_borrow_index_by_index(
-                       target_field_path, (uint64_t) lca_index);
-               ctx_index = ctf_field_path_borrow_index_by_index(
-                       &ctx_field_path, (uint64_t) lca_index);
-
-               if (target_index >= ctx_index) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Target field class's index is greater than or equal to source field class's index in LCA: "
-                               "lca-index=%" PRId64 ", "
-                               "target-index=%" PRId64 ", "
-                               "source-index=%" PRId64,
-                               lca_index, target_index, ctx_index);
-                       ret = -1;
-                       goto end;
-               }
-       }
-
-       /*
-        * Make sure the target class has the right class and properties.
-        */
-       switch (ctx->cur_fc->type) {
-       case CTF_FIELD_CLASS_TYPE_VARIANT:
-               if (target_fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Variant field class's tag field class is not an enumeration field class: "
-                               "tag-fc-addr=%p, tag-fc-id=%d",
-                               target_fc, target_fc->type);
-                       ret = -1;
-                       goto end;
-               }
-               break;
-       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-       {
-               struct ctf_field_class_int *int_fc = (void *) target_fc;
-
-               if (target_fc->type != CTF_FIELD_CLASS_TYPE_INT &&
-                               target_fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Sequence field class's length field class is not an unsigned integer field class: "
-                               "length-fc-addr=%p, length-fc-id=%d",
-                               target_fc, target_fc->type);
-                       ret = -1;
-                       goto end;
-               }
-
-               if (int_fc->is_signed) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Sequence field class's length field class is not an unsigned integer field class: "
-                               "length-fc-addr=%p, length-fc-id=%d",
-                               target_fc, target_fc->type);
-                       ret = -1;
-                       goto end;
-               }
-               break;
-       }
-       default:
-               bt_common_abort();
-       }
-
-end:
-       ctf_field_path_fini(&ctx_field_path);
-       return ret;
-}
-
-/*
- * Resolves a variant or sequence field class `fc`.
- */
-static
-int resolve_sequence_or_variant_field_class(struct ctf_field_class *fc,
-               struct resolve_context *ctx)
-{
-       int ret = 0;
-       const char *pathstr;
-       struct ctf_field_path target_field_path;
-       struct ctf_field_class *target_fc = NULL;
-       GString *target_field_path_pretty = NULL;
-       const char *target_field_path_pretty_str;
-
-       ctf_field_path_init(&target_field_path);
-
-       /* Get path string */
-       switch (fc->type) {
-       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-       {
-               struct ctf_field_class_sequence *seq_fc = (void *) fc;
-               pathstr = seq_fc->length_ref->str;
-               break;
-       }
-       case CTF_FIELD_CLASS_TYPE_VARIANT:
-       {
-               struct ctf_field_class_variant *var_fc = (void *) fc;
-               pathstr = var_fc->tag_ref->str;
-               break;
-       }
-       default:
-               bt_common_abort();
-       }
-
-       if (!pathstr) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot get path string.");
-               ret = -1;
-               goto end;
-       }
-
-       /* Get target field path out of path string */
-       ret = pathstr_to_field_path(pathstr, &target_field_path, ctx);
-       if (ret) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot get target field path for path string: "
-                       "path=\"%s\"", pathstr);
-               goto end;
-       }
-
-       target_field_path_pretty = ctf_field_path_string(
-               &target_field_path);
-       target_field_path_pretty_str =
-               target_field_path_pretty ? target_field_path_pretty->str : NULL;
-
-       /* Get target field class */
-       target_fc = field_path_to_field_class(&target_field_path, ctx);
-       if (!target_fc) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot get target field class for path string: "
-                       "path=\"%s\", target-field-path=\"%s\"",
-                       pathstr, target_field_path_pretty_str);
-               ret = -1;
-               goto end;
-       }
-
-       ret = validate_target_field_path(&target_field_path,
-               target_fc, ctx);
-       if (ret) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid target field path for path string: "
-                       "path=\"%s\", target-field-path=\"%s\"",
-                       pathstr, target_field_path_pretty_str);
-               goto end;
-       }
-
-       /* Set target field path and target field class */
-       switch (fc->type) {
-       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-       {
-               struct ctf_field_class_sequence *seq_fc = (void *) fc;
-
-               ctf_field_path_copy_content(&seq_fc->length_path,
-                       &target_field_path);
-               seq_fc->length_fc = (void *) target_fc;
-               break;
-       }
-       case CTF_FIELD_CLASS_TYPE_VARIANT:
-       {
-               struct ctf_field_class_variant *var_fc = (void *) fc;
-
-               ctf_field_path_copy_content(&var_fc->tag_path,
-                       &target_field_path);
-               ctf_field_class_variant_set_tag_field_class(var_fc,
-                       (void *) target_fc);
-               break;
-       }
-       default:
-               bt_common_abort();
-       }
-
-end:
-       if (target_field_path_pretty) {
-               g_string_free(target_field_path_pretty, TRUE);
-       }
-
-       ctf_field_path_fini(&target_field_path);
-       return ret;
-}
-
-/*
- * Resolves a field class `fc`.
- */
-static
-int resolve_field_class(struct ctf_field_class *fc, struct resolve_context *ctx)
-{
-       int ret = 0;
-
-       if (!fc) {
-               /* Field class is not available; still valid */
-               goto end;
-       }
-
-       ctx->cur_fc = fc;
-
-       /* Resolve sequence/variant field class */
-       switch (fc->type) {
-       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-       case CTF_FIELD_CLASS_TYPE_VARIANT:
-               ret = resolve_sequence_or_variant_field_class(fc, ctx);
-               if (ret) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot resolve sequence field class's length or variant field class's tag: "
-                               "ret=%d, fc-addr=%p", ret, fc);
-                       goto end;
-               }
-
-               break;
-       default:
-               break;
-       }
-
-       /* Recurse into compound classes */
-       switch (fc->type) {
-       case CTF_FIELD_CLASS_TYPE_STRUCT:
-       case CTF_FIELD_CLASS_TYPE_VARIANT:
-       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-       case CTF_FIELD_CLASS_TYPE_ARRAY:
-       {
-               uint64_t i;
-               uint64_t field_count =
-                       ctf_field_class_compound_get_field_class_count(fc);
-
-               ret = field_class_stack_push(ctx->field_class_stack, fc, ctx);
-               if (ret) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot push field class on context's stack: "
-                               "fc-addr=%p", fc);
-                       goto end;
-               }
-
-               for (i = 0; i < field_count; i++) {
-                       struct ctf_field_class *child_fc =
-                               ctf_field_class_compound_borrow_field_class_by_index(
-                                       fc, i);
-
-                       BT_ASSERT(child_fc);
-
-                       if (fc->type == CTF_FIELD_CLASS_TYPE_ARRAY||
-                                       fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE) {
-                               field_class_stack_peek(
-                                       ctx->field_class_stack)->index = -1;
-                       } else {
-                               field_class_stack_peek(
-                                       ctx->field_class_stack)->index =
-                                               (int64_t) i;
-                       }
-
-                       BT_COMP_LOGD("Resolving field class's child field class: "
-                               "parent-fc-addr=%p, child-fc-addr=%p, "
-                               "index=%" PRIu64 ", count=%" PRIu64,
-                               fc, child_fc, i, field_count);
-                       ret = resolve_field_class(child_fc, ctx);
-                       if (ret) {
-                               goto end;
-                       }
-               }
-
-               field_class_stack_pop(ctx->field_class_stack, ctx);
-               break;
-       }
-       default:
-               break;
-       }
-
-end:
-       return ret;
-}
-
-/*
- * Resolves the root field class corresponding to the scope `root_scope`.
- */
-static
-int resolve_root_class(enum ctf_scope root_scope, struct resolve_context *ctx)
-{
-       int ret;
-
-       BT_ASSERT(field_class_stack_size(ctx->field_class_stack) == 0);
-       ctx->root_scope = root_scope;
-       ret = resolve_field_class(borrow_class_from_ctx(ctx, root_scope), ctx);
-       ctx->root_scope = -1;
-       return ret;
-}
-
-static
-int resolve_event_class_field_classes(struct resolve_context *ctx,
-               struct ctf_event_class *ec)
-{
-       int ret = 0;
-
-       BT_ASSERT(!ctx->scopes.event_spec_context);
-       BT_ASSERT(!ctx->scopes.event_payload);
-
-       if (ec->is_translated) {
-               goto end;
-       }
-
-       ctx->ec = ec;
-       ctx->scopes.event_spec_context = ec->spec_context_fc;
-       ret = resolve_root_class(CTF_SCOPE_EVENT_COMMON_CONTEXT, ctx);
-       if (ret) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot resolve event specific context field class: "
-                       "ret=%d", ret);
-               goto end;
-       }
-
-       ctx->scopes.event_payload = ec->payload_fc;
-       ret = resolve_root_class(CTF_SCOPE_EVENT_PAYLOAD, ctx);
-       if (ret) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot resolve event payload field class: "
-                       "ret=%d", ret);
-               goto end;
-       }
-
-end:
-       ctx->scopes.event_spec_context = NULL;
-       ctx->scopes.event_payload = NULL;
-       ctx->ec = NULL;
-       return ret;
-}
-
-static
-int resolve_stream_class_field_classes(struct resolve_context *ctx,
-               struct ctf_stream_class *sc)
-{
-       int ret = 0;
-       uint64_t i;
-
-       BT_ASSERT(!ctx->scopes.packet_context);
-       BT_ASSERT(!ctx->scopes.event_header);
-       BT_ASSERT(!ctx->scopes.event_common_context);
-       ctx->sc = sc;
-
-       if (!sc->is_translated) {
-               ctx->scopes.packet_context = sc->packet_context_fc;
-               ret = resolve_root_class(CTF_SCOPE_PACKET_CONTEXT, ctx);
-               if (ret) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot resolve packet context field class: "
-                               "ret=%d", ret);
-                       goto end;
-               }
-
-               ctx->scopes.event_header = sc->event_header_fc;
-               ret = resolve_root_class(CTF_SCOPE_EVENT_HEADER, ctx);
-               if (ret) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot resolve event header field class: "
-                               "ret=%d", ret);
-                       goto end;
-               }
-
-               ctx->scopes.event_common_context = sc->event_common_context_fc;
-               ret = resolve_root_class(CTF_SCOPE_EVENT_COMMON_CONTEXT, ctx);
-               if (ret) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot resolve event common context field class: "
-                               "ret=%d", ret);
-                       goto end;
-               }
-       }
-
-       ctx->scopes.packet_context = sc->packet_context_fc;
-       ctx->scopes.event_header = sc->event_header_fc;
-       ctx->scopes.event_common_context = sc->event_common_context_fc;
-
-       for (i = 0; i < sc->event_classes->len; i++) {
-               struct ctf_event_class *ec = sc->event_classes->pdata[i];
-
-               ret = resolve_event_class_field_classes(ctx, ec);
-               if (ret) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot resolve event class's field classes: "
-                               "ec-id=%" PRIu64 ", ec-name=\"%s\"",
-                               ec->id, ec->name->str);
-                       goto end;
-               }
-       }
-
-end:
-       ctx->scopes.packet_context = NULL;
-       ctx->scopes.event_header = NULL;
-       ctx->scopes.event_common_context = NULL;
-       ctx->sc = NULL;
-       return ret;
-}
-
-BT_HIDDEN
-int ctf_trace_class_resolve_field_classes(struct ctf_trace_class *tc,
-               struct meta_log_config *log_cfg)
-{
-       int ret = 0;
-       uint64_t i;
-       struct resolve_context local_ctx = {
-               .log_level = log_cfg->log_level,
-               .self_comp = log_cfg->self_comp,
-               .self_comp_class = log_cfg->self_comp_class,
-               .tc = tc,
-               .sc = NULL,
-               .ec = NULL,
-               .scopes = {
-                       .packet_header = tc->packet_header_fc,
-                       .packet_context = NULL,
-                       .event_header = NULL,
-                       .event_common_context = NULL,
-                       .event_spec_context = NULL,
-                       .event_payload = NULL,
-               },
-               .root_scope = CTF_SCOPE_PACKET_HEADER,
-               .cur_fc = NULL,
-       };
-       struct resolve_context *ctx = &local_ctx;
-
-       /* Initialize class stack */
-       ctx->field_class_stack = field_class_stack_create();
-       if (!ctx->field_class_stack) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot create field class stack.");
-               ret = -1;
-               goto end;
-       }
-
-       if (!tc->is_translated) {
-               ctx->scopes.packet_header = tc->packet_header_fc;
-               ret = resolve_root_class(CTF_SCOPE_PACKET_HEADER, ctx);
-               if (ret) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot resolve packet header field class: "
-                               "ret=%d", ret);
-                       goto end;
-               }
-       }
-
-       ctx->scopes.packet_header = tc->packet_header_fc;
-
-       for (i = 0; i < tc->stream_classes->len; i++) {
-               struct ctf_stream_class *sc = tc->stream_classes->pdata[i];
-
-               ret = resolve_stream_class_field_classes(ctx, sc);
-               if (ret) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot resolve stream class's field classes: "
-                               "sc-id=%" PRIu64, sc->id);
-                       goto end;
-               }
-       }
-
-end:
-       field_class_stack_destroy(ctx->field_class_stack);
-       return ret;
-}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-resolve.cpp b/src/plugins/ctf/common/metadata/ctf-meta-resolve.cpp
new file mode 100644 (file)
index 0000000..972ea70
--- /dev/null
@@ -0,0 +1,1310 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2016-2018 Philippe Proulx <pproulx@efficios.com>
+ * Copyright 2015 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ */
+
+#define BT_COMP_LOG_SELF_COMP (ctx->self_comp)
+#define BT_COMP_LOG_SELF_COMP_CLASS (ctx->self_comp_class)
+#define BT_LOG_OUTPUT_LEVEL (ctx->log_level)
+#define BT_LOG_TAG "PLUGIN/CTF/META/RESOLVE"
+#include "logging/comp-logging.h"
+
+#include <babeltrace2/babeltrace.h>
+#include "common/macros.h"
+#include "common/assert.h"
+#include "common/common.h"
+#include <glib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <glib.h>
+
+#include "ctf-meta-visitors.hpp"
+#include "logging.hpp"
+
+using field_class_stack_t = GPtrArray ;
+
+/*
+ * A stack frame.
+ *
+ * `fc` contains a compound field class (structure, variant, array,
+ * or sequence) and `index` indicates the index of the field class in
+ * the upper frame (-1 for array and sequence field classes). `name`
+ * indicates the name of the field class in the upper frame (empty
+ * string for array and sequence field classes).
+ */
+struct field_class_stack_frame {
+       struct ctf_field_class *fc;
+       int64_t index;
+};
+
+/*
+ * The current context of the resolving engine.
+ */
+struct resolve_context {
+       bt_logging_level log_level;
+
+       /* Weak, exactly one of these must be set */
+       bt_self_component *self_comp;
+       bt_self_component_class *self_comp_class;
+
+       struct ctf_trace_class *tc;
+       struct ctf_stream_class *sc;
+       struct ctf_event_class *ec;
+
+       struct {
+               struct ctf_field_class *packet_header;
+               struct ctf_field_class *packet_context;
+               struct ctf_field_class *event_header;
+               struct ctf_field_class *event_common_context;
+               struct ctf_field_class *event_spec_context;
+               struct ctf_field_class *event_payload;
+       } scopes;
+
+       /* Root scope being visited */
+       enum ctf_scope root_scope;
+       field_class_stack_t *field_class_stack;
+       struct ctf_field_class *cur_fc;
+};
+
+/* TSDL dynamic scope prefixes as defined in CTF Section 7.3.2 */
+static const char * const absolute_path_prefixes[] = {
+       /* CTF_SCOPE_PACKET_HEADER */           "trace.packet.header.",
+       /* CTF_SCOPE_PACKET_CONTEXT */          "stream.packet.context.",
+       /* CTF_SCOPE_EVENT_HEADER */            "stream.event.header.",
+       /* CTF_SCOPE_EVENT_COMMON_CONTEXT */    "stream.event.context.",
+       /* CTF_SCOPE_EVENT_SPECIFIC_CONTEXT */  "event.context.",
+       /* CTF_SCOPE_EVENT_PAYLOAD */           "event.fields.",
+};
+
+/* Number of path tokens used for the absolute prefixes */
+static const uint64_t absolute_path_prefix_ptoken_counts[] = {
+       /* CTF_SCOPE_PACKET_HEADER */           3,
+       /* CTF_SCOPE_PACKET_CONTEXT */          3,
+       /* CTF_SCOPE_EVENT_HEADER */            3,
+       /* CTF_SCOPE_EVENT_COMMON_CONTEXT */    3,
+       /* CTF_SCOPE_EVENT_SPECIFIC_CONTEXT */  2,
+       /* CTF_SCOPE_EVENT_PAYLOAD */           2,
+};
+
+static
+void destroy_field_class_stack_frame(struct field_class_stack_frame *frame)
+{
+       if (!frame) {
+               return;
+       }
+
+       g_free(frame);
+}
+
+/*
+ * Creates a class stack.
+ */
+static
+field_class_stack_t *field_class_stack_create(void)
+{
+       return g_ptr_array_new_with_free_func(
+               (GDestroyNotify) destroy_field_class_stack_frame);
+}
+
+/*
+ * Destroys a class stack.
+ */
+static
+void field_class_stack_destroy(field_class_stack_t *stack)
+{
+       if (stack) {
+               g_ptr_array_free(stack, TRUE);
+       }
+}
+
+/*
+ * Pushes a field class onto a class stack.
+ */
+static
+int field_class_stack_push(field_class_stack_t *stack, struct ctf_field_class *fc,
+               struct resolve_context *ctx)
+{
+       int ret = 0;
+       struct field_class_stack_frame *frame = NULL;
+
+       if (!stack || !fc) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid parameter: stack or field class is `NULL`.");
+               ret = -1;
+               goto end;
+       }
+
+       frame = g_new0(struct field_class_stack_frame, 1);
+       if (!frame) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Failed to allocate one field class stack frame.");
+               ret = -1;
+               goto end;
+       }
+
+       BT_COMP_LOGD("Pushing field class on context's stack: "
+               "fc-addr=%p, stack-size-before=%u", fc, stack->len);
+       frame->fc = fc;
+       g_ptr_array_add(stack, frame);
+
+end:
+       return ret;
+}
+
+/*
+ * Checks whether or not `stack` is empty.
+ */
+static
+bool field_class_stack_empty(field_class_stack_t *stack)
+{
+       return stack->len == 0;
+}
+
+/*
+ * Returns the number of frames in `stack`.
+ */
+static
+size_t field_class_stack_size(field_class_stack_t *stack)
+{
+       return stack->len;
+}
+
+/*
+ * Returns the top frame of `stack`.
+ */
+static
+struct field_class_stack_frame *field_class_stack_peek(field_class_stack_t *stack)
+{
+       BT_ASSERT(stack);
+       BT_ASSERT(!field_class_stack_empty(stack));
+
+       return (field_class_stack_frame *) g_ptr_array_index(stack, stack->len - 1);
+}
+
+/*
+ * Returns the frame at index `index` in `stack`.
+ */
+static
+struct field_class_stack_frame *field_class_stack_at(field_class_stack_t *stack,
+               size_t index)
+{
+       BT_ASSERT(stack);
+       BT_ASSERT(index < stack->len);
+
+       return (field_class_stack_frame *) g_ptr_array_index(stack, index);
+}
+
+/*
+ * Removes the top frame of `stack`.
+ */
+static
+void field_class_stack_pop(field_class_stack_t *stack,
+               struct resolve_context *ctx)
+{
+       if (!field_class_stack_empty(stack)) {
+               /*
+                * This will call the frame's destructor and free it, as
+                * well as put its contained field class.
+                */
+               BT_COMP_LOGD("Popping context's stack: stack-size-before=%u",
+                       stack->len);
+               g_ptr_array_set_size(stack, stack->len - 1);
+       }
+}
+
+/*
+ * Returns the scope field class of `scope` in the context `ctx`.
+ */
+static
+struct ctf_field_class *borrow_class_from_ctx(struct resolve_context *ctx,
+               enum ctf_scope scope)
+{
+       switch (scope) {
+       case CTF_SCOPE_PACKET_HEADER:
+               return ctx->scopes.packet_header;
+       case CTF_SCOPE_PACKET_CONTEXT:
+               return ctx->scopes.packet_context;
+       case CTF_SCOPE_EVENT_HEADER:
+               return ctx->scopes.event_header;
+       case CTF_SCOPE_EVENT_COMMON_CONTEXT:
+               return ctx->scopes.event_common_context;
+       case CTF_SCOPE_EVENT_SPECIFIC_CONTEXT:
+               return ctx->scopes.event_spec_context;
+       case CTF_SCOPE_EVENT_PAYLOAD:
+               return ctx->scopes.event_payload;
+       default:
+               bt_common_abort();
+       }
+
+       return NULL;
+}
+
+/*
+ * Returns the CTF scope from a path string. May return -1 if the path
+ * is found to be relative.
+ */
+static
+enum ctf_scope get_root_scope_from_absolute_pathstr(const char *pathstr,
+               struct resolve_context *ctx)
+{
+       enum ctf_scope scope;
+       enum ctf_scope ret = CTF_SCOPE_PACKET_UNKNOWN;
+       const size_t prefixes_count = sizeof(absolute_path_prefixes) /
+               sizeof(*absolute_path_prefixes);
+
+       for (scope = CTF_SCOPE_PACKET_HEADER; scope < CTF_SCOPE_PACKET_HEADER +
+                       prefixes_count; scope = (ctf_scope) (scope + 1)) {
+               /*
+                * Check if path string starts with a known absolute
+                * path prefix.
+                *
+                * Refer to CTF 7.3.2 STATIC AND DYNAMIC SCOPES.
+                */
+               if (strncmp(pathstr, absolute_path_prefixes[scope],
+                               strlen(absolute_path_prefixes[scope]))) {
+                       /* Prefix does not match: try the next one */
+                       BT_COMP_LOGD("Prefix does not match: trying the next one: "
+                               "path=\"%s\", path-prefix=\"%s\", scope=%s",
+                               pathstr, absolute_path_prefixes[scope],
+                               ctf_scope_string(scope));
+                       continue;
+               }
+
+               /* Found it! */
+               ret = scope;
+               BT_COMP_LOGD("Found root scope from absolute path: "
+                       "path=\"%s\", scope=%s", pathstr,
+                       ctf_scope_string(scope));
+               goto end;
+       }
+
+end:
+       return ret;
+}
+
+/*
+ * Destroys a path token.
+ */
+static
+void ptokens_destroy_func(gpointer ptoken, gpointer data)
+{
+       g_string_free((GString *) ptoken, TRUE);
+}
+
+/*
+ * Destroys a path token list.
+ */
+static
+void ptokens_destroy(GList *ptokens)
+{
+       if (!ptokens) {
+               return;
+       }
+
+       g_list_foreach(ptokens, ptokens_destroy_func, NULL);
+       g_list_free(ptokens);
+}
+
+/*
+ * Returns the string contained in a path token.
+ */
+static
+const char *ptoken_get_string(GList *ptoken)
+{
+       GString *tokenstr = (GString *) ptoken->data;
+
+       return tokenstr->str;
+}
+
+/*
+ * Converts a path string to a path token list, that is, splits the
+ * individual words of a path string into a list of individual
+ * strings.
+ */
+static
+GList *pathstr_to_ptokens(const char *pathstr, struct resolve_context *ctx)
+{
+       const char *at = pathstr;
+       const char *last = at;
+       GList *ptokens = NULL;
+
+       for (;;) {
+               if (*at == '.' || *at == '\0') {
+                       GString *tokenstr;
+
+                       if (at == last) {
+                               /* Error: empty token */
+                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Empty path token: path=\"%s\", pos=%u",
+                                       pathstr, (unsigned int) (at - pathstr));
+                               goto error;
+                       }
+
+                       tokenstr = g_string_new(NULL);
+                       g_string_append_len(tokenstr, last, at - last);
+                       ptokens = g_list_append(ptokens, tokenstr);
+                       last = at + 1;
+               }
+
+               if (*at == '\0') {
+                       break;
+               }
+
+               at++;
+       }
+
+       return ptokens;
+
+error:
+       ptokens_destroy(ptokens);
+       return NULL;
+}
+
+/*
+ * Converts a path token list to a field path object. The path token
+ * list is relative from `fc`. The index of the source looking for its
+ * target within `fc` is indicated by `src_index`. This can be
+ * `INT64_MAX` if the source is contained in `fc`.
+ *
+ * `field_path` is an output parameter owned by the caller that must be
+ * filled here.
+ */
+static
+int ptokens_to_field_path(GList *ptokens, struct ctf_field_path *field_path,
+               struct ctf_field_class *fc, int64_t src_index,
+               struct resolve_context *ctx)
+{
+       int ret = 0;
+       GList *cur_ptoken = ptokens;
+       bool first_level_done = false;
+
+       /* Locate target */
+       while (cur_ptoken) {
+               int64_t child_index;
+               struct ctf_field_class *child_fc;
+               const char *ft_name = ptoken_get_string(cur_ptoken);
+
+               BT_COMP_LOGD("Current path token: token=\"%s\"", ft_name);
+
+               /* Find to which index corresponds the current path token */
+               if (fc->type == CTF_FIELD_CLASS_TYPE_ARRAY ||
+                               fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE) {
+                       child_index = -1;
+               } else {
+                       child_index =
+                               ctf_field_class_compound_get_field_class_index_from_orig_name(
+                                       fc, ft_name);
+                       if (child_index < 0) {
+                               /*
+                                * Error: field name does not exist or
+                                * wrong current class.
+                                */
+                               BT_COMP_LOGD("Cannot get index of field class: "
+                                       "field-name=\"%s\", "
+                                       "src-index=%" PRId64 ", "
+                                       "child-index=%" PRId64 ", "
+                                       "first-level-done=%d",
+                                       ft_name, src_index, child_index,
+                                       first_level_done);
+                               ret = -1;
+                               goto end;
+                       } else if (child_index > src_index &&
+                                       !first_level_done) {
+                               BT_COMP_LOGD("Child field class is located after source field class: "
+                                       "field-name=\"%s\", "
+                                       "src-index=%" PRId64 ", "
+                                       "child-index=%" PRId64 ", "
+                                       "first-level-done=%d",
+                                       ft_name, src_index, child_index,
+                                       first_level_done);
+                               ret = -1;
+                               goto end;
+                       }
+
+                       /* Next path token */
+                       cur_ptoken = g_list_next(cur_ptoken);
+                       first_level_done = true;
+               }
+
+               /* Create new field path entry */
+               ctf_field_path_append_index(field_path, child_index);
+
+               /* Get child field class */
+               child_fc = ctf_field_class_compound_borrow_field_class_by_index(
+                       fc, child_index);
+               BT_ASSERT(child_fc);
+
+               /* Move child class to current class */
+               fc = child_fc;
+       }
+
+end:
+       return ret;
+}
+
+/*
+ * Converts a known absolute path token list to a field path object
+ * within the resolving context `ctx`.
+ *
+ * `field_path` is an output parameter owned by the caller that must be
+ * filled here.
+ */
+static
+int absolute_ptokens_to_field_path(GList *ptokens,
+               struct ctf_field_path *field_path,
+               struct resolve_context *ctx)
+{
+       int ret = 0;
+       GList *cur_ptoken;
+       struct ctf_field_class *fc;
+
+       /*
+        * Make sure we're not referring to a scope within a translated
+        * object.
+        */
+       switch (field_path->root) {
+       case CTF_SCOPE_PACKET_HEADER:
+               if (ctx->tc->is_translated) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Trace class is already translated: "
+                               "root-scope=%s",
+                               ctf_scope_string(field_path->root));
+                       ret = -1;
+                       goto end;
+               }
+
+               break;
+       case CTF_SCOPE_PACKET_CONTEXT:
+       case CTF_SCOPE_EVENT_HEADER:
+       case CTF_SCOPE_EVENT_COMMON_CONTEXT:
+               if (!ctx->sc) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("No current stream class: "
+                               "root-scope=%s",
+                               ctf_scope_string(field_path->root));
+                       ret = -1;
+                       goto end;
+               }
+
+               if (ctx->sc->is_translated) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Stream class is already translated: "
+                               "root-scope=%s",
+                               ctf_scope_string(field_path->root));
+                       ret = -1;
+                       goto end;
+               }
+
+               break;
+       case CTF_SCOPE_EVENT_SPECIFIC_CONTEXT:
+       case CTF_SCOPE_EVENT_PAYLOAD:
+               if (!ctx->ec) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("No current event class: "
+                               "root-scope=%s",
+                               ctf_scope_string(field_path->root));
+                       ret = -1;
+                       goto end;
+               }
+
+               if (ctx->ec->is_translated) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Event class is already translated: "
+                               "root-scope=%s",
+                               ctf_scope_string(field_path->root));
+                       ret = -1;
+                       goto end;
+               }
+
+               break;
+
+       default:
+               bt_common_abort();
+       }
+
+       /* Skip absolute path tokens */
+       cur_ptoken = g_list_nth(ptokens,
+               absolute_path_prefix_ptoken_counts[field_path->root]);
+
+       /* Start with root class */
+       fc = borrow_class_from_ctx(ctx, field_path->root);
+       if (!fc) {
+               /* Error: root class is not available */
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Root field class is not available: "
+                       "root-scope=%s",
+                       ctf_scope_string(field_path->root));
+               ret = -1;
+               goto end;
+       }
+
+       /* Locate target */
+       ret = ptokens_to_field_path(cur_ptoken, field_path, fc, INT64_MAX, ctx);
+
+end:
+       return ret;
+}
+
+/*
+ * Converts a known relative path token list to a field path object
+ * within the resolving context `ctx`.
+ *
+ * `field_path` is an output parameter owned by the caller that must be
+ * filled here.
+ */
+static
+int relative_ptokens_to_field_path(GList *ptokens,
+               struct ctf_field_path *field_path, struct resolve_context *ctx)
+{
+       int ret = 0;
+       int64_t parent_pos_in_stack;
+       struct ctf_field_path tail_field_path;
+
+       ctf_field_path_init(&tail_field_path);
+       parent_pos_in_stack = field_class_stack_size(ctx->field_class_stack) - 1;
+
+       while (parent_pos_in_stack >= 0) {
+               struct ctf_field_class *parent_class =
+                       field_class_stack_at(ctx->field_class_stack,
+                               parent_pos_in_stack)->fc;
+               int64_t cur_index = field_class_stack_at(ctx->field_class_stack,
+                       parent_pos_in_stack)->index;
+
+               BT_COMP_LOGD("Locating target field class from current parent field class: "
+                       "parent-pos=%" PRId64 ", parent-fc-addr=%p, "
+                       "cur-index=%" PRId64,
+                       parent_pos_in_stack, parent_class, cur_index);
+
+               /* Locate target from current parent class */
+               ret = ptokens_to_field_path(ptokens, &tail_field_path,
+                       parent_class, cur_index, ctx);
+               if (ret) {
+                       /* Not found... yet */
+                       BT_COMP_LOGD_STR("Not found at this point.");
+                       ctf_field_path_clear(&tail_field_path);
+               } else {
+                       /* Found: stitch tail field path to head field path */
+                       uint64_t i = 0;
+                       size_t tail_field_path_len =
+                               tail_field_path.path->len;
+
+                       while (BT_TRUE) {
+                               struct ctf_field_class *cur_class =
+                                       field_class_stack_at(
+                                               ctx->field_class_stack, i)->fc;
+                               int64_t index = field_class_stack_at(
+                                       ctx->field_class_stack, i)->index;
+
+                               if (cur_class == parent_class) {
+                                       break;
+                               }
+
+                               ctf_field_path_append_index(field_path,
+                                       index);
+                               i++;
+                       }
+
+                       for (i = 0; i < tail_field_path_len; i++) {
+                               int64_t index =
+                                       ctf_field_path_borrow_index_by_index(
+                                               &tail_field_path, i);
+
+                               ctf_field_path_append_index(field_path,
+                                       (int64_t) index);
+                       }
+                       break;
+               }
+
+               parent_pos_in_stack--;
+       }
+
+       if (parent_pos_in_stack < 0) {
+               /* Not found */
+               ret = -1;
+       }
+
+       ctf_field_path_fini(&tail_field_path);
+       return ret;
+}
+
+/*
+ * Converts a path string to a field path object within the resolving
+ * context `ctx`.
+ */
+static
+int pathstr_to_field_path(const char *pathstr,
+               struct ctf_field_path *field_path, struct resolve_context *ctx)
+{
+       int ret = 0;
+       enum ctf_scope root_scope;
+       GList *ptokens = NULL;
+
+       /* Convert path string to path tokens */
+       ptokens = pathstr_to_ptokens(pathstr, ctx);
+       if (!ptokens) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot convert path string to path tokens: "
+                       "path=\"%s\"", pathstr);
+               ret = -1;
+               goto end;
+       }
+
+       /* Absolute or relative path? */
+       root_scope = get_root_scope_from_absolute_pathstr(pathstr, ctx);
+
+       if (root_scope == CTF_SCOPE_PACKET_UNKNOWN) {
+               /* Relative path: start with current root scope */
+               field_path->root = ctx->root_scope;
+               BT_COMP_LOGD("Detected relative path: starting with current root scope: "
+                       "scope=%s", ctf_scope_string(field_path->root));
+               ret = relative_ptokens_to_field_path(ptokens, field_path, ctx);
+               if (ret) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot get relative field path of path string: "
+                               "path=\"%s\", start-scope=%s, end-scope=%s",
+                               pathstr, ctf_scope_string(ctx->root_scope),
+                               ctf_scope_string(field_path->root));
+                       goto end;
+               }
+       } else {
+               /* Absolute path: use found root scope */
+               field_path->root = root_scope;
+               BT_COMP_LOGD("Detected absolute path: using root scope: "
+                       "scope=%s", ctf_scope_string(field_path->root));
+               ret = absolute_ptokens_to_field_path(ptokens, field_path, ctx);
+               if (ret) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot get absolute field path of path string: "
+                               "path=\"%s\", root-scope=%s",
+                               pathstr, ctf_scope_string(root_scope));
+                       goto end;
+               }
+       }
+
+       if (BT_LOG_ON_TRACE && ret == 0) {
+               GString *field_path_pretty = ctf_field_path_string(field_path);
+               const char *field_path_pretty_str =
+                       field_path_pretty ? field_path_pretty->str : NULL;
+
+               BT_COMP_LOGD("Found field path: path=\"%s\", field-path=\"%s\"",
+                       pathstr, field_path_pretty_str);
+
+               if (field_path_pretty) {
+                       g_string_free(field_path_pretty, TRUE);
+               }
+       }
+
+end:
+       ptokens_destroy(ptokens);
+       return ret;
+}
+
+/*
+ * Retrieves a field class by following the field path `field_path` in
+ * the resolving context `ctx`.
+ */
+static
+struct ctf_field_class *field_path_to_field_class(
+               struct ctf_field_path *field_path, struct resolve_context *ctx)
+{
+       uint64_t i;
+       struct ctf_field_class *fc;
+
+       /* Start with root class */
+       fc = borrow_class_from_ctx(ctx, field_path->root);
+       if (!fc) {
+               /* Error: root class is not available */
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Root field class is not available: root-scope=%s",
+                       ctf_scope_string(field_path->root));
+               goto end;
+       }
+
+       /* Locate target */
+       for (i = 0; i < field_path->path->len; i++) {
+               struct ctf_field_class *child_fc;
+               int64_t child_index =
+                       ctf_field_path_borrow_index_by_index(field_path, i);
+
+               /* Get child field class */
+               child_fc = ctf_field_class_compound_borrow_field_class_by_index(
+                       fc, child_index);
+               BT_ASSERT(child_fc);
+
+               /* Move child class to current class */
+               fc = child_fc;
+       }
+
+end:
+       return fc;
+}
+
+/*
+ * Fills the equivalent field path object of the context class stack.
+ */
+static
+void get_ctx_stack_field_path(struct resolve_context *ctx,
+               struct ctf_field_path *field_path)
+{
+       uint64_t i;
+
+       BT_ASSERT(field_path);
+       field_path->root = ctx->root_scope;
+       ctf_field_path_clear(field_path);
+
+       for (i = 0; i < field_class_stack_size(ctx->field_class_stack); i++) {
+               struct field_class_stack_frame *frame =
+                       field_class_stack_at(ctx->field_class_stack, i);
+
+               ctf_field_path_append_index(field_path, frame->index);
+       }
+}
+
+/*
+ * Returns the index of the lowest common ancestor of two field path
+ * objects having the same root scope.
+ */
+static
+int64_t get_field_paths_lca_index(struct ctf_field_path *field_path1,
+               struct ctf_field_path *field_path2,
+               struct resolve_context *ctx)
+{
+       int64_t lca_index = 0;
+       uint64_t field_path1_len, field_path2_len;
+
+       if (BT_LOG_ON_TRACE) {
+               GString *field_path1_pretty =
+                       ctf_field_path_string(field_path1);
+               GString *field_path2_pretty =
+                       ctf_field_path_string(field_path2);
+               const char *field_path1_pretty_str =
+                       field_path1_pretty ? field_path1_pretty->str : NULL;
+               const char *field_path2_pretty_str =
+                       field_path2_pretty ? field_path2_pretty->str : NULL;
+
+               BT_COMP_LOGD("Finding lowest common ancestor (LCA) between two field paths: "
+                       "field-path-1=\"%s\", field-path-2=\"%s\"",
+                       field_path1_pretty_str, field_path2_pretty_str);
+
+               if (field_path1_pretty) {
+                       g_string_free(field_path1_pretty, TRUE);
+               }
+
+               if (field_path2_pretty) {
+                       g_string_free(field_path2_pretty, TRUE);
+               }
+       }
+
+       /*
+        * Start from both roots and find the first mismatch.
+        */
+       BT_ASSERT(field_path1->root == field_path2->root);
+       field_path1_len = field_path1->path->len;
+       field_path2_len = field_path2->path->len;
+
+       while (true) {
+               int64_t target_index, ctx_index;
+
+               if (lca_index == (int64_t) field_path2_len ||
+                               lca_index == (int64_t) field_path1_len) {
+                       /*
+                        * This means that both field paths never split.
+                        * This is invalid because the target cannot be
+                        * an ancestor of the source.
+                        */
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Source field class is an ancestor of target field class or vice versa: "
+                               "lca-index=%" PRId64 ", "
+                               "field-path-1-len=%" PRIu64 ", "
+                               "field-path-2-len=%" PRIu64,
+                               lca_index, field_path1_len, field_path2_len);
+                       lca_index = -1;
+                       break;
+               }
+
+               target_index = ctf_field_path_borrow_index_by_index(field_path1,
+                       lca_index);
+               ctx_index = ctf_field_path_borrow_index_by_index(field_path2,
+                       lca_index);
+
+               if (target_index != ctx_index) {
+                       /* LCA index is the previous */
+                       break;
+               }
+
+               lca_index++;
+       }
+
+       BT_COMP_LOGD("Found LCA: lca-index=%" PRId64, lca_index);
+       return lca_index;
+}
+
+/*
+ * Validates a target field path.
+ */
+static
+int validate_target_field_path(struct ctf_field_path *target_field_path,
+               struct ctf_field_class *target_fc,
+               struct resolve_context *ctx)
+{
+       int ret = 0;
+       struct ctf_field_path ctx_field_path;
+       uint64_t target_field_path_len = target_field_path->path->len;
+       int64_t lca_index;
+
+       /* Get context field path */
+       ctf_field_path_init(&ctx_field_path);
+       get_ctx_stack_field_path(ctx, &ctx_field_path);
+
+       /*
+        * Make sure the target is not a root.
+        */
+       if (target_field_path_len == 0) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Target field path's length is 0 (targeting the root).");
+               ret = -1;
+               goto end;
+       }
+
+       /*
+        * Make sure the root of the target field path is not located
+        * after the context field path's root.
+        */
+       if (target_field_path->root > ctx_field_path.root) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Target field class is located after source field class: "
+                       "target-root=%s, source-root=%s",
+                       ctf_scope_string(target_field_path->root),
+                       ctf_scope_string(ctx_field_path.root));
+               ret = -1;
+               goto end;
+       }
+
+       if (target_field_path->root == ctx_field_path.root) {
+               int64_t target_index, ctx_index;
+
+               /*
+                * Find the index of the lowest common ancestor of both field
+                * paths.
+                */
+               lca_index = get_field_paths_lca_index(target_field_path,
+                       &ctx_field_path, ctx);
+               if (lca_index < 0) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot get least common ancestor.");
+                       ret = -1;
+                       goto end;
+               }
+
+               /*
+                * Make sure the target field path is located before the
+                * context field path.
+                */
+               target_index = ctf_field_path_borrow_index_by_index(
+                       target_field_path, (uint64_t) lca_index);
+               ctx_index = ctf_field_path_borrow_index_by_index(
+                       &ctx_field_path, (uint64_t) lca_index);
+
+               if (target_index >= ctx_index) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Target field class's index is greater than or equal to source field class's index in LCA: "
+                               "lca-index=%" PRId64 ", "
+                               "target-index=%" PRId64 ", "
+                               "source-index=%" PRId64,
+                               lca_index, target_index, ctx_index);
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       /*
+        * Make sure the target class has the right class and properties.
+        */
+       switch (ctx->cur_fc->type) {
+       case CTF_FIELD_CLASS_TYPE_VARIANT:
+               if (target_fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Variant field class's tag field class is not an enumeration field class: "
+                               "tag-fc-addr=%p, tag-fc-id=%d",
+                               target_fc, target_fc->type);
+                       ret = -1;
+                       goto end;
+               }
+               break;
+       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+       {
+               struct ctf_field_class_int *int_fc = ctf_field_class_as_int(target_fc);
+
+               if (target_fc->type != CTF_FIELD_CLASS_TYPE_INT &&
+                               target_fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Sequence field class's length field class is not an unsigned integer field class: "
+                               "length-fc-addr=%p, length-fc-id=%d",
+                               target_fc, target_fc->type);
+                       ret = -1;
+                       goto end;
+               }
+
+               if (int_fc->is_signed) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Sequence field class's length field class is not an unsigned integer field class: "
+                               "length-fc-addr=%p, length-fc-id=%d",
+                               target_fc, target_fc->type);
+                       ret = -1;
+                       goto end;
+               }
+               break;
+       }
+       default:
+               bt_common_abort();
+       }
+
+end:
+       ctf_field_path_fini(&ctx_field_path);
+       return ret;
+}
+
+/*
+ * Resolves a variant or sequence field class `fc`.
+ */
+static
+int resolve_sequence_or_variant_field_class(struct ctf_field_class *fc,
+               struct resolve_context *ctx)
+{
+       int ret = 0;
+       const char *pathstr;
+       struct ctf_field_path target_field_path;
+       struct ctf_field_class *target_fc = NULL;
+       GString *target_field_path_pretty = NULL;
+       const char *target_field_path_pretty_str;
+
+       ctf_field_path_init(&target_field_path);
+
+       /* Get path string */
+       switch (fc->type) {
+       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+       {
+               struct ctf_field_class_sequence *seq_fc = ctf_field_class_as_sequence(fc);
+               pathstr = seq_fc->length_ref->str;
+               break;
+       }
+       case CTF_FIELD_CLASS_TYPE_VARIANT:
+       {
+               struct ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
+               pathstr = var_fc->tag_ref->str;
+               break;
+       }
+       default:
+               bt_common_abort();
+       }
+
+       if (!pathstr) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot get path string.");
+               ret = -1;
+               goto end;
+       }
+
+       /* Get target field path out of path string */
+       ret = pathstr_to_field_path(pathstr, &target_field_path, ctx);
+       if (ret) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot get target field path for path string: "
+                       "path=\"%s\"", pathstr);
+               goto end;
+       }
+
+       target_field_path_pretty = ctf_field_path_string(
+               &target_field_path);
+       target_field_path_pretty_str =
+               target_field_path_pretty ? target_field_path_pretty->str : NULL;
+
+       /* Get target field class */
+       target_fc = field_path_to_field_class(&target_field_path, ctx);
+       if (!target_fc) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot get target field class for path string: "
+                       "path=\"%s\", target-field-path=\"%s\"",
+                       pathstr, target_field_path_pretty_str);
+               ret = -1;
+               goto end;
+       }
+
+       ret = validate_target_field_path(&target_field_path,
+               target_fc, ctx);
+       if (ret) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid target field path for path string: "
+                       "path=\"%s\", target-field-path=\"%s\"",
+                       pathstr, target_field_path_pretty_str);
+               goto end;
+       }
+
+       /* Set target field path and target field class */
+       switch (fc->type) {
+       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+       {
+               ctf_field_class_sequence *seq_fc = ctf_field_class_as_sequence(fc);
+
+               ctf_field_path_copy_content(&seq_fc->length_path,
+                       &target_field_path);
+               seq_fc->length_fc = ctf_field_class_as_int(target_fc);
+               break;
+       }
+       case CTF_FIELD_CLASS_TYPE_VARIANT:
+       {
+               ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
+
+               ctf_field_path_copy_content(&var_fc->tag_path,
+                       &target_field_path);
+               ctf_field_class_variant_set_tag_field_class(var_fc,
+                       ctf_field_class_as_enum(target_fc));
+               break;
+       }
+       default:
+               bt_common_abort();
+       }
+
+end:
+       if (target_field_path_pretty) {
+               g_string_free(target_field_path_pretty, TRUE);
+       }
+
+       ctf_field_path_fini(&target_field_path);
+       return ret;
+}
+
+/*
+ * Resolves a field class `fc`.
+ */
+static
+int resolve_field_class(struct ctf_field_class *fc, struct resolve_context *ctx)
+{
+       int ret = 0;
+
+       if (!fc) {
+               /* Field class is not available; still valid */
+               goto end;
+       }
+
+       ctx->cur_fc = fc;
+
+       /* Resolve sequence/variant field class */
+       switch (fc->type) {
+       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+       case CTF_FIELD_CLASS_TYPE_VARIANT:
+               ret = resolve_sequence_or_variant_field_class(fc, ctx);
+               if (ret) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot resolve sequence field class's length or variant field class's tag: "
+                               "ret=%d, fc-addr=%p", ret, fc);
+                       goto end;
+               }
+
+               break;
+       default:
+               break;
+       }
+
+       /* Recurse into compound classes */
+       switch (fc->type) {
+       case CTF_FIELD_CLASS_TYPE_STRUCT:
+       case CTF_FIELD_CLASS_TYPE_VARIANT:
+       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+       case CTF_FIELD_CLASS_TYPE_ARRAY:
+       {
+               uint64_t i;
+               uint64_t field_count =
+                       ctf_field_class_compound_get_field_class_count(fc);
+
+               ret = field_class_stack_push(ctx->field_class_stack, fc, ctx);
+               if (ret) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot push field class on context's stack: "
+                               "fc-addr=%p", fc);
+                       goto end;
+               }
+
+               for (i = 0; i < field_count; i++) {
+                       struct ctf_field_class *child_fc =
+                               ctf_field_class_compound_borrow_field_class_by_index(
+                                       fc, i);
+
+                       BT_ASSERT(child_fc);
+
+                       if (fc->type == CTF_FIELD_CLASS_TYPE_ARRAY||
+                                       fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE) {
+                               field_class_stack_peek(
+                                       ctx->field_class_stack)->index = -1;
+                       } else {
+                               field_class_stack_peek(
+                                       ctx->field_class_stack)->index =
+                                               (int64_t) i;
+                       }
+
+                       BT_COMP_LOGD("Resolving field class's child field class: "
+                               "parent-fc-addr=%p, child-fc-addr=%p, "
+                               "index=%" PRIu64 ", count=%" PRIu64,
+                               fc, child_fc, i, field_count);
+                       ret = resolve_field_class(child_fc, ctx);
+                       if (ret) {
+                               goto end;
+                       }
+               }
+
+               field_class_stack_pop(ctx->field_class_stack, ctx);
+               break;
+       }
+       default:
+               break;
+       }
+
+end:
+       return ret;
+}
+
+/*
+ * Resolves the root field class corresponding to the scope `root_scope`.
+ */
+static
+int resolve_root_class(enum ctf_scope root_scope, struct resolve_context *ctx)
+{
+       int ret;
+
+       BT_ASSERT(field_class_stack_size(ctx->field_class_stack) == 0);
+       ctx->root_scope = root_scope;
+       ret = resolve_field_class(borrow_class_from_ctx(ctx, root_scope), ctx);
+       ctx->root_scope = CTF_SCOPE_PACKET_UNKNOWN;
+       return ret;
+}
+
+static
+int resolve_event_class_field_classes(struct resolve_context *ctx,
+               struct ctf_event_class *ec)
+{
+       int ret = 0;
+
+       BT_ASSERT(!ctx->scopes.event_spec_context);
+       BT_ASSERT(!ctx->scopes.event_payload);
+
+       if (ec->is_translated) {
+               goto end;
+       }
+
+       ctx->ec = ec;
+       ctx->scopes.event_spec_context = ec->spec_context_fc;
+       ret = resolve_root_class(CTF_SCOPE_EVENT_COMMON_CONTEXT, ctx);
+       if (ret) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot resolve event specific context field class: "
+                       "ret=%d", ret);
+               goto end;
+       }
+
+       ctx->scopes.event_payload = ec->payload_fc;
+       ret = resolve_root_class(CTF_SCOPE_EVENT_PAYLOAD, ctx);
+       if (ret) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot resolve event payload field class: "
+                       "ret=%d", ret);
+               goto end;
+       }
+
+end:
+       ctx->scopes.event_spec_context = NULL;
+       ctx->scopes.event_payload = NULL;
+       ctx->ec = NULL;
+       return ret;
+}
+
+static
+int resolve_stream_class_field_classes(struct resolve_context *ctx,
+               struct ctf_stream_class *sc)
+{
+       int ret = 0;
+       uint64_t i;
+
+       BT_ASSERT(!ctx->scopes.packet_context);
+       BT_ASSERT(!ctx->scopes.event_header);
+       BT_ASSERT(!ctx->scopes.event_common_context);
+       ctx->sc = sc;
+
+       if (!sc->is_translated) {
+               ctx->scopes.packet_context = sc->packet_context_fc;
+               ret = resolve_root_class(CTF_SCOPE_PACKET_CONTEXT, ctx);
+               if (ret) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot resolve packet context field class: "
+                               "ret=%d", ret);
+                       goto end;
+               }
+
+               ctx->scopes.event_header = sc->event_header_fc;
+               ret = resolve_root_class(CTF_SCOPE_EVENT_HEADER, ctx);
+               if (ret) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot resolve event header field class: "
+                               "ret=%d", ret);
+                       goto end;
+               }
+
+               ctx->scopes.event_common_context = sc->event_common_context_fc;
+               ret = resolve_root_class(CTF_SCOPE_EVENT_COMMON_CONTEXT, ctx);
+               if (ret) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot resolve event common context field class: "
+                               "ret=%d", ret);
+                       goto end;
+               }
+       }
+
+       ctx->scopes.packet_context = sc->packet_context_fc;
+       ctx->scopes.event_header = sc->event_header_fc;
+       ctx->scopes.event_common_context = sc->event_common_context_fc;
+
+       for (i = 0; i < sc->event_classes->len; i++) {
+               ctf_event_class *ec = (ctf_event_class *) sc->event_classes->pdata[i];
+
+               ret = resolve_event_class_field_classes(ctx, ec);
+               if (ret) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot resolve event class's field classes: "
+                               "ec-id=%" PRIu64 ", ec-name=\"%s\"",
+                               ec->id, ec->name->str);
+                       goto end;
+               }
+       }
+
+end:
+       ctx->scopes.packet_context = NULL;
+       ctx->scopes.event_header = NULL;
+       ctx->scopes.event_common_context = NULL;
+       ctx->sc = NULL;
+       return ret;
+}
+
+BT_HIDDEN
+int ctf_trace_class_resolve_field_classes(struct ctf_trace_class *tc,
+               struct meta_log_config *log_cfg)
+{
+       int ret = 0;
+       uint64_t i;
+
+       resolve_context local_ctx{};
+       local_ctx.log_level = log_cfg->log_level;
+       local_ctx.self_comp = log_cfg->self_comp;
+       local_ctx.self_comp_class = log_cfg->self_comp_class;
+       local_ctx.tc = tc;
+       local_ctx.scopes.packet_header = tc->packet_header_fc;
+       local_ctx.root_scope = CTF_SCOPE_PACKET_HEADER;
+
+       struct resolve_context *ctx = &local_ctx;
+
+       /* Initialize class stack */
+       ctx->field_class_stack = field_class_stack_create();
+       if (!ctx->field_class_stack) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot create field class stack.");
+               ret = -1;
+               goto end;
+       }
+
+       if (!tc->is_translated) {
+               ctx->scopes.packet_header = tc->packet_header_fc;
+               ret = resolve_root_class(CTF_SCOPE_PACKET_HEADER, ctx);
+               if (ret) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot resolve packet header field class: "
+                               "ret=%d", ret);
+                       goto end;
+               }
+       }
+
+       ctx->scopes.packet_header = tc->packet_header_fc;
+
+       for (i = 0; i < tc->stream_classes->len; i++) {
+               ctf_stream_class *sc = (ctf_stream_class *) tc->stream_classes->pdata[i];
+
+               ret = resolve_stream_class_field_classes(ctx, sc);
+               if (ret) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot resolve stream class's field classes: "
+                               "sc-id=%" PRIu64, sc->id);
+                       goto end;
+               }
+       }
+
+end:
+       field_class_stack_destroy(ctx->field_class_stack);
+       return ret;
+}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-translate.c b/src/plugins/ctf/common/metadata/ctf-meta-translate.c
deleted file mode 100644 (file)
index 9c9e23e..0000000
+++ /dev/null
@@ -1,702 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
- */
-
-#include <babeltrace2/babeltrace.h>
-#include "common/macros.h"
-#include "common/assert.h"
-#include <glib.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <string.h>
-#include <inttypes.h>
-
-#include "ctf-meta-visitors.h"
-
-struct ctx {
-       bt_self_component *self_comp;
-       bt_trace_class *ir_tc;
-       bt_stream_class *ir_sc;
-       struct ctf_trace_class *tc;
-       struct ctf_stream_class *sc;
-       struct ctf_event_class *ec;
-       enum ctf_scope scope;
-};
-
-static inline
-bt_field_class *ctf_field_class_to_ir(struct ctx *ctx,
-               struct ctf_field_class *fc);
-
-static inline
-void ctf_field_class_int_set_props(struct ctf_field_class_int *fc,
-               bt_field_class *ir_fc)
-{
-       bt_field_class_integer_set_field_value_range(ir_fc,
-               fc->base.size);
-       bt_field_class_integer_set_preferred_display_base(ir_fc,
-               fc->disp_base);
-}
-
-static inline
-bt_field_class *ctf_field_class_int_to_ir(struct ctx *ctx,
-               struct ctf_field_class_int *fc)
-{
-       bt_field_class *ir_fc;
-
-       if (fc->is_signed) {
-               ir_fc = bt_field_class_integer_signed_create(ctx->ir_tc);
-       } else {
-               ir_fc = bt_field_class_integer_unsigned_create(ctx->ir_tc);
-       }
-
-       BT_ASSERT(ir_fc);
-       ctf_field_class_int_set_props(fc, ir_fc);
-       return ir_fc;
-}
-
-static inline
-bt_field_class *ctf_field_class_enum_to_ir(struct ctx *ctx,
-               struct ctf_field_class_enum *fc)
-{
-       int ret;
-       bt_field_class *ir_fc;
-       uint64_t i;
-
-       if (fc->base.is_signed) {
-               ir_fc = bt_field_class_enumeration_signed_create(ctx->ir_tc);
-       } else {
-               ir_fc = bt_field_class_enumeration_unsigned_create(ctx->ir_tc);
-       }
-
-       BT_ASSERT(ir_fc);
-       ctf_field_class_int_set_props((void *) fc, ir_fc);
-
-       for (i = 0; i < fc->mappings->len; i++) {
-               struct ctf_field_class_enum_mapping *mapping =
-                       ctf_field_class_enum_borrow_mapping_by_index(fc, i);
-               void *range_set;
-               uint64_t range_i;
-
-               if (fc->base.is_signed) {
-                       range_set = bt_integer_range_set_signed_create();
-               } else {
-                       range_set = bt_integer_range_set_unsigned_create();
-               }
-
-               BT_ASSERT(range_set);
-
-               for (range_i = 0; range_i < mapping->ranges->len; range_i++) {
-                       struct ctf_range *range =
-                               ctf_field_class_enum_mapping_borrow_range_by_index(
-                                       mapping, range_i);
-
-                       if (fc->base.is_signed) {
-                               ret = bt_integer_range_set_signed_add_range(
-                                       range_set, range->lower.i,
-                                       range->upper.i);
-                       } else {
-                               ret = bt_integer_range_set_unsigned_add_range(
-                                       range_set, range->lower.u,
-                                       range->upper.u);
-                       }
-
-                       BT_ASSERT(ret == 0);
-               }
-
-               if (fc->base.is_signed) {
-                       ret = bt_field_class_enumeration_signed_add_mapping(
-                               ir_fc, mapping->label->str, range_set);
-                       BT_INTEGER_RANGE_SET_SIGNED_PUT_REF_AND_RESET(range_set);
-               } else {
-                       ret = bt_field_class_enumeration_unsigned_add_mapping(
-                               ir_fc, mapping->label->str, range_set);
-                       BT_INTEGER_RANGE_SET_UNSIGNED_PUT_REF_AND_RESET(range_set);
-               }
-
-               BT_ASSERT(ret == 0);
-       }
-
-       return ir_fc;
-}
-
-static inline
-bt_field_class *ctf_field_class_float_to_ir(struct ctx *ctx,
-               struct ctf_field_class_float *fc)
-{
-       bt_field_class *ir_fc;
-
-       if (fc->base.size == 32) {
-               ir_fc = bt_field_class_real_single_precision_create(ctx->ir_tc);
-       } else {
-               ir_fc = bt_field_class_real_double_precision_create(ctx->ir_tc);
-       }
-       BT_ASSERT(ir_fc);
-
-       return ir_fc;
-}
-
-static inline
-bt_field_class *ctf_field_class_string_to_ir(struct ctx *ctx,
-               struct ctf_field_class_string *fc)
-{
-       bt_field_class *ir_fc = bt_field_class_string_create(ctx->ir_tc);
-
-       BT_ASSERT(ir_fc);
-       return ir_fc;
-}
-
-static inline
-void translate_struct_field_class_members(struct ctx *ctx,
-               struct ctf_field_class_struct *fc, bt_field_class *ir_fc,
-               bool with_header_prefix,
-               struct ctf_field_class_struct *context_fc)
-{
-       uint64_t i;
-       int ret;
-
-       for (i = 0; i < fc->members->len; i++) {
-               struct ctf_named_field_class *named_fc =
-                       ctf_field_class_struct_borrow_member_by_index(fc, i);
-               bt_field_class *member_ir_fc;
-               const char *name = named_fc->name->str;
-
-               if (!named_fc->fc->in_ir) {
-                       continue;
-               }
-
-               member_ir_fc = ctf_field_class_to_ir(ctx, named_fc->fc);
-               BT_ASSERT(member_ir_fc);
-               ret = bt_field_class_structure_append_member(ir_fc, name,
-                       member_ir_fc);
-               BT_ASSERT(ret == 0);
-               bt_field_class_put_ref(member_ir_fc);
-       }
-}
-
-static inline
-bt_field_class *ctf_field_class_struct_to_ir(struct ctx *ctx,
-               struct ctf_field_class_struct *fc)
-{
-       bt_field_class *ir_fc = bt_field_class_structure_create(ctx->ir_tc);
-
-       BT_ASSERT(ir_fc);
-       translate_struct_field_class_members(ctx, fc, ir_fc, false, NULL);
-       return ir_fc;
-}
-
-static inline
-bt_field_class *borrow_ir_fc_from_field_path(struct ctx *ctx,
-               struct ctf_field_path *field_path)
-{
-       bt_field_class *ir_fc = NULL;
-       struct ctf_field_class *fc = ctf_field_path_borrow_field_class(
-               field_path, ctx->tc, ctx->sc, ctx->ec);
-
-       BT_ASSERT(fc);
-
-       if (fc->in_ir) {
-               ir_fc = fc->ir_fc;
-       }
-
-       return ir_fc;
-}
-
-static inline
-const void *find_ir_enum_field_class_mapping_by_label(const bt_field_class *fc,
-               const char *label, bool is_signed)
-{
-       const void *mapping = NULL;
-       uint64_t i;
-
-       for (i = 0; i < bt_field_class_enumeration_get_mapping_count(fc); i++) {
-               const bt_field_class_enumeration_mapping *this_mapping;
-               const void *spec_this_mapping;
-
-               if (is_signed) {
-                       spec_this_mapping =
-                               bt_field_class_enumeration_signed_borrow_mapping_by_index_const(
-                                       fc, i);
-                       this_mapping =
-                               bt_field_class_enumeration_signed_mapping_as_mapping_const(
-                                       spec_this_mapping);
-               } else {
-                       spec_this_mapping =
-                               bt_field_class_enumeration_unsigned_borrow_mapping_by_index_const(
-                                       fc, i);
-                       this_mapping =
-                               bt_field_class_enumeration_unsigned_mapping_as_mapping_const(
-                                       spec_this_mapping);
-               }
-
-               BT_ASSERT(this_mapping);
-               BT_ASSERT(spec_this_mapping);
-
-               if (strcmp(bt_field_class_enumeration_mapping_get_label(
-                               this_mapping), label) == 0) {
-                       mapping = spec_this_mapping;
-                       goto end;
-               }
-       }
-
-end:
-       return mapping;
-}
-
-static inline
-bt_field_class *ctf_field_class_variant_to_ir(struct ctx *ctx,
-               struct ctf_field_class_variant *fc)
-{
-       int ret;
-       bt_field_class *ir_fc;
-       uint64_t i;
-       bt_field_class *ir_tag_fc = NULL;
-
-       if (fc->tag_path.root != CTF_SCOPE_PACKET_HEADER &&
-                       fc->tag_path.root != CTF_SCOPE_EVENT_HEADER) {
-               ir_tag_fc = borrow_ir_fc_from_field_path(ctx, &fc->tag_path);
-               BT_ASSERT(ir_tag_fc);
-       }
-
-       ir_fc = bt_field_class_variant_create(ctx->ir_tc, ir_tag_fc);
-       BT_ASSERT(ir_fc);
-
-       for (i = 0; i < fc->options->len; i++) {
-               struct ctf_named_field_class *named_fc =
-                       ctf_field_class_variant_borrow_option_by_index(fc, i);
-               bt_field_class *option_ir_fc;
-
-               BT_ASSERT(named_fc->fc->in_ir);
-               option_ir_fc = ctf_field_class_to_ir(ctx, named_fc->fc);
-               BT_ASSERT(option_ir_fc);
-
-               if (ir_tag_fc) {
-                       /*
-                        * At this point the trace IR selector
-                        * (enumeration) field class already exists if
-                        * the variant is tagged (`ir_tag_fc`). This one
-                        * already contains range sets for its mappings,
-                        * so we just reuse the same, finding them by
-                        * matching a variant field class's option's
-                        * _original_ name (with a leading underscore,
-                        * possibly) with a selector field class's
-                        * mapping name.
-                        */
-                       if (fc->tag_fc->base.is_signed) {
-                               const bt_field_class_enumeration_signed_mapping *mapping =
-                                       find_ir_enum_field_class_mapping_by_label(
-                                               ir_tag_fc,
-                                               named_fc->orig_name->str, true);
-                               const bt_integer_range_set_signed *range_set;
-
-                               BT_ASSERT(mapping);
-                               range_set =
-                                       bt_field_class_enumeration_signed_mapping_borrow_ranges_const(
-                                               mapping);
-                               BT_ASSERT(range_set);
-                               ret = bt_field_class_variant_with_selector_field_integer_signed_append_option(
-                                       ir_fc, named_fc->name->str,
-                                       option_ir_fc, range_set);
-                       } else {
-                               const bt_field_class_enumeration_unsigned_mapping *mapping =
-                                       find_ir_enum_field_class_mapping_by_label(
-                                               ir_tag_fc,
-                                               named_fc->orig_name->str,
-                                               false);
-                               const bt_integer_range_set_unsigned *range_set;
-
-                               BT_ASSERT(mapping);
-                               range_set =
-                                       bt_field_class_enumeration_unsigned_mapping_borrow_ranges_const(
-                                               mapping);
-                               BT_ASSERT(range_set);
-                               ret = bt_field_class_variant_with_selector_field_integer_unsigned_append_option(
-                                       ir_fc, named_fc->name->str,
-                                       option_ir_fc, range_set);
-                       }
-               } else {
-                       ret = bt_field_class_variant_without_selector_append_option(
-                               ir_fc, named_fc->name->str, option_ir_fc);
-               }
-
-               BT_ASSERT(ret == 0);
-               bt_field_class_put_ref(option_ir_fc);
-       }
-
-       return ir_fc;
-}
-
-static inline
-bt_field_class *ctf_field_class_array_to_ir(struct ctx *ctx,
-               struct ctf_field_class_array *fc)
-{
-       bt_field_class *ir_fc;
-       bt_field_class *elem_ir_fc;
-
-       if (fc->base.is_text) {
-               ir_fc = bt_field_class_string_create(ctx->ir_tc);
-               BT_ASSERT(ir_fc);
-               goto end;
-       }
-
-       elem_ir_fc = ctf_field_class_to_ir(ctx, fc->base.elem_fc);
-       BT_ASSERT(elem_ir_fc);
-       ir_fc = bt_field_class_array_static_create(ctx->ir_tc, elem_ir_fc,
-               fc->length);
-       BT_ASSERT(ir_fc);
-       bt_field_class_put_ref(elem_ir_fc);
-
-end:
-       return ir_fc;
-}
-
-static inline
-bt_field_class *ctf_field_class_sequence_to_ir(struct ctx *ctx,
-               struct ctf_field_class_sequence *fc)
-{
-       bt_field_class *ir_fc;
-       bt_field_class *elem_ir_fc;
-       bt_field_class *length_fc = NULL;
-
-       if (fc->base.is_text) {
-               ir_fc = bt_field_class_string_create(ctx->ir_tc);
-               BT_ASSERT(ir_fc);
-               goto end;
-       }
-
-       elem_ir_fc = ctf_field_class_to_ir(ctx, fc->base.elem_fc);
-       BT_ASSERT(elem_ir_fc);
-
-       if (fc->length_path.root != CTF_SCOPE_PACKET_HEADER &&
-                       fc->length_path.root != CTF_SCOPE_EVENT_HEADER) {
-               length_fc = borrow_ir_fc_from_field_path(ctx, &fc->length_path);
-               BT_ASSERT(length_fc);
-       }
-
-       ir_fc = bt_field_class_array_dynamic_create(ctx->ir_tc, elem_ir_fc,
-               length_fc);
-       BT_ASSERT(ir_fc);
-       bt_field_class_put_ref(elem_ir_fc);
-       BT_ASSERT(ir_fc);
-
-end:
-       return ir_fc;
-}
-
-static inline
-bt_field_class *ctf_field_class_to_ir(struct ctx *ctx,
-               struct ctf_field_class *fc)
-{
-       bt_field_class *ir_fc = NULL;
-
-       BT_ASSERT(fc);
-       BT_ASSERT(fc->in_ir);
-
-       switch (fc->type) {
-       case CTF_FIELD_CLASS_TYPE_INT:
-               ir_fc = ctf_field_class_int_to_ir(ctx, (void *) fc);
-               break;
-       case CTF_FIELD_CLASS_TYPE_ENUM:
-               ir_fc = ctf_field_class_enum_to_ir(ctx, (void *) fc);
-               break;
-       case CTF_FIELD_CLASS_TYPE_FLOAT:
-               ir_fc = ctf_field_class_float_to_ir(ctx, (void *) fc);
-               break;
-       case CTF_FIELD_CLASS_TYPE_STRING:
-               ir_fc = ctf_field_class_string_to_ir(ctx, (void *) fc);
-               break;
-       case CTF_FIELD_CLASS_TYPE_STRUCT:
-               ir_fc = ctf_field_class_struct_to_ir(ctx, (void *) fc);
-               break;
-       case CTF_FIELD_CLASS_TYPE_ARRAY:
-               ir_fc = ctf_field_class_array_to_ir(ctx, (void *) fc);
-               break;
-       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-               ir_fc = ctf_field_class_sequence_to_ir(ctx, (void *) fc);
-               break;
-       case CTF_FIELD_CLASS_TYPE_VARIANT:
-               ir_fc = ctf_field_class_variant_to_ir(ctx, (void *) fc);
-               break;
-       default:
-               bt_common_abort();
-       }
-
-       fc->ir_fc = ir_fc;
-       return ir_fc;
-}
-
-static inline
-bool ctf_field_class_struct_has_immediate_member_in_ir(
-               struct ctf_field_class_struct *fc)
-{
-       uint64_t i;
-       bool has_immediate_member_in_ir = false;
-
-       /*
-        * If the structure field class has no members at all, then it
-        * was an empty structure in the beginning, so leave it existing
-        * and empty.
-        */
-       if (fc->members->len == 0) {
-               has_immediate_member_in_ir = true;
-               goto end;
-       }
-
-       for (i = 0; i < fc->members->len; i++) {
-               struct ctf_named_field_class *named_fc =
-                       ctf_field_class_struct_borrow_member_by_index(fc, i);
-
-               if (named_fc->fc->in_ir) {
-                       has_immediate_member_in_ir = true;
-                       goto end;
-               }
-       }
-
-end:
-       return has_immediate_member_in_ir;
-}
-
-static inline
-bt_field_class *scope_ctf_field_class_to_ir(struct ctx *ctx)
-{
-       bt_field_class *ir_fc = NULL;
-       struct ctf_field_class *fc = NULL;
-
-       switch (ctx->scope) {
-       case CTF_SCOPE_PACKET_CONTEXT:
-               fc = ctx->sc->packet_context_fc;
-               break;
-       case CTF_SCOPE_EVENT_COMMON_CONTEXT:
-               fc = ctx->sc->event_common_context_fc;
-               break;
-       case CTF_SCOPE_EVENT_SPECIFIC_CONTEXT:
-               fc = ctx->ec->spec_context_fc;
-               break;
-       case CTF_SCOPE_EVENT_PAYLOAD:
-               fc = ctx->ec->payload_fc;
-               break;
-       default:
-               bt_common_abort();
-       }
-
-       if (fc && ctf_field_class_struct_has_immediate_member_in_ir(
-                       (void *) fc)) {
-               ir_fc = ctf_field_class_to_ir(ctx, fc);
-       }
-
-       return ir_fc;
-}
-
-static inline
-void ctf_event_class_to_ir(struct ctx *ctx)
-{
-       int ret;
-       bt_event_class *ir_ec = NULL;
-       bt_field_class *ir_fc;
-
-       BT_ASSERT(ctx->ec);
-
-       if (ctx->ec->is_translated) {
-               ir_ec = bt_stream_class_borrow_event_class_by_id(
-                       ctx->ir_sc, ctx->ec->id);
-               BT_ASSERT(ir_ec);
-               goto end;
-       }
-
-       ir_ec = bt_event_class_create_with_id(ctx->ir_sc, ctx->ec->id);
-       BT_ASSERT(ir_ec);
-       bt_event_class_put_ref(ir_ec);
-       ctx->scope = CTF_SCOPE_EVENT_SPECIFIC_CONTEXT;
-       ir_fc = scope_ctf_field_class_to_ir(ctx);
-       if (ir_fc) {
-               ret = bt_event_class_set_specific_context_field_class(
-                       ir_ec, ir_fc);
-               BT_ASSERT(ret == 0);
-               bt_field_class_put_ref(ir_fc);
-       }
-
-       ctx->scope = CTF_SCOPE_EVENT_PAYLOAD;
-       ir_fc = scope_ctf_field_class_to_ir(ctx);
-       if (ir_fc) {
-               ret = bt_event_class_set_payload_field_class(ir_ec,
-                       ir_fc);
-               BT_ASSERT(ret == 0);
-               bt_field_class_put_ref(ir_fc);
-       }
-
-       if (ctx->ec->name->len > 0) {
-               ret = bt_event_class_set_name(ir_ec, ctx->ec->name->str);
-               BT_ASSERT(ret == 0);
-       }
-
-       if (ctx->ec->emf_uri->len > 0) {
-               ret = bt_event_class_set_emf_uri(ir_ec, ctx->ec->emf_uri->str);
-               BT_ASSERT(ret == 0);
-       }
-
-       if (ctx->ec->is_log_level_set) {
-               bt_event_class_set_log_level(ir_ec, ctx->ec->log_level);
-       }
-
-       ctx->ec->is_translated = true;
-       ctx->ec->ir_ec = ir_ec;
-
-end:
-       return;
-}
-
-
-static inline
-void ctf_stream_class_to_ir(struct ctx *ctx)
-{
-       int ret;
-       bt_field_class *ir_fc;
-
-       BT_ASSERT(ctx->sc);
-
-       if (ctx->sc->is_translated) {
-               ctx->ir_sc = bt_trace_class_borrow_stream_class_by_id(
-                       ctx->ir_tc, ctx->sc->id);
-               BT_ASSERT(ctx->ir_sc);
-               goto end;
-       }
-
-       ctx->ir_sc = bt_stream_class_create_with_id(ctx->ir_tc, ctx->sc->id);
-       BT_ASSERT(ctx->ir_sc);
-       bt_stream_class_put_ref(ctx->ir_sc);
-
-       if (ctx->sc->default_clock_class) {
-               BT_ASSERT(ctx->sc->default_clock_class->ir_cc);
-               ret = bt_stream_class_set_default_clock_class(ctx->ir_sc,
-                       ctx->sc->default_clock_class->ir_cc);
-               BT_ASSERT(ret == 0);
-       }
-
-       bt_stream_class_set_supports_packets(ctx->ir_sc, BT_TRUE,
-               ctx->sc->packets_have_ts_begin, ctx->sc->packets_have_ts_end);
-       bt_stream_class_set_supports_discarded_events(ctx->ir_sc,
-               ctx->sc->has_discarded_events,
-               ctx->sc->discarded_events_have_default_cs);
-       bt_stream_class_set_supports_discarded_packets(ctx->ir_sc,
-               ctx->sc->has_discarded_packets,
-               ctx->sc->discarded_packets_have_default_cs);
-       ctx->scope = CTF_SCOPE_PACKET_CONTEXT;
-       ir_fc = scope_ctf_field_class_to_ir(ctx);
-       if (ir_fc) {
-               ret = bt_stream_class_set_packet_context_field_class(
-                       ctx->ir_sc, ir_fc);
-               BT_ASSERT(ret == 0);
-               bt_field_class_put_ref(ir_fc);
-       }
-
-       ctx->scope = CTF_SCOPE_EVENT_COMMON_CONTEXT;
-       ir_fc = scope_ctf_field_class_to_ir(ctx);
-       if (ir_fc) {
-               ret = bt_stream_class_set_event_common_context_field_class(
-                       ctx->ir_sc, ir_fc);
-               BT_ASSERT(ret == 0);
-               bt_field_class_put_ref(ir_fc);
-       }
-
-       bt_stream_class_set_assigns_automatic_event_class_id(ctx->ir_sc,
-               BT_FALSE);
-       bt_stream_class_set_assigns_automatic_stream_id(ctx->ir_sc, BT_FALSE);
-
-       ctx->sc->is_translated = true;
-       ctx->sc->ir_sc = ctx->ir_sc;
-
-end:
-       return;
-}
-
-static inline
-void ctf_clock_class_to_ir(bt_clock_class *ir_cc, struct ctf_clock_class *cc)
-{
-       int ret;
-
-       if (strlen(cc->name->str) > 0) {
-               ret = bt_clock_class_set_name(ir_cc, cc->name->str);
-               BT_ASSERT(ret == 0);
-       }
-
-       if (strlen(cc->description->str) > 0) {
-               ret = bt_clock_class_set_description(ir_cc, cc->description->str);
-               BT_ASSERT(ret == 0);
-       }
-
-       bt_clock_class_set_frequency(ir_cc, cc->frequency);
-       bt_clock_class_set_precision(ir_cc, cc->precision);
-       bt_clock_class_set_offset(ir_cc, cc->offset_seconds, cc->offset_cycles);
-
-       if (cc->has_uuid) {
-               bt_clock_class_set_uuid(ir_cc, cc->uuid);
-       }
-
-       bt_clock_class_set_origin_is_unix_epoch(ir_cc, cc->is_absolute);
-}
-
-static inline
-int ctf_trace_class_to_ir(struct ctx *ctx)
-{
-       int ret = 0;
-       uint64_t i;
-
-       BT_ASSERT(ctx->tc);
-       BT_ASSERT(ctx->ir_tc);
-
-       if (ctx->tc->is_translated) {
-               goto end;
-       }
-
-       for (i = 0; i < ctx->tc->clock_classes->len; i++) {
-               struct ctf_clock_class *cc = ctx->tc->clock_classes->pdata[i];
-
-               cc->ir_cc = bt_clock_class_create(ctx->self_comp);
-               ctf_clock_class_to_ir(cc->ir_cc, cc);
-       }
-
-       bt_trace_class_set_assigns_automatic_stream_class_id(ctx->ir_tc,
-               BT_FALSE);
-       ctx->tc->is_translated = true;
-       ctx->tc->ir_tc = ctx->ir_tc;
-
-end:
-       return ret;
-}
-
-BT_HIDDEN
-int ctf_trace_class_translate(bt_self_component *self_comp,
-               bt_trace_class *ir_tc, struct ctf_trace_class *tc)
-{
-       int ret = 0;
-       uint64_t i;
-       struct ctx ctx = { 0 };
-
-       ctx.self_comp = self_comp;
-       ctx.tc = tc;
-       ctx.ir_tc = ir_tc;
-       ret = ctf_trace_class_to_ir(&ctx);
-       if (ret) {
-               goto end;
-       }
-
-       for (i = 0; i < tc->stream_classes->len; i++) {
-               uint64_t j;
-               ctx.sc = tc->stream_classes->pdata[i];
-
-               ctf_stream_class_to_ir(&ctx);
-
-               for (j = 0; j < ctx.sc->event_classes->len; j++) {
-                       ctx.ec = ctx.sc->event_classes->pdata[j];
-
-                       ctf_event_class_to_ir(&ctx);
-                       ctx.ec = NULL;
-               }
-
-               ctx.sc = NULL;
-       }
-
-end:
-       return ret;
-}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-translate.cpp b/src/plugins/ctf/common/metadata/ctf-meta-translate.cpp
new file mode 100644 (file)
index 0000000..0d66dac
--- /dev/null
@@ -0,0 +1,707 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include <babeltrace2/babeltrace.h>
+#include "common/macros.h"
+#include "common/assert.h"
+#include <glib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "ctf-meta-visitors.hpp"
+
+struct ctx {
+       bt_self_component *self_comp;
+       bt_trace_class *ir_tc;
+       bt_stream_class *ir_sc;
+       struct ctf_trace_class *tc;
+       struct ctf_stream_class *sc;
+       struct ctf_event_class *ec;
+       enum ctf_scope scope;
+};
+
+static inline
+bt_field_class *ctf_field_class_to_ir(struct ctx *ctx,
+               struct ctf_field_class *fc);
+
+static inline
+void ctf_field_class_int_set_props(struct ctf_field_class_int *fc,
+               bt_field_class *ir_fc)
+{
+       bt_field_class_integer_set_field_value_range(ir_fc,
+               fc->base.size);
+       bt_field_class_integer_set_preferred_display_base(ir_fc,
+               fc->disp_base);
+}
+
+static inline
+bt_field_class *ctf_field_class_int_to_ir(struct ctx *ctx,
+               struct ctf_field_class_int *fc)
+{
+       bt_field_class *ir_fc;
+
+       if (fc->is_signed) {
+               ir_fc = bt_field_class_integer_signed_create(ctx->ir_tc);
+       } else {
+               ir_fc = bt_field_class_integer_unsigned_create(ctx->ir_tc);
+       }
+
+       BT_ASSERT(ir_fc);
+       ctf_field_class_int_set_props(fc, ir_fc);
+       return ir_fc;
+}
+
+static inline
+bt_field_class *ctf_field_class_enum_to_ir(struct ctx *ctx,
+               struct ctf_field_class_enum *fc)
+{
+       int ret;
+       bt_field_class *ir_fc;
+       uint64_t i;
+
+       if (fc->base.is_signed) {
+               ir_fc = bt_field_class_enumeration_signed_create(ctx->ir_tc);
+       } else {
+               ir_fc = bt_field_class_enumeration_unsigned_create(ctx->ir_tc);
+       }
+
+       BT_ASSERT(ir_fc);
+       ctf_field_class_int_set_props(&fc->base, ir_fc);
+
+       for (i = 0; i < fc->mappings->len; i++) {
+               struct ctf_field_class_enum_mapping *mapping =
+                       ctf_field_class_enum_borrow_mapping_by_index(fc, i);
+               bt_integer_range_set_signed *range_set_signed = NULL;
+               bt_integer_range_set_unsigned *range_set_unsigned = NULL;
+               uint64_t range_i;
+
+               if (fc->base.is_signed) {
+                       range_set_signed = bt_integer_range_set_signed_create();
+                       BT_ASSERT(range_set_signed);
+               } else {
+                       range_set_unsigned = bt_integer_range_set_unsigned_create();
+                       BT_ASSERT(range_set_unsigned);
+               }
+
+               for (range_i = 0; range_i < mapping->ranges->len; range_i++) {
+                       struct ctf_range *range =
+                               ctf_field_class_enum_mapping_borrow_range_by_index(
+                                       mapping, range_i);
+
+                       if (fc->base.is_signed) {
+                               ret = bt_integer_range_set_signed_add_range(
+                                       range_set_signed, range->lower.i,
+                                       range->upper.i);
+                       } else {
+                               ret = bt_integer_range_set_unsigned_add_range(
+                                       range_set_unsigned, range->lower.u,
+                                       range->upper.u);
+                       }
+
+                       BT_ASSERT(ret == 0);
+               }
+
+               if (fc->base.is_signed) {
+                       ret = bt_field_class_enumeration_signed_add_mapping(
+                               ir_fc, mapping->label->str, range_set_signed);
+                       BT_INTEGER_RANGE_SET_SIGNED_PUT_REF_AND_RESET(range_set_signed);
+               } else {
+                       ret = bt_field_class_enumeration_unsigned_add_mapping(
+                               ir_fc, mapping->label->str, range_set_unsigned);
+                       BT_INTEGER_RANGE_SET_UNSIGNED_PUT_REF_AND_RESET(range_set_unsigned);
+               }
+
+               BT_ASSERT(ret == 0);
+       }
+
+       return ir_fc;
+}
+
+static inline
+bt_field_class *ctf_field_class_float_to_ir(struct ctx *ctx,
+               struct ctf_field_class_float *fc)
+{
+       bt_field_class *ir_fc;
+
+       if (fc->base.size == 32) {
+               ir_fc = bt_field_class_real_single_precision_create(ctx->ir_tc);
+       } else {
+               ir_fc = bt_field_class_real_double_precision_create(ctx->ir_tc);
+       }
+       BT_ASSERT(ir_fc);
+
+       return ir_fc;
+}
+
+static inline
+bt_field_class *ctf_field_class_string_to_ir(struct ctx *ctx,
+               struct ctf_field_class_string *fc)
+{
+       bt_field_class *ir_fc = bt_field_class_string_create(ctx->ir_tc);
+
+       BT_ASSERT(ir_fc);
+       return ir_fc;
+}
+
+static inline
+void translate_struct_field_class_members(struct ctx *ctx,
+               struct ctf_field_class_struct *fc, bt_field_class *ir_fc,
+               bool with_header_prefix,
+               struct ctf_field_class_struct *context_fc)
+{
+       uint64_t i;
+       int ret;
+
+       for (i = 0; i < fc->members->len; i++) {
+               struct ctf_named_field_class *named_fc =
+                       ctf_field_class_struct_borrow_member_by_index(fc, i);
+               bt_field_class *member_ir_fc;
+               const char *name = named_fc->name->str;
+
+               if (!named_fc->fc->in_ir) {
+                       continue;
+               }
+
+               member_ir_fc = ctf_field_class_to_ir(ctx, named_fc->fc);
+               BT_ASSERT(member_ir_fc);
+               ret = bt_field_class_structure_append_member(ir_fc, name,
+                       member_ir_fc);
+               BT_ASSERT(ret == 0);
+               bt_field_class_put_ref(member_ir_fc);
+       }
+}
+
+static inline
+bt_field_class *ctf_field_class_struct_to_ir(struct ctx *ctx,
+               struct ctf_field_class_struct *fc)
+{
+       bt_field_class *ir_fc = bt_field_class_structure_create(ctx->ir_tc);
+
+       BT_ASSERT(ir_fc);
+       translate_struct_field_class_members(ctx, fc, ir_fc, false, NULL);
+       return ir_fc;
+}
+
+static inline
+bt_field_class *borrow_ir_fc_from_field_path(struct ctx *ctx,
+               struct ctf_field_path *field_path)
+{
+       bt_field_class *ir_fc = NULL;
+       struct ctf_field_class *fc = ctf_field_path_borrow_field_class(
+               field_path, ctx->tc, ctx->sc, ctx->ec);
+
+       BT_ASSERT(fc);
+
+       if (fc->in_ir) {
+               ir_fc = fc->ir_fc;
+       }
+
+       return ir_fc;
+}
+
+static inline
+const bt_field_class_enumeration_mapping *find_ir_enum_field_class_mapping_by_label(
+               const bt_field_class *fc, const char *label, bool is_signed)
+{
+       const bt_field_class_enumeration_mapping *mapping = NULL;
+       uint64_t i;
+
+       for (i = 0; i < bt_field_class_enumeration_get_mapping_count(fc); i++) {
+               const bt_field_class_enumeration_mapping *this_mapping;
+               const bt_field_class_enumeration_signed_mapping *signed_this_mapping = NULL;
+               const bt_field_class_enumeration_unsigned_mapping *unsigned_this_mapping = NULL;
+
+               if (is_signed) {
+                       signed_this_mapping =
+                               bt_field_class_enumeration_signed_borrow_mapping_by_index_const(
+                                       fc, i);
+                       BT_ASSERT(signed_this_mapping);
+                       this_mapping =
+                               bt_field_class_enumeration_signed_mapping_as_mapping_const(
+                                       signed_this_mapping);
+               } else {
+                       unsigned_this_mapping =
+                               bt_field_class_enumeration_unsigned_borrow_mapping_by_index_const(
+                                       fc, i);
+                       BT_ASSERT(unsigned_this_mapping);
+                       this_mapping =
+                               bt_field_class_enumeration_unsigned_mapping_as_mapping_const(
+                                       unsigned_this_mapping);
+               }
+
+               BT_ASSERT(this_mapping);
+
+               if (strcmp(bt_field_class_enumeration_mapping_get_label(
+                               this_mapping), label) == 0) {
+                       mapping = this_mapping;
+                       goto end;
+               }
+       }
+
+end:
+       return mapping;
+}
+
+static inline
+bt_field_class *ctf_field_class_variant_to_ir(struct ctx *ctx,
+               struct ctf_field_class_variant *fc)
+{
+       int ret;
+       bt_field_class *ir_fc;
+       uint64_t i;
+       bt_field_class *ir_tag_fc = NULL;
+
+       if (fc->tag_path.root != CTF_SCOPE_PACKET_HEADER &&
+                       fc->tag_path.root != CTF_SCOPE_EVENT_HEADER) {
+               ir_tag_fc = borrow_ir_fc_from_field_path(ctx, &fc->tag_path);
+               BT_ASSERT(ir_tag_fc);
+       }
+
+       ir_fc = bt_field_class_variant_create(ctx->ir_tc, ir_tag_fc);
+       BT_ASSERT(ir_fc);
+
+       for (i = 0; i < fc->options->len; i++) {
+               struct ctf_named_field_class *named_fc =
+                       ctf_field_class_variant_borrow_option_by_index(fc, i);
+               bt_field_class *option_ir_fc;
+
+               BT_ASSERT(named_fc->fc->in_ir);
+               option_ir_fc = ctf_field_class_to_ir(ctx, named_fc->fc);
+               BT_ASSERT(option_ir_fc);
+
+               if (ir_tag_fc) {
+                       /*
+                        * At this point the trace IR selector
+                        * (enumeration) field class already exists if
+                        * the variant is tagged (`ir_tag_fc`). This one
+                        * already contains range sets for its mappings,
+                        * so we just reuse the same, finding them by
+                        * matching a variant field class's option's
+                        * _original_ name (with a leading underscore,
+                        * possibly) with a selector field class's
+                        * mapping name.
+                        */
+                       if (fc->tag_fc->base.is_signed) {
+                               const bt_field_class_enumeration_signed_mapping *mapping =
+                                       (bt_field_class_enumeration_signed_mapping *)
+                                               find_ir_enum_field_class_mapping_by_label(
+                                                       ir_tag_fc,
+                                                       named_fc->orig_name->str, true);
+                               const bt_integer_range_set_signed *range_set;
+
+                               BT_ASSERT(mapping);
+                               range_set =
+                                       bt_field_class_enumeration_signed_mapping_borrow_ranges_const(
+                                               mapping);
+                               BT_ASSERT(range_set);
+                               ret = bt_field_class_variant_with_selector_field_integer_signed_append_option(
+                                       ir_fc, named_fc->name->str,
+                                       option_ir_fc, range_set);
+                       } else {
+                               const bt_field_class_enumeration_unsigned_mapping *mapping =
+                                       (bt_field_class_enumeration_unsigned_mapping *)
+                                               find_ir_enum_field_class_mapping_by_label(
+                                                       ir_tag_fc,
+                                                       named_fc->orig_name->str,
+                                                       false);
+                               const bt_integer_range_set_unsigned *range_set;
+
+                               BT_ASSERT(mapping);
+                               range_set =
+                                       bt_field_class_enumeration_unsigned_mapping_borrow_ranges_const(
+                                               mapping);
+                               BT_ASSERT(range_set);
+                               ret = bt_field_class_variant_with_selector_field_integer_unsigned_append_option(
+                                       ir_fc, named_fc->name->str,
+                                       option_ir_fc, range_set);
+                       }
+               } else {
+                       ret = bt_field_class_variant_without_selector_append_option(
+                               ir_fc, named_fc->name->str, option_ir_fc);
+               }
+
+               BT_ASSERT(ret == 0);
+               bt_field_class_put_ref(option_ir_fc);
+       }
+
+       return ir_fc;
+}
+
+static inline
+bt_field_class *ctf_field_class_array_to_ir(struct ctx *ctx,
+               struct ctf_field_class_array *fc)
+{
+       bt_field_class *ir_fc;
+       bt_field_class *elem_ir_fc;
+
+       if (fc->base.is_text) {
+               ir_fc = bt_field_class_string_create(ctx->ir_tc);
+               BT_ASSERT(ir_fc);
+               goto end;
+       }
+
+       elem_ir_fc = ctf_field_class_to_ir(ctx, fc->base.elem_fc);
+       BT_ASSERT(elem_ir_fc);
+       ir_fc = bt_field_class_array_static_create(ctx->ir_tc, elem_ir_fc,
+               fc->length);
+       BT_ASSERT(ir_fc);
+       bt_field_class_put_ref(elem_ir_fc);
+
+end:
+       return ir_fc;
+}
+
+static inline
+bt_field_class *ctf_field_class_sequence_to_ir(struct ctx *ctx,
+               struct ctf_field_class_sequence *fc)
+{
+       bt_field_class *ir_fc;
+       bt_field_class *elem_ir_fc;
+       bt_field_class *length_fc = NULL;
+
+       if (fc->base.is_text) {
+               ir_fc = bt_field_class_string_create(ctx->ir_tc);
+               BT_ASSERT(ir_fc);
+               goto end;
+       }
+
+       elem_ir_fc = ctf_field_class_to_ir(ctx, fc->base.elem_fc);
+       BT_ASSERT(elem_ir_fc);
+
+       if (fc->length_path.root != CTF_SCOPE_PACKET_HEADER &&
+                       fc->length_path.root != CTF_SCOPE_EVENT_HEADER) {
+               length_fc = borrow_ir_fc_from_field_path(ctx, &fc->length_path);
+               BT_ASSERT(length_fc);
+       }
+
+       ir_fc = bt_field_class_array_dynamic_create(ctx->ir_tc, elem_ir_fc,
+               length_fc);
+       BT_ASSERT(ir_fc);
+       bt_field_class_put_ref(elem_ir_fc);
+       BT_ASSERT(ir_fc);
+
+end:
+       return ir_fc;
+}
+
+static inline
+bt_field_class *ctf_field_class_to_ir(struct ctx *ctx,
+               struct ctf_field_class *fc)
+{
+       bt_field_class *ir_fc = NULL;
+
+       BT_ASSERT(fc);
+       BT_ASSERT(fc->in_ir);
+
+       switch (fc->type) {
+       case CTF_FIELD_CLASS_TYPE_INT:
+               ir_fc = ctf_field_class_int_to_ir(ctx, ctf_field_class_as_int(fc));
+               break;
+       case CTF_FIELD_CLASS_TYPE_ENUM:
+               ir_fc = ctf_field_class_enum_to_ir(ctx, ctf_field_class_as_enum(fc));
+               break;
+       case CTF_FIELD_CLASS_TYPE_FLOAT:
+               ir_fc = ctf_field_class_float_to_ir(ctx, ctf_field_class_as_float(fc));
+               break;
+       case CTF_FIELD_CLASS_TYPE_STRING:
+               ir_fc = ctf_field_class_string_to_ir(ctx, ctf_field_class_as_string(fc));
+               break;
+       case CTF_FIELD_CLASS_TYPE_STRUCT:
+               ir_fc = ctf_field_class_struct_to_ir(ctx, ctf_field_class_as_struct(fc));
+               break;
+       case CTF_FIELD_CLASS_TYPE_ARRAY:
+               ir_fc = ctf_field_class_array_to_ir(ctx, ctf_field_class_as_array(fc));
+               break;
+       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+               ir_fc = ctf_field_class_sequence_to_ir(ctx, ctf_field_class_as_sequence(fc));
+               break;
+       case CTF_FIELD_CLASS_TYPE_VARIANT:
+               ir_fc = ctf_field_class_variant_to_ir(ctx, ctf_field_class_as_variant(fc));
+               break;
+       default:
+               bt_common_abort();
+       }
+
+       fc->ir_fc = ir_fc;
+       return ir_fc;
+}
+
+static inline
+bool ctf_field_class_struct_has_immediate_member_in_ir(
+               struct ctf_field_class_struct *fc)
+{
+       uint64_t i;
+       bool has_immediate_member_in_ir = false;
+
+       /*
+        * If the structure field class has no members at all, then it
+        * was an empty structure in the beginning, so leave it existing
+        * and empty.
+        */
+       if (fc->members->len == 0) {
+               has_immediate_member_in_ir = true;
+               goto end;
+       }
+
+       for (i = 0; i < fc->members->len; i++) {
+               struct ctf_named_field_class *named_fc =
+                       ctf_field_class_struct_borrow_member_by_index(fc, i);
+
+               if (named_fc->fc->in_ir) {
+                       has_immediate_member_in_ir = true;
+                       goto end;
+               }
+       }
+
+end:
+       return has_immediate_member_in_ir;
+}
+
+static inline
+bt_field_class *scope_ctf_field_class_to_ir(struct ctx *ctx)
+{
+       bt_field_class *ir_fc = NULL;
+       struct ctf_field_class *fc = NULL;
+
+       switch (ctx->scope) {
+       case CTF_SCOPE_PACKET_CONTEXT:
+               fc = ctx->sc->packet_context_fc;
+               break;
+       case CTF_SCOPE_EVENT_COMMON_CONTEXT:
+               fc = ctx->sc->event_common_context_fc;
+               break;
+       case CTF_SCOPE_EVENT_SPECIFIC_CONTEXT:
+               fc = ctx->ec->spec_context_fc;
+               break;
+       case CTF_SCOPE_EVENT_PAYLOAD:
+               fc = ctx->ec->payload_fc;
+               break;
+       default:
+               bt_common_abort();
+       }
+
+       if (fc && ctf_field_class_struct_has_immediate_member_in_ir(
+                       ctf_field_class_as_struct(fc))) {
+               ir_fc = ctf_field_class_to_ir(ctx, fc);
+       }
+
+       return ir_fc;
+}
+
+static inline
+void ctf_event_class_to_ir(struct ctx *ctx)
+{
+       int ret;
+       bt_event_class *ir_ec = NULL;
+       bt_field_class *ir_fc;
+
+       BT_ASSERT(ctx->ec);
+
+       if (ctx->ec->is_translated) {
+               ir_ec = bt_stream_class_borrow_event_class_by_id(
+                       ctx->ir_sc, ctx->ec->id);
+               BT_ASSERT(ir_ec);
+               goto end;
+       }
+
+       ir_ec = bt_event_class_create_with_id(ctx->ir_sc, ctx->ec->id);
+       BT_ASSERT(ir_ec);
+       bt_event_class_put_ref(ir_ec);
+       ctx->scope = CTF_SCOPE_EVENT_SPECIFIC_CONTEXT;
+       ir_fc = scope_ctf_field_class_to_ir(ctx);
+       if (ir_fc) {
+               ret = bt_event_class_set_specific_context_field_class(
+                       ir_ec, ir_fc);
+               BT_ASSERT(ret == 0);
+               bt_field_class_put_ref(ir_fc);
+       }
+
+       ctx->scope = CTF_SCOPE_EVENT_PAYLOAD;
+       ir_fc = scope_ctf_field_class_to_ir(ctx);
+       if (ir_fc) {
+               ret = bt_event_class_set_payload_field_class(ir_ec,
+                       ir_fc);
+               BT_ASSERT(ret == 0);
+               bt_field_class_put_ref(ir_fc);
+       }
+
+       if (ctx->ec->name->len > 0) {
+               ret = bt_event_class_set_name(ir_ec, ctx->ec->name->str);
+               BT_ASSERT(ret == 0);
+       }
+
+       if (ctx->ec->emf_uri->len > 0) {
+               ret = bt_event_class_set_emf_uri(ir_ec, ctx->ec->emf_uri->str);
+               BT_ASSERT(ret == 0);
+       }
+
+       if (ctx->ec->is_log_level_set) {
+               bt_event_class_set_log_level(ir_ec, ctx->ec->log_level);
+       }
+
+       ctx->ec->is_translated = true;
+       ctx->ec->ir_ec = ir_ec;
+
+end:
+       return;
+}
+
+
+static inline
+void ctf_stream_class_to_ir(struct ctx *ctx)
+{
+       int ret;
+       bt_field_class *ir_fc;
+
+       BT_ASSERT(ctx->sc);
+
+       if (ctx->sc->is_translated) {
+               ctx->ir_sc = bt_trace_class_borrow_stream_class_by_id(
+                       ctx->ir_tc, ctx->sc->id);
+               BT_ASSERT(ctx->ir_sc);
+               goto end;
+       }
+
+       ctx->ir_sc = bt_stream_class_create_with_id(ctx->ir_tc, ctx->sc->id);
+       BT_ASSERT(ctx->ir_sc);
+       bt_stream_class_put_ref(ctx->ir_sc);
+
+       if (ctx->sc->default_clock_class) {
+               BT_ASSERT(ctx->sc->default_clock_class->ir_cc);
+               ret = bt_stream_class_set_default_clock_class(ctx->ir_sc,
+                       ctx->sc->default_clock_class->ir_cc);
+               BT_ASSERT(ret == 0);
+       }
+
+       bt_stream_class_set_supports_packets(ctx->ir_sc, BT_TRUE,
+               ctx->sc->packets_have_ts_begin, ctx->sc->packets_have_ts_end);
+       bt_stream_class_set_supports_discarded_events(ctx->ir_sc,
+               ctx->sc->has_discarded_events,
+               ctx->sc->discarded_events_have_default_cs);
+       bt_stream_class_set_supports_discarded_packets(ctx->ir_sc,
+               ctx->sc->has_discarded_packets,
+               ctx->sc->discarded_packets_have_default_cs);
+       ctx->scope = CTF_SCOPE_PACKET_CONTEXT;
+       ir_fc = scope_ctf_field_class_to_ir(ctx);
+       if (ir_fc) {
+               ret = bt_stream_class_set_packet_context_field_class(
+                       ctx->ir_sc, ir_fc);
+               BT_ASSERT(ret == 0);
+               bt_field_class_put_ref(ir_fc);
+       }
+
+       ctx->scope = CTF_SCOPE_EVENT_COMMON_CONTEXT;
+       ir_fc = scope_ctf_field_class_to_ir(ctx);
+       if (ir_fc) {
+               ret = bt_stream_class_set_event_common_context_field_class(
+                       ctx->ir_sc, ir_fc);
+               BT_ASSERT(ret == 0);
+               bt_field_class_put_ref(ir_fc);
+       }
+
+       bt_stream_class_set_assigns_automatic_event_class_id(ctx->ir_sc,
+               BT_FALSE);
+       bt_stream_class_set_assigns_automatic_stream_id(ctx->ir_sc, BT_FALSE);
+
+       ctx->sc->is_translated = true;
+       ctx->sc->ir_sc = ctx->ir_sc;
+
+end:
+       return;
+}
+
+static inline
+void ctf_clock_class_to_ir(bt_clock_class *ir_cc, struct ctf_clock_class *cc)
+{
+       int ret;
+
+       if (strlen(cc->name->str) > 0) {
+               ret = bt_clock_class_set_name(ir_cc, cc->name->str);
+               BT_ASSERT(ret == 0);
+       }
+
+       if (strlen(cc->description->str) > 0) {
+               ret = bt_clock_class_set_description(ir_cc, cc->description->str);
+               BT_ASSERT(ret == 0);
+       }
+
+       bt_clock_class_set_frequency(ir_cc, cc->frequency);
+       bt_clock_class_set_precision(ir_cc, cc->precision);
+       bt_clock_class_set_offset(ir_cc, cc->offset_seconds, cc->offset_cycles);
+
+       if (cc->has_uuid) {
+               bt_clock_class_set_uuid(ir_cc, cc->uuid);
+       }
+
+       bt_clock_class_set_origin_is_unix_epoch(ir_cc, cc->is_absolute);
+}
+
+static inline
+int ctf_trace_class_to_ir(struct ctx *ctx)
+{
+       int ret = 0;
+       uint64_t i;
+
+       BT_ASSERT(ctx->tc);
+       BT_ASSERT(ctx->ir_tc);
+
+       if (ctx->tc->is_translated) {
+               goto end;
+       }
+
+       for (i = 0; i < ctx->tc->clock_classes->len; i++) {
+               ctf_clock_class *cc = (ctf_clock_class *) ctx->tc->clock_classes->pdata[i];
+
+               cc->ir_cc = bt_clock_class_create(ctx->self_comp);
+               ctf_clock_class_to_ir(cc->ir_cc, cc);
+       }
+
+       bt_trace_class_set_assigns_automatic_stream_class_id(ctx->ir_tc,
+               BT_FALSE);
+       ctx->tc->is_translated = true;
+       ctx->tc->ir_tc = ctx->ir_tc;
+
+end:
+       return ret;
+}
+
+BT_HIDDEN
+int ctf_trace_class_translate(bt_self_component *self_comp,
+               bt_trace_class *ir_tc, struct ctf_trace_class *tc)
+{
+       int ret = 0;
+       uint64_t i;
+       struct ctx ctx = { 0 };
+
+       ctx.self_comp = self_comp;
+       ctx.tc = tc;
+       ctx.ir_tc = ir_tc;
+       ret = ctf_trace_class_to_ir(&ctx);
+       if (ret) {
+               goto end;
+       }
+
+       for (i = 0; i < tc->stream_classes->len; i++) {
+               uint64_t j;
+               ctx.sc = (ctf_stream_class *) tc->stream_classes->pdata[i];
+
+               ctf_stream_class_to_ir(&ctx);
+
+               for (j = 0; j < ctx.sc->event_classes->len; j++) {
+                       ctx.ec = (ctf_event_class *) ctx.sc->event_classes->pdata[j];
+
+                       ctf_event_class_to_ir(&ctx);
+                       ctx.ec = NULL;
+               }
+
+               ctx.sc = NULL;
+       }
+
+end:
+       return ret;
+}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-update-alignments.c b/src/plugins/ctf/common/metadata/ctf-meta-update-alignments.c
deleted file mode 100644 (file)
index 8cea669..0000000
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2020 Philippe Proulx <pproulx@efficios.com>
- */
-
-#include <babeltrace2/babeltrace.h>
-#include "common/macros.h"
-#include "common/assert.h"
-#include <glib.h>
-#include <stdint.h>
-#include <string.h>
-#include <inttypes.h>
-
-#include "ctf-meta-visitors.h"
-
-static inline
-int set_alignments(struct ctf_field_class *fc)
-{
-       int ret = 0;
-       uint64_t i;
-
-       if (!fc) {
-               goto end;
-       }
-
-       switch (fc->type) {
-       case CTF_FIELD_CLASS_TYPE_STRUCT:
-       {
-               struct ctf_field_class_struct *struct_fc = (void *) fc;
-
-               for (i = 0; i < struct_fc->members->len; i++) {
-                       struct ctf_named_field_class *named_fc =
-                               ctf_field_class_struct_borrow_member_by_index(
-                                       struct_fc, i);
-
-                       ret = set_alignments(named_fc->fc);
-                       if (ret) {
-                               goto end;
-                       }
-
-                       if (named_fc->fc->alignment > fc->alignment) {
-                               fc->alignment = named_fc->fc->alignment;
-                       }
-               }
-
-               break;
-       }
-       case CTF_FIELD_CLASS_TYPE_VARIANT:
-       {
-               struct ctf_field_class_variant *var_fc = (void *) fc;
-
-               for (i = 0; i < var_fc->options->len; i++) {
-                       struct ctf_named_field_class *named_fc =
-                               ctf_field_class_variant_borrow_option_by_index(
-                                       var_fc, i);
-
-                       ret = set_alignments(named_fc->fc);
-                       if (ret) {
-                               goto end;
-                       }
-               }
-
-               break;
-       }
-       case CTF_FIELD_CLASS_TYPE_ARRAY:
-       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-       {
-               struct ctf_field_class_array_base *array_fc = (void *) fc;
-
-               ret = set_alignments(array_fc->elem_fc);
-               if (ret) {
-                       goto end;
-               }
-
-               /*
-                * Use the alignment of the array/sequence field class's
-                * element FC as its own alignment.
-                *
-                * This is especially important when the array/sequence
-                * field's effective length is zero: as per CTF 1.8, the
-                * stream data decoding process still needs to align the
-                * cursor using the element's alignment [1]:
-                *
-                * > Arrays are always aligned on their element
-                * > alignment requirement.
-                *
-                * For example:
-                *
-                *     struct {
-                *         integer { size = 8; } a;
-                *         integer { size = 8; align = 16; } b[0];
-                *         integer { size = 8; } c;
-                *     };
-                *
-                * When using this to decode the bytes 1, 2, and 3, then
-                * the decoded values are:
-                *
-                * `a`: 1
-                * `b`: []
-                * `c`: 3
-                *
-                * [1]: https://diamon.org/ctf/#spec4.2.3
-                */
-               array_fc->base.alignment = array_fc->elem_fc->alignment;
-               break;
-       }
-       default:
-               break;
-       }
-
-end:
-       return ret;
-}
-
-BT_HIDDEN
-int ctf_trace_class_update_alignments(
-               struct ctf_trace_class *ctf_tc)
-{
-       int ret = 0;
-       uint64_t i;
-
-       if (!ctf_tc->is_translated) {
-               ret = set_alignments(ctf_tc->packet_header_fc);
-               if (ret) {
-                       goto end;
-               }
-       }
-
-       for (i = 0; i < ctf_tc->stream_classes->len; i++) {
-               struct ctf_stream_class *sc = ctf_tc->stream_classes->pdata[i];
-               uint64_t j;
-
-               if (!sc->is_translated) {
-                       ret = set_alignments(sc->packet_context_fc);
-                       if (ret) {
-                               goto end;
-                       }
-
-                       ret = set_alignments(sc->event_header_fc);
-                       if (ret) {
-                               goto end;
-                       }
-
-                       ret = set_alignments(sc->event_common_context_fc);
-                       if (ret) {
-                               goto end;
-                       }
-               }
-
-               for (j = 0; j < sc->event_classes->len; j++) {
-                       struct ctf_event_class *ec =
-                               sc->event_classes->pdata[j];
-
-                       if (ec->is_translated) {
-                               continue;
-                       }
-
-                       ret = set_alignments(ec->spec_context_fc);
-                       if (ret) {
-                               goto end;
-                       }
-
-                       ret = set_alignments(ec->payload_fc);
-                       if (ret) {
-                               goto end;
-                       }
-               }
-       }
-
-end:
-       return ret;
-}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-update-alignments.cpp b/src/plugins/ctf/common/metadata/ctf-meta-update-alignments.cpp
new file mode 100644 (file)
index 0000000..6e93b67
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2020 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include <babeltrace2/babeltrace.h>
+#include "common/macros.h"
+#include "common/assert.h"
+#include <glib.h>
+#include <stdint.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "ctf-meta-visitors.hpp"
+
+static inline
+int set_alignments(struct ctf_field_class *fc)
+{
+       int ret = 0;
+       uint64_t i;
+
+       if (!fc) {
+               goto end;
+       }
+
+       switch (fc->type) {
+       case CTF_FIELD_CLASS_TYPE_STRUCT:
+       {
+               struct ctf_field_class_struct *struct_fc = ctf_field_class_as_struct(fc);
+
+               for (i = 0; i < struct_fc->members->len; i++) {
+                       struct ctf_named_field_class *named_fc =
+                               ctf_field_class_struct_borrow_member_by_index(
+                                       struct_fc, i);
+
+                       ret = set_alignments(named_fc->fc);
+                       if (ret) {
+                               goto end;
+                       }
+
+                       if (named_fc->fc->alignment > fc->alignment) {
+                               fc->alignment = named_fc->fc->alignment;
+                       }
+               }
+
+               break;
+       }
+       case CTF_FIELD_CLASS_TYPE_VARIANT:
+       {
+               struct ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
+
+               for (i = 0; i < var_fc->options->len; i++) {
+                       struct ctf_named_field_class *named_fc =
+                               ctf_field_class_variant_borrow_option_by_index(
+                                       var_fc, i);
+
+                       ret = set_alignments(named_fc->fc);
+                       if (ret) {
+                               goto end;
+                       }
+               }
+
+               break;
+       }
+       case CTF_FIELD_CLASS_TYPE_ARRAY:
+       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+       {
+               struct ctf_field_class_array_base *array_fc = ctf_field_class_as_array_base(fc);
+
+               ret = set_alignments(array_fc->elem_fc);
+               if (ret) {
+                       goto end;
+               }
+
+               /*
+                * Use the alignment of the array/sequence field class's
+                * element FC as its own alignment.
+                *
+                * This is especially important when the array/sequence
+                * field's effective length is zero: as per CTF 1.8, the
+                * stream data decoding process still needs to align the
+                * cursor using the element's alignment [1]:
+                *
+                * > Arrays are always aligned on their element
+                * > alignment requirement.
+                *
+                * For example:
+                *
+                *     struct {
+                *         integer { size = 8; } a;
+                *         integer { size = 8; align = 16; } b[0];
+                *         integer { size = 8; } c;
+                *     };
+                *
+                * When using this to decode the bytes 1, 2, and 3, then
+                * the decoded values are:
+                *
+                * `a`: 1
+                * `b`: []
+                * `c`: 3
+                *
+                * [1]: https://diamon.org/ctf/#spec4.2.3
+                */
+               array_fc->base.alignment = array_fc->elem_fc->alignment;
+               break;
+       }
+       default:
+               break;
+       }
+
+end:
+       return ret;
+}
+
+BT_HIDDEN
+int ctf_trace_class_update_alignments(
+               struct ctf_trace_class *ctf_tc)
+{
+       int ret = 0;
+       uint64_t i;
+
+       if (!ctf_tc->is_translated) {
+               ret = set_alignments(ctf_tc->packet_header_fc);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+       for (i = 0; i < ctf_tc->stream_classes->len; i++) {
+               ctf_stream_class *sc = (ctf_stream_class *) ctf_tc->stream_classes->pdata[i];
+               uint64_t j;
+
+               if (!sc->is_translated) {
+                       ret = set_alignments(sc->packet_context_fc);
+                       if (ret) {
+                               goto end;
+                       }
+
+                       ret = set_alignments(sc->event_header_fc);
+                       if (ret) {
+                               goto end;
+                       }
+
+                       ret = set_alignments(sc->event_common_context_fc);
+                       if (ret) {
+                               goto end;
+                       }
+               }
+
+               for (j = 0; j < sc->event_classes->len; j++) {
+                       struct ctf_event_class *ec =
+                               (ctf_event_class *) sc->event_classes->pdata[j];
+
+                       if (ec->is_translated) {
+                               continue;
+                       }
+
+                       ret = set_alignments(ec->spec_context_fc);
+                       if (ret) {
+                               goto end;
+                       }
+
+                       ret = set_alignments(ec->payload_fc);
+                       if (ret) {
+                               goto end;
+                       }
+               }
+       }
+
+end:
+       return ret;
+}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-update-default-clock-classes.c b/src/plugins/ctf/common/metadata/ctf-meta-update-default-clock-classes.c
deleted file mode 100644 (file)
index f6c19b1..0000000
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
- */
-
-#define BT_COMP_LOG_SELF_COMP (log_cfg->self_comp)
-#define BT_COMP_LOG_SELF_COMP_CLASS (log_cfg->self_comp_class)
-#define BT_LOG_OUTPUT_LEVEL (log_cfg->log_level)
-#define BT_LOG_TAG "PLUGIN/CTF/META/UPDATE-DEF-CC"
-#include "logging/comp-logging.h"
-
-#include <babeltrace2/babeltrace.h>
-#include "common/macros.h"
-#include "common/assert.h"
-#include <glib.h>
-#include <stdint.h>
-#include <string.h>
-#include <inttypes.h>
-
-#include "ctf-meta-visitors.h"
-#include "logging.h"
-
-static inline
-int find_mapped_clock_class(struct ctf_field_class *fc,
-               struct ctf_clock_class **clock_class,
-               struct meta_log_config *log_cfg)
-{
-       int ret = 0;
-       uint64_t i;
-
-       if (!fc) {
-               goto end;
-       }
-
-       switch (fc->type) {
-       case CTF_FIELD_CLASS_TYPE_INT:
-       case CTF_FIELD_CLASS_TYPE_ENUM:
-       {
-               struct ctf_field_class_int *int_fc = (void *) fc;
-
-               if (int_fc->mapped_clock_class) {
-                       if (*clock_class && *clock_class !=
-                                       int_fc->mapped_clock_class) {
-                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Stream class contains more than one "
-                                       "clock class: expected-cc-name=\"%s\", "
-                                       "other-cc-name=\"%s\"",
-                                       (*clock_class)->name->str,
-                                       int_fc->mapped_clock_class->name->str);
-                               ret = -1;
-                               goto end;
-                       }
-
-                       *clock_class = int_fc->mapped_clock_class;
-               }
-
-               break;
-       }
-       case CTF_FIELD_CLASS_TYPE_STRUCT:
-       {
-               struct ctf_field_class_struct *struct_fc = (void *) fc;
-
-               for (i = 0; i < struct_fc->members->len; i++) {
-                       struct ctf_named_field_class *named_fc =
-                               ctf_field_class_struct_borrow_member_by_index(
-                                       struct_fc, i);
-
-                       ret = find_mapped_clock_class(named_fc->fc,
-                               clock_class, log_cfg);
-                       if (ret) {
-                               goto end;
-                       }
-               }
-
-               break;
-       }
-       case CTF_FIELD_CLASS_TYPE_VARIANT:
-       {
-               struct ctf_field_class_variant *var_fc = (void *) fc;
-
-               for (i = 0; i < var_fc->options->len; i++) {
-                       struct ctf_named_field_class *named_fc =
-                               ctf_field_class_variant_borrow_option_by_index(
-                                       var_fc, i);
-
-                       ret = find_mapped_clock_class(named_fc->fc,
-                               clock_class, log_cfg);
-                       if (ret) {
-                               goto end;
-                       }
-               }
-
-               break;
-       }
-       case CTF_FIELD_CLASS_TYPE_ARRAY:
-       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-       {
-               struct ctf_field_class_array_base *array_fc = (void *) fc;
-
-               ret = find_mapped_clock_class(array_fc->elem_fc, clock_class,
-                       log_cfg);
-               if (ret) {
-                       goto end;
-               }
-
-               break;
-       }
-       default:
-               break;
-       }
-
-end:
-       return ret;
-}
-
-static inline
-int update_stream_class_default_clock_class(
-               struct ctf_stream_class *stream_class,
-               struct meta_log_config *log_cfg)
-{
-       int ret = 0;
-       struct ctf_clock_class *clock_class =
-               stream_class->default_clock_class;
-       uint64_t i;
-
-       ret = find_mapped_clock_class(stream_class->packet_context_fc,
-               &clock_class, log_cfg);
-       if (ret) {
-               goto end;
-       }
-
-       ret = find_mapped_clock_class(stream_class->event_header_fc,
-               &clock_class, log_cfg);
-       if (ret) {
-               goto end;
-       }
-
-       ret = find_mapped_clock_class(stream_class->event_common_context_fc,
-               &clock_class, log_cfg);
-       if (ret) {
-               goto end;
-       }
-
-       for (i = 0; i < stream_class->event_classes->len; i++) {
-               struct ctf_event_class *event_class =
-                       stream_class->event_classes->pdata[i];
-
-               ret = find_mapped_clock_class(event_class->spec_context_fc,
-                       &clock_class, log_cfg);
-               if (ret) {
-                       goto end;
-               }
-
-               ret = find_mapped_clock_class(event_class->payload_fc,
-                       &clock_class, log_cfg);
-               if (ret) {
-                       goto end;
-               }
-       }
-
-       if (!stream_class->default_clock_class) {
-               stream_class->default_clock_class = clock_class;
-       }
-
-end:
-       return ret;
-}
-
-BT_HIDDEN
-int ctf_trace_class_update_default_clock_classes(struct ctf_trace_class *ctf_tc,
-               struct meta_log_config *log_cfg)
-{
-       uint64_t i;
-       int ret = 0;
-       struct ctf_clock_class *clock_class = NULL;
-
-       ret = find_mapped_clock_class(ctf_tc->packet_header_fc, &clock_class,
-               log_cfg);
-       if (ret) {
-               goto end;
-       }
-
-       if (clock_class) {
-               ret = -1;
-               goto end;
-       }
-
-       for (i = 0; i < ctf_tc->stream_classes->len; i++) {
-               struct ctf_stream_class *sc =
-                       ctf_tc->stream_classes->pdata[i];
-
-               ret = update_stream_class_default_clock_class(
-                       ctf_tc->stream_classes->pdata[i], log_cfg);
-               if (ret) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Stream class contains more than one "
-                               "clock class: stream-class-id=%" PRIu64,
-                               sc->id);
-                       goto end;
-               }
-       }
-
-end:
-       return ret;
-}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-update-default-clock-classes.cpp b/src/plugins/ctf/common/metadata/ctf-meta-update-default-clock-classes.cpp
new file mode 100644 (file)
index 0000000..d40adda
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#define BT_COMP_LOG_SELF_COMP (log_cfg->self_comp)
+#define BT_COMP_LOG_SELF_COMP_CLASS (log_cfg->self_comp_class)
+#define BT_LOG_OUTPUT_LEVEL (log_cfg->log_level)
+#define BT_LOG_TAG "PLUGIN/CTF/META/UPDATE-DEF-CC"
+#include "logging/comp-logging.h"
+
+#include <babeltrace2/babeltrace.h>
+#include "common/macros.h"
+#include "common/assert.h"
+#include <glib.h>
+#include <stdint.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "ctf-meta-visitors.hpp"
+#include "logging.hpp"
+
+static inline
+int find_mapped_clock_class(struct ctf_field_class *fc,
+               struct ctf_clock_class **clock_class,
+               struct meta_log_config *log_cfg)
+{
+       int ret = 0;
+       uint64_t i;
+
+       if (!fc) {
+               goto end;
+       }
+
+       switch (fc->type) {
+       case CTF_FIELD_CLASS_TYPE_INT:
+       case CTF_FIELD_CLASS_TYPE_ENUM:
+       {
+               struct ctf_field_class_int *int_fc = ctf_field_class_as_int(fc);
+
+               if (int_fc->mapped_clock_class) {
+                       if (*clock_class && *clock_class !=
+                                       int_fc->mapped_clock_class) {
+                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Stream class contains more than one "
+                                       "clock class: expected-cc-name=\"%s\", "
+                                       "other-cc-name=\"%s\"",
+                                       (*clock_class)->name->str,
+                                       int_fc->mapped_clock_class->name->str);
+                               ret = -1;
+                               goto end;
+                       }
+
+                       *clock_class = int_fc->mapped_clock_class;
+               }
+
+               break;
+       }
+       case CTF_FIELD_CLASS_TYPE_STRUCT:
+       {
+               struct ctf_field_class_struct *struct_fc = ctf_field_class_as_struct(fc);
+
+               for (i = 0; i < struct_fc->members->len; i++) {
+                       struct ctf_named_field_class *named_fc =
+                               ctf_field_class_struct_borrow_member_by_index(
+                                       struct_fc, i);
+
+                       ret = find_mapped_clock_class(named_fc->fc,
+                               clock_class, log_cfg);
+                       if (ret) {
+                               goto end;
+                       }
+               }
+
+               break;
+       }
+       case CTF_FIELD_CLASS_TYPE_VARIANT:
+       {
+               struct ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
+
+               for (i = 0; i < var_fc->options->len; i++) {
+                       struct ctf_named_field_class *named_fc =
+                               ctf_field_class_variant_borrow_option_by_index(
+                                       var_fc, i);
+
+                       ret = find_mapped_clock_class(named_fc->fc,
+                               clock_class, log_cfg);
+                       if (ret) {
+                               goto end;
+                       }
+               }
+
+               break;
+       }
+       case CTF_FIELD_CLASS_TYPE_ARRAY:
+       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+       {
+               struct ctf_field_class_array_base *array_fc = ctf_field_class_as_array_base(fc);
+
+               ret = find_mapped_clock_class(array_fc->elem_fc, clock_class,
+                       log_cfg);
+               if (ret) {
+                       goto end;
+               }
+
+               break;
+       }
+       default:
+               break;
+       }
+
+end:
+       return ret;
+}
+
+static inline
+int update_stream_class_default_clock_class(
+               struct ctf_stream_class *stream_class,
+               struct meta_log_config *log_cfg)
+{
+       int ret = 0;
+       struct ctf_clock_class *clock_class =
+               stream_class->default_clock_class;
+       uint64_t i;
+
+       ret = find_mapped_clock_class(stream_class->packet_context_fc,
+               &clock_class, log_cfg);
+       if (ret) {
+               goto end;
+       }
+
+       ret = find_mapped_clock_class(stream_class->event_header_fc,
+               &clock_class, log_cfg);
+       if (ret) {
+               goto end;
+       }
+
+       ret = find_mapped_clock_class(stream_class->event_common_context_fc,
+               &clock_class, log_cfg);
+       if (ret) {
+               goto end;
+       }
+
+       for (i = 0; i < stream_class->event_classes->len; i++) {
+               struct ctf_event_class *event_class =
+                       (ctf_event_class *) stream_class->event_classes->pdata[i];
+
+               ret = find_mapped_clock_class(event_class->spec_context_fc,
+                       &clock_class, log_cfg);
+               if (ret) {
+                       goto end;
+               }
+
+               ret = find_mapped_clock_class(event_class->payload_fc,
+                       &clock_class, log_cfg);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+       if (!stream_class->default_clock_class) {
+               stream_class->default_clock_class = clock_class;
+       }
+
+end:
+       return ret;
+}
+
+BT_HIDDEN
+int ctf_trace_class_update_default_clock_classes(struct ctf_trace_class *ctf_tc,
+               struct meta_log_config *log_cfg)
+{
+       uint64_t i;
+       int ret = 0;
+       struct ctf_clock_class *clock_class = NULL;
+
+       ret = find_mapped_clock_class(ctf_tc->packet_header_fc, &clock_class,
+               log_cfg);
+       if (ret) {
+               goto end;
+       }
+
+       if (clock_class) {
+               ret = -1;
+               goto end;
+       }
+
+       for (i = 0; i < ctf_tc->stream_classes->len; i++) {
+               struct ctf_stream_class *sc =
+                       (ctf_stream_class *) ctf_tc->stream_classes->pdata[i];
+
+               ret = update_stream_class_default_clock_class(
+                       (ctf_stream_class *) ctf_tc->stream_classes->pdata[i], log_cfg);
+               if (ret) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Stream class contains more than one "
+                               "clock class: stream-class-id=%" PRIu64,
+                               sc->id);
+                       goto end;
+               }
+       }
+
+end:
+       return ret;
+}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-update-in-ir.c b/src/plugins/ctf/common/metadata/ctf-meta-update-in-ir.c
deleted file mode 100644 (file)
index ffd0925..0000000
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
- */
-
-#include <babeltrace2/babeltrace.h>
-#include "common/macros.h"
-#include "common/assert.h"
-#include "compat/glib.h"
-#include <glib.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <string.h>
-#include <inttypes.h>
-#include "common/assert.h"
-
-#include "ctf-meta-visitors.h"
-
-static
-void force_update_field_class_in_ir(struct ctf_field_class *fc, bool in_ir)
-{
-       uint64_t i;
-
-       if (!fc) {
-               goto end;
-       }
-
-       fc->in_ir = in_ir;
-
-       switch (fc->type) {
-       case CTF_FIELD_CLASS_TYPE_STRUCT:
-       {
-               struct ctf_field_class_struct *struct_fc = (void *) fc;
-
-               for (i = 0; i < struct_fc->members->len; i++) {
-                       struct ctf_named_field_class *named_fc =
-                               ctf_field_class_struct_borrow_member_by_index(
-                                       struct_fc, i);
-
-                       force_update_field_class_in_ir(named_fc->fc, in_ir);
-               }
-
-               break;
-       }
-       case CTF_FIELD_CLASS_TYPE_VARIANT:
-       {
-               struct ctf_named_field_class *named_fc;
-               struct ctf_field_class_variant *var_fc = (void *) fc;
-
-               for (i = 0; i < var_fc->options->len; i++) {
-                       named_fc =
-                               ctf_field_class_variant_borrow_option_by_index(
-                                       var_fc, i);
-
-                       force_update_field_class_in_ir(named_fc->fc, in_ir);
-               }
-
-               break;
-       }
-       case CTF_FIELD_CLASS_TYPE_ARRAY:
-       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-       {
-               struct ctf_field_class_array_base *array_fc = (void *) fc;
-
-               force_update_field_class_in_ir(array_fc->elem_fc, in_ir);
-               break;
-       }
-       default:
-               break;
-       }
-
-end:
-       return;
-}
-
-static
-void update_field_class_in_ir(struct ctf_field_class *fc,
-               GHashTable *ft_dependents)
-{
-       int64_t i;
-
-       if (!fc) {
-               goto end;
-       }
-
-       switch (fc->type) {
-       case CTF_FIELD_CLASS_TYPE_INT:
-       case CTF_FIELD_CLASS_TYPE_ENUM:
-       {
-               struct ctf_field_class_int *int_fc = (void *) fc;
-
-               /*
-                * Conditions to be in trace IR; one of:
-                *
-                * 1. Does NOT have a mapped clock class AND does not
-                *    have a special meaning.
-                * 2. Another field class depends on it.
-                */
-               if ((!int_fc->mapped_clock_class &&
-                               int_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE) ||
-                               bt_g_hash_table_contains(ft_dependents, fc)) {
-                       fc->in_ir = true;
-               }
-
-               break;
-       }
-       case CTF_FIELD_CLASS_TYPE_STRUCT:
-       {
-               struct ctf_field_class_struct *struct_fc = (void *) fc;
-
-               /*
-                * Make it part of IR if it's empty because it was
-                * originally empty.
-                */
-               if (struct_fc->members->len == 0) {
-                       fc->in_ir = true;
-               }
-
-               /* Reverse order */
-               for (i = (int64_t) struct_fc->members->len - 1; i >= 0; i--) {
-                       struct ctf_named_field_class *named_fc =
-                               ctf_field_class_struct_borrow_member_by_index(
-                                       struct_fc, i);
-
-                       update_field_class_in_ir(named_fc->fc, ft_dependents);
-
-                       if (named_fc->fc->in_ir) {
-                               /* At least one member is part of IR */
-                               fc->in_ir = true;
-                       }
-               }
-
-               break;
-       }
-       case CTF_FIELD_CLASS_TYPE_VARIANT:
-       {
-               struct ctf_named_field_class *named_fc;
-               struct ctf_field_class_variant *var_fc = (void *) fc;
-
-               /*
-                * Reverse order, although it is not important for this
-                * loop because a field class within a variant field
-                * type's option cannot depend on a field class in
-                * another option of the same variant field class.
-                */
-               for (i = (int64_t) var_fc->options->len - 1; i >= 0; i--) {
-                       named_fc =
-                               ctf_field_class_variant_borrow_option_by_index(
-                                       var_fc, i);
-
-                       update_field_class_in_ir(named_fc->fc, ft_dependents);
-
-                       if (named_fc->fc->in_ir) {
-                               /* At least one option is part of IR */
-                               fc->in_ir = true;
-                       }
-               }
-
-               if (fc->in_ir) {
-                       /*
-                        * At least one option will make it to IR. In
-                        * this case, make all options part of IR
-                        * because the variant's tag could still select
-                        * (dynamically) a removed option. This can mean
-                        * having an empty structure as an option, for
-                        * example, but at least all the options are
-                        * selectable.
-                        */
-                       for (i = 0; i < var_fc->options->len; i++) {
-                               ctf_field_class_variant_borrow_option_by_index(
-                                       var_fc, i)->fc->in_ir = true;
-                       }
-
-                       /*
-                        * This variant field class is part of IR and
-                        * depends on a tag field class (which must also
-                        * be part of IR).
-                        */
-                       g_hash_table_insert(ft_dependents, var_fc->tag_fc,
-                               var_fc->tag_fc);
-               }
-
-               break;
-       }
-       case CTF_FIELD_CLASS_TYPE_ARRAY:
-       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-       {
-               struct ctf_field_class_array_base *array_fc = (void *) fc;
-
-               update_field_class_in_ir(array_fc->elem_fc, ft_dependents);
-               fc->in_ir = array_fc->elem_fc->in_ir;
-
-               if (fc->type == CTF_FIELD_CLASS_TYPE_ARRAY) {
-                       struct ctf_field_class_array *arr_fc = (void *) fc;
-
-                       assert(arr_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE ||
-                               arr_fc->meaning == CTF_FIELD_CLASS_MEANING_UUID);
-
-                       /*
-                        * UUID field class: nothing depends on this, so
-                        * it's not part of IR.
-                        */
-                       if (arr_fc->meaning == CTF_FIELD_CLASS_MEANING_UUID) {
-                               fc->in_ir = false;
-                               array_fc->elem_fc->in_ir = false;
-                       }
-               } else if (fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE) {
-                       if (fc->in_ir) {
-                               struct ctf_field_class_sequence *seq_fc = (void *) fc;
-
-                               /*
-                                * This sequence field class is part of
-                                * IR and depends on a length field class
-                                * (which must also be part of IR).
-                                */
-                               g_hash_table_insert(ft_dependents,
-                                       seq_fc->length_fc, seq_fc->length_fc);
-                       }
-               }
-
-               break;
-       }
-       default:
-               fc->in_ir = true;
-               break;
-       }
-
-end:
-       return;
-}
-
-/*
- * Scopes and field classes are processed in reverse order because we need
- * to know if a given integer field class has dependents (sequence or
- * variant field classes) when we reach it. Dependents can only be located
- * after the length/tag field class in the metadata tree.
- */
-BT_HIDDEN
-int ctf_trace_class_update_in_ir(struct ctf_trace_class *ctf_tc)
-{
-       int ret = 0;
-       uint64_t i;
-
-       GHashTable *ft_dependents = g_hash_table_new(g_direct_hash,
-               g_direct_equal);
-
-       BT_ASSERT(ft_dependents);
-
-       for (i = 0; i < ctf_tc->stream_classes->len; i++) {
-               struct ctf_stream_class *sc = ctf_tc->stream_classes->pdata[i];
-               uint64_t j;
-
-               for (j = 0; j < sc->event_classes->len; j++) {
-                       struct ctf_event_class *ec = sc->event_classes->pdata[j];
-
-                       if (ec->is_translated) {
-                               continue;
-                       }
-
-                       update_field_class_in_ir(ec->payload_fc, ft_dependents);
-                       update_field_class_in_ir(ec->spec_context_fc,
-                               ft_dependents);
-               }
-
-               if (!sc->is_translated) {
-                       update_field_class_in_ir(sc->event_common_context_fc,
-                               ft_dependents);
-                       force_update_field_class_in_ir(sc->event_header_fc,
-                               false);
-                       update_field_class_in_ir(sc->packet_context_fc,
-                               ft_dependents);
-               }
-       }
-
-       if (!ctf_tc->is_translated) {
-               force_update_field_class_in_ir(ctf_tc->packet_header_fc,
-                       false);
-       }
-
-       g_hash_table_destroy(ft_dependents);
-       return ret;
-}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-update-in-ir.cpp b/src/plugins/ctf/common/metadata/ctf-meta-update-in-ir.cpp
new file mode 100644 (file)
index 0000000..765f38c
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include <babeltrace2/babeltrace.h>
+#include "common/macros.h"
+#include "common/assert.h"
+#include "compat/glib.h"
+#include <glib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <inttypes.h>
+#include "common/assert.h"
+
+#include "ctf-meta-visitors.hpp"
+
+static
+void force_update_field_class_in_ir(struct ctf_field_class *fc, bool in_ir)
+{
+       uint64_t i;
+
+       if (!fc) {
+               goto end;
+       }
+
+       fc->in_ir = in_ir;
+
+       switch (fc->type) {
+       case CTF_FIELD_CLASS_TYPE_STRUCT:
+       {
+               struct ctf_field_class_struct *struct_fc = ctf_field_class_as_struct(fc);
+
+               for (i = 0; i < struct_fc->members->len; i++) {
+                       struct ctf_named_field_class *named_fc =
+                               ctf_field_class_struct_borrow_member_by_index(
+                                       struct_fc, i);
+
+                       force_update_field_class_in_ir(named_fc->fc, in_ir);
+               }
+
+               break;
+       }
+       case CTF_FIELD_CLASS_TYPE_VARIANT:
+       {
+               struct ctf_named_field_class *named_fc;
+               struct ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
+
+               for (i = 0; i < var_fc->options->len; i++) {
+                       named_fc =
+                               ctf_field_class_variant_borrow_option_by_index(
+                                       var_fc, i);
+
+                       force_update_field_class_in_ir(named_fc->fc, in_ir);
+               }
+
+               break;
+       }
+       case CTF_FIELD_CLASS_TYPE_ARRAY:
+       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+       {
+               struct ctf_field_class_array_base *array_fc = ctf_field_class_as_array_base(fc);
+
+               force_update_field_class_in_ir(array_fc->elem_fc, in_ir);
+               break;
+       }
+       default:
+               break;
+       }
+
+end:
+       return;
+}
+
+static
+void update_field_class_in_ir(struct ctf_field_class *fc,
+               GHashTable *ft_dependents)
+{
+       int64_t i;
+
+       if (!fc) {
+               goto end;
+       }
+
+       switch (fc->type) {
+       case CTF_FIELD_CLASS_TYPE_INT:
+       case CTF_FIELD_CLASS_TYPE_ENUM:
+       {
+               struct ctf_field_class_int *int_fc = ctf_field_class_as_int(fc);
+
+               /*
+                * Conditions to be in trace IR; one of:
+                *
+                * 1. Does NOT have a mapped clock class AND does not
+                *    have a special meaning.
+                * 2. Another field class depends on it.
+                */
+               if ((!int_fc->mapped_clock_class &&
+                               int_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE) ||
+                               bt_g_hash_table_contains(ft_dependents, fc)) {
+                       fc->in_ir = true;
+               }
+
+               break;
+       }
+       case CTF_FIELD_CLASS_TYPE_STRUCT:
+       {
+               struct ctf_field_class_struct *struct_fc = ctf_field_class_as_struct(fc);
+
+               /*
+                * Make it part of IR if it's empty because it was
+                * originally empty.
+                */
+               if (struct_fc->members->len == 0) {
+                       fc->in_ir = true;
+               }
+
+               /* Reverse order */
+               for (i = (int64_t) struct_fc->members->len - 1; i >= 0; i--) {
+                       struct ctf_named_field_class *named_fc =
+                               ctf_field_class_struct_borrow_member_by_index(
+                                       struct_fc, i);
+
+                       update_field_class_in_ir(named_fc->fc, ft_dependents);
+
+                       if (named_fc->fc->in_ir) {
+                               /* At least one member is part of IR */
+                               fc->in_ir = true;
+                       }
+               }
+
+               break;
+       }
+       case CTF_FIELD_CLASS_TYPE_VARIANT:
+       {
+               struct ctf_named_field_class *named_fc;
+               struct ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
+
+               /*
+                * Reverse order, although it is not important for this
+                * loop because a field class within a variant field
+                * type's option cannot depend on a field class in
+                * another option of the same variant field class.
+                */
+               for (i = (int64_t) var_fc->options->len - 1; i >= 0; i--) {
+                       named_fc =
+                               ctf_field_class_variant_borrow_option_by_index(
+                                       var_fc, i);
+
+                       update_field_class_in_ir(named_fc->fc, ft_dependents);
+
+                       if (named_fc->fc->in_ir) {
+                               /* At least one option is part of IR */
+                               fc->in_ir = true;
+                       }
+               }
+
+               if (fc->in_ir) {
+                       /*
+                        * At least one option will make it to IR. In
+                        * this case, make all options part of IR
+                        * because the variant's tag could still select
+                        * (dynamically) a removed option. This can mean
+                        * having an empty structure as an option, for
+                        * example, but at least all the options are
+                        * selectable.
+                        */
+                       for (i = 0; i < var_fc->options->len; i++) {
+                               ctf_field_class_variant_borrow_option_by_index(
+                                       var_fc, i)->fc->in_ir = true;
+                       }
+
+                       /*
+                        * This variant field class is part of IR and
+                        * depends on a tag field class (which must also
+                        * be part of IR).
+                        */
+                       g_hash_table_insert(ft_dependents, var_fc->tag_fc,
+                               var_fc->tag_fc);
+               }
+
+               break;
+       }
+       case CTF_FIELD_CLASS_TYPE_ARRAY:
+       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+       {
+               struct ctf_field_class_array_base *array_fc = ctf_field_class_as_array_base(fc);
+
+               update_field_class_in_ir(array_fc->elem_fc, ft_dependents);
+               fc->in_ir = array_fc->elem_fc->in_ir;
+
+               if (fc->type == CTF_FIELD_CLASS_TYPE_ARRAY) {
+                       struct ctf_field_class_array *arr_fc = ctf_field_class_as_array(fc);
+
+                       assert(arr_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE ||
+                               arr_fc->meaning == CTF_FIELD_CLASS_MEANING_UUID);
+
+                       /*
+                        * UUID field class: nothing depends on this, so
+                        * it's not part of IR.
+                        */
+                       if (arr_fc->meaning == CTF_FIELD_CLASS_MEANING_UUID) {
+                               fc->in_ir = false;
+                               array_fc->elem_fc->in_ir = false;
+                       }
+               } else if (fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE) {
+                       if (fc->in_ir) {
+                               struct ctf_field_class_sequence *seq_fc = ctf_field_class_as_sequence(fc);
+
+                               /*
+                                * This sequence field class is part of
+                                * IR and depends on a length field class
+                                * (which must also be part of IR).
+                                */
+                               g_hash_table_insert(ft_dependents,
+                                       seq_fc->length_fc, seq_fc->length_fc);
+                       }
+               }
+
+               break;
+       }
+       default:
+               fc->in_ir = true;
+               break;
+       }
+
+end:
+       return;
+}
+
+/*
+ * Scopes and field classes are processed in reverse order because we need
+ * to know if a given integer field class has dependents (sequence or
+ * variant field classes) when we reach it. Dependents can only be located
+ * after the length/tag field class in the metadata tree.
+ */
+BT_HIDDEN
+int ctf_trace_class_update_in_ir(struct ctf_trace_class *ctf_tc)
+{
+       int ret = 0;
+       uint64_t i;
+
+       GHashTable *ft_dependents = g_hash_table_new(g_direct_hash,
+               g_direct_equal);
+
+       BT_ASSERT(ft_dependents);
+
+       for (i = 0; i < ctf_tc->stream_classes->len; i++) {
+               ctf_stream_class *sc = (ctf_stream_class *) ctf_tc->stream_classes->pdata[i];
+               uint64_t j;
+
+               for (j = 0; j < sc->event_classes->len; j++) {
+                       ctf_event_class *ec = (ctf_event_class *) sc->event_classes->pdata[j];
+
+                       if (ec->is_translated) {
+                               continue;
+                       }
+
+                       update_field_class_in_ir(ec->payload_fc, ft_dependents);
+                       update_field_class_in_ir(ec->spec_context_fc,
+                               ft_dependents);
+               }
+
+               if (!sc->is_translated) {
+                       update_field_class_in_ir(sc->event_common_context_fc,
+                               ft_dependents);
+                       force_update_field_class_in_ir(sc->event_header_fc,
+                               false);
+                       update_field_class_in_ir(sc->packet_context_fc,
+                               ft_dependents);
+               }
+       }
+
+       if (!ctf_tc->is_translated) {
+               force_update_field_class_in_ir(ctf_tc->packet_header_fc,
+                       false);
+       }
+
+       g_hash_table_destroy(ft_dependents);
+       return ret;
+}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-update-meanings.c b/src/plugins/ctf/common/metadata/ctf-meta-update-meanings.c
deleted file mode 100644 (file)
index 838c9eb..0000000
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
- */
-
-#include <babeltrace2/babeltrace.h>
-#include "common/macros.h"
-#include "common/assert.h"
-#include <glib.h>
-#include <stdint.h>
-#include <string.h>
-#include <inttypes.h>
-
-#include "ctf-meta-visitors.h"
-
-static
-int set_int_field_class_meaning_by_name(struct ctf_field_class *fc,
-               const char *field_name, const char *id_name,
-               enum ctf_field_class_meaning meaning)
-{
-       int ret = 0;
-       uint64_t i;
-
-       if (!fc) {
-               goto end;
-       }
-
-       switch (fc->type) {
-       case CTF_FIELD_CLASS_TYPE_INT:
-       case CTF_FIELD_CLASS_TYPE_ENUM:
-       {
-               struct ctf_field_class_int *int_fc = (void *) fc;
-
-               if (field_name && strcmp(field_name, id_name) == 0) {
-                       int_fc->meaning = meaning;
-               }
-
-               break;
-       }
-       case CTF_FIELD_CLASS_TYPE_STRUCT:
-       {
-               struct ctf_field_class_struct *struct_fc = (void *) fc;
-
-               for (i = 0; i < struct_fc->members->len; i++) {
-                       struct ctf_named_field_class *named_fc =
-                               ctf_field_class_struct_borrow_member_by_index(
-                                       struct_fc, i);
-
-                       ret = set_int_field_class_meaning_by_name(named_fc->fc,
-                               named_fc->name->str, id_name, meaning);
-                       if (ret) {
-                               goto end;
-                       }
-               }
-
-               break;
-       }
-       case CTF_FIELD_CLASS_TYPE_VARIANT:
-       {
-               struct ctf_field_class_variant *var_fc = (void *) fc;
-
-               for (i = 0; i < var_fc->options->len; i++) {
-                       struct ctf_named_field_class *named_fc =
-                               ctf_field_class_variant_borrow_option_by_index(
-                                       var_fc, i);
-
-                       ret = set_int_field_class_meaning_by_name(named_fc->fc,
-                               NULL, id_name, meaning);
-                       if (ret) {
-                               goto end;
-                       }
-               }
-
-               break;
-       }
-       case CTF_FIELD_CLASS_TYPE_ARRAY:
-       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-       {
-               struct ctf_field_class_array_base *array_fc = (void *) fc;
-
-               ret = set_int_field_class_meaning_by_name(array_fc->elem_fc,
-                       NULL, id_name, meaning);
-               if (ret) {
-                       goto end;
-               }
-
-               break;
-       }
-       default:
-               break;
-       }
-
-end:
-       return ret;
-}
-
-static
-int update_stream_class_meanings(struct ctf_stream_class *sc)
-{
-       int ret = 0;
-       struct ctf_field_class_int *int_fc;
-       uint64_t i;
-
-       if (!sc->is_translated) {
-               if (sc->packet_context_fc) {
-                       int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
-                               (void *) sc->packet_context_fc, "timestamp_begin");
-                       if (int_fc) {
-                               int_fc->meaning = CTF_FIELD_CLASS_MEANING_PACKET_BEGINNING_TIME;
-                       }
-
-                       int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
-                               (void *) sc->packet_context_fc, "timestamp_end");
-                       if (int_fc) {
-                               int_fc->meaning = CTF_FIELD_CLASS_MEANING_PACKET_END_TIME;
-
-                               /*
-                                * Remove mapped clock class to avoid updating
-                                * the clock immediately when decoding.
-                                */
-                               int_fc->mapped_clock_class = NULL;
-                       }
-
-                       int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
-                               (void *) sc->packet_context_fc, "events_discarded");
-                       if (int_fc) {
-                               int_fc->meaning = CTF_FIELD_CLASS_MEANING_DISC_EV_REC_COUNTER_SNAPSHOT;
-                       }
-
-                       int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
-                               (void *) sc->packet_context_fc, "packet_seq_num");
-                       if (int_fc) {
-                               int_fc->meaning = CTF_FIELD_CLASS_MEANING_PACKET_COUNTER_SNAPSHOT;
-
-                       }
-
-                       int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
-                               (void *) sc->packet_context_fc, "packet_size");
-                       if (int_fc) {
-                               int_fc->meaning = CTF_FIELD_CLASS_MEANING_EXP_PACKET_TOTAL_SIZE;
-                       }
-
-                       int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
-                               (void *) sc->packet_context_fc, "content_size");
-                       if (int_fc) {
-                               int_fc->meaning = CTF_FIELD_CLASS_MEANING_EXP_PACKET_CONTENT_SIZE;
-                       }
-               }
-
-               if (sc->event_header_fc) {
-                       ret = set_int_field_class_meaning_by_name(
-                               sc->event_header_fc, NULL, "id",
-                               CTF_FIELD_CLASS_MEANING_EVENT_CLASS_ID);
-                       if (ret) {
-                               goto end;
-                       }
-               }
-       }
-
-       for (i = 0; i < sc->event_classes->len; i++) {
-               struct ctf_event_class *ec = sc->event_classes->pdata[i];
-
-               if (ec->is_translated) {
-                       continue;
-               }
-       }
-
-end:
-       return ret;
-}
-
-BT_HIDDEN
-int ctf_trace_class_update_meanings(struct ctf_trace_class *ctf_tc)
-{
-       int ret = 0;
-       struct ctf_field_class_int *int_fc;
-       struct ctf_named_field_class *named_fc;
-       uint64_t i;
-
-       if (!ctf_tc->is_translated && ctf_tc->packet_header_fc) {
-               int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
-                       (void *) ctf_tc->packet_header_fc, "magic");
-               if (int_fc) {
-                       int_fc->meaning = CTF_FIELD_CLASS_MEANING_MAGIC;
-               }
-
-               int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
-                       (void *) ctf_tc->packet_header_fc, "stream_id");
-               if (int_fc) {
-                       int_fc->meaning = CTF_FIELD_CLASS_MEANING_STREAM_CLASS_ID;
-               }
-
-               int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
-                       (void *) ctf_tc->packet_header_fc,
-                       "stream_instance_id");
-               if (int_fc) {
-                       int_fc->meaning = CTF_FIELD_CLASS_MEANING_DATA_STREAM_ID;
-               }
-
-               named_fc = ctf_field_class_struct_borrow_member_by_name(
-                       (void *) ctf_tc->packet_header_fc, "uuid");
-               if (named_fc && named_fc->fc->type == CTF_FIELD_CLASS_TYPE_ARRAY) {
-                       struct ctf_field_class_array *array_fc =
-                               (void *) named_fc->fc;
-
-                       array_fc->meaning = CTF_FIELD_CLASS_MEANING_UUID;
-               }
-       }
-
-       for (i = 0; i < ctf_tc->stream_classes->len; i++) {
-               ret = update_stream_class_meanings(
-                       ctf_tc->stream_classes->pdata[i]);
-               if (ret) {
-                       goto end;
-               }
-       }
-
-end:
-       return ret;
-}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-update-meanings.cpp b/src/plugins/ctf/common/metadata/ctf-meta-update-meanings.cpp
new file mode 100644 (file)
index 0000000..4e8d647
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include <babeltrace2/babeltrace.h>
+#include "common/macros.h"
+#include "common/assert.h"
+#include <glib.h>
+#include <stdint.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "ctf-meta-visitors.hpp"
+
+static
+int set_int_field_class_meaning_by_name(struct ctf_field_class *fc,
+               const char *field_name, const char *id_name,
+               enum ctf_field_class_meaning meaning)
+{
+       int ret = 0;
+       uint64_t i;
+
+       if (!fc) {
+               goto end;
+       }
+
+       switch (fc->type) {
+       case CTF_FIELD_CLASS_TYPE_INT:
+       case CTF_FIELD_CLASS_TYPE_ENUM:
+       {
+               struct ctf_field_class_int *int_fc = ctf_field_class_as_int(fc);
+
+               if (field_name && strcmp(field_name, id_name) == 0) {
+                       int_fc->meaning = meaning;
+               }
+
+               break;
+       }
+       case CTF_FIELD_CLASS_TYPE_STRUCT:
+       {
+               struct ctf_field_class_struct *struct_fc = ctf_field_class_as_struct(fc);
+
+               for (i = 0; i < struct_fc->members->len; i++) {
+                       struct ctf_named_field_class *named_fc =
+                               ctf_field_class_struct_borrow_member_by_index(
+                                       struct_fc, i);
+
+                       ret = set_int_field_class_meaning_by_name(named_fc->fc,
+                               named_fc->name->str, id_name, meaning);
+                       if (ret) {
+                               goto end;
+                       }
+               }
+
+               break;
+       }
+       case CTF_FIELD_CLASS_TYPE_VARIANT:
+       {
+               struct ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
+
+               for (i = 0; i < var_fc->options->len; i++) {
+                       struct ctf_named_field_class *named_fc =
+                               ctf_field_class_variant_borrow_option_by_index(
+                                       var_fc, i);
+
+                       ret = set_int_field_class_meaning_by_name(named_fc->fc,
+                               NULL, id_name, meaning);
+                       if (ret) {
+                               goto end;
+                       }
+               }
+
+               break;
+       }
+       case CTF_FIELD_CLASS_TYPE_ARRAY:
+       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+       {
+               struct ctf_field_class_array_base *array_fc = ctf_field_class_as_array_base(fc);
+
+               ret = set_int_field_class_meaning_by_name(array_fc->elem_fc,
+                       NULL, id_name, meaning);
+               if (ret) {
+                       goto end;
+               }
+
+               break;
+       }
+       default:
+               break;
+       }
+
+end:
+       return ret;
+}
+
+static
+int update_stream_class_meanings(struct ctf_stream_class *sc)
+{
+       int ret = 0;
+       struct ctf_field_class_int *int_fc;
+       uint64_t i;
+
+       if (!sc->is_translated) {
+               if (sc->packet_context_fc) {
+                       int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
+                               ctf_field_class_as_struct(sc->packet_context_fc), "timestamp_begin");
+                       if (int_fc) {
+                               int_fc->meaning = CTF_FIELD_CLASS_MEANING_PACKET_BEGINNING_TIME;
+                       }
+
+                       int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
+                               ctf_field_class_as_struct(sc->packet_context_fc), "timestamp_end");
+                       if (int_fc) {
+                               int_fc->meaning = CTF_FIELD_CLASS_MEANING_PACKET_END_TIME;
+
+                               /*
+                                * Remove mapped clock class to avoid updating
+                                * the clock immediately when decoding.
+                                */
+                               int_fc->mapped_clock_class = NULL;
+                       }
+
+                       int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
+                               ctf_field_class_as_struct(sc->packet_context_fc), "events_discarded");
+                       if (int_fc) {
+                               int_fc->meaning = CTF_FIELD_CLASS_MEANING_DISC_EV_REC_COUNTER_SNAPSHOT;
+                       }
+
+                       int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
+                               ctf_field_class_as_struct(sc->packet_context_fc), "packet_seq_num");
+                       if (int_fc) {
+                               int_fc->meaning = CTF_FIELD_CLASS_MEANING_PACKET_COUNTER_SNAPSHOT;
+
+                       }
+
+                       int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
+                               ctf_field_class_as_struct(sc->packet_context_fc), "packet_size");
+                       if (int_fc) {
+                               int_fc->meaning = CTF_FIELD_CLASS_MEANING_EXP_PACKET_TOTAL_SIZE;
+                       }
+
+                       int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
+                               ctf_field_class_as_struct(sc->packet_context_fc), "content_size");
+                       if (int_fc) {
+                               int_fc->meaning = CTF_FIELD_CLASS_MEANING_EXP_PACKET_CONTENT_SIZE;
+                       }
+               }
+
+               if (sc->event_header_fc) {
+                       ret = set_int_field_class_meaning_by_name(
+                               sc->event_header_fc, NULL, "id",
+                               CTF_FIELD_CLASS_MEANING_EVENT_CLASS_ID);
+                       if (ret) {
+                               goto end;
+                       }
+               }
+       }
+
+       for (i = 0; i < sc->event_classes->len; i++) {
+               struct ctf_event_class *ec = (ctf_event_class *) sc->event_classes->pdata[i];
+
+               if (ec->is_translated) {
+                       continue;
+               }
+       }
+
+end:
+       return ret;
+}
+
+BT_HIDDEN
+int ctf_trace_class_update_meanings(struct ctf_trace_class *ctf_tc)
+{
+       int ret = 0;
+       struct ctf_field_class_int *int_fc;
+       struct ctf_named_field_class *named_fc;
+       uint64_t i;
+
+       if (!ctf_tc->is_translated && ctf_tc->packet_header_fc) {
+               int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
+                       ctf_field_class_as_struct(ctf_tc->packet_header_fc), "magic");
+               if (int_fc) {
+                       int_fc->meaning = CTF_FIELD_CLASS_MEANING_MAGIC;
+               }
+
+               int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
+                       ctf_field_class_as_struct(ctf_tc->packet_header_fc), "stream_id");
+               if (int_fc) {
+                       int_fc->meaning = CTF_FIELD_CLASS_MEANING_STREAM_CLASS_ID;
+               }
+
+               int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
+                       ctf_field_class_as_struct(ctf_tc->packet_header_fc),
+                       "stream_instance_id");
+               if (int_fc) {
+                       int_fc->meaning = CTF_FIELD_CLASS_MEANING_DATA_STREAM_ID;
+               }
+
+               named_fc = ctf_field_class_struct_borrow_member_by_name(
+                       ctf_field_class_as_struct(ctf_tc->packet_header_fc), "uuid");
+               if (named_fc && named_fc->fc->type == CTF_FIELD_CLASS_TYPE_ARRAY) {
+                       struct ctf_field_class_array *array_fc =
+                               ctf_field_class_as_array(named_fc->fc);
+
+                       array_fc->meaning = CTF_FIELD_CLASS_MEANING_UUID;
+               }
+       }
+
+       for (i = 0; i < ctf_tc->stream_classes->len; i++) {
+               ret = update_stream_class_meanings(
+                       (ctf_stream_class *) ctf_tc->stream_classes->pdata[i]);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+end:
+       return ret;
+}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-update-stream-class-config.c b/src/plugins/ctf/common/metadata/ctf-meta-update-stream-class-config.c
deleted file mode 100644 (file)
index ae88a87..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
- */
-
-#include <babeltrace2/babeltrace.h>
-#include "common/macros.h"
-#include "common/assert.h"
-#include <glib.h>
-#include <stdint.h>
-#include <string.h>
-#include <inttypes.h>
-
-#include "ctf-meta-visitors.h"
-
-BT_HIDDEN
-int ctf_trace_class_update_stream_class_config(struct ctf_trace_class *ctf_tc)
-{
-       struct ctf_field_class_int *int_fc;
-       uint64_t i;
-
-       for (i = 0; i < ctf_tc->stream_classes->len; i++) {
-               struct ctf_stream_class *sc =
-                       ctf_tc->stream_classes->pdata[i];
-
-               if (sc->is_translated) {
-                       continue;
-               }
-
-               if (!sc->packet_context_fc) {
-                       continue;
-               }
-
-               int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
-                       (void *) sc->packet_context_fc, "timestamp_begin");
-               if (int_fc && int_fc->meaning ==
-                               CTF_FIELD_CLASS_MEANING_PACKET_BEGINNING_TIME) {
-                       sc->packets_have_ts_begin = true;
-               }
-
-               int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
-                       (void *) sc->packet_context_fc, "timestamp_end");
-               if (int_fc && int_fc->meaning ==
-                               CTF_FIELD_CLASS_MEANING_PACKET_END_TIME) {
-                       sc->packets_have_ts_end = true;
-               }
-
-               int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
-                       (void *) sc->packet_context_fc, "events_discarded");
-               if (int_fc && int_fc->meaning ==
-                               CTF_FIELD_CLASS_MEANING_DISC_EV_REC_COUNTER_SNAPSHOT) {
-                       sc->has_discarded_events = true;
-               }
-
-               sc->discarded_events_have_default_cs =
-                       sc->has_discarded_events && sc->packets_have_ts_begin &&
-                       sc->packets_have_ts_end;
-               int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
-                       (void *) sc->packet_context_fc, "packet_seq_num");
-               if (int_fc && int_fc->meaning ==
-                               CTF_FIELD_CLASS_MEANING_PACKET_COUNTER_SNAPSHOT) {
-                       sc->has_discarded_packets = true;
-               }
-
-               sc->discarded_packets_have_default_cs =
-                       sc->has_discarded_packets &&
-                       sc->packets_have_ts_begin && sc->packets_have_ts_end;
-       }
-
-       return 0;
-}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-update-stream-class-config.cpp b/src/plugins/ctf/common/metadata/ctf-meta-update-stream-class-config.cpp
new file mode 100644 (file)
index 0000000..b3e0d6e
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include <babeltrace2/babeltrace.h>
+#include "common/macros.h"
+#include "common/assert.h"
+#include <glib.h>
+#include <stdint.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "ctf-meta-visitors.hpp"
+
+BT_HIDDEN
+int ctf_trace_class_update_stream_class_config(struct ctf_trace_class *ctf_tc)
+{
+       struct ctf_field_class_int *int_fc;
+       uint64_t i;
+
+       for (i = 0; i < ctf_tc->stream_classes->len; i++) {
+               struct ctf_stream_class *sc =
+                       (ctf_stream_class *) ctf_tc->stream_classes->pdata[i];
+
+               if (sc->is_translated) {
+                       continue;
+               }
+
+               if (!sc->packet_context_fc) {
+                       continue;
+               }
+
+               int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
+                       ctf_field_class_as_struct(sc->packet_context_fc), "timestamp_begin");
+               if (int_fc && int_fc->meaning ==
+                               CTF_FIELD_CLASS_MEANING_PACKET_BEGINNING_TIME) {
+                       sc->packets_have_ts_begin = true;
+               }
+
+               int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
+                       ctf_field_class_as_struct(sc->packet_context_fc), "timestamp_end");
+               if (int_fc && int_fc->meaning ==
+                               CTF_FIELD_CLASS_MEANING_PACKET_END_TIME) {
+                       sc->packets_have_ts_end = true;
+               }
+
+               int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
+                       ctf_field_class_as_struct(sc->packet_context_fc), "events_discarded");
+               if (int_fc && int_fc->meaning ==
+                               CTF_FIELD_CLASS_MEANING_DISC_EV_REC_COUNTER_SNAPSHOT) {
+                       sc->has_discarded_events = true;
+               }
+
+               sc->discarded_events_have_default_cs =
+                       sc->has_discarded_events && sc->packets_have_ts_begin &&
+                       sc->packets_have_ts_end;
+               int_fc = ctf_field_class_struct_borrow_member_int_field_class_by_name(
+                       ctf_field_class_as_struct(sc->packet_context_fc), "packet_seq_num");
+               if (int_fc && int_fc->meaning ==
+                               CTF_FIELD_CLASS_MEANING_PACKET_COUNTER_SNAPSHOT) {
+                       sc->has_discarded_packets = true;
+               }
+
+               sc->discarded_packets_have_default_cs =
+                       sc->has_discarded_packets &&
+                       sc->packets_have_ts_begin && sc->packets_have_ts_end;
+       }
+
+       return 0;
+}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-update-text-array-sequence.c b/src/plugins/ctf/common/metadata/ctf-meta-update-text-array-sequence.c
deleted file mode 100644 (file)
index 2830caa..0000000
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
- */
-
-#include <babeltrace2/babeltrace.h>
-#include "common/macros.h"
-#include "common/assert.h"
-#include <glib.h>
-#include <stdint.h>
-#include <string.h>
-#include <inttypes.h>
-
-#include "ctf-meta-visitors.h"
-
-static inline
-int set_text_array_sequence_field_class(struct ctf_field_class *fc)
-{
-       int ret = 0;
-       uint64_t i;
-
-       if (!fc) {
-               goto end;
-       }
-
-       switch (fc->type) {
-       case CTF_FIELD_CLASS_TYPE_STRUCT:
-       {
-               struct ctf_field_class_struct *struct_fc = (void *) fc;
-
-               for (i = 0; i < struct_fc->members->len; i++) {
-                       struct ctf_named_field_class *named_fc =
-                               ctf_field_class_struct_borrow_member_by_index(
-                                       struct_fc, i);
-
-                       ret = set_text_array_sequence_field_class(named_fc->fc);
-                       if (ret) {
-                               goto end;
-                       }
-               }
-
-               break;
-       }
-       case CTF_FIELD_CLASS_TYPE_VARIANT:
-       {
-               struct ctf_field_class_variant *var_fc = (void *) fc;
-
-               for (i = 0; i < var_fc->options->len; i++) {
-                       struct ctf_named_field_class *named_fc =
-                               ctf_field_class_variant_borrow_option_by_index(
-                                       var_fc, i);
-
-                       ret = set_text_array_sequence_field_class(named_fc->fc);
-                       if (ret) {
-                               goto end;
-                       }
-               }
-
-               break;
-       }
-       case CTF_FIELD_CLASS_TYPE_ARRAY:
-       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-       {
-               struct ctf_field_class_array_base *array_fc = (void *) fc;
-
-               if (array_fc->elem_fc->type == CTF_FIELD_CLASS_TYPE_INT ||
-                               array_fc->elem_fc->type == CTF_FIELD_CLASS_TYPE_ENUM) {
-                       struct ctf_field_class_int *int_fc =
-                               (void *) array_fc->elem_fc;
-
-                       if (int_fc->base.base.alignment == 8 &&
-                                       int_fc->base.size == 8 &&
-                                       int_fc->encoding == CTF_ENCODING_UTF8) {
-                               array_fc->is_text = true;
-
-                               /*
-                                * Force integer element to be unsigned;
-                                * this makes the decoder enter a single
-                                * path when reading a text
-                                * array/sequence and we can safely
-                                * decode bytes as characters anyway.
-                                */
-                               int_fc->is_signed = false;
-                       }
-               }
-
-               ret = set_text_array_sequence_field_class(array_fc->elem_fc);
-               if (ret) {
-                       goto end;
-               }
-
-               break;
-       }
-       default:
-               break;
-       }
-
-end:
-       return ret;
-}
-
-BT_HIDDEN
-int ctf_trace_class_update_text_array_sequence(struct ctf_trace_class *ctf_tc)
-{
-       int ret = 0;
-       uint64_t i;
-
-       if (!ctf_tc->is_translated) {
-               ret = set_text_array_sequence_field_class(
-                       ctf_tc->packet_header_fc);
-               if (ret) {
-                       goto end;
-               }
-       }
-
-       for (i = 0; i < ctf_tc->stream_classes->len; i++) {
-               struct ctf_stream_class *sc = ctf_tc->stream_classes->pdata[i];
-               uint64_t j;
-
-               if (!sc->is_translated) {
-                       ret = set_text_array_sequence_field_class(
-                               sc->packet_context_fc);
-                       if (ret) {
-                               goto end;
-                       }
-
-                       ret = set_text_array_sequence_field_class(
-                               sc->event_header_fc);
-                       if (ret) {
-                               goto end;
-                       }
-
-                       ret = set_text_array_sequence_field_class(
-                               sc->event_common_context_fc);
-                       if (ret) {
-                               goto end;
-                       }
-               }
-
-               for (j = 0; j < sc->event_classes->len; j++) {
-                       struct ctf_event_class *ec =
-                               sc->event_classes->pdata[j];
-
-                       if (ec->is_translated) {
-                               continue;
-                       }
-
-                       ret = set_text_array_sequence_field_class(
-                               ec->spec_context_fc);
-                       if (ret) {
-                               goto end;
-                       }
-
-                       ret = set_text_array_sequence_field_class(
-                               ec->payload_fc);
-                       if (ret) {
-                               goto end;
-                       }
-               }
-       }
-
-end:
-       return ret;
-}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-update-text-array-sequence.cpp b/src/plugins/ctf/common/metadata/ctf-meta-update-text-array-sequence.cpp
new file mode 100644 (file)
index 0000000..186b98a
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include <babeltrace2/babeltrace.h>
+#include "common/macros.h"
+#include "common/assert.h"
+#include <glib.h>
+#include <stdint.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "ctf-meta-visitors.hpp"
+
+static inline
+int set_text_array_sequence_field_class(struct ctf_field_class *fc)
+{
+       int ret = 0;
+       uint64_t i;
+
+       if (!fc) {
+               goto end;
+       }
+
+       switch (fc->type) {
+       case CTF_FIELD_CLASS_TYPE_STRUCT:
+       {
+               struct ctf_field_class_struct *struct_fc = ctf_field_class_as_struct(fc);
+
+               for (i = 0; i < struct_fc->members->len; i++) {
+                       struct ctf_named_field_class *named_fc =
+                               ctf_field_class_struct_borrow_member_by_index(
+                                       struct_fc, i);
+
+                       ret = set_text_array_sequence_field_class(named_fc->fc);
+                       if (ret) {
+                               goto end;
+                       }
+               }
+
+               break;
+       }
+       case CTF_FIELD_CLASS_TYPE_VARIANT:
+       {
+               struct ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
+
+               for (i = 0; i < var_fc->options->len; i++) {
+                       struct ctf_named_field_class *named_fc =
+                               ctf_field_class_variant_borrow_option_by_index(
+                                       var_fc, i);
+
+                       ret = set_text_array_sequence_field_class(named_fc->fc);
+                       if (ret) {
+                               goto end;
+                       }
+               }
+
+               break;
+       }
+       case CTF_FIELD_CLASS_TYPE_ARRAY:
+       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+       {
+               struct ctf_field_class_array_base *array_fc = ctf_field_class_as_array_base(fc);
+
+               if (array_fc->elem_fc->type == CTF_FIELD_CLASS_TYPE_INT ||
+                               array_fc->elem_fc->type == CTF_FIELD_CLASS_TYPE_ENUM) {
+                       struct ctf_field_class_int *int_fc =
+                               ctf_field_class_as_int(array_fc->elem_fc);
+
+                       if (int_fc->base.base.alignment == 8 &&
+                                       int_fc->base.size == 8 &&
+                                       int_fc->encoding == CTF_ENCODING_UTF8) {
+                               array_fc->is_text = true;
+
+                               /*
+                                * Force integer element to be unsigned;
+                                * this makes the decoder enter a single
+                                * path when reading a text
+                                * array/sequence and we can safely
+                                * decode bytes as characters anyway.
+                                */
+                               int_fc->is_signed = false;
+                       }
+               }
+
+               ret = set_text_array_sequence_field_class(array_fc->elem_fc);
+               if (ret) {
+                       goto end;
+               }
+
+               break;
+       }
+       default:
+               break;
+       }
+
+end:
+       return ret;
+}
+
+BT_HIDDEN
+int ctf_trace_class_update_text_array_sequence(struct ctf_trace_class *ctf_tc)
+{
+       int ret = 0;
+       uint64_t i;
+
+       if (!ctf_tc->is_translated) {
+               ret = set_text_array_sequence_field_class(
+                       ctf_tc->packet_header_fc);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+       for (i = 0; i < ctf_tc->stream_classes->len; i++) {
+               ctf_stream_class *sc = (ctf_stream_class *) ctf_tc->stream_classes->pdata[i];
+               uint64_t j;
+
+               if (!sc->is_translated) {
+                       ret = set_text_array_sequence_field_class(
+                               sc->packet_context_fc);
+                       if (ret) {
+                               goto end;
+                       }
+
+                       ret = set_text_array_sequence_field_class(
+                               sc->event_header_fc);
+                       if (ret) {
+                               goto end;
+                       }
+
+                       ret = set_text_array_sequence_field_class(
+                               sc->event_common_context_fc);
+                       if (ret) {
+                               goto end;
+                       }
+               }
+
+               for (j = 0; j < sc->event_classes->len; j++) {
+                       struct ctf_event_class *ec =
+                               (ctf_event_class *) sc->event_classes->pdata[j];
+
+                       if (ec->is_translated) {
+                               continue;
+                       }
+
+                       ret = set_text_array_sequence_field_class(
+                               ec->spec_context_fc);
+                       if (ret) {
+                               goto end;
+                       }
+
+                       ret = set_text_array_sequence_field_class(
+                               ec->payload_fc);
+                       if (ret) {
+                               goto end;
+                       }
+               }
+       }
+
+end:
+       return ret;
+}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-update-value-storing-indexes.c b/src/plugins/ctf/common/metadata/ctf-meta-update-value-storing-indexes.c
deleted file mode 100644 (file)
index dd9dfff..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
- */
-
-#include <babeltrace2/babeltrace.h>
-#include "common/macros.h"
-#include "common/assert.h"
-#include <glib.h>
-#include <stdint.h>
-#include <string.h>
-#include <inttypes.h>
-
-#include "ctf-meta-visitors.h"
-
-static
-int update_field_class_stored_value_index(struct ctf_field_class *fc,
-               struct ctf_trace_class *tc,
-               struct ctf_stream_class *sc,
-               struct ctf_event_class *ec)
-{
-       int ret = 0;
-       uint64_t i;
-       struct ctf_field_path *field_path = NULL;
-       struct ctf_field_class_int *tgt_fc = NULL;
-       uint64_t *stored_value_index = NULL;
-
-       if (!fc) {
-               goto end;
-       }
-
-       switch (fc->type) {
-       case CTF_FIELD_CLASS_TYPE_VARIANT:
-       {
-               struct ctf_field_class_variant *var_fc = (void *) fc;
-
-               field_path = &var_fc->tag_path;
-               stored_value_index = &var_fc->stored_tag_index;
-               tgt_fc = (void *) var_fc->tag_fc;
-               break;
-       }
-       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-       {
-               struct ctf_field_class_sequence *seq_fc = (void *) fc;
-
-               field_path = &seq_fc->length_path;
-               stored_value_index = &seq_fc->stored_length_index;
-               tgt_fc = seq_fc->length_fc;
-               break;
-       }
-       default:
-               break;
-       }
-
-       if (field_path) {
-               BT_ASSERT(tgt_fc);
-               BT_ASSERT(tgt_fc->base.base.type == CTF_FIELD_CLASS_TYPE_INT ||
-                       tgt_fc->base.base.type == CTF_FIELD_CLASS_TYPE_ENUM);
-               if (tgt_fc->storing_index >= 0) {
-                       /* Already storing its value */
-                       *stored_value_index = (uint64_t) tgt_fc->storing_index;
-               } else {
-                       /* Not storing its value: allocate new index */
-                       tgt_fc->storing_index = tc->stored_value_count;
-                       *stored_value_index = (uint64_t) tgt_fc->storing_index;
-                       tc->stored_value_count++;
-               }
-       }
-
-       switch (fc->type) {
-       case CTF_FIELD_CLASS_TYPE_STRUCT:
-       {
-               struct ctf_field_class_struct *struct_fc = (void *) fc;
-
-               for (i = 0; i < struct_fc->members->len; i++) {
-                       struct ctf_named_field_class *named_fc =
-                               ctf_field_class_struct_borrow_member_by_index(
-                                       struct_fc, i);
-
-                       ret = update_field_class_stored_value_index(named_fc->fc,
-                               tc, sc, ec);
-                       if (ret) {
-                               goto end;
-                       }
-               }
-
-               break;
-       }
-       case CTF_FIELD_CLASS_TYPE_VARIANT:
-       {
-               struct ctf_field_class_variant *var_fc = (void *) fc;
-
-               for (i = 0; i < var_fc->options->len; i++) {
-                       struct ctf_named_field_class *named_fc =
-                               ctf_field_class_variant_borrow_option_by_index(
-                                       var_fc, i);
-
-                       ret = update_field_class_stored_value_index(named_fc->fc,
-                               tc, sc, ec);
-                       if (ret) {
-                               goto end;
-                       }
-               }
-
-               break;
-       }
-       case CTF_FIELD_CLASS_TYPE_ARRAY:
-       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-       {
-               struct ctf_field_class_array_base *array_fc = (void *) fc;
-
-               ret = update_field_class_stored_value_index(array_fc->elem_fc,
-                       tc, sc, ec);
-               if (ret) {
-                       goto end;
-               }
-
-               break;
-       }
-       default:
-               break;
-       }
-
-end:
-       return ret;
-}
-
-BT_HIDDEN
-int ctf_trace_class_update_value_storing_indexes(struct ctf_trace_class *ctf_tc)
-{
-       uint64_t i;
-
-       if (!ctf_tc->is_translated) {
-               update_field_class_stored_value_index(
-                       ctf_tc->packet_header_fc, ctf_tc, NULL, NULL);
-       }
-
-       for (i = 0; i < ctf_tc->stream_classes->len; i++) {
-               uint64_t j;
-               struct ctf_stream_class *sc = ctf_tc->stream_classes->pdata[i];
-
-               if (!sc->is_translated) {
-                       update_field_class_stored_value_index(sc->packet_context_fc,
-                               ctf_tc, sc, NULL);
-                       update_field_class_stored_value_index(sc->event_header_fc,
-                               ctf_tc, sc, NULL);
-                       update_field_class_stored_value_index(
-                               sc->event_common_context_fc, ctf_tc, sc, NULL);
-               }
-
-               for (j = 0; j < sc->event_classes->len; j++) {
-                       struct ctf_event_class *ec =
-                               sc->event_classes->pdata[j];
-
-                       if (!ec->is_translated) {
-                               update_field_class_stored_value_index(
-                                       ec->spec_context_fc, ctf_tc, sc, ec);
-                               update_field_class_stored_value_index(
-                                       ec->payload_fc, ctf_tc, sc, ec);
-                       }
-               }
-       }
-
-       return 0;
-}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-update-value-storing-indexes.cpp b/src/plugins/ctf/common/metadata/ctf-meta-update-value-storing-indexes.cpp
new file mode 100644 (file)
index 0000000..794c542
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include <babeltrace2/babeltrace.h>
+#include "common/macros.h"
+#include "common/assert.h"
+#include <glib.h>
+#include <stdint.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "ctf-meta-visitors.hpp"
+
+static
+int update_field_class_stored_value_index(struct ctf_field_class *fc,
+               struct ctf_trace_class *tc,
+               struct ctf_stream_class *sc,
+               struct ctf_event_class *ec)
+{
+       int ret = 0;
+       uint64_t i;
+       struct ctf_field_path *field_path = NULL;
+       struct ctf_field_class_int *tgt_fc = NULL;
+       uint64_t *stored_value_index = NULL;
+
+       if (!fc) {
+               goto end;
+       }
+
+       switch (fc->type) {
+       case CTF_FIELD_CLASS_TYPE_VARIANT:
+       {
+               ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
+
+               field_path = &var_fc->tag_path;
+               stored_value_index = &var_fc->stored_tag_index;
+               tgt_fc = &var_fc->tag_fc->base;
+               break;
+       }
+       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+       {
+               struct ctf_field_class_sequence *seq_fc = ctf_field_class_as_sequence(fc);
+
+               field_path = &seq_fc->length_path;
+               stored_value_index = &seq_fc->stored_length_index;
+               tgt_fc = seq_fc->length_fc;
+               break;
+       }
+       default:
+               break;
+       }
+
+       if (field_path) {
+               BT_ASSERT(tgt_fc);
+               BT_ASSERT(tgt_fc->base.base.type == CTF_FIELD_CLASS_TYPE_INT ||
+                       tgt_fc->base.base.type == CTF_FIELD_CLASS_TYPE_ENUM);
+               if (tgt_fc->storing_index >= 0) {
+                       /* Already storing its value */
+                       *stored_value_index = (uint64_t) tgt_fc->storing_index;
+               } else {
+                       /* Not storing its value: allocate new index */
+                       tgt_fc->storing_index = tc->stored_value_count;
+                       *stored_value_index = (uint64_t) tgt_fc->storing_index;
+                       tc->stored_value_count++;
+               }
+       }
+
+       switch (fc->type) {
+       case CTF_FIELD_CLASS_TYPE_STRUCT:
+       {
+               struct ctf_field_class_struct *struct_fc = ctf_field_class_as_struct(fc);
+
+               for (i = 0; i < struct_fc->members->len; i++) {
+                       struct ctf_named_field_class *named_fc =
+                               ctf_field_class_struct_borrow_member_by_index(
+                                       struct_fc, i);
+
+                       ret = update_field_class_stored_value_index(named_fc->fc,
+                               tc, sc, ec);
+                       if (ret) {
+                               goto end;
+                       }
+               }
+
+               break;
+       }
+       case CTF_FIELD_CLASS_TYPE_VARIANT:
+       {
+               struct ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
+
+               for (i = 0; i < var_fc->options->len; i++) {
+                       struct ctf_named_field_class *named_fc =
+                               ctf_field_class_variant_borrow_option_by_index(
+                                       var_fc, i);
+
+                       ret = update_field_class_stored_value_index(named_fc->fc,
+                               tc, sc, ec);
+                       if (ret) {
+                               goto end;
+                       }
+               }
+
+               break;
+       }
+       case CTF_FIELD_CLASS_TYPE_ARRAY:
+       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+       {
+               struct ctf_field_class_array_base *array_fc = ctf_field_class_as_array_base(fc);
+
+               ret = update_field_class_stored_value_index(array_fc->elem_fc,
+                       tc, sc, ec);
+               if (ret) {
+                       goto end;
+               }
+
+               break;
+       }
+       default:
+               break;
+       }
+
+end:
+       return ret;
+}
+
+BT_HIDDEN
+int ctf_trace_class_update_value_storing_indexes(struct ctf_trace_class *ctf_tc)
+{
+       uint64_t i;
+
+       if (!ctf_tc->is_translated) {
+               update_field_class_stored_value_index(
+                       ctf_tc->packet_header_fc, ctf_tc, NULL, NULL);
+       }
+
+       for (i = 0; i < ctf_tc->stream_classes->len; i++) {
+               uint64_t j;
+               ctf_stream_class *sc = (ctf_stream_class *) ctf_tc->stream_classes->pdata[i];
+
+               if (!sc->is_translated) {
+                       update_field_class_stored_value_index(sc->packet_context_fc,
+                               ctf_tc, sc, NULL);
+                       update_field_class_stored_value_index(sc->event_header_fc,
+                               ctf_tc, sc, NULL);
+                       update_field_class_stored_value_index(
+                               sc->event_common_context_fc, ctf_tc, sc, NULL);
+               }
+
+               for (j = 0; j < sc->event_classes->len; j++) {
+                       struct ctf_event_class *ec =
+                               (ctf_event_class *) sc->event_classes->pdata[j];
+
+                       if (!ec->is_translated) {
+                               update_field_class_stored_value_index(
+                                       ec->spec_context_fc, ctf_tc, sc, ec);
+                               update_field_class_stored_value_index(
+                                       ec->payload_fc, ctf_tc, sc, ec);
+                       }
+               }
+       }
+
+       return 0;
+}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-validate.c b/src/plugins/ctf/common/metadata/ctf-meta-validate.c
deleted file mode 100644 (file)
index da5c95c..0000000
+++ /dev/null
@@ -1,347 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
- */
-
-#define BT_COMP_LOG_SELF_COMP (log_cfg->self_comp)
-#define BT_COMP_LOG_SELF_COMP_CLASS (log_cfg->self_comp_class)
-#define BT_LOG_OUTPUT_LEVEL (log_cfg->log_level)
-#define BT_LOG_TAG "PLUGIN/CTF/META/VALIDATE"
-#include "logging/comp-logging.h"
-
-#include <babeltrace2/babeltrace.h>
-#include "common/macros.h"
-#include "common/assert.h"
-#include <glib.h>
-#include <stdint.h>
-#include <string.h>
-#include <inttypes.h>
-
-#include "ctf-meta-visitors.h"
-#include "logging.h"
-
-static
-int validate_stream_class(struct ctf_stream_class *sc,
-               struct meta_log_config *log_cfg)
-{
-       int ret = 0;
-       struct ctf_field_class_int *int_fc;
-       struct ctf_field_class *fc;
-
-       if (sc->is_translated) {
-               goto end;
-       }
-
-       fc = ctf_field_class_struct_borrow_member_field_class_by_name(
-               (void *) sc->packet_context_fc, "timestamp_begin");
-       if (fc) {
-               if (fc->type != CTF_FIELD_CLASS_TYPE_INT &&
-                               fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet context field class: "
-                               "`timestamp_begin` member is not an integer field class.");
-                       goto invalid;
-               }
-
-               int_fc = (void *) fc;
-
-               if (int_fc->is_signed) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet context field class: "
-                               "`timestamp_begin` member is signed.");
-                       goto invalid;
-               }
-       }
-
-       fc = ctf_field_class_struct_borrow_member_field_class_by_name(
-               (void *) sc->packet_context_fc, "timestamp_end");
-       if (fc) {
-               if (fc->type != CTF_FIELD_CLASS_TYPE_INT &&
-                               fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet context field class: "
-                               "`timestamp_end` member is not an integer field class.");
-                       goto invalid;
-               }
-
-               int_fc = (void *) fc;
-
-               if (int_fc->is_signed) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet context field class: "
-                               "`timestamp_end` member is signed.");
-                       goto invalid;
-               }
-       }
-
-       fc = ctf_field_class_struct_borrow_member_field_class_by_name(
-               (void *) sc->packet_context_fc, "events_discarded");
-       if (fc) {
-               if (fc->type != CTF_FIELD_CLASS_TYPE_INT &&
-                               fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet context field class: "
-                               "`events_discarded` member is not an integer field class.");
-                       goto invalid;
-               }
-
-               int_fc = (void *) fc;
-
-               if (int_fc->is_signed) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet context field class: "
-                               "`events_discarded` member is signed.");
-                       goto invalid;
-               }
-       }
-
-       fc = ctf_field_class_struct_borrow_member_field_class_by_name(
-               (void *) sc->packet_context_fc, "packet_seq_num");
-       if (fc) {
-               if (fc->type != CTF_FIELD_CLASS_TYPE_INT &&
-                               fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet context field class: "
-                               "`packet_seq_num` member is not an integer field class.");
-                       goto invalid;
-               }
-
-               int_fc = (void *) fc;
-
-               if (int_fc->is_signed) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet context field class: "
-                               "`packet_seq_num` member is signed.");
-                       goto invalid;
-               }
-       }
-
-       fc = ctf_field_class_struct_borrow_member_field_class_by_name(
-               (void *) sc->packet_context_fc, "packet_size");
-       if (fc) {
-               if (fc->type != CTF_FIELD_CLASS_TYPE_INT &&
-                               fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet context field class: "
-                               "`packet_size` member is not an integer field class.");
-                       goto invalid;
-               }
-
-               int_fc = (void *) fc;
-
-               if (int_fc->is_signed) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet context field class: "
-                               "`packet_size` member is signed.");
-                       goto invalid;
-               }
-       }
-
-       fc = ctf_field_class_struct_borrow_member_field_class_by_name(
-               (void *) sc->packet_context_fc, "content_size");
-       if (fc) {
-               if (fc->type != CTF_FIELD_CLASS_TYPE_INT &&
-                               fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet context field class: "
-                               "`content_size` member is not an integer field class.");
-                       goto invalid;
-               }
-
-               int_fc = (void *) fc;
-
-               if (int_fc->is_signed) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet context field class: "
-                               "`content_size` member is signed.");
-                       goto invalid;
-               }
-       }
-
-       fc = ctf_field_class_struct_borrow_member_field_class_by_name(
-               (void *) sc->event_header_fc, "id");
-       if (fc) {
-               if (fc->type != CTF_FIELD_CLASS_TYPE_INT &&
-                               fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid event header field class: "
-                               "`id` member is not an integer field class.");
-                       goto invalid;
-               }
-
-               int_fc = (void *) fc;
-
-               if (int_fc->is_signed) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid event header field class: "
-                               "`id` member is signed.");
-                       goto invalid;
-               }
-       } else {
-               if (sc->event_classes->len > 1) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid event header field class: "
-                               "missing `id` member as there's "
-                               "more than one event class.");
-                       goto invalid;
-               }
-       }
-
-       goto end;
-
-invalid:
-       ret = -1;
-
-end:
-       return ret;
-}
-
-BT_HIDDEN
-int ctf_trace_class_validate(struct ctf_trace_class *ctf_tc,
-               struct meta_log_config *log_cfg)
-{
-       int ret = 0;
-       struct ctf_field_class_int *int_fc;
-       uint64_t i;
-
-       if (!ctf_tc->is_translated) {
-               struct ctf_field_class *fc;
-
-               fc = ctf_field_class_struct_borrow_member_field_class_by_name(
-                       (void *) ctf_tc->packet_header_fc, "magic");
-               if (fc) {
-                       struct ctf_named_field_class *named_fc =
-                               ctf_field_class_struct_borrow_member_by_index(
-                                       (void *) ctf_tc->packet_header_fc,
-                                       0);
-
-                       if (named_fc->fc != fc) {
-                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
-                                       "`magic` member is not the first member.");
-                               goto invalid;
-                       }
-
-                       if (fc->type != CTF_FIELD_CLASS_TYPE_INT &&
-                                       fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
-                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
-                                       "`magic` member is not an integer field class.");
-                               goto invalid;
-                       }
-
-                       int_fc = (void *) fc;
-
-                       if (int_fc->is_signed) {
-                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
-                                       "`magic` member is signed.");
-                               goto invalid;
-                       }
-
-                       if (int_fc->base.size != 32) {
-                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
-                                       "`magic` member is not 32-bit.");
-                               goto invalid;
-                       }
-               }
-
-               fc = ctf_field_class_struct_borrow_member_field_class_by_name(
-                       (void *) ctf_tc->packet_header_fc, "stream_id");
-               if (fc) {
-                       if (fc->type != CTF_FIELD_CLASS_TYPE_INT &&
-                                       fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
-                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
-                                       "`stream_id` member is not an integer field class.");
-                               goto invalid;
-                       }
-
-                       int_fc = (void *) fc;
-
-                       if (int_fc->is_signed) {
-                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
-                                       "`stream_id` member is signed.");
-                               goto invalid;
-                       }
-               } else {
-                       if (ctf_tc->stream_classes->len > 1) {
-                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
-                                       "missing `stream_id` member as there's "
-                                       "more than one stream class.");
-                               goto invalid;
-                       }
-               }
-
-               fc = ctf_field_class_struct_borrow_member_field_class_by_name(
-                       (void *) ctf_tc->packet_header_fc,
-                       "stream_instance_id");
-               if (fc) {
-                       if (fc->type != CTF_FIELD_CLASS_TYPE_INT &&
-                                       fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
-                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
-                                       "`stream_instance_id` member is not an integer field class.");
-                               goto invalid;
-                       }
-
-                       int_fc = (void *) fc;
-
-                       if (int_fc->is_signed) {
-                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
-                                       "`stream_instance_id` member is signed.");
-                               goto invalid;
-                       }
-               }
-
-               fc = ctf_field_class_struct_borrow_member_field_class_by_name(
-                       (void *) ctf_tc->packet_header_fc, "uuid");
-               if (fc) {
-                       struct ctf_field_class_array *array_fc = (void *) fc;
-
-                       if (fc->type != CTF_FIELD_CLASS_TYPE_ARRAY) {
-                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
-                                       "`uuid` member is not an array field class.");
-                               goto invalid;
-                       }
-
-                       array_fc = (void *) fc;
-
-                       if (array_fc->length != 16) {
-                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
-                                       "`uuid` member is not a 16-element array field class.");
-                               goto invalid;
-                       }
-
-                       if (array_fc->base.elem_fc->type != CTF_FIELD_CLASS_TYPE_INT) {
-                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
-                                       "`uuid` member's element field class is not "
-                                       "an integer field class.");
-                               goto invalid;
-                       }
-
-                       int_fc = (void *) array_fc->base.elem_fc;
-
-                       if (int_fc->is_signed) {
-                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
-                                       "`uuid` member's element field class "
-                                       "is a signed integer field class.");
-                               goto invalid;
-                       }
-
-                       if (int_fc->base.size != 8) {
-                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
-                                       "`uuid` member's element field class "
-                                       "is not an 8-bit integer field class.");
-                               goto invalid;
-                       }
-
-                       if (int_fc->base.base.alignment != 8) {
-                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
-                                       "`uuid` member's element field class's "
-                                       "alignment is not 8.");
-                               goto invalid;
-                       }
-               }
-       }
-
-       for (i = 0; i < ctf_tc->stream_classes->len; i++) {
-               struct ctf_stream_class *sc =
-                       ctf_tc->stream_classes->pdata[i];
-
-               ret = validate_stream_class(sc, log_cfg);
-               if (ret) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid stream class: sc-id=%" PRIu64, sc->id);
-                       goto invalid;
-               }
-       }
-
-       goto end;
-
-invalid:
-       ret = -1;
-
-end:
-       return ret;
-}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-validate.cpp b/src/plugins/ctf/common/metadata/ctf-meta-validate.cpp
new file mode 100644 (file)
index 0000000..d28a5fe
--- /dev/null
@@ -0,0 +1,345 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#define BT_COMP_LOG_SELF_COMP (log_cfg->self_comp)
+#define BT_COMP_LOG_SELF_COMP_CLASS (log_cfg->self_comp_class)
+#define BT_LOG_OUTPUT_LEVEL (log_cfg->log_level)
+#define BT_LOG_TAG "PLUGIN/CTF/META/VALIDATE"
+#include "logging/comp-logging.h"
+
+#include <babeltrace2/babeltrace.h>
+#include "common/macros.h"
+#include "common/assert.h"
+#include <glib.h>
+#include <stdint.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "ctf-meta-visitors.hpp"
+#include "logging.hpp"
+
+static
+int validate_stream_class(struct ctf_stream_class *sc,
+               struct meta_log_config *log_cfg)
+{
+       int ret = 0;
+       struct ctf_field_class_int *int_fc;
+       struct ctf_field_class *fc;
+
+       if (sc->is_translated) {
+               goto end;
+       }
+
+       fc = ctf_field_class_struct_borrow_member_field_class_by_name(
+               ctf_field_class_as_struct(sc->packet_context_fc), "timestamp_begin");
+       if (fc) {
+               if (fc->type != CTF_FIELD_CLASS_TYPE_INT &&
+                               fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet context field class: "
+                               "`timestamp_begin` member is not an integer field class.");
+                       goto invalid;
+               }
+
+               int_fc = ctf_field_class_as_int(fc);
+
+               if (int_fc->is_signed) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet context field class: "
+                               "`timestamp_begin` member is signed.");
+                       goto invalid;
+               }
+       }
+
+       fc = ctf_field_class_struct_borrow_member_field_class_by_name(
+               ctf_field_class_as_struct(sc->packet_context_fc), "timestamp_end");
+       if (fc) {
+               if (fc->type != CTF_FIELD_CLASS_TYPE_INT &&
+                               fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet context field class: "
+                               "`timestamp_end` member is not an integer field class.");
+                       goto invalid;
+               }
+
+               int_fc = ctf_field_class_as_int(fc);
+
+               if (int_fc->is_signed) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet context field class: "
+                               "`timestamp_end` member is signed.");
+                       goto invalid;
+               }
+       }
+
+       fc = ctf_field_class_struct_borrow_member_field_class_by_name(
+               ctf_field_class_as_struct(sc->packet_context_fc), "events_discarded");
+       if (fc) {
+               if (fc->type != CTF_FIELD_CLASS_TYPE_INT &&
+                               fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet context field class: "
+                               "`events_discarded` member is not an integer field class.");
+                       goto invalid;
+               }
+
+               int_fc = ctf_field_class_as_int(fc);
+
+               if (int_fc->is_signed) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet context field class: "
+                               "`events_discarded` member is signed.");
+                       goto invalid;
+               }
+       }
+
+       fc = ctf_field_class_struct_borrow_member_field_class_by_name(
+               ctf_field_class_as_struct(sc->packet_context_fc), "packet_seq_num");
+       if (fc) {
+               if (fc->type != CTF_FIELD_CLASS_TYPE_INT &&
+                               fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet context field class: "
+                               "`packet_seq_num` member is not an integer field class.");
+                       goto invalid;
+               }
+
+               int_fc = ctf_field_class_as_int(fc);
+
+               if (int_fc->is_signed) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet context field class: "
+                               "`packet_seq_num` member is signed.");
+                       goto invalid;
+               }
+       }
+
+       fc = ctf_field_class_struct_borrow_member_field_class_by_name(
+               ctf_field_class_as_struct(sc->packet_context_fc), "packet_size");
+       if (fc) {
+               if (fc->type != CTF_FIELD_CLASS_TYPE_INT &&
+                               fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet context field class: "
+                               "`packet_size` member is not an integer field class.");
+                       goto invalid;
+               }
+
+               int_fc = ctf_field_class_as_int(fc);
+
+               if (int_fc->is_signed) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet context field class: "
+                               "`packet_size` member is signed.");
+                       goto invalid;
+               }
+       }
+
+       fc = ctf_field_class_struct_borrow_member_field_class_by_name(
+               ctf_field_class_as_struct(sc->packet_context_fc), "content_size");
+       if (fc) {
+               if (fc->type != CTF_FIELD_CLASS_TYPE_INT &&
+                               fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet context field class: "
+                               "`content_size` member is not an integer field class.");
+                       goto invalid;
+               }
+
+               int_fc = ctf_field_class_as_int(fc);
+
+               if (int_fc->is_signed) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet context field class: "
+                               "`content_size` member is signed.");
+                       goto invalid;
+               }
+       }
+
+       fc = ctf_field_class_struct_borrow_member_field_class_by_name(
+               ctf_field_class_as_struct(sc->event_header_fc), "id");
+       if (fc) {
+               if (fc->type != CTF_FIELD_CLASS_TYPE_INT &&
+                               fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid event header field class: "
+                               "`id` member is not an integer field class.");
+                       goto invalid;
+               }
+
+               int_fc = ctf_field_class_as_int(fc);
+
+               if (int_fc->is_signed) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid event header field class: "
+                               "`id` member is signed.");
+                       goto invalid;
+               }
+       } else {
+               if (sc->event_classes->len > 1) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid event header field class: "
+                               "missing `id` member as there's "
+                               "more than one event class.");
+                       goto invalid;
+               }
+       }
+
+       goto end;
+
+invalid:
+       ret = -1;
+
+end:
+       return ret;
+}
+
+BT_HIDDEN
+int ctf_trace_class_validate(struct ctf_trace_class *ctf_tc,
+               struct meta_log_config *log_cfg)
+{
+       int ret = 0;
+       struct ctf_field_class_int *int_fc;
+       uint64_t i;
+
+       if (!ctf_tc->is_translated) {
+               struct ctf_field_class *fc;
+
+               fc = ctf_field_class_struct_borrow_member_field_class_by_name(
+                       ctf_field_class_as_struct(ctf_tc->packet_header_fc), "magic");
+               if (fc) {
+                       struct ctf_named_field_class *named_fc =
+                               ctf_field_class_struct_borrow_member_by_index(
+                                       ctf_field_class_as_struct(ctf_tc->packet_header_fc),
+                                       0);
+
+                       if (named_fc->fc != fc) {
+                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
+                                       "`magic` member is not the first member.");
+                               goto invalid;
+                       }
+
+                       if (fc->type != CTF_FIELD_CLASS_TYPE_INT &&
+                                       fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
+                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
+                                       "`magic` member is not an integer field class.");
+                               goto invalid;
+                       }
+
+                       int_fc = ctf_field_class_as_int(fc);
+
+                       if (int_fc->is_signed) {
+                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
+                                       "`magic` member is signed.");
+                               goto invalid;
+                       }
+
+                       if (int_fc->base.size != 32) {
+                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
+                                       "`magic` member is not 32-bit.");
+                               goto invalid;
+                       }
+               }
+
+               fc = ctf_field_class_struct_borrow_member_field_class_by_name(
+                       ctf_field_class_as_struct(ctf_tc->packet_header_fc), "stream_id");
+               if (fc) {
+                       if (fc->type != CTF_FIELD_CLASS_TYPE_INT &&
+                                       fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
+                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
+                                       "`stream_id` member is not an integer field class.");
+                               goto invalid;
+                       }
+
+                       int_fc = ctf_field_class_as_int(fc);
+
+                       if (int_fc->is_signed) {
+                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
+                                       "`stream_id` member is signed.");
+                               goto invalid;
+                       }
+               } else {
+                       if (ctf_tc->stream_classes->len > 1) {
+                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
+                                       "missing `stream_id` member as there's "
+                                       "more than one stream class.");
+                               goto invalid;
+                       }
+               }
+
+               fc = ctf_field_class_struct_borrow_member_field_class_by_name(
+                       ctf_field_class_as_struct(ctf_tc->packet_header_fc),
+                       "stream_instance_id");
+               if (fc) {
+                       if (fc->type != CTF_FIELD_CLASS_TYPE_INT &&
+                                       fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
+                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
+                                       "`stream_instance_id` member is not an integer field class.");
+                               goto invalid;
+                       }
+
+                       int_fc = ctf_field_class_as_int(fc);
+
+                       if (int_fc->is_signed) {
+                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
+                                       "`stream_instance_id` member is signed.");
+                               goto invalid;
+                       }
+               }
+
+               fc = ctf_field_class_struct_borrow_member_field_class_by_name(
+                       ctf_field_class_as_struct(ctf_tc->packet_header_fc), "uuid");
+               if (fc) {
+                       if (fc->type != CTF_FIELD_CLASS_TYPE_ARRAY) {
+                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
+                                       "`uuid` member is not an array field class.");
+                               goto invalid;
+                       }
+
+                       ctf_field_class_array *array_fc = ctf_field_class_as_array(fc);
+
+                       if (array_fc->length != 16) {
+                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
+                                       "`uuid` member is not a 16-element array field class.");
+                               goto invalid;
+                       }
+
+                       if (array_fc->base.elem_fc->type != CTF_FIELD_CLASS_TYPE_INT) {
+                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
+                                       "`uuid` member's element field class is not "
+                                       "an integer field class.");
+                               goto invalid;
+                       }
+
+                       int_fc = ctf_field_class_as_int(array_fc->base.elem_fc);
+
+                       if (int_fc->is_signed) {
+                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
+                                       "`uuid` member's element field class "
+                                       "is a signed integer field class.");
+                               goto invalid;
+                       }
+
+                       if (int_fc->base.size != 8) {
+                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
+                                       "`uuid` member's element field class "
+                                       "is not an 8-bit integer field class.");
+                               goto invalid;
+                       }
+
+                       if (int_fc->base.base.alignment != 8) {
+                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid packet header field class: "
+                                       "`uuid` member's element field class's "
+                                       "alignment is not 8.");
+                               goto invalid;
+                       }
+               }
+       }
+
+       for (i = 0; i < ctf_tc->stream_classes->len; i++) {
+               struct ctf_stream_class *sc =
+                       (ctf_stream_class *) ctf_tc->stream_classes->pdata[i];
+
+               ret = validate_stream_class(sc, log_cfg);
+               if (ret) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid stream class: sc-id=%" PRIu64, sc->id);
+                       goto invalid;
+               }
+       }
+
+       goto end;
+
+invalid:
+       ret = -1;
+
+end:
+       return ret;
+}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-visitors.h b/src/plugins/ctf/common/metadata/ctf-meta-visitors.h
deleted file mode 100644 (file)
index 34edb37..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
- */
-
-#ifndef _CTF_META_VISITORS_H
-#define _CTF_META_VISITORS_H
-
-#include <babeltrace2/babeltrace.h>
-#include "common/macros.h"
-
-#include "ctf-meta.h"
-
-struct meta_log_config;
-
-BT_HIDDEN
-int ctf_trace_class_resolve_field_classes(struct ctf_trace_class *tc,
-               struct meta_log_config *log_cfg);
-
-BT_HIDDEN
-int ctf_trace_class_translate(bt_self_component *self_comp,
-               bt_trace_class *ir_tc, struct ctf_trace_class *tc);
-
-BT_HIDDEN
-int ctf_trace_class_update_default_clock_classes(
-               struct ctf_trace_class *ctf_tc,
-               struct meta_log_config *log_cfg);
-
-BT_HIDDEN
-int ctf_trace_class_update_in_ir(struct ctf_trace_class *ctf_tc);
-
-BT_HIDDEN
-int ctf_trace_class_update_meanings(struct ctf_trace_class *ctf_tc);
-
-BT_HIDDEN
-int ctf_trace_class_update_text_array_sequence(struct ctf_trace_class *ctf_tc);
-
-BT_HIDDEN
-int ctf_trace_class_update_alignments(struct ctf_trace_class *ctf_tc);
-
-BT_HIDDEN
-int ctf_trace_class_update_value_storing_indexes(struct ctf_trace_class *ctf_tc);
-
-BT_HIDDEN
-int ctf_trace_class_update_stream_class_config(struct ctf_trace_class *ctf_tc);
-
-BT_HIDDEN
-int ctf_trace_class_validate(struct ctf_trace_class *ctf_tc,
-               struct meta_log_config *log_cfg);
-
-BT_HIDDEN
-void ctf_trace_class_warn_meaningless_header_fields(
-               struct ctf_trace_class *ctf_tc,
-               struct meta_log_config *log_cfg);
-
-#endif /* _CTF_META_VISITORS_H */
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-visitors.hpp b/src/plugins/ctf/common/metadata/ctf-meta-visitors.hpp
new file mode 100644 (file)
index 0000000..ab22751
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#ifndef _CTF_META_VISITORS_H
+#define _CTF_META_VISITORS_H
+
+#include <babeltrace2/babeltrace.h>
+#include "common/macros.h"
+
+#include "ctf-meta.hpp"
+
+struct meta_log_config;
+
+BT_HIDDEN
+int ctf_trace_class_resolve_field_classes(struct ctf_trace_class *tc,
+               struct meta_log_config *log_cfg);
+
+BT_HIDDEN
+int ctf_trace_class_translate(bt_self_component *self_comp,
+               bt_trace_class *ir_tc, struct ctf_trace_class *tc);
+
+BT_HIDDEN
+int ctf_trace_class_update_default_clock_classes(
+               struct ctf_trace_class *ctf_tc,
+               struct meta_log_config *log_cfg);
+
+BT_HIDDEN
+int ctf_trace_class_update_in_ir(struct ctf_trace_class *ctf_tc);
+
+BT_HIDDEN
+int ctf_trace_class_update_meanings(struct ctf_trace_class *ctf_tc);
+
+BT_HIDDEN
+int ctf_trace_class_update_text_array_sequence(struct ctf_trace_class *ctf_tc);
+
+BT_HIDDEN
+int ctf_trace_class_update_alignments(struct ctf_trace_class *ctf_tc);
+
+BT_HIDDEN
+int ctf_trace_class_update_value_storing_indexes(struct ctf_trace_class *ctf_tc);
+
+BT_HIDDEN
+int ctf_trace_class_update_stream_class_config(struct ctf_trace_class *ctf_tc);
+
+BT_HIDDEN
+int ctf_trace_class_validate(struct ctf_trace_class *ctf_tc,
+               struct meta_log_config *log_cfg);
+
+BT_HIDDEN
+void ctf_trace_class_warn_meaningless_header_fields(
+               struct ctf_trace_class *ctf_tc,
+               struct meta_log_config *log_cfg);
+
+#endif /* _CTF_META_VISITORS_H */
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-warn-meaningless-header-fields.c b/src/plugins/ctf/common/metadata/ctf-meta-warn-meaningless-header-fields.c
deleted file mode 100644 (file)
index d768f97..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
- */
-
-#define BT_COMP_LOG_SELF_COMP (log_cfg->self_comp)
-#define BT_LOG_OUTPUT_LEVEL (log_cfg->log_level)
-#define BT_LOG_TAG "PLUGIN/CTF/META/WARN-MEANINGLESS-HEADER-FIELDS"
-#include "logging/comp-logging.h"
-
-#include <babeltrace2/babeltrace.h>
-#include "common/macros.h"
-#include "common/assert.h"
-#include <glib.h>
-#include <stdint.h>
-#include <string.h>
-#include <inttypes.h>
-
-#include "ctf-meta-visitors.h"
-#include "logging.h"
-
-static inline
-void warn_meaningless_field(const char *name, const char *scope_name,
-               struct meta_log_config *log_cfg)
-{
-       BT_ASSERT(name);
-       BT_COMP_LOGW("User field found in %s: ignoring: name=\"%s\"",
-               scope_name, name);
-}
-
-static inline
-void warn_meaningless_fields(struct ctf_field_class *fc, const char *name,
-               const char *scope_name, struct meta_log_config *log_cfg)
-{
-       uint64_t i;
-
-       if (!fc) {
-               goto end;
-       }
-
-       /*
-        * 'name' is guaranteed to be non-NULL whenever the field class is not a
-        * structure. In the case of a structure field class, its members' names
-        * are used.
-        */
-       switch (fc->type) {
-       case CTF_FIELD_CLASS_TYPE_FLOAT:
-       case CTF_FIELD_CLASS_TYPE_STRING:
-               warn_meaningless_field(name, scope_name, log_cfg);
-               break;
-       case CTF_FIELD_CLASS_TYPE_INT:
-       case CTF_FIELD_CLASS_TYPE_ENUM:
-       {
-               struct ctf_field_class_int *int_fc = (void *) fc;
-
-               if (int_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE &&
-                               !int_fc->mapped_clock_class) {
-                       warn_meaningless_field(name, scope_name, log_cfg);
-               }
-
-               break;
-       }
-       case CTF_FIELD_CLASS_TYPE_STRUCT:
-       {
-               struct ctf_field_class_struct *struct_fc = (void *) fc;
-
-               for (i = 0; i < struct_fc->members->len; i++) {
-                       struct ctf_named_field_class *named_fc =
-                               ctf_field_class_struct_borrow_member_by_index(
-                                       struct_fc, i);
-
-                       warn_meaningless_fields(named_fc->fc,
-                               named_fc->name->str, scope_name, log_cfg);
-               }
-
-               break;
-       }
-       case CTF_FIELD_CLASS_TYPE_VARIANT:
-       {
-               struct ctf_field_class_variant *var_fc = (void *) fc;
-
-               for (i = 0; i < var_fc->options->len; i++) {
-                       struct ctf_named_field_class *named_fc =
-                               ctf_field_class_variant_borrow_option_by_index(
-                                       var_fc, i);
-
-                       warn_meaningless_fields(named_fc->fc,
-                               named_fc->name->str, scope_name, log_cfg);
-               }
-
-               break;
-       }
-       case CTF_FIELD_CLASS_TYPE_ARRAY:
-       {
-               struct ctf_field_class_array *array_fc = (void *) fc;
-
-               if (array_fc->meaning != CTF_FIELD_CLASS_MEANING_NONE) {
-                       goto end;
-               }
-
-       }
-       /* fall-through */
-       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-       {
-               struct ctf_field_class_array_base *array_fc = (void *) fc;
-
-               warn_meaningless_fields(array_fc->elem_fc, name, scope_name,
-                       log_cfg);
-               break;
-       }
-       default:
-               bt_common_abort();
-       }
-
-end:
-       return;
-}
-
-BT_HIDDEN
-void ctf_trace_class_warn_meaningless_header_fields(
-               struct ctf_trace_class *ctf_tc,
-               struct meta_log_config *log_cfg)
-{
-       uint64_t i;
-
-       if (!ctf_tc->is_translated) {
-               warn_meaningless_fields(
-                       ctf_tc->packet_header_fc, NULL, "packet header",
-                       log_cfg);
-       }
-
-       for (i = 0; i < ctf_tc->stream_classes->len; i++) {
-               struct ctf_stream_class *sc = ctf_tc->stream_classes->pdata[i];
-
-               if (!sc->is_translated) {
-                       warn_meaningless_fields(sc->event_header_fc, NULL,
-                               "event header", log_cfg);
-               }
-       }
-}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta-warn-meaningless-header-fields.cpp b/src/plugins/ctf/common/metadata/ctf-meta-warn-meaningless-header-fields.cpp
new file mode 100644 (file)
index 0000000..03daddf
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#define BT_COMP_LOG_SELF_COMP (log_cfg->self_comp)
+#define BT_LOG_OUTPUT_LEVEL (log_cfg->log_level)
+#define BT_LOG_TAG "PLUGIN/CTF/META/WARN-MEANINGLESS-HEADER-FIELDS"
+#include "logging/comp-logging.h"
+
+#include <babeltrace2/babeltrace.h>
+#include "common/macros.h"
+#include "common/assert.h"
+#include <glib.h>
+#include <stdint.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "ctf-meta-visitors.hpp"
+#include "logging.hpp"
+
+static inline
+void warn_meaningless_field(const char *name, const char *scope_name,
+               struct meta_log_config *log_cfg)
+{
+       BT_ASSERT(name);
+       BT_COMP_LOGW("User field found in %s: ignoring: name=\"%s\"",
+               scope_name, name);
+}
+
+static inline
+void warn_meaningless_fields(struct ctf_field_class *fc, const char *name,
+               const char *scope_name, struct meta_log_config *log_cfg)
+{
+       uint64_t i;
+
+       if (!fc) {
+               goto end;
+       }
+
+       /*
+        * 'name' is guaranteed to be non-NULL whenever the field class is not a
+        * structure. In the case of a structure field class, its members' names
+        * are used.
+        */
+       switch (fc->type) {
+       case CTF_FIELD_CLASS_TYPE_FLOAT:
+       case CTF_FIELD_CLASS_TYPE_STRING:
+               warn_meaningless_field(name, scope_name, log_cfg);
+               break;
+       case CTF_FIELD_CLASS_TYPE_INT:
+       case CTF_FIELD_CLASS_TYPE_ENUM:
+       {
+               struct ctf_field_class_int *int_fc = ctf_field_class_as_int(fc);
+
+               if (int_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE &&
+                               !int_fc->mapped_clock_class) {
+                       warn_meaningless_field(name, scope_name, log_cfg);
+               }
+
+               break;
+       }
+       case CTF_FIELD_CLASS_TYPE_STRUCT:
+       {
+               struct ctf_field_class_struct *struct_fc = ctf_field_class_as_struct(fc);
+
+               for (i = 0; i < struct_fc->members->len; i++) {
+                       struct ctf_named_field_class *named_fc =
+                               ctf_field_class_struct_borrow_member_by_index(
+                                       struct_fc, i);
+
+                       warn_meaningless_fields(named_fc->fc,
+                               named_fc->name->str, scope_name, log_cfg);
+               }
+
+               break;
+       }
+       case CTF_FIELD_CLASS_TYPE_VARIANT:
+       {
+               struct ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
+
+               for (i = 0; i < var_fc->options->len; i++) {
+                       struct ctf_named_field_class *named_fc =
+                               ctf_field_class_variant_borrow_option_by_index(
+                                       var_fc, i);
+
+                       warn_meaningless_fields(named_fc->fc,
+                               named_fc->name->str, scope_name, log_cfg);
+               }
+
+               break;
+       }
+       case CTF_FIELD_CLASS_TYPE_ARRAY:
+       {
+               struct ctf_field_class_array *array_fc = ctf_field_class_as_array(fc);
+
+               if (array_fc->meaning != CTF_FIELD_CLASS_MEANING_NONE) {
+                       goto end;
+               }
+
+       }
+       /* fall-through */
+       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+       {
+               struct ctf_field_class_array_base *array_fc = ctf_field_class_as_array_base(fc);
+
+               warn_meaningless_fields(array_fc->elem_fc, name, scope_name,
+                       log_cfg);
+               break;
+       }
+       default:
+               bt_common_abort();
+       }
+
+end:
+       return;
+}
+
+BT_HIDDEN
+void ctf_trace_class_warn_meaningless_header_fields(
+               struct ctf_trace_class *ctf_tc,
+               struct meta_log_config *log_cfg)
+{
+       uint64_t i;
+
+       if (!ctf_tc->is_translated) {
+               warn_meaningless_fields(
+                       ctf_tc->packet_header_fc, NULL, "packet header",
+                       log_cfg);
+       }
+
+       for (i = 0; i < ctf_tc->stream_classes->len; i++) {
+               ctf_stream_class *sc = (ctf_stream_class *) ctf_tc->stream_classes->pdata[i];
+
+               if (!sc->is_translated) {
+                       warn_meaningless_fields(sc->event_header_fc, NULL,
+                               "event header", log_cfg);
+               }
+       }
+}
diff --git a/src/plugins/ctf/common/metadata/ctf-meta.h b/src/plugins/ctf/common/metadata/ctf-meta.h
deleted file mode 100644 (file)
index ea6b926..0000000
+++ /dev/null
@@ -1,1789 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
- */
-
-#ifndef _CTF_META_H
-#define _CTF_META_H
-
-#include <babeltrace2/babeltrace.h>
-#include "common/common.h"
-#include "common/uuid.h"
-#include "common/assert.h"
-#include <glib.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <string.h>
-
-enum ctf_field_class_type {
-       CTF_FIELD_CLASS_TYPE_INT,
-       CTF_FIELD_CLASS_TYPE_ENUM,
-       CTF_FIELD_CLASS_TYPE_FLOAT,
-       CTF_FIELD_CLASS_TYPE_STRING,
-       CTF_FIELD_CLASS_TYPE_STRUCT,
-       CTF_FIELD_CLASS_TYPE_ARRAY,
-       CTF_FIELD_CLASS_TYPE_SEQUENCE,
-       CTF_FIELD_CLASS_TYPE_VARIANT,
-};
-
-enum ctf_field_class_meaning {
-       CTF_FIELD_CLASS_MEANING_NONE,
-       CTF_FIELD_CLASS_MEANING_PACKET_BEGINNING_TIME,
-       CTF_FIELD_CLASS_MEANING_PACKET_END_TIME,
-       CTF_FIELD_CLASS_MEANING_EVENT_CLASS_ID,
-       CTF_FIELD_CLASS_MEANING_STREAM_CLASS_ID,
-       CTF_FIELD_CLASS_MEANING_DATA_STREAM_ID,
-       CTF_FIELD_CLASS_MEANING_MAGIC,
-       CTF_FIELD_CLASS_MEANING_PACKET_COUNTER_SNAPSHOT,
-       CTF_FIELD_CLASS_MEANING_DISC_EV_REC_COUNTER_SNAPSHOT,
-       CTF_FIELD_CLASS_MEANING_EXP_PACKET_TOTAL_SIZE,
-       CTF_FIELD_CLASS_MEANING_EXP_PACKET_CONTENT_SIZE,
-       CTF_FIELD_CLASS_MEANING_UUID,
-};
-
-enum ctf_byte_order {
-       CTF_BYTE_ORDER_UNKNOWN,
-       CTF_BYTE_ORDER_DEFAULT,
-       CTF_BYTE_ORDER_LITTLE,
-       CTF_BYTE_ORDER_BIG,
-};
-
-enum ctf_encoding {
-       CTF_ENCODING_NONE,
-       CTF_ENCODING_UTF8,
-};
-
-enum ctf_scope {
-       CTF_SCOPE_PACKET_UNKNOWN = -1,
-       CTF_SCOPE_PACKET_HEADER = 0,
-       CTF_SCOPE_PACKET_CONTEXT,
-       CTF_SCOPE_EVENT_HEADER,
-       CTF_SCOPE_EVENT_COMMON_CONTEXT,
-       CTF_SCOPE_EVENT_SPECIFIC_CONTEXT,
-       CTF_SCOPE_EVENT_PAYLOAD,
-};
-
-struct ctf_clock_class {
-       GString *name;
-       GString *description;
-       uint64_t frequency;
-       uint64_t precision;
-       int64_t offset_seconds;
-       uint64_t offset_cycles;
-       bt_uuid_t uuid;
-       bool has_uuid;
-       bool is_absolute;
-
-       /* Weak, set during translation */
-       bt_clock_class *ir_cc;
-};
-
-struct ctf_field_class {
-       enum ctf_field_class_type type;
-       unsigned int alignment;
-       bool is_compound;
-       bool in_ir;
-
-       /* Weak, set during translation. NULL if `in_ir` is false below. */
-       bt_field_class *ir_fc;
-};
-
-struct ctf_field_class_bit_array {
-       struct ctf_field_class base;
-       enum ctf_byte_order byte_order;
-       unsigned int size;
-};
-
-struct ctf_field_class_int {
-       struct ctf_field_class_bit_array base;
-       enum ctf_field_class_meaning meaning;
-       bool is_signed;
-       bt_field_class_integer_preferred_display_base disp_base;
-       enum ctf_encoding encoding;
-       int64_t storing_index;
-
-       /* Weak */
-       struct ctf_clock_class *mapped_clock_class;
-};
-
-struct ctf_range {
-       union {
-               uint64_t u;
-               int64_t i;
-       } lower;
-
-       union {
-               uint64_t u;
-               int64_t i;
-       } upper;
-};
-
-struct ctf_field_class_enum_mapping {
-       GString *label;
-
-       /* Array of `struct ctf_range` */
-       GArray *ranges;
-};
-
-struct ctf_field_class_enum {
-       struct ctf_field_class_int base;
-
-       /* Array of `struct ctf_field_class_enum_mapping` */
-       GArray *mappings;
-};
-
-struct ctf_field_class_float {
-       struct ctf_field_class_bit_array base;
-};
-
-struct ctf_field_class_string {
-       struct ctf_field_class base;
-       enum ctf_encoding encoding;
-};
-
-struct ctf_named_field_class {
-       /* Original name which can include a leading `_` */
-       GString *orig_name;
-
-       /* Name as translated to trace IR (leading `_` removed) */
-       GString *name;
-
-       /* Owned by this */
-       struct ctf_field_class *fc;
-};
-
-struct ctf_field_class_struct {
-       struct ctf_field_class base;
-
-       /* Array of `struct ctf_named_field_class` */
-       GArray *members;
-};
-
-struct ctf_field_path {
-       enum ctf_scope root;
-
-       /* Array of `int64_t` */
-       GArray *path;
-};
-
-struct ctf_field_class_variant_range {
-       struct ctf_range range;
-       uint64_t option_index;
-};
-
-struct ctf_field_class_variant {
-       struct ctf_field_class base;
-       GString *tag_ref;
-       struct ctf_field_path tag_path;
-       uint64_t stored_tag_index;
-
-       /* Array of `struct ctf_named_field_class` */
-       GArray *options;
-
-       /* Array of `struct ctf_field_class_variant_range` */
-       GArray *ranges;
-
-       /* Weak */
-       struct ctf_field_class_enum *tag_fc;
-};
-
-struct ctf_field_class_array_base {
-       struct ctf_field_class base;
-       struct ctf_field_class *elem_fc;
-       bool is_text;
-};
-
-struct ctf_field_class_array {
-       struct ctf_field_class_array_base base;
-       enum ctf_field_class_meaning meaning;
-       uint64_t length;
-};
-
-struct ctf_field_class_sequence {
-       struct ctf_field_class_array_base base;
-       GString *length_ref;
-       struct ctf_field_path length_path;
-       uint64_t stored_length_index;
-
-       /* Weak */
-       struct ctf_field_class_int *length_fc;
-};
-
-struct ctf_event_class {
-       GString *name;
-       uint64_t id;
-       GString *emf_uri;
-       bt_event_class_log_level log_level;
-       bool is_translated;
-       bool is_log_level_set;
-
-       /* Owned by this */
-       struct ctf_field_class *spec_context_fc;
-
-       /* Owned by this */
-       struct ctf_field_class *payload_fc;
-
-       /* Weak, set during translation */
-       bt_event_class *ir_ec;
-};
-
-struct ctf_stream_class {
-       uint64_t id;
-       bool is_translated;
-       bool packets_have_ts_begin;
-       bool packets_have_ts_end;
-       bool has_discarded_events;
-       bool has_discarded_packets;
-       bool discarded_events_have_default_cs;
-       bool discarded_packets_have_default_cs;
-
-       /* Owned by this */
-       struct ctf_field_class *packet_context_fc;
-
-       /* Owned by this */
-       struct ctf_field_class *event_header_fc;
-
-       /* Owned by this */
-       struct ctf_field_class *event_common_context_fc;
-
-       /* Array of `struct ctf_event_class *`, owned by this */
-       GPtrArray *event_classes;
-
-       /*
-        * Hash table mapping event class IDs to `struct ctf_event_class *`,
-        * weak.
-        */
-       GHashTable *event_classes_by_id;
-
-       /* Weak */
-       struct ctf_clock_class *default_clock_class;
-
-       /* Weak, set during translation */
-       bt_stream_class *ir_sc;
-};
-
-enum ctf_trace_class_env_entry_type {
-       CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT,
-       CTF_TRACE_CLASS_ENV_ENTRY_TYPE_STR,
-};
-
-struct ctf_trace_class_env_entry {
-       enum ctf_trace_class_env_entry_type type;
-       GString *name;
-
-       struct {
-               int64_t i;
-               GString *str;
-       } value;
-};
-
-struct ctf_trace_class {
-       unsigned int major;
-       unsigned int minor;
-       bt_uuid_t uuid;
-       bool is_uuid_set;
-       enum ctf_byte_order default_byte_order;
-
-       /* Owned by this */
-       struct ctf_field_class *packet_header_fc;
-
-       uint64_t stored_value_count;
-
-       /* Array of `struct ctf_clock_class *` (owned by this) */
-       GPtrArray *clock_classes;
-
-       /* Array of `struct ctf_stream_class *` */
-       GPtrArray *stream_classes;
-
-       /* Array of `struct ctf_trace_class_env_entry` */
-       GArray *env_entries;
-
-       bool is_translated;
-
-       /* Weak, set during translation */
-       bt_trace_class *ir_tc;
-
-       struct {
-               bool lttng_crash;
-               bool lttng_event_after_packet;
-               bool barectf_event_before_packet;
-       } quirks;
-};
-
-static inline
-void ctf_field_class_destroy(struct ctf_field_class *fc);
-
-static inline
-void _ctf_field_class_init(struct ctf_field_class *fc,
-               enum ctf_field_class_type type, unsigned int alignment)
-{
-       BT_ASSERT(fc);
-       fc->type = type;
-       fc->alignment = alignment;
-       fc->in_ir = false;
-}
-
-static inline
-void _ctf_field_class_bit_array_init(struct ctf_field_class_bit_array *fc,
-               enum ctf_field_class_type type)
-{
-       _ctf_field_class_init((void *) fc, type, 1);
-}
-
-static inline
-void _ctf_field_class_int_init(struct ctf_field_class_int *fc,
-               enum ctf_field_class_type type)
-{
-       _ctf_field_class_bit_array_init((void *) fc, type);
-       fc->meaning = CTF_FIELD_CLASS_MEANING_NONE;
-       fc->storing_index = -1;
-}
-
-static inline
-void ctf_field_path_init(struct ctf_field_path *field_path)
-{
-       BT_ASSERT(field_path);
-       field_path->path = g_array_new(FALSE, TRUE, sizeof(int64_t));
-       BT_ASSERT(field_path->path);
-}
-
-static inline
-void ctf_field_path_fini(struct ctf_field_path *field_path)
-{
-       BT_ASSERT(field_path);
-
-       if (field_path->path) {
-               g_array_free(field_path->path, TRUE);
-       }
-}
-
-static inline
-void _ctf_named_field_class_init(struct ctf_named_field_class *named_fc)
-{
-       BT_ASSERT(named_fc);
-       named_fc->name = g_string_new(NULL);
-       BT_ASSERT(named_fc->name);
-       named_fc->orig_name = g_string_new(NULL);
-       BT_ASSERT(named_fc->orig_name);
-}
-
-static inline
-void _ctf_named_field_class_fini(struct ctf_named_field_class *named_fc)
-{
-       BT_ASSERT(named_fc);
-
-       if (named_fc->name) {
-               g_string_free(named_fc->name, TRUE);
-       }
-
-       if (named_fc->orig_name) {
-               g_string_free(named_fc->orig_name, TRUE);
-       }
-
-       ctf_field_class_destroy(named_fc->fc);
-}
-
-static inline
-void _ctf_field_class_enum_mapping_init(
-               struct ctf_field_class_enum_mapping *mapping)
-{
-       BT_ASSERT(mapping);
-       mapping->label = g_string_new(NULL);
-       BT_ASSERT(mapping->label);
-       mapping->ranges = g_array_new(FALSE, TRUE, sizeof(struct ctf_range));
-       BT_ASSERT(mapping->ranges);
-}
-
-static inline
-void _ctf_field_class_enum_mapping_fini(
-               struct ctf_field_class_enum_mapping *mapping)
-{
-       BT_ASSERT(mapping);
-
-       if (mapping->label) {
-               g_string_free(mapping->label, TRUE);
-       }
-
-       if (mapping->ranges) {
-               g_array_free(mapping->ranges, TRUE);
-       }
-}
-
-static inline
-struct ctf_field_class_int *ctf_field_class_int_create(void)
-{
-       struct ctf_field_class_int *fc = g_new0(struct ctf_field_class_int, 1);
-
-       BT_ASSERT(fc);
-       _ctf_field_class_int_init(fc, CTF_FIELD_CLASS_TYPE_INT);
-       return fc;
-}
-
-static inline
-struct ctf_field_class_float *ctf_field_class_float_create(void)
-{
-       struct ctf_field_class_float *fc =
-               g_new0(struct ctf_field_class_float, 1);
-
-       BT_ASSERT(fc);
-       _ctf_field_class_bit_array_init((void *) fc, CTF_FIELD_CLASS_TYPE_FLOAT);
-       return fc;
-}
-
-static inline
-struct ctf_field_class_string *ctf_field_class_string_create(void)
-{
-       struct ctf_field_class_string *fc =
-               g_new0(struct ctf_field_class_string, 1);
-
-       BT_ASSERT(fc);
-       _ctf_field_class_init((void *) fc, CTF_FIELD_CLASS_TYPE_STRING, 8);
-       return fc;
-}
-
-static inline
-struct ctf_field_class_enum *ctf_field_class_enum_create(void)
-{
-       struct ctf_field_class_enum *fc = g_new0(struct ctf_field_class_enum, 1);
-
-       BT_ASSERT(fc);
-       _ctf_field_class_int_init((void *) fc, CTF_FIELD_CLASS_TYPE_ENUM);
-       fc->mappings = g_array_new(FALSE, TRUE,
-               sizeof(struct ctf_field_class_enum_mapping));
-       BT_ASSERT(fc->mappings);
-       return fc;
-}
-
-static inline
-struct ctf_field_class_struct *ctf_field_class_struct_create(void)
-{
-       struct ctf_field_class_struct *fc =
-               g_new0(struct ctf_field_class_struct, 1);
-
-       BT_ASSERT(fc);
-       _ctf_field_class_init((void *) fc, CTF_FIELD_CLASS_TYPE_STRUCT, 1);
-       fc->members = g_array_new(FALSE, TRUE,
-               sizeof(struct ctf_named_field_class));
-       BT_ASSERT(fc->members);
-       fc->base.is_compound = true;
-       return fc;
-}
-
-static inline
-struct ctf_field_class_variant *ctf_field_class_variant_create(void)
-{
-       struct ctf_field_class_variant *fc =
-               g_new0(struct ctf_field_class_variant, 1);
-
-       BT_ASSERT(fc);
-       _ctf_field_class_init((void *) fc, CTF_FIELD_CLASS_TYPE_VARIANT, 1);
-       fc->options = g_array_new(FALSE, TRUE,
-               sizeof(struct ctf_named_field_class));
-       BT_ASSERT(fc->options);
-       fc->ranges = g_array_new(FALSE, TRUE,
-               sizeof(struct ctf_field_class_variant_range));
-       BT_ASSERT(fc->ranges);
-       fc->tag_ref = g_string_new(NULL);
-       BT_ASSERT(fc->tag_ref);
-       ctf_field_path_init(&fc->tag_path);
-       fc->base.is_compound = true;
-       return fc;
-}
-
-static inline
-struct ctf_field_class_array *ctf_field_class_array_create(void)
-{
-       struct ctf_field_class_array *fc =
-               g_new0(struct ctf_field_class_array, 1);
-
-       BT_ASSERT(fc);
-       _ctf_field_class_init((void *) fc, CTF_FIELD_CLASS_TYPE_ARRAY, 1);
-       fc->base.base.is_compound = true;
-       return fc;
-}
-
-static inline
-struct ctf_field_class_sequence *ctf_field_class_sequence_create(void)
-{
-       struct ctf_field_class_sequence *fc =
-               g_new0(struct ctf_field_class_sequence, 1);
-
-       BT_ASSERT(fc);
-       _ctf_field_class_init((void *) fc, CTF_FIELD_CLASS_TYPE_SEQUENCE, 1);
-       fc->length_ref = g_string_new(NULL);
-       BT_ASSERT(fc->length_ref);
-       ctf_field_path_init(&fc->length_path);
-       fc->base.base.is_compound = true;
-       return fc;
-}
-
-static inline
-void _ctf_field_class_int_destroy(struct ctf_field_class_int *fc)
-{
-       BT_ASSERT(fc);
-       g_free(fc);
-}
-
-static inline
-void _ctf_field_class_enum_destroy(struct ctf_field_class_enum *fc)
-{
-       BT_ASSERT(fc);
-
-       if (fc->mappings) {
-               uint64_t i;
-
-               for (i = 0; i < fc->mappings->len; i++) {
-                       struct ctf_field_class_enum_mapping *mapping =
-                               &g_array_index(fc->mappings,
-                                       struct ctf_field_class_enum_mapping, i);
-
-                       _ctf_field_class_enum_mapping_fini(mapping);
-               }
-
-               g_array_free(fc->mappings, TRUE);
-       }
-
-       g_free(fc);
-}
-
-static inline
-void _ctf_field_class_float_destroy(struct ctf_field_class_float *fc)
-{
-       BT_ASSERT(fc);
-       g_free(fc);
-}
-
-static inline
-void _ctf_field_class_string_destroy(struct ctf_field_class_string *fc)
-{
-       BT_ASSERT(fc);
-       g_free(fc);
-}
-
-static inline
-void _ctf_field_class_struct_destroy(struct ctf_field_class_struct *fc)
-{
-       BT_ASSERT(fc);
-
-       if (fc->members) {
-               uint64_t i;
-
-               for (i = 0; i < fc->members->len; i++) {
-                       struct ctf_named_field_class *named_fc =
-                               &g_array_index(fc->members,
-                                       struct ctf_named_field_class, i);
-
-                       _ctf_named_field_class_fini(named_fc);
-               }
-
-               g_array_free(fc->members, TRUE);
-       }
-
-       g_free(fc);
-}
-
-static inline
-void _ctf_field_class_array_base_fini(struct ctf_field_class_array_base *fc)
-{
-       BT_ASSERT(fc);
-       ctf_field_class_destroy(fc->elem_fc);
-}
-
-static inline
-void _ctf_field_class_array_destroy(struct ctf_field_class_array *fc)
-{
-       BT_ASSERT(fc);
-       _ctf_field_class_array_base_fini((void *) fc);
-       g_free(fc);
-}
-
-static inline
-void _ctf_field_class_sequence_destroy(struct ctf_field_class_sequence *fc)
-{
-       BT_ASSERT(fc);
-       _ctf_field_class_array_base_fini((void *) fc);
-
-       if (fc->length_ref) {
-               g_string_free(fc->length_ref, TRUE);
-       }
-
-       ctf_field_path_fini(&fc->length_path);
-       g_free(fc);
-}
-
-static inline
-void _ctf_field_class_variant_destroy(struct ctf_field_class_variant *fc)
-{
-       BT_ASSERT(fc);
-
-       if (fc->options) {
-               uint64_t i;
-
-               for (i = 0; i < fc->options->len; i++) {
-                       struct ctf_named_field_class *named_fc =
-                               &g_array_index(fc->options,
-                                       struct ctf_named_field_class, i);
-
-                       _ctf_named_field_class_fini(named_fc);
-               }
-
-               g_array_free(fc->options, TRUE);
-       }
-
-       if (fc->ranges) {
-               g_array_free(fc->ranges, TRUE);
-       }
-
-       if (fc->tag_ref) {
-               g_string_free(fc->tag_ref, TRUE);
-       }
-
-       ctf_field_path_fini(&fc->tag_path);
-       g_free(fc);
-}
-
-static inline
-void ctf_field_class_destroy(struct ctf_field_class *fc)
-{
-       if (!fc) {
-               return;
-       }
-
-       switch (fc->type) {
-       case CTF_FIELD_CLASS_TYPE_INT:
-               _ctf_field_class_int_destroy((void *) fc);
-               break;
-       case CTF_FIELD_CLASS_TYPE_ENUM:
-               _ctf_field_class_enum_destroy((void *) fc);
-               break;
-       case CTF_FIELD_CLASS_TYPE_FLOAT:
-               _ctf_field_class_float_destroy((void *) fc);
-               break;
-       case CTF_FIELD_CLASS_TYPE_STRING:
-               _ctf_field_class_string_destroy((void *) fc);
-               break;
-       case CTF_FIELD_CLASS_TYPE_STRUCT:
-               _ctf_field_class_struct_destroy((void *) fc);
-               break;
-       case CTF_FIELD_CLASS_TYPE_ARRAY:
-               _ctf_field_class_array_destroy((void *) fc);
-               break;
-       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-               _ctf_field_class_sequence_destroy((void *) fc);
-               break;
-       case CTF_FIELD_CLASS_TYPE_VARIANT:
-               _ctf_field_class_variant_destroy((void *) fc);
-               break;
-       default:
-               bt_common_abort();
-       }
-}
-
-static inline
-struct ctf_range *ctf_field_class_enum_mapping_borrow_range_by_index(
-               struct ctf_field_class_enum_mapping *mapping, uint64_t index)
-{
-       BT_ASSERT_DBG(mapping);
-       BT_ASSERT_DBG(index < mapping->ranges->len);
-       return &g_array_index(mapping->ranges, struct ctf_range, index);
-}
-
-static inline
-struct ctf_field_class_enum_mapping *ctf_field_class_enum_borrow_mapping_by_index(
-               struct ctf_field_class_enum *fc, uint64_t index)
-{
-       BT_ASSERT_DBG(fc);
-       BT_ASSERT_DBG(index < fc->mappings->len);
-       return &g_array_index(fc->mappings, struct ctf_field_class_enum_mapping,
-               index);
-}
-
-static inline
-struct ctf_field_class_enum_mapping *ctf_field_class_enum_borrow_mapping_by_label(
-               struct ctf_field_class_enum *fc, const char *label)
-{
-       struct ctf_field_class_enum_mapping *ret_mapping = NULL;
-       uint64_t i;
-
-       BT_ASSERT_DBG(fc);
-       BT_ASSERT_DBG(label);
-
-       for (i = 0; i < fc->mappings->len; i++) {
-               struct ctf_field_class_enum_mapping *mapping =
-                       ctf_field_class_enum_borrow_mapping_by_index(fc, i);
-
-               if (strcmp(mapping->label->str, label) == 0) {
-                       ret_mapping = mapping;
-                       goto end;
-               }
-       }
-
-end:
-       return ret_mapping;
-}
-
-static inline
-void ctf_field_class_enum_map_range(struct ctf_field_class_enum *fc,
-               const char *label, uint64_t u_lower, uint64_t u_upper)
-{
-       struct ctf_field_class_enum_mapping *mapping = NULL;
-       struct ctf_range range = {
-               .lower.u = u_lower,
-               .upper.u = u_upper,
-       };
-       uint64_t i;
-
-       BT_ASSERT(fc);
-       BT_ASSERT(label);
-
-       for (i = 0; i < fc->mappings->len; i++) {
-               mapping = ctf_field_class_enum_borrow_mapping_by_index(
-                       fc, i);
-
-               if (strcmp(mapping->label->str, label) == 0) {
-                       break;
-               }
-       }
-
-       if (i == fc->mappings->len) {
-               mapping = NULL;
-       }
-
-       if (!mapping) {
-               g_array_set_size(fc->mappings, fc->mappings->len + 1);
-               mapping = ctf_field_class_enum_borrow_mapping_by_index(
-                       fc, fc->mappings->len - 1);
-               _ctf_field_class_enum_mapping_init(mapping);
-               g_string_assign(mapping->label, label);
-       }
-
-       g_array_append_val(mapping->ranges, range);
-}
-
-static inline
-struct ctf_named_field_class *ctf_field_class_struct_borrow_member_by_index(
-               struct ctf_field_class_struct *fc, uint64_t index)
-{
-       BT_ASSERT_DBG(fc);
-       BT_ASSERT_DBG(index < fc->members->len);
-       return &g_array_index(fc->members, struct ctf_named_field_class,
-               index);
-}
-
-static inline
-struct ctf_named_field_class *ctf_field_class_struct_borrow_member_by_name(
-               struct ctf_field_class_struct *fc, const char *name)
-{
-       uint64_t i;
-       struct ctf_named_field_class *ret_named_fc = NULL;
-
-       BT_ASSERT_DBG(fc);
-       BT_ASSERT_DBG(name);
-
-       for (i = 0; i < fc->members->len; i++) {
-               struct ctf_named_field_class *named_fc =
-                       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 ctf_field_class *ctf_field_class_struct_borrow_member_field_class_by_name(
-               struct ctf_field_class_struct *struct_fc, const char *name)
-{
-       struct ctf_named_field_class *named_fc = NULL;
-       struct ctf_field_class *fc = NULL;
-
-       if (!struct_fc) {
-               goto end;
-       }
-
-       named_fc = 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 ctf_field_class_int *
-ctf_field_class_struct_borrow_member_int_field_class_by_name(
-               struct ctf_field_class_struct *struct_fc, const char *name)
-{
-       struct ctf_field_class_int *int_fc = NULL;
-
-       int_fc = (void *)
-               ctf_field_class_struct_borrow_member_field_class_by_name(
-                       struct_fc, name);
-       if (!int_fc) {
-               goto end;
-       }
-
-       if (int_fc->base.base.type != CTF_FIELD_CLASS_TYPE_INT &&
-                       int_fc->base.base.type != CTF_FIELD_CLASS_TYPE_ENUM) {
-               int_fc = NULL;
-               goto end;
-       }
-
-end:
-       return int_fc;
-}
-
-static inline
-void _ctf_named_field_class_unescape_orig_name(
-               struct ctf_named_field_class *named_fc)
-{
-       const char *name = named_fc->orig_name->str;
-
-       if (name[0] == '_') {
-               name++;
-       }
-
-       g_string_assign(named_fc->name, name);
-}
-
-static inline
-void ctf_field_class_struct_append_member(struct ctf_field_class_struct *fc,
-               const char *orig_name, struct ctf_field_class *member_fc)
-{
-       struct ctf_named_field_class *named_fc;
-
-       BT_ASSERT(fc);
-       BT_ASSERT(orig_name);
-       g_array_set_size(fc->members, fc->members->len + 1);
-
-       named_fc = &g_array_index(fc->members, struct ctf_named_field_class,
-               fc->members->len - 1);
-       _ctf_named_field_class_init(named_fc);
-       g_string_assign(named_fc->orig_name, orig_name);
-       _ctf_named_field_class_unescape_orig_name(named_fc);
-       named_fc->fc = member_fc;
-
-       if (member_fc->alignment > fc->base.alignment) {
-               fc->base.alignment = member_fc->alignment;
-       }
-}
-
-static inline
-struct ctf_named_field_class *ctf_field_class_variant_borrow_option_by_index(
-               struct ctf_field_class_variant *fc, uint64_t index)
-{
-       BT_ASSERT_DBG(fc);
-       BT_ASSERT_DBG(index < fc->options->len);
-       return &g_array_index(fc->options, struct ctf_named_field_class,
-               index);
-}
-
-static inline
-struct ctf_named_field_class *ctf_field_class_variant_borrow_option_by_name(
-               struct ctf_field_class_variant *fc, const char *name)
-{
-       uint64_t i;
-       struct ctf_named_field_class *ret_named_fc = NULL;
-
-       BT_ASSERT_DBG(fc);
-       BT_ASSERT_DBG(name);
-
-       for (i = 0; i < fc->options->len; i++) {
-               struct ctf_named_field_class *named_fc =
-                       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
-struct ctf_field_class_variant_range *
-ctf_field_class_variant_borrow_range_by_index(
-               struct ctf_field_class_variant *fc, uint64_t index)
-{
-       BT_ASSERT_DBG(fc);
-       BT_ASSERT_DBG(index < fc->ranges->len);
-       return &g_array_index(fc->ranges, struct ctf_field_class_variant_range,
-               index);
-}
-
-static inline
-void ctf_field_class_variant_append_option(struct ctf_field_class_variant *fc,
-               const char *orig_name, struct ctf_field_class *option_fc)
-{
-       struct ctf_named_field_class *named_fc;
-
-       BT_ASSERT(fc);
-       BT_ASSERT(orig_name);
-       g_array_set_size(fc->options, fc->options->len + 1);
-
-       named_fc = &g_array_index(fc->options, struct ctf_named_field_class,
-               fc->options->len - 1);
-       _ctf_named_field_class_init(named_fc);
-       g_string_assign(named_fc->orig_name, orig_name);
-       _ctf_named_field_class_unescape_orig_name(named_fc);
-       named_fc->fc = option_fc;
-}
-
-static inline
-void ctf_field_class_variant_set_tag_field_class(
-               struct ctf_field_class_variant *fc,
-               struct ctf_field_class_enum *tag_fc)
-{
-       uint64_t option_i;
-
-       BT_ASSERT(fc);
-       BT_ASSERT(tag_fc);
-       fc->tag_fc = tag_fc;
-
-       for (option_i = 0; option_i < fc->options->len; option_i++) {
-               uint64_t range_i;
-               struct ctf_named_field_class *named_fc =
-                       ctf_field_class_variant_borrow_option_by_index(
-                               fc, option_i);
-               struct ctf_field_class_enum_mapping *mapping;
-
-               mapping = ctf_field_class_enum_borrow_mapping_by_label(
-                       tag_fc, named_fc->orig_name->str);
-               if (!mapping) {
-                       continue;
-               }
-
-               for (range_i = 0; range_i < mapping->ranges->len;
-                               range_i++) {
-                       struct ctf_range *range =
-                               ctf_field_class_enum_mapping_borrow_range_by_index(
-                                       mapping, range_i);
-                       struct ctf_field_class_variant_range var_range;
-
-                       var_range.range = *range;
-                       var_range.option_index = option_i;
-                       g_array_append_val(fc->ranges, var_range);
-               }
-       }
-}
-
-static inline
-struct ctf_field_class *ctf_field_class_compound_borrow_field_class_by_index(
-               struct ctf_field_class *comp_fc, uint64_t index)
-{
-       struct ctf_field_class *fc = NULL;
-
-       switch (comp_fc->type) {
-       case CTF_FIELD_CLASS_TYPE_STRUCT:
-       {
-               struct ctf_named_field_class *named_fc =
-                       ctf_field_class_struct_borrow_member_by_index(
-                               (void *) comp_fc, index);
-
-               BT_ASSERT_DBG(named_fc);
-               fc = named_fc->fc;
-               break;
-       }
-       case CTF_FIELD_CLASS_TYPE_VARIANT:
-       {
-               struct ctf_named_field_class *named_fc =
-                       ctf_field_class_variant_borrow_option_by_index(
-                               (void *) comp_fc, index);
-
-               BT_ASSERT_DBG(named_fc);
-               fc = named_fc->fc;
-               break;
-       }
-       case CTF_FIELD_CLASS_TYPE_ARRAY:
-       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-       {
-               struct ctf_field_class_array_base *array_fc = (void *) comp_fc;
-
-               fc = array_fc->elem_fc;
-               break;
-       }
-       default:
-               break;
-       }
-
-       return fc;
-}
-
-static inline
-uint64_t ctf_field_class_compound_get_field_class_count(struct ctf_field_class *fc)
-{
-       uint64_t field_count;
-
-       switch (fc->type) {
-       case CTF_FIELD_CLASS_TYPE_STRUCT:
-       {
-               struct ctf_field_class_struct *struct_fc = (void *) fc;
-
-               field_count = struct_fc->members->len;
-               break;
-       }
-       case CTF_FIELD_CLASS_TYPE_VARIANT:
-       {
-               struct ctf_field_class_variant *var_fc = (void *) fc;
-
-               field_count = var_fc->options->len;
-               break;
-       }
-       case CTF_FIELD_CLASS_TYPE_ARRAY:
-       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-               /*
-                * Array and sequence types always contain a single
-                * member (the element type).
-                */
-               field_count = 1;
-               break;
-       default:
-               bt_common_abort();
-       }
-
-       return field_count;
-}
-
-static inline
-int64_t ctf_field_class_compound_get_field_class_index_from_orig_name(
-               struct ctf_field_class *fc, const char *orig_name)
-{
-       int64_t ret_index = -1;
-       uint64_t i;
-
-       switch (fc->type) {
-       case CTF_FIELD_CLASS_TYPE_STRUCT:
-       {
-               struct ctf_field_class_struct *struct_fc = (void *) fc;
-
-               for (i = 0; i < struct_fc->members->len; i++) {
-                       struct ctf_named_field_class *named_fc =
-                               ctf_field_class_struct_borrow_member_by_index(
-                                       struct_fc, i);
-
-                       if (strcmp(orig_name, named_fc->orig_name->str) == 0) {
-                               ret_index = (int64_t) i;
-                               goto end;
-                       }
-               }
-
-               break;
-       }
-       case CTF_FIELD_CLASS_TYPE_VARIANT:
-       {
-               struct ctf_field_class_variant *var_fc = (void *) fc;
-
-               for (i = 0; i < var_fc->options->len; i++) {
-                       struct ctf_named_field_class *named_fc =
-                               ctf_field_class_variant_borrow_option_by_index(
-                                       var_fc, i);
-
-                       if (strcmp(orig_name, named_fc->orig_name->str) == 0) {
-                               ret_index = (int64_t) i;
-                               goto end;
-                       }
-               }
-
-               break;
-       }
-       default:
-               break;
-       }
-
-end:
-       return ret_index;
-}
-
-static inline
-void ctf_field_path_append_index(struct ctf_field_path *fp, int64_t index)
-{
-       BT_ASSERT(fp);
-       g_array_append_val(fp->path, index);
-}
-
-static inline
-int64_t ctf_field_path_borrow_index_by_index(struct ctf_field_path *fp,
-               uint64_t index)
-{
-       BT_ASSERT_DBG(fp);
-       BT_ASSERT_DBG(index < fp->path->len);
-       return g_array_index(fp->path, int64_t, index);
-}
-
-static inline
-void ctf_field_path_clear(struct ctf_field_path *fp)
-{
-       BT_ASSERT(fp);
-       g_array_set_size(fp->path, 0);
-}
-
-static inline
-const char *ctf_scope_string(enum ctf_scope scope)
-{
-       switch (scope) {
-       case CTF_SCOPE_PACKET_HEADER:
-               return "PACKET_HEADER";
-       case CTF_SCOPE_PACKET_CONTEXT:
-               return "PACKET_CONTEXT";
-       case CTF_SCOPE_EVENT_HEADER:
-               return "EVENT_HEADER";
-       case CTF_SCOPE_EVENT_COMMON_CONTEXT:
-               return "EVENT_COMMON_CONTEXT";
-       case CTF_SCOPE_EVENT_SPECIFIC_CONTEXT:
-               return "EVENT_SPECIFIC_CONTEXT";
-       case CTF_SCOPE_EVENT_PAYLOAD:
-               return "EVENT_PAYLOAD";
-       default:
-               bt_common_abort();
-       }
-}
-
-static inline
-GString *ctf_field_path_string(struct ctf_field_path *path)
-{
-       GString *str = g_string_new(NULL);
-       uint64_t i;
-
-       BT_ASSERT(path);
-
-       if (!str) {
-               goto end;
-       }
-
-       g_string_append_printf(str, "[%s", ctf_scope_string(path->root));
-
-       for (i = 0; i < path->path->len; i++) {
-               g_string_append_printf(str, ", %" PRId64,
-                       ctf_field_path_borrow_index_by_index(path, i));
-       }
-
-       g_string_append(str, "]");
-
-end:
-       return str;
-}
-
-static inline
-struct ctf_field_class *ctf_field_path_borrow_field_class(
-               struct ctf_field_path *field_path,
-               struct ctf_trace_class *tc,
-               struct ctf_stream_class *sc,
-               struct ctf_event_class *ec)
-{
-       uint64_t i;
-       struct ctf_field_class *fc;
-
-       switch (field_path->root) {
-       case CTF_SCOPE_PACKET_HEADER:
-               fc = tc->packet_header_fc;
-               break;
-       case CTF_SCOPE_PACKET_CONTEXT:
-               fc = sc->packet_context_fc;
-               break;
-       case CTF_SCOPE_EVENT_HEADER:
-               fc = sc->event_header_fc;
-               break;
-       case CTF_SCOPE_EVENT_COMMON_CONTEXT:
-               fc = sc->event_common_context_fc;
-               break;
-       case CTF_SCOPE_EVENT_SPECIFIC_CONTEXT:
-               fc = ec->spec_context_fc;
-               break;
-       case CTF_SCOPE_EVENT_PAYLOAD:
-               fc = ec->payload_fc;
-               break;
-       default:
-               bt_common_abort();
-       }
-
-       BT_ASSERT_DBG(fc);
-
-       for (i = 0; i < field_path->path->len; i++) {
-               int64_t child_index =
-                       ctf_field_path_borrow_index_by_index(field_path, i);
-               struct ctf_field_class *child_fc =
-                       ctf_field_class_compound_borrow_field_class_by_index(
-                               fc, child_index);
-               BT_ASSERT_DBG(child_fc);
-               fc = child_fc;
-       }
-
-       BT_ASSERT_DBG(fc);
-       return fc;
-}
-
-static inline
-struct ctf_field_class *ctf_field_class_copy(struct ctf_field_class *fc);
-
-static inline
-void ctf_field_class_bit_array_copy_content(
-               struct ctf_field_class_bit_array *dst_fc,
-               struct ctf_field_class_bit_array *src_fc)
-{
-       BT_ASSERT(dst_fc);
-       BT_ASSERT(src_fc);
-       dst_fc->byte_order = src_fc->byte_order;
-       dst_fc->size = src_fc->size;
-}
-
-static inline
-void ctf_field_class_int_copy_content(
-               struct ctf_field_class_int *dst_fc,
-               struct ctf_field_class_int *src_fc)
-{
-       ctf_field_class_bit_array_copy_content((void *) dst_fc, (void *) src_fc);
-       dst_fc->meaning = src_fc->meaning;
-       dst_fc->is_signed = src_fc->is_signed;
-       dst_fc->disp_base = src_fc->disp_base;
-       dst_fc->encoding = src_fc->encoding;
-       dst_fc->mapped_clock_class = src_fc->mapped_clock_class;
-       dst_fc->storing_index = src_fc->storing_index;
-}
-
-static inline
-struct ctf_field_class_int *_ctf_field_class_int_copy(
-               struct ctf_field_class_int *fc)
-{
-       struct ctf_field_class_int *copy_fc = ctf_field_class_int_create();
-
-       BT_ASSERT(copy_fc);
-       ctf_field_class_int_copy_content(copy_fc, fc);
-       return copy_fc;
-}
-
-static inline
-struct ctf_field_class_enum *_ctf_field_class_enum_copy(
-               struct ctf_field_class_enum *fc)
-{
-       struct ctf_field_class_enum *copy_fc = ctf_field_class_enum_create();
-       uint64_t i;
-
-       BT_ASSERT(copy_fc);
-       ctf_field_class_int_copy_content((void *) copy_fc, (void *) fc);
-
-       for (i = 0; i < fc->mappings->len; i++) {
-               uint64_t range_i;
-
-               struct ctf_field_class_enum_mapping *mapping =
-                       &g_array_index(fc->mappings,
-                               struct ctf_field_class_enum_mapping, i);
-
-               for (range_i = 0; range_i < mapping->ranges->len; range_i++) {
-                       struct ctf_range *range =
-                               &g_array_index(mapping->ranges,
-                                       struct ctf_range, range_i);
-
-                       ctf_field_class_enum_map_range(copy_fc,
-                               mapping->label->str, range->lower.u,
-                               range->upper.u);
-               }
-       }
-
-       return copy_fc;
-}
-
-static inline
-struct ctf_field_class_float *_ctf_field_class_float_copy(
-               struct ctf_field_class_float *fc)
-{
-       struct ctf_field_class_float *copy_fc = ctf_field_class_float_create();
-
-       BT_ASSERT(copy_fc);
-       ctf_field_class_bit_array_copy_content((void *) copy_fc, (void *) fc);
-       return copy_fc;
-}
-
-static inline
-struct ctf_field_class_string *_ctf_field_class_string_copy(
-               struct ctf_field_class_string *fc)
-{
-       struct ctf_field_class_string *copy_fc = ctf_field_class_string_create();
-
-       BT_ASSERT(copy_fc);
-       return copy_fc;
-}
-
-static inline
-struct ctf_field_class_struct *_ctf_field_class_struct_copy(
-               struct ctf_field_class_struct *fc)
-{
-       struct ctf_field_class_struct *copy_fc = ctf_field_class_struct_create();
-       uint64_t i;
-
-       BT_ASSERT(copy_fc);
-
-       for (i = 0; i < fc->members->len; i++) {
-               struct ctf_named_field_class *named_fc =
-                       &g_array_index(fc->members,
-                               struct ctf_named_field_class, i);
-
-               ctf_field_class_struct_append_member(copy_fc,
-                       named_fc->name->str,
-                       ctf_field_class_copy(named_fc->fc));
-       }
-
-       return copy_fc;
-}
-
-static inline
-void ctf_field_path_copy_content(struct ctf_field_path *dst_fp,
-               struct ctf_field_path *src_fp)
-{
-       uint64_t i;
-
-       BT_ASSERT(dst_fp);
-       BT_ASSERT(src_fp);
-       dst_fp->root = src_fp->root;
-       ctf_field_path_clear(dst_fp);
-
-       for (i = 0; i < src_fp->path->len; i++) {
-               int64_t index = ctf_field_path_borrow_index_by_index(
-                       src_fp, i);
-
-               ctf_field_path_append_index(dst_fp, index);
-       }
-}
-
-static inline
-struct ctf_field_class_variant *_ctf_field_class_variant_copy(
-               struct ctf_field_class_variant *fc)
-{
-       struct ctf_field_class_variant *copy_fc =
-               ctf_field_class_variant_create();
-       uint64_t i;
-
-       BT_ASSERT(copy_fc);
-
-       for (i = 0; i < fc->options->len; i++) {
-               struct ctf_named_field_class *named_fc =
-                       &g_array_index(fc->options,
-                               struct ctf_named_field_class, i);
-
-               ctf_field_class_variant_append_option(copy_fc,
-                       named_fc->name->str,
-                       ctf_field_class_copy(named_fc->fc));
-       }
-
-       for (i = 0; i < fc->ranges->len; i++) {
-               struct ctf_field_class_variant_range *range =
-                       &g_array_index(fc->ranges,
-                               struct ctf_field_class_variant_range, i);
-
-               g_array_append_val(copy_fc->ranges, *range);
-       }
-
-       ctf_field_path_copy_content(&copy_fc->tag_path, &fc->tag_path);
-       g_string_assign(copy_fc->tag_ref, fc->tag_ref->str);
-       copy_fc->stored_tag_index = fc->stored_tag_index;
-       return copy_fc;
-}
-
-static inline
-void ctf_field_class_array_base_copy_content(
-               struct ctf_field_class_array_base *dst_fc,
-               struct ctf_field_class_array_base *src_fc)
-{
-       BT_ASSERT(dst_fc);
-       BT_ASSERT(src_fc);
-       dst_fc->elem_fc = ctf_field_class_copy(src_fc->elem_fc);
-       dst_fc->is_text = src_fc->is_text;
-}
-
-static inline
-struct ctf_field_class_array *_ctf_field_class_array_copy(
-               struct ctf_field_class_array *fc)
-{
-       struct ctf_field_class_array *copy_fc = ctf_field_class_array_create();
-
-       BT_ASSERT(copy_fc);
-       ctf_field_class_array_base_copy_content((void *) copy_fc, (void *) fc);
-       copy_fc->length = fc->length;
-       return copy_fc;
-}
-
-static inline
-struct ctf_field_class_sequence *_ctf_field_class_sequence_copy(
-               struct ctf_field_class_sequence *fc)
-{
-       struct ctf_field_class_sequence *copy_fc =
-               ctf_field_class_sequence_create();
-
-       BT_ASSERT(copy_fc);
-       ctf_field_class_array_base_copy_content((void *) copy_fc, (void *) fc);
-       ctf_field_path_copy_content(&copy_fc->length_path, &fc->length_path);
-       g_string_assign(copy_fc->length_ref, fc->length_ref->str);
-       copy_fc->stored_length_index = fc->stored_length_index;
-       return copy_fc;
-}
-
-static inline
-struct ctf_field_class *ctf_field_class_copy(struct ctf_field_class *fc)
-{
-       struct ctf_field_class *copy_fc = NULL;
-
-       if (!fc) {
-               goto end;
-       }
-
-       /*
-        * Translation should not have happened yet.
-        */
-       BT_ASSERT(!fc->ir_fc);
-
-       switch (fc->type) {
-       case CTF_FIELD_CLASS_TYPE_INT:
-               copy_fc = (void *) _ctf_field_class_int_copy((void *) fc);
-               break;
-       case CTF_FIELD_CLASS_TYPE_ENUM:
-               copy_fc = (void *) _ctf_field_class_enum_copy((void *) fc);
-               break;
-       case CTF_FIELD_CLASS_TYPE_FLOAT:
-               copy_fc = (void *) _ctf_field_class_float_copy((void *) fc);
-               break;
-       case CTF_FIELD_CLASS_TYPE_STRING:
-               copy_fc = (void *) _ctf_field_class_string_copy((void *) fc);
-               break;
-       case CTF_FIELD_CLASS_TYPE_STRUCT:
-               copy_fc = (void *) _ctf_field_class_struct_copy((void *) fc);
-               break;
-       case CTF_FIELD_CLASS_TYPE_ARRAY:
-               copy_fc = (void *) _ctf_field_class_array_copy((void *) fc);
-               break;
-       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
-               copy_fc = (void *) _ctf_field_class_sequence_copy((void *) fc);
-               break;
-       case CTF_FIELD_CLASS_TYPE_VARIANT:
-               copy_fc = (void *) _ctf_field_class_variant_copy((void *) fc);
-               break;
-       default:
-               bt_common_abort();
-       }
-
-       copy_fc->type = fc->type;
-       copy_fc->alignment = fc->alignment;
-       copy_fc->in_ir = fc->in_ir;
-
-end:
-       return copy_fc;
-}
-
-static inline
-struct ctf_event_class *ctf_event_class_create(void)
-{
-       struct ctf_event_class *ec = g_new0(struct ctf_event_class, 1);
-
-       BT_ASSERT(ec);
-       ec->name = g_string_new(NULL);
-       BT_ASSERT(ec->name);
-       ec->emf_uri = g_string_new(NULL);
-       BT_ASSERT(ec->emf_uri);
-       ec->is_log_level_set = false;
-       return ec;
-}
-
-static inline
-void ctf_event_class_set_log_level(struct ctf_event_class *ec,
-               enum bt_event_class_log_level log_level)
-{
-       BT_ASSERT(ec);
-       ec->log_level = log_level;
-       ec->is_log_level_set = true;
-}
-
-static inline
-void ctf_event_class_destroy(struct ctf_event_class *ec)
-{
-       if (!ec) {
-               return;
-       }
-
-       if (ec->name) {
-               g_string_free(ec->name, TRUE);
-       }
-
-       if (ec->emf_uri) {
-               g_string_free(ec->emf_uri, TRUE);
-       }
-
-       ctf_field_class_destroy(ec->spec_context_fc);
-       ctf_field_class_destroy(ec->payload_fc);
-       g_free(ec);
-}
-
-static inline
-struct ctf_stream_class *ctf_stream_class_create(void)
-{
-       struct ctf_stream_class *sc = g_new0(struct ctf_stream_class, 1);
-
-       BT_ASSERT(sc);
-       sc->event_classes = g_ptr_array_new_with_free_func(
-               (GDestroyNotify) ctf_event_class_destroy);
-       BT_ASSERT(sc->event_classes);
-       sc->event_classes_by_id = g_hash_table_new(g_direct_hash,
-               g_direct_equal);
-       BT_ASSERT(sc->event_classes_by_id);
-       return sc;
-}
-
-static inline
-void ctf_stream_class_destroy(struct ctf_stream_class *sc)
-{
-       if (!sc) {
-               return;
-       }
-
-       if (sc->event_classes) {
-               g_ptr_array_free(sc->event_classes, TRUE);
-       }
-
-       if (sc->event_classes_by_id) {
-               g_hash_table_destroy(sc->event_classes_by_id);
-       }
-
-       ctf_field_class_destroy(sc->packet_context_fc);
-       ctf_field_class_destroy(sc->event_header_fc);
-       ctf_field_class_destroy(sc->event_common_context_fc);
-       g_free(sc);
-}
-
-static inline
-void ctf_stream_class_append_event_class(struct ctf_stream_class *sc,
-               struct ctf_event_class *ec)
-{
-       g_ptr_array_add(sc->event_classes, ec);
-       g_hash_table_insert(sc->event_classes_by_id,
-               GUINT_TO_POINTER((guint) ec->id), ec);
-}
-
-static inline
-struct ctf_event_class *ctf_stream_class_borrow_event_class_by_id(
-               struct ctf_stream_class *sc, uint64_t type)
-{
-       BT_ASSERT_DBG(sc);
-       return g_hash_table_lookup(sc->event_classes_by_id,
-               GUINT_TO_POINTER((guint) type));
-}
-
-static inline
-void _ctf_trace_class_env_entry_init(struct ctf_trace_class_env_entry *entry)
-{
-       BT_ASSERT(entry);
-       entry->name = g_string_new(NULL);
-       BT_ASSERT(entry->name);
-       entry->value.str = g_string_new(NULL);
-       BT_ASSERT(entry->value.str);
-}
-
-static inline
-void _ctf_trace_class_env_entry_fini(struct ctf_trace_class_env_entry *entry)
-{
-       BT_ASSERT(entry);
-
-       if (entry->name) {
-               g_string_free(entry->name, TRUE);
-       }
-
-       if (entry->value.str) {
-               g_string_free(entry->value.str, TRUE);
-       }
-}
-
-static inline
-struct ctf_clock_class *ctf_clock_class_create(void)
-{
-       struct ctf_clock_class *cc = g_new0(struct ctf_clock_class, 1);
-
-       BT_ASSERT(cc);
-       cc->name = g_string_new(NULL);
-       BT_ASSERT(cc->name);
-       cc->description = g_string_new(NULL);
-       BT_ASSERT(cc->description);
-       return cc;
-}
-
-static inline
-void ctf_clock_class_destroy(struct ctf_clock_class *cc)
-{
-       if (!cc) {
-               return;
-       }
-
-       if (cc->name) {
-               g_string_free(cc->name, TRUE);
-       }
-
-       if (cc->description) {
-               g_string_free(cc->description, TRUE);
-       }
-
-       bt_clock_class_put_ref(cc->ir_cc);
-       g_free(cc);
-}
-
-static inline
-struct ctf_trace_class *ctf_trace_class_create(void)
-{
-       struct ctf_trace_class *tc = g_new0(struct ctf_trace_class, 1);
-
-       BT_ASSERT(tc);
-       tc->default_byte_order = CTF_BYTE_ORDER_UNKNOWN;
-       tc->clock_classes = g_ptr_array_new_with_free_func(
-               (GDestroyNotify) ctf_clock_class_destroy);
-       BT_ASSERT(tc->clock_classes);
-       tc->stream_classes = g_ptr_array_new_with_free_func(
-               (GDestroyNotify) ctf_stream_class_destroy);
-       BT_ASSERT(tc->stream_classes);
-       tc->env_entries = g_array_new(FALSE, TRUE,
-               sizeof(struct ctf_trace_class_env_entry));
-       return tc;
-}
-
-static inline
-void ctf_trace_class_destroy(struct ctf_trace_class *tc)
-{
-       if (!tc) {
-               return;
-       }
-
-       ctf_field_class_destroy(tc->packet_header_fc);
-
-       if (tc->clock_classes) {
-               g_ptr_array_free(tc->clock_classes, TRUE);
-       }
-
-       if (tc->stream_classes) {
-               g_ptr_array_free(tc->stream_classes, TRUE);
-       }
-
-       if (tc->env_entries) {
-               uint64_t i;
-
-               for (i = 0; i < tc->env_entries->len; i++) {
-                       struct ctf_trace_class_env_entry *entry =
-                               &g_array_index(tc->env_entries,
-                                       struct ctf_trace_class_env_entry, i);
-
-                       _ctf_trace_class_env_entry_fini(entry);
-               }
-
-               g_array_free(tc->env_entries, TRUE);
-       }
-
-       g_free(tc);
-}
-
-static inline
-void ctf_trace_class_append_env_entry(struct ctf_trace_class *tc,
-               const char *name, enum ctf_trace_class_env_entry_type type,
-               const char *str_value, int64_t i_value)
-{
-       struct ctf_trace_class_env_entry *entry;
-
-       BT_ASSERT(tc);
-       BT_ASSERT(name);
-       g_array_set_size(tc->env_entries, tc->env_entries->len + 1);
-
-       entry = &g_array_index(tc->env_entries,
-               struct ctf_trace_class_env_entry, tc->env_entries->len - 1);
-       entry->type = type;
-       _ctf_trace_class_env_entry_init(entry);
-       g_string_assign(entry->name, name);
-
-       if (str_value) {
-               g_string_assign(entry->value.str, str_value);
-       }
-
-       entry->value.i = i_value;
-}
-
-static inline
-struct ctf_stream_class *ctf_trace_class_borrow_stream_class_by_id(
-               struct ctf_trace_class *tc, uint64_t id)
-{
-       uint64_t i;
-       struct ctf_stream_class *ret_sc = NULL;
-
-       BT_ASSERT_DBG(tc);
-
-       for (i = 0; i < tc->stream_classes->len; i++) {
-               struct ctf_stream_class *sc = tc->stream_classes->pdata[i];
-
-               if (sc->id == id) {
-                       ret_sc = sc;
-                       goto end;
-               }
-       }
-
-end:
-       return ret_sc;
-}
-
-static inline
-struct ctf_clock_class *ctf_trace_class_borrow_clock_class_by_name(
-               struct ctf_trace_class *tc, const char *name)
-{
-       uint64_t i;
-       struct ctf_clock_class *ret_cc = NULL;
-
-       BT_ASSERT_DBG(tc);
-       BT_ASSERT_DBG(name);
-
-       for (i = 0; i < tc->clock_classes->len; i++) {
-               struct ctf_clock_class *cc = tc->clock_classes->pdata[i];
-
-               BT_ASSERT_DBG(cc->name);
-               if (strcmp(cc->name->str, name) == 0) {
-                       ret_cc = cc;
-                       goto end;
-               }
-       }
-
-end:
-       return ret_cc;
-}
-
-static inline
-struct ctf_trace_class_env_entry *ctf_trace_class_borrow_env_entry_by_index(
-               struct ctf_trace_class *tc, uint64_t index)
-{
-       BT_ASSERT_DBG(tc);
-       BT_ASSERT_DBG(index < tc->env_entries->len);
-       return &g_array_index(tc->env_entries, struct ctf_trace_class_env_entry,
-               index);
-}
-
-static inline
-struct ctf_trace_class_env_entry *ctf_trace_class_borrow_env_entry_by_name(
-               struct ctf_trace_class *tc, const char *name)
-{
-       struct ctf_trace_class_env_entry *ret_entry = NULL;
-       uint64_t i;
-
-       BT_ASSERT_DBG(tc);
-       BT_ASSERT_DBG(name);
-
-       for (i = 0; i < tc->env_entries->len; i++) {
-               struct ctf_trace_class_env_entry *env_entry =
-                       ctf_trace_class_borrow_env_entry_by_index(tc, i);
-
-               if (strcmp(env_entry->name->str, name) == 0) {
-                       ret_entry = env_entry;
-                       goto end;
-               }
-       }
-
-end:
-       return ret_entry;
-}
-
-#endif /* _CTF_META_H */
diff --git a/src/plugins/ctf/common/metadata/ctf-meta.hpp b/src/plugins/ctf/common/metadata/ctf-meta.hpp
new file mode 100644 (file)
index 0000000..74c5fac
--- /dev/null
@@ -0,0 +1,1876 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#ifndef _CTF_META_H
+#define _CTF_META_H
+
+#include <babeltrace2/babeltrace.h>
+#include "common/common.h"
+#include "common/uuid.h"
+#include "common/assert.h"
+#include <glib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+enum ctf_field_class_type {
+       CTF_FIELD_CLASS_TYPE_INT,
+       CTF_FIELD_CLASS_TYPE_ENUM,
+       CTF_FIELD_CLASS_TYPE_FLOAT,
+       CTF_FIELD_CLASS_TYPE_STRING,
+       CTF_FIELD_CLASS_TYPE_STRUCT,
+       CTF_FIELD_CLASS_TYPE_ARRAY,
+       CTF_FIELD_CLASS_TYPE_SEQUENCE,
+       CTF_FIELD_CLASS_TYPE_VARIANT,
+};
+
+enum ctf_field_class_meaning {
+       CTF_FIELD_CLASS_MEANING_NONE,
+       CTF_FIELD_CLASS_MEANING_PACKET_BEGINNING_TIME,
+       CTF_FIELD_CLASS_MEANING_PACKET_END_TIME,
+       CTF_FIELD_CLASS_MEANING_EVENT_CLASS_ID,
+       CTF_FIELD_CLASS_MEANING_STREAM_CLASS_ID,
+       CTF_FIELD_CLASS_MEANING_DATA_STREAM_ID,
+       CTF_FIELD_CLASS_MEANING_MAGIC,
+       CTF_FIELD_CLASS_MEANING_PACKET_COUNTER_SNAPSHOT,
+       CTF_FIELD_CLASS_MEANING_DISC_EV_REC_COUNTER_SNAPSHOT,
+       CTF_FIELD_CLASS_MEANING_EXP_PACKET_TOTAL_SIZE,
+       CTF_FIELD_CLASS_MEANING_EXP_PACKET_CONTENT_SIZE,
+       CTF_FIELD_CLASS_MEANING_UUID,
+};
+
+enum ctf_byte_order {
+       CTF_BYTE_ORDER_UNKNOWN,
+       CTF_BYTE_ORDER_DEFAULT,
+       CTF_BYTE_ORDER_LITTLE,
+       CTF_BYTE_ORDER_BIG,
+};
+
+enum ctf_encoding {
+       CTF_ENCODING_NONE,
+       CTF_ENCODING_UTF8,
+};
+
+enum ctf_scope {
+       CTF_SCOPE_PACKET_UNKNOWN = -1,
+       CTF_SCOPE_PACKET_HEADER = 0,
+       CTF_SCOPE_PACKET_CONTEXT,
+       CTF_SCOPE_EVENT_HEADER,
+       CTF_SCOPE_EVENT_COMMON_CONTEXT,
+       CTF_SCOPE_EVENT_SPECIFIC_CONTEXT,
+       CTF_SCOPE_EVENT_PAYLOAD,
+};
+
+struct ctf_clock_class {
+       GString *name;
+       GString *description;
+       uint64_t frequency;
+       uint64_t precision;
+       int64_t offset_seconds;
+       uint64_t offset_cycles;
+       bt_uuid_t uuid;
+       bool has_uuid;
+       bool is_absolute;
+
+       /* Weak, set during translation */
+       bt_clock_class *ir_cc;
+};
+
+struct ctf_field_class {
+       enum ctf_field_class_type type;
+       unsigned int alignment;
+       bool is_compound;
+       bool in_ir;
+
+       /* Weak, set during translation. NULL if `in_ir` is false below. */
+       bt_field_class *ir_fc;
+};
+
+struct ctf_field_class_bit_array {
+       struct ctf_field_class base;
+       enum ctf_byte_order byte_order;
+       unsigned int size;
+};
+
+struct ctf_field_class_int {
+       struct ctf_field_class_bit_array base;
+       enum ctf_field_class_meaning meaning;
+       bool is_signed;
+       bt_field_class_integer_preferred_display_base disp_base;
+       enum ctf_encoding encoding;
+       int64_t storing_index;
+
+       /* Weak */
+       struct ctf_clock_class *mapped_clock_class;
+};
+
+struct ctf_range {
+       union {
+               uint64_t u;
+               int64_t i;
+       } lower;
+
+       union {
+               uint64_t u;
+               int64_t i;
+       } upper;
+};
+
+struct ctf_field_class_enum_mapping {
+       GString *label;
+
+       /* Array of `struct ctf_range` */
+       GArray *ranges;
+};
+
+struct ctf_field_class_enum {
+       struct ctf_field_class_int base;
+
+       /* Array of `struct ctf_field_class_enum_mapping` */
+       GArray *mappings;
+};
+
+struct ctf_field_class_float {
+       struct ctf_field_class_bit_array base;
+};
+
+struct ctf_field_class_string {
+       struct ctf_field_class base;
+       enum ctf_encoding encoding;
+};
+
+struct ctf_named_field_class {
+       /* Original name which can include a leading `_` */
+       GString *orig_name;
+
+       /* Name as translated to trace IR (leading `_` removed) */
+       GString *name;
+
+       /* Owned by this */
+       struct ctf_field_class *fc;
+};
+
+struct ctf_field_class_struct {
+       struct ctf_field_class base;
+
+       /* Array of `struct ctf_named_field_class` */
+       GArray *members;
+};
+
+struct ctf_field_path {
+       enum ctf_scope root;
+
+       /* Array of `int64_t` */
+       GArray *path;
+};
+
+struct ctf_field_class_variant_range {
+       struct ctf_range range;
+       uint64_t option_index;
+};
+
+struct ctf_field_class_variant {
+       struct ctf_field_class base;
+       GString *tag_ref;
+       struct ctf_field_path tag_path;
+       uint64_t stored_tag_index;
+
+       /* Array of `struct ctf_named_field_class` */
+       GArray *options;
+
+       /* Array of `struct ctf_field_class_variant_range` */
+       GArray *ranges;
+
+       /* Weak */
+       struct ctf_field_class_enum *tag_fc;
+};
+
+struct ctf_field_class_array_base {
+       struct ctf_field_class base;
+       struct ctf_field_class *elem_fc;
+       bool is_text;
+};
+
+struct ctf_field_class_array {
+       struct ctf_field_class_array_base base;
+       enum ctf_field_class_meaning meaning;
+       uint64_t length;
+};
+
+struct ctf_field_class_sequence {
+       struct ctf_field_class_array_base base;
+       GString *length_ref;
+       struct ctf_field_path length_path;
+       uint64_t stored_length_index;
+
+       /* Weak */
+       struct ctf_field_class_int *length_fc;
+};
+
+struct ctf_event_class {
+       GString *name;
+       uint64_t id;
+       GString *emf_uri;
+       bt_event_class_log_level log_level;
+       bool is_translated;
+       bool is_log_level_set;
+
+       /* Owned by this */
+       struct ctf_field_class *spec_context_fc;
+
+       /* Owned by this */
+       struct ctf_field_class *payload_fc;
+
+       /* Weak, set during translation */
+       bt_event_class *ir_ec;
+};
+
+struct ctf_stream_class {
+       uint64_t id;
+       bool is_translated;
+       bool packets_have_ts_begin;
+       bool packets_have_ts_end;
+       bool has_discarded_events;
+       bool has_discarded_packets;
+       bool discarded_events_have_default_cs;
+       bool discarded_packets_have_default_cs;
+
+       /* Owned by this */
+       struct ctf_field_class *packet_context_fc;
+
+       /* Owned by this */
+       struct ctf_field_class *event_header_fc;
+
+       /* Owned by this */
+       struct ctf_field_class *event_common_context_fc;
+
+       /* Array of `struct ctf_event_class *`, owned by this */
+       GPtrArray *event_classes;
+
+       /*
+        * Hash table mapping event class IDs to `struct ctf_event_class *`,
+        * weak.
+        */
+       GHashTable *event_classes_by_id;
+
+       /* Weak */
+       struct ctf_clock_class *default_clock_class;
+
+       /* Weak, set during translation */
+       bt_stream_class *ir_sc;
+};
+
+enum ctf_trace_class_env_entry_type {
+       CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT,
+       CTF_TRACE_CLASS_ENV_ENTRY_TYPE_STR,
+};
+
+struct ctf_trace_class_env_entry {
+       enum ctf_trace_class_env_entry_type type;
+       GString *name;
+
+       struct {
+               int64_t i;
+               GString *str;
+       } value;
+};
+
+struct ctf_trace_class {
+       unsigned int major;
+       unsigned int minor;
+       bt_uuid_t uuid;
+       bool is_uuid_set;
+       enum ctf_byte_order default_byte_order;
+
+       /* Owned by this */
+       struct ctf_field_class *packet_header_fc;
+
+       uint64_t stored_value_count;
+
+       /* Array of `struct ctf_clock_class *` (owned by this) */
+       GPtrArray *clock_classes;
+
+       /* Array of `struct ctf_stream_class *` */
+       GPtrArray *stream_classes;
+
+       /* Array of `struct ctf_trace_class_env_entry` */
+       GArray *env_entries;
+
+       bool is_translated;
+
+       /* Weak, set during translation */
+       bt_trace_class *ir_tc;
+
+       struct {
+               bool lttng_crash;
+               bool lttng_event_after_packet;
+               bool barectf_event_before_packet;
+       } quirks;
+};
+
+static inline
+ctf_field_class_bit_array *ctf_field_class_as_bit_array(ctf_field_class *fc)
+{
+       BT_ASSERT_DBG(!fc || (fc->type == CTF_FIELD_CLASS_TYPE_INT ||
+               fc->type == CTF_FIELD_CLASS_TYPE_ENUM ||
+               fc->type == CTF_FIELD_CLASS_TYPE_FLOAT));
+       return (ctf_field_class_bit_array *) fc;
+}
+
+static inline
+ctf_field_class_int *ctf_field_class_as_int(ctf_field_class *fc)
+{
+       BT_ASSERT_DBG(!fc || (fc->type == CTF_FIELD_CLASS_TYPE_INT ||
+               fc->type == CTF_FIELD_CLASS_TYPE_ENUM));
+        return (ctf_field_class_int *) fc;
+}
+
+static inline
+ctf_field_class_enum *ctf_field_class_as_enum(ctf_field_class *fc)
+{
+       BT_ASSERT_DBG(!fc || fc->type == CTF_FIELD_CLASS_TYPE_ENUM);
+        return (ctf_field_class_enum *) fc;
+}
+
+static inline
+ctf_field_class_float *ctf_field_class_as_float(ctf_field_class *fc)
+{
+       BT_ASSERT_DBG(!fc || fc->type == CTF_FIELD_CLASS_TYPE_FLOAT);
+        return (ctf_field_class_float *) fc;
+}
+
+static inline
+ctf_field_class_string *ctf_field_class_as_string(ctf_field_class *fc)
+{
+       BT_ASSERT_DBG(!fc || fc->type == CTF_FIELD_CLASS_TYPE_STRING);
+        return (ctf_field_class_string *) fc;
+}
+
+static inline
+ctf_field_class_struct *ctf_field_class_as_struct(ctf_field_class *fc)
+{
+       BT_ASSERT_DBG(!fc || fc->type == CTF_FIELD_CLASS_TYPE_STRUCT);
+        return (ctf_field_class_struct *) fc;
+}
+
+static inline
+ctf_field_class_array_base *ctf_field_class_as_array_base(ctf_field_class *fc)
+{
+       BT_ASSERT_DBG(!fc || (fc->type == CTF_FIELD_CLASS_TYPE_ARRAY ||
+               fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE));
+       return (ctf_field_class_array_base *) fc;
+}
+
+static inline
+ctf_field_class_array *ctf_field_class_as_array(ctf_field_class *fc)
+{
+       BT_ASSERT_DBG(!fc || fc->type == CTF_FIELD_CLASS_TYPE_ARRAY);
+        return (ctf_field_class_array *) fc;
+}
+
+static inline
+ctf_field_class_sequence *ctf_field_class_as_sequence(ctf_field_class *fc)
+{
+       BT_ASSERT_DBG(!fc || fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE);
+        return (ctf_field_class_sequence *) fc;
+}
+
+static inline
+ctf_field_class_variant *ctf_field_class_as_variant(
+               ctf_field_class *fc)
+{
+       BT_ASSERT_DBG(!fc || fc->type == CTF_FIELD_CLASS_TYPE_VARIANT);
+        return (ctf_field_class_variant *) fc;
+}
+
+
+static inline
+void ctf_field_class_destroy(struct ctf_field_class *fc);
+
+static inline
+void _ctf_field_class_init(struct ctf_field_class *fc,
+               enum ctf_field_class_type type, unsigned int alignment)
+{
+       BT_ASSERT(fc);
+       fc->type = type;
+       fc->alignment = alignment;
+       fc->in_ir = false;
+}
+
+static inline
+void _ctf_field_class_bit_array_init(struct ctf_field_class_bit_array *fc,
+               enum ctf_field_class_type type)
+{
+       _ctf_field_class_init(&fc->base, type, 1);
+}
+
+static inline
+void _ctf_field_class_int_init(struct ctf_field_class_int *fc,
+               enum ctf_field_class_type type)
+{
+       _ctf_field_class_bit_array_init(&fc->base, type);
+       fc->meaning = CTF_FIELD_CLASS_MEANING_NONE;
+       fc->storing_index = -1;
+}
+
+static inline
+void ctf_field_path_init(struct ctf_field_path *field_path)
+{
+       BT_ASSERT(field_path);
+       field_path->path = g_array_new(FALSE, TRUE, sizeof(int64_t));
+       BT_ASSERT(field_path->path);
+}
+
+static inline
+void ctf_field_path_fini(struct ctf_field_path *field_path)
+{
+       BT_ASSERT(field_path);
+
+       if (field_path->path) {
+               g_array_free(field_path->path, TRUE);
+       }
+}
+
+static inline
+void _ctf_named_field_class_init(struct ctf_named_field_class *named_fc)
+{
+       BT_ASSERT(named_fc);
+       named_fc->name = g_string_new(NULL);
+       BT_ASSERT(named_fc->name);
+       named_fc->orig_name = g_string_new(NULL);
+       BT_ASSERT(named_fc->orig_name);
+}
+
+static inline
+void _ctf_named_field_class_fini(struct ctf_named_field_class *named_fc)
+{
+       BT_ASSERT(named_fc);
+
+       if (named_fc->name) {
+               g_string_free(named_fc->name, TRUE);
+       }
+
+       if (named_fc->orig_name) {
+               g_string_free(named_fc->orig_name, TRUE);
+       }
+
+       ctf_field_class_destroy(named_fc->fc);
+}
+
+static inline
+void _ctf_field_class_enum_mapping_init(
+               struct ctf_field_class_enum_mapping *mapping)
+{
+       BT_ASSERT(mapping);
+       mapping->label = g_string_new(NULL);
+       BT_ASSERT(mapping->label);
+       mapping->ranges = g_array_new(FALSE, TRUE, sizeof(struct ctf_range));
+       BT_ASSERT(mapping->ranges);
+}
+
+static inline
+void _ctf_field_class_enum_mapping_fini(
+               struct ctf_field_class_enum_mapping *mapping)
+{
+       BT_ASSERT(mapping);
+
+       if (mapping->label) {
+               g_string_free(mapping->label, TRUE);
+       }
+
+       if (mapping->ranges) {
+               g_array_free(mapping->ranges, TRUE);
+       }
+}
+
+static inline
+struct ctf_field_class_int *ctf_field_class_int_create(void)
+{
+       struct ctf_field_class_int *fc = g_new0(struct ctf_field_class_int, 1);
+
+       BT_ASSERT(fc);
+       _ctf_field_class_int_init(fc, CTF_FIELD_CLASS_TYPE_INT);
+       return fc;
+}
+
+static inline
+struct ctf_field_class_float *ctf_field_class_float_create(void)
+{
+       struct ctf_field_class_float *fc =
+               g_new0(struct ctf_field_class_float, 1);
+
+       BT_ASSERT(fc);
+       _ctf_field_class_bit_array_init(&fc->base, CTF_FIELD_CLASS_TYPE_FLOAT);
+       return fc;
+}
+
+static inline
+struct ctf_field_class_string *ctf_field_class_string_create(void)
+{
+       struct ctf_field_class_string *fc =
+               g_new0(struct ctf_field_class_string, 1);
+
+       BT_ASSERT(fc);
+       _ctf_field_class_init(&fc->base, CTF_FIELD_CLASS_TYPE_STRING, 8);
+       return fc;
+}
+
+static inline
+struct ctf_field_class_enum *ctf_field_class_enum_create(void)
+{
+       struct ctf_field_class_enum *fc = g_new0(struct ctf_field_class_enum, 1);
+
+       BT_ASSERT(fc);
+       _ctf_field_class_int_init(&fc->base, CTF_FIELD_CLASS_TYPE_ENUM);
+       fc->mappings = g_array_new(FALSE, TRUE,
+               sizeof(struct ctf_field_class_enum_mapping));
+       BT_ASSERT(fc->mappings);
+       return fc;
+}
+
+static inline
+struct ctf_field_class_struct *ctf_field_class_struct_create(void)
+{
+       struct ctf_field_class_struct *fc =
+               g_new0(struct ctf_field_class_struct, 1);
+
+       BT_ASSERT(fc);
+       _ctf_field_class_init(&fc->base, CTF_FIELD_CLASS_TYPE_STRUCT, 1);
+       fc->members = g_array_new(FALSE, TRUE,
+               sizeof(struct ctf_named_field_class));
+       BT_ASSERT(fc->members);
+       fc->base.is_compound = true;
+       return fc;
+}
+
+static inline
+struct ctf_field_class_variant *ctf_field_class_variant_create(void)
+{
+       struct ctf_field_class_variant *fc =
+               g_new0(struct ctf_field_class_variant, 1);
+
+       BT_ASSERT(fc);
+       _ctf_field_class_init(&fc->base, CTF_FIELD_CLASS_TYPE_VARIANT, 1);
+       fc->options = g_array_new(FALSE, TRUE,
+               sizeof(struct ctf_named_field_class));
+       BT_ASSERT(fc->options);
+       fc->ranges = g_array_new(FALSE, TRUE,
+               sizeof(struct ctf_field_class_variant_range));
+       BT_ASSERT(fc->ranges);
+       fc->tag_ref = g_string_new(NULL);
+       BT_ASSERT(fc->tag_ref);
+       ctf_field_path_init(&fc->tag_path);
+       fc->base.is_compound = true;
+       return fc;
+}
+
+static inline
+struct ctf_field_class_array *ctf_field_class_array_create(void)
+{
+       struct ctf_field_class_array *fc =
+               g_new0(struct ctf_field_class_array, 1);
+
+       BT_ASSERT(fc);
+       _ctf_field_class_init(&fc->base.base, CTF_FIELD_CLASS_TYPE_ARRAY, 1);
+       fc->base.base.is_compound = true;
+       return fc;
+}
+
+static inline
+struct ctf_field_class_sequence *ctf_field_class_sequence_create(void)
+{
+       struct ctf_field_class_sequence *fc =
+               g_new0(struct ctf_field_class_sequence, 1);
+
+       BT_ASSERT(fc);
+       _ctf_field_class_init(&fc->base.base, CTF_FIELD_CLASS_TYPE_SEQUENCE, 1);
+       fc->length_ref = g_string_new(NULL);
+       BT_ASSERT(fc->length_ref);
+       ctf_field_path_init(&fc->length_path);
+       fc->base.base.is_compound = true;
+       return fc;
+}
+
+static inline
+void _ctf_field_class_int_destroy(struct ctf_field_class_int *fc)
+{
+       BT_ASSERT(fc);
+       g_free(fc);
+}
+
+static inline
+void _ctf_field_class_enum_destroy(struct ctf_field_class_enum *fc)
+{
+       BT_ASSERT(fc);
+
+       if (fc->mappings) {
+               uint64_t i;
+
+               for (i = 0; i < fc->mappings->len; i++) {
+                       struct ctf_field_class_enum_mapping *mapping =
+                               &g_array_index(fc->mappings,
+                                       struct ctf_field_class_enum_mapping, i);
+
+                       _ctf_field_class_enum_mapping_fini(mapping);
+               }
+
+               g_array_free(fc->mappings, TRUE);
+       }
+
+       g_free(fc);
+}
+
+static inline
+void _ctf_field_class_float_destroy(struct ctf_field_class_float *fc)
+{
+       BT_ASSERT(fc);
+       g_free(fc);
+}
+
+static inline
+void _ctf_field_class_string_destroy(struct ctf_field_class_string *fc)
+{
+       BT_ASSERT(fc);
+       g_free(fc);
+}
+
+static inline
+void _ctf_field_class_struct_destroy(struct ctf_field_class_struct *fc)
+{
+       BT_ASSERT(fc);
+
+       if (fc->members) {
+               uint64_t i;
+
+               for (i = 0; i < fc->members->len; i++) {
+                       struct ctf_named_field_class *named_fc =
+                               &g_array_index(fc->members,
+                                       struct ctf_named_field_class, i);
+
+                       _ctf_named_field_class_fini(named_fc);
+               }
+
+               g_array_free(fc->members, TRUE);
+       }
+
+       g_free(fc);
+}
+
+static inline
+void _ctf_field_class_array_base_fini(struct ctf_field_class_array_base *fc)
+{
+       BT_ASSERT(fc);
+       ctf_field_class_destroy(fc->elem_fc);
+}
+
+static inline
+void _ctf_field_class_array_destroy(struct ctf_field_class_array *fc)
+{
+       BT_ASSERT(fc);
+       _ctf_field_class_array_base_fini(&fc->base);
+       g_free(fc);
+}
+
+static inline
+void _ctf_field_class_sequence_destroy(struct ctf_field_class_sequence *fc)
+{
+       BT_ASSERT(fc);
+       _ctf_field_class_array_base_fini(&fc->base);
+
+       if (fc->length_ref) {
+               g_string_free(fc->length_ref, TRUE);
+       }
+
+       ctf_field_path_fini(&fc->length_path);
+       g_free(fc);
+}
+
+static inline
+void _ctf_field_class_variant_destroy(struct ctf_field_class_variant *fc)
+{
+       BT_ASSERT(fc);
+
+       if (fc->options) {
+               uint64_t i;
+
+               for (i = 0; i < fc->options->len; i++) {
+                       struct ctf_named_field_class *named_fc =
+                               &g_array_index(fc->options,
+                                       struct ctf_named_field_class, i);
+
+                       _ctf_named_field_class_fini(named_fc);
+               }
+
+               g_array_free(fc->options, TRUE);
+       }
+
+       if (fc->ranges) {
+               g_array_free(fc->ranges, TRUE);
+       }
+
+       if (fc->tag_ref) {
+               g_string_free(fc->tag_ref, TRUE);
+       }
+
+       ctf_field_path_fini(&fc->tag_path);
+       g_free(fc);
+}
+
+static inline
+void ctf_field_class_destroy(struct ctf_field_class *fc)
+{
+       if (!fc) {
+               return;
+       }
+
+       switch (fc->type) {
+       case CTF_FIELD_CLASS_TYPE_INT:
+               _ctf_field_class_int_destroy(ctf_field_class_as_int(fc));
+               break;
+       case CTF_FIELD_CLASS_TYPE_ENUM:
+               _ctf_field_class_enum_destroy(ctf_field_class_as_enum(fc));
+               break;
+       case CTF_FIELD_CLASS_TYPE_FLOAT:
+               _ctf_field_class_float_destroy(ctf_field_class_as_float(fc));
+               break;
+       case CTF_FIELD_CLASS_TYPE_STRING:
+               _ctf_field_class_string_destroy(ctf_field_class_as_string(fc));
+               break;
+       case CTF_FIELD_CLASS_TYPE_STRUCT:
+               _ctf_field_class_struct_destroy(ctf_field_class_as_struct(fc));
+               break;
+       case CTF_FIELD_CLASS_TYPE_ARRAY:
+               _ctf_field_class_array_destroy(ctf_field_class_as_array(fc));
+               break;
+       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+               _ctf_field_class_sequence_destroy(ctf_field_class_as_sequence(fc));
+               break;
+       case CTF_FIELD_CLASS_TYPE_VARIANT:
+               _ctf_field_class_variant_destroy(ctf_field_class_as_variant(fc));
+               break;
+       default:
+               bt_common_abort();
+       }
+}
+
+static inline
+struct ctf_range *ctf_field_class_enum_mapping_borrow_range_by_index(
+               struct ctf_field_class_enum_mapping *mapping, uint64_t index)
+{
+       BT_ASSERT_DBG(mapping);
+       BT_ASSERT_DBG(index < mapping->ranges->len);
+       return &g_array_index(mapping->ranges, struct ctf_range, index);
+}
+
+static inline
+struct ctf_field_class_enum_mapping *ctf_field_class_enum_borrow_mapping_by_index(
+               struct ctf_field_class_enum *fc, uint64_t index)
+{
+       BT_ASSERT_DBG(fc);
+       BT_ASSERT_DBG(index < fc->mappings->len);
+       return &g_array_index(fc->mappings, struct ctf_field_class_enum_mapping,
+               index);
+}
+
+static inline
+struct ctf_field_class_enum_mapping *ctf_field_class_enum_borrow_mapping_by_label(
+               struct ctf_field_class_enum *fc, const char *label)
+{
+       struct ctf_field_class_enum_mapping *ret_mapping = NULL;
+       uint64_t i;
+
+       BT_ASSERT_DBG(fc);
+       BT_ASSERT_DBG(label);
+
+       for (i = 0; i < fc->mappings->len; i++) {
+               struct ctf_field_class_enum_mapping *mapping =
+                       ctf_field_class_enum_borrow_mapping_by_index(fc, i);
+
+               if (strcmp(mapping->label->str, label) == 0) {
+                       ret_mapping = mapping;
+                       goto end;
+               }
+       }
+
+end:
+       return ret_mapping;
+}
+
+static inline
+void ctf_field_class_enum_map_range(struct ctf_field_class_enum *fc,
+               const char *label, uint64_t u_lower, uint64_t u_upper)
+{
+       struct ctf_field_class_enum_mapping *mapping = NULL;
+       struct ctf_range range = {
+               .lower = {
+                   .u = u_lower,
+               },
+               .upper = {
+                   .u = u_upper,
+               },
+       };
+       uint64_t i;
+
+       BT_ASSERT(fc);
+       BT_ASSERT(label);
+
+       for (i = 0; i < fc->mappings->len; i++) {
+               mapping = ctf_field_class_enum_borrow_mapping_by_index(
+                       fc, i);
+
+               if (strcmp(mapping->label->str, label) == 0) {
+                       break;
+               }
+       }
+
+       if (i == fc->mappings->len) {
+               mapping = NULL;
+       }
+
+       if (!mapping) {
+               g_array_set_size(fc->mappings, fc->mappings->len + 1);
+               mapping = ctf_field_class_enum_borrow_mapping_by_index(
+                       fc, fc->mappings->len - 1);
+               _ctf_field_class_enum_mapping_init(mapping);
+               g_string_assign(mapping->label, label);
+       }
+
+       g_array_append_val(mapping->ranges, range);
+}
+
+static inline
+struct ctf_named_field_class *ctf_field_class_struct_borrow_member_by_index(
+               struct ctf_field_class_struct *fc, uint64_t index)
+{
+       BT_ASSERT_DBG(fc);
+       BT_ASSERT_DBG(index < fc->members->len);
+       return &g_array_index(fc->members, struct ctf_named_field_class,
+               index);
+}
+
+static inline
+struct ctf_named_field_class *ctf_field_class_struct_borrow_member_by_name(
+               struct ctf_field_class_struct *fc, const char *name)
+{
+       uint64_t i;
+       struct ctf_named_field_class *ret_named_fc = NULL;
+
+       BT_ASSERT_DBG(fc);
+       BT_ASSERT_DBG(name);
+
+       for (i = 0; i < fc->members->len; i++) {
+               struct ctf_named_field_class *named_fc =
+                       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 ctf_field_class *ctf_field_class_struct_borrow_member_field_class_by_name(
+               struct ctf_field_class_struct *struct_fc, const char *name)
+{
+       struct ctf_named_field_class *named_fc = NULL;
+       struct ctf_field_class *fc = NULL;
+
+       if (!struct_fc) {
+               goto end;
+       }
+
+       named_fc = 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 ctf_field_class_int *
+ctf_field_class_struct_borrow_member_int_field_class_by_name(
+               struct ctf_field_class_struct *struct_fc, const char *name)
+{
+       struct ctf_field_class_int *int_fc = NULL;
+
+       int_fc = ctf_field_class_as_int(
+               ctf_field_class_struct_borrow_member_field_class_by_name(
+                       struct_fc, name));
+       if (!int_fc) {
+               goto end;
+       }
+
+       if (int_fc->base.base.type != CTF_FIELD_CLASS_TYPE_INT &&
+                       int_fc->base.base.type != CTF_FIELD_CLASS_TYPE_ENUM) {
+               int_fc = NULL;
+               goto end;
+       }
+
+end:
+       return int_fc;
+}
+
+static inline
+void _ctf_named_field_class_unescape_orig_name(
+               struct ctf_named_field_class *named_fc)
+{
+       const char *name = named_fc->orig_name->str;
+
+       if (name[0] == '_') {
+               name++;
+       }
+
+       g_string_assign(named_fc->name, name);
+}
+
+static inline
+void ctf_field_class_struct_append_member(struct ctf_field_class_struct *fc,
+               const char *orig_name, struct ctf_field_class *member_fc)
+{
+       struct ctf_named_field_class *named_fc;
+
+       BT_ASSERT(fc);
+       BT_ASSERT(orig_name);
+       g_array_set_size(fc->members, fc->members->len + 1);
+
+       named_fc = &g_array_index(fc->members, struct ctf_named_field_class,
+               fc->members->len - 1);
+       _ctf_named_field_class_init(named_fc);
+       g_string_assign(named_fc->orig_name, orig_name);
+       _ctf_named_field_class_unescape_orig_name(named_fc);
+       named_fc->fc = member_fc;
+
+       if (member_fc->alignment > fc->base.alignment) {
+               fc->base.alignment = member_fc->alignment;
+       }
+}
+
+static inline
+struct ctf_named_field_class *ctf_field_class_variant_borrow_option_by_index(
+               struct ctf_field_class_variant *fc, uint64_t index)
+{
+       BT_ASSERT_DBG(fc);
+       BT_ASSERT_DBG(index < fc->options->len);
+       return &g_array_index(fc->options, struct ctf_named_field_class,
+               index);
+}
+
+static inline
+struct ctf_named_field_class *ctf_field_class_variant_borrow_option_by_name(
+               struct ctf_field_class_variant *fc, const char *name)
+{
+       uint64_t i;
+       struct ctf_named_field_class *ret_named_fc = NULL;
+
+       BT_ASSERT_DBG(fc);
+       BT_ASSERT_DBG(name);
+
+       for (i = 0; i < fc->options->len; i++) {
+               struct ctf_named_field_class *named_fc =
+                       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
+struct ctf_field_class_variant_range *
+ctf_field_class_variant_borrow_range_by_index(
+               struct ctf_field_class_variant *fc, uint64_t index)
+{
+       BT_ASSERT_DBG(fc);
+       BT_ASSERT_DBG(index < fc->ranges->len);
+       return &g_array_index(fc->ranges, struct ctf_field_class_variant_range,
+               index);
+}
+
+static inline
+void ctf_field_class_variant_append_option(struct ctf_field_class_variant *fc,
+               const char *orig_name, struct ctf_field_class *option_fc)
+{
+       struct ctf_named_field_class *named_fc;
+
+       BT_ASSERT(fc);
+       BT_ASSERT(orig_name);
+       g_array_set_size(fc->options, fc->options->len + 1);
+
+       named_fc = &g_array_index(fc->options, struct ctf_named_field_class,
+               fc->options->len - 1);
+       _ctf_named_field_class_init(named_fc);
+       g_string_assign(named_fc->orig_name, orig_name);
+       _ctf_named_field_class_unescape_orig_name(named_fc);
+       named_fc->fc = option_fc;
+}
+
+static inline
+void ctf_field_class_variant_set_tag_field_class(
+               struct ctf_field_class_variant *fc,
+               struct ctf_field_class_enum *tag_fc)
+{
+       uint64_t option_i;
+
+       BT_ASSERT(fc);
+       BT_ASSERT(tag_fc);
+       fc->tag_fc = tag_fc;
+
+       for (option_i = 0; option_i < fc->options->len; option_i++) {
+               uint64_t range_i;
+               struct ctf_named_field_class *named_fc =
+                       ctf_field_class_variant_borrow_option_by_index(
+                               fc, option_i);
+               struct ctf_field_class_enum_mapping *mapping;
+
+               mapping = ctf_field_class_enum_borrow_mapping_by_label(
+                       tag_fc, named_fc->orig_name->str);
+               if (!mapping) {
+                       continue;
+               }
+
+               for (range_i = 0; range_i < mapping->ranges->len;
+                               range_i++) {
+                       struct ctf_range *range =
+                               ctf_field_class_enum_mapping_borrow_range_by_index(
+                                       mapping, range_i);
+                       struct ctf_field_class_variant_range var_range;
+
+                       var_range.range = *range;
+                       var_range.option_index = option_i;
+                       g_array_append_val(fc->ranges, var_range);
+               }
+       }
+}
+
+static inline
+struct ctf_field_class *ctf_field_class_compound_borrow_field_class_by_index(
+               struct ctf_field_class *comp_fc, uint64_t index)
+{
+       struct ctf_field_class *fc = NULL;
+
+       switch (comp_fc->type) {
+       case CTF_FIELD_CLASS_TYPE_STRUCT:
+       {
+               struct ctf_named_field_class *named_fc =
+                       ctf_field_class_struct_borrow_member_by_index(
+                               (struct ctf_field_class_struct *) comp_fc, index);
+
+               BT_ASSERT_DBG(named_fc);
+               fc = named_fc->fc;
+               break;
+       }
+       case CTF_FIELD_CLASS_TYPE_VARIANT:
+       {
+               struct ctf_named_field_class *named_fc =
+                       ctf_field_class_variant_borrow_option_by_index(
+                               (struct ctf_field_class_variant *) comp_fc, index);
+
+               BT_ASSERT_DBG(named_fc);
+               fc = named_fc->fc;
+               break;
+       }
+       case CTF_FIELD_CLASS_TYPE_ARRAY:
+       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+       {
+               struct ctf_field_class_array_base *array_fc =
+                       (struct ctf_field_class_array_base *) comp_fc;
+
+               fc = array_fc->elem_fc;
+               break;
+       }
+       default:
+               break;
+       }
+
+       return fc;
+}
+
+static inline
+uint64_t ctf_field_class_compound_get_field_class_count(struct ctf_field_class *fc)
+{
+       uint64_t field_count;
+
+       switch (fc->type) {
+       case CTF_FIELD_CLASS_TYPE_STRUCT:
+       {
+               struct ctf_field_class_struct *struct_fc =
+                       (struct ctf_field_class_struct *) fc;
+
+               field_count = struct_fc->members->len;
+               break;
+       }
+       case CTF_FIELD_CLASS_TYPE_VARIANT:
+       {
+               struct ctf_field_class_variant *var_fc =
+                       (struct ctf_field_class_variant *) fc;
+
+               field_count = var_fc->options->len;
+               break;
+       }
+       case CTF_FIELD_CLASS_TYPE_ARRAY:
+       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+               /*
+                * Array and sequence types always contain a single
+                * member (the element type).
+                */
+               field_count = 1;
+               break;
+       default:
+               bt_common_abort();
+       }
+
+       return field_count;
+}
+
+static inline
+int64_t ctf_field_class_compound_get_field_class_index_from_orig_name(
+               struct ctf_field_class *fc, const char *orig_name)
+{
+       int64_t ret_index = -1;
+       uint64_t i;
+
+       switch (fc->type) {
+       case CTF_FIELD_CLASS_TYPE_STRUCT:
+       {
+               struct ctf_field_class_struct *struct_fc =
+                       (struct ctf_field_class_struct *) fc;
+
+               for (i = 0; i < struct_fc->members->len; i++) {
+                       struct ctf_named_field_class *named_fc =
+                               ctf_field_class_struct_borrow_member_by_index(
+                                       struct_fc, i);
+
+                       if (strcmp(orig_name, named_fc->orig_name->str) == 0) {
+                               ret_index = (int64_t) i;
+                               goto end;
+                       }
+               }
+
+               break;
+       }
+       case CTF_FIELD_CLASS_TYPE_VARIANT:
+       {
+               struct ctf_field_class_variant *var_fc
+                       = (struct ctf_field_class_variant *) fc;
+
+               for (i = 0; i < var_fc->options->len; i++) {
+                       struct ctf_named_field_class *named_fc =
+                               ctf_field_class_variant_borrow_option_by_index(
+                                       var_fc, i);
+
+                       if (strcmp(orig_name, named_fc->orig_name->str) == 0) {
+                               ret_index = (int64_t) i;
+                               goto end;
+                       }
+               }
+
+               break;
+       }
+       default:
+               break;
+       }
+
+end:
+       return ret_index;
+}
+
+static inline
+void ctf_field_path_append_index(struct ctf_field_path *fp, int64_t index)
+{
+       BT_ASSERT(fp);
+       g_array_append_val(fp->path, index);
+}
+
+static inline
+int64_t ctf_field_path_borrow_index_by_index(struct ctf_field_path *fp,
+               uint64_t index)
+{
+       BT_ASSERT_DBG(fp);
+       BT_ASSERT_DBG(index < fp->path->len);
+       return g_array_index(fp->path, int64_t, index);
+}
+
+static inline
+void ctf_field_path_clear(struct ctf_field_path *fp)
+{
+       BT_ASSERT(fp);
+       g_array_set_size(fp->path, 0);
+}
+
+static inline
+const char *ctf_scope_string(enum ctf_scope scope)
+{
+       switch (scope) {
+       case CTF_SCOPE_PACKET_HEADER:
+               return "PACKET_HEADER";
+       case CTF_SCOPE_PACKET_CONTEXT:
+               return "PACKET_CONTEXT";
+       case CTF_SCOPE_EVENT_HEADER:
+               return "EVENT_HEADER";
+       case CTF_SCOPE_EVENT_COMMON_CONTEXT:
+               return "EVENT_COMMON_CONTEXT";
+       case CTF_SCOPE_EVENT_SPECIFIC_CONTEXT:
+               return "EVENT_SPECIFIC_CONTEXT";
+       case CTF_SCOPE_EVENT_PAYLOAD:
+               return "EVENT_PAYLOAD";
+       default:
+               bt_common_abort();
+       }
+}
+
+static inline
+GString *ctf_field_path_string(struct ctf_field_path *path)
+{
+       GString *str = g_string_new(NULL);
+       uint64_t i;
+
+       BT_ASSERT(path);
+
+       if (!str) {
+               goto end;
+       }
+
+       g_string_append_printf(str, "[%s", ctf_scope_string(path->root));
+
+       for (i = 0; i < path->path->len; i++) {
+               g_string_append_printf(str, ", %" PRId64,
+                       ctf_field_path_borrow_index_by_index(path, i));
+       }
+
+       g_string_append(str, "]");
+
+end:
+       return str;
+}
+
+static inline
+struct ctf_field_class *ctf_field_path_borrow_field_class(
+               struct ctf_field_path *field_path,
+               struct ctf_trace_class *tc,
+               struct ctf_stream_class *sc,
+               struct ctf_event_class *ec)
+{
+       uint64_t i;
+       struct ctf_field_class *fc;
+
+       switch (field_path->root) {
+       case CTF_SCOPE_PACKET_HEADER:
+               fc = tc->packet_header_fc;
+               break;
+       case CTF_SCOPE_PACKET_CONTEXT:
+               fc = sc->packet_context_fc;
+               break;
+       case CTF_SCOPE_EVENT_HEADER:
+               fc = sc->event_header_fc;
+               break;
+       case CTF_SCOPE_EVENT_COMMON_CONTEXT:
+               fc = sc->event_common_context_fc;
+               break;
+       case CTF_SCOPE_EVENT_SPECIFIC_CONTEXT:
+               fc = ec->spec_context_fc;
+               break;
+       case CTF_SCOPE_EVENT_PAYLOAD:
+               fc = ec->payload_fc;
+               break;
+       default:
+               bt_common_abort();
+       }
+
+       BT_ASSERT_DBG(fc);
+
+       for (i = 0; i < field_path->path->len; i++) {
+               int64_t child_index =
+                       ctf_field_path_borrow_index_by_index(field_path, i);
+               struct ctf_field_class *child_fc =
+                       ctf_field_class_compound_borrow_field_class_by_index(
+                               fc, child_index);
+               BT_ASSERT_DBG(child_fc);
+               fc = child_fc;
+       }
+
+       BT_ASSERT_DBG(fc);
+       return fc;
+}
+
+static inline
+struct ctf_field_class *ctf_field_class_copy(struct ctf_field_class *fc);
+
+static inline
+void ctf_field_class_bit_array_copy_content(
+               struct ctf_field_class_bit_array *dst_fc,
+               struct ctf_field_class_bit_array *src_fc)
+{
+       BT_ASSERT(dst_fc);
+       BT_ASSERT(src_fc);
+       dst_fc->byte_order = src_fc->byte_order;
+       dst_fc->size = src_fc->size;
+}
+
+static inline
+void ctf_field_class_int_copy_content(
+               struct ctf_field_class_int *dst_fc,
+               struct ctf_field_class_int *src_fc)
+{
+       ctf_field_class_bit_array_copy_content(&dst_fc->base, &src_fc->base);
+       dst_fc->meaning = src_fc->meaning;
+       dst_fc->is_signed = src_fc->is_signed;
+       dst_fc->disp_base = src_fc->disp_base;
+       dst_fc->encoding = src_fc->encoding;
+       dst_fc->mapped_clock_class = src_fc->mapped_clock_class;
+       dst_fc->storing_index = src_fc->storing_index;
+}
+
+static inline
+struct ctf_field_class_int *_ctf_field_class_int_copy(
+               struct ctf_field_class_int *fc)
+{
+       struct ctf_field_class_int *copy_fc = ctf_field_class_int_create();
+
+       BT_ASSERT(copy_fc);
+       ctf_field_class_int_copy_content(copy_fc, fc);
+       return copy_fc;
+}
+
+static inline
+struct ctf_field_class_enum *_ctf_field_class_enum_copy(
+               struct ctf_field_class_enum *fc)
+{
+       struct ctf_field_class_enum *copy_fc = ctf_field_class_enum_create();
+       uint64_t i;
+
+       BT_ASSERT(copy_fc);
+       ctf_field_class_int_copy_content(&copy_fc->base, &fc->base);
+
+       for (i = 0; i < fc->mappings->len; i++) {
+               uint64_t range_i;
+
+               struct ctf_field_class_enum_mapping *mapping =
+                       &g_array_index(fc->mappings,
+                               struct ctf_field_class_enum_mapping, i);
+
+               for (range_i = 0; range_i < mapping->ranges->len; range_i++) {
+                       struct ctf_range *range =
+                               &g_array_index(mapping->ranges,
+                                       struct ctf_range, range_i);
+
+                       ctf_field_class_enum_map_range(copy_fc,
+                               mapping->label->str, range->lower.u,
+                               range->upper.u);
+               }
+       }
+
+       return copy_fc;
+}
+
+static inline
+struct ctf_field_class_float *_ctf_field_class_float_copy(
+               struct ctf_field_class_float *fc)
+{
+       struct ctf_field_class_float *copy_fc = ctf_field_class_float_create();
+
+       BT_ASSERT(copy_fc);
+       ctf_field_class_bit_array_copy_content(&copy_fc->base, &fc->base);
+       return copy_fc;
+}
+
+static inline
+struct ctf_field_class_string *_ctf_field_class_string_copy(
+               struct ctf_field_class_string *fc)
+{
+       struct ctf_field_class_string *copy_fc = ctf_field_class_string_create();
+
+       BT_ASSERT(copy_fc);
+       return copy_fc;
+}
+
+static inline
+struct ctf_field_class_struct *_ctf_field_class_struct_copy(
+               struct ctf_field_class_struct *fc)
+{
+       struct ctf_field_class_struct *copy_fc = ctf_field_class_struct_create();
+       uint64_t i;
+
+       BT_ASSERT(copy_fc);
+
+       for (i = 0; i < fc->members->len; i++) {
+               struct ctf_named_field_class *named_fc =
+                       &g_array_index(fc->members,
+                               struct ctf_named_field_class, i);
+
+               ctf_field_class_struct_append_member(copy_fc,
+                       named_fc->name->str,
+                       ctf_field_class_copy(named_fc->fc));
+       }
+
+       return copy_fc;
+}
+
+static inline
+void ctf_field_path_copy_content(struct ctf_field_path *dst_fp,
+               struct ctf_field_path *src_fp)
+{
+       uint64_t i;
+
+       BT_ASSERT(dst_fp);
+       BT_ASSERT(src_fp);
+       dst_fp->root = src_fp->root;
+       ctf_field_path_clear(dst_fp);
+
+       for (i = 0; i < src_fp->path->len; i++) {
+               int64_t index = ctf_field_path_borrow_index_by_index(
+                       src_fp, i);
+
+               ctf_field_path_append_index(dst_fp, index);
+       }
+}
+
+static inline
+struct ctf_field_class_variant *_ctf_field_class_variant_copy(
+               struct ctf_field_class_variant *fc)
+{
+       struct ctf_field_class_variant *copy_fc =
+               ctf_field_class_variant_create();
+       uint64_t i;
+
+       BT_ASSERT(copy_fc);
+
+       for (i = 0; i < fc->options->len; i++) {
+               struct ctf_named_field_class *named_fc =
+                       &g_array_index(fc->options,
+                               struct ctf_named_field_class, i);
+
+               ctf_field_class_variant_append_option(copy_fc,
+                       named_fc->name->str,
+                       ctf_field_class_copy(named_fc->fc));
+       }
+
+       for (i = 0; i < fc->ranges->len; i++) {
+               struct ctf_field_class_variant_range *range =
+                       &g_array_index(fc->ranges,
+                               struct ctf_field_class_variant_range, i);
+
+               g_array_append_val(copy_fc->ranges, *range);
+       }
+
+       ctf_field_path_copy_content(&copy_fc->tag_path, &fc->tag_path);
+       g_string_assign(copy_fc->tag_ref, fc->tag_ref->str);
+       copy_fc->stored_tag_index = fc->stored_tag_index;
+       return copy_fc;
+}
+
+static inline
+void ctf_field_class_array_base_copy_content(
+               struct ctf_field_class_array_base *dst_fc,
+               struct ctf_field_class_array_base *src_fc)
+{
+       BT_ASSERT(dst_fc);
+       BT_ASSERT(src_fc);
+       dst_fc->elem_fc = ctf_field_class_copy(src_fc->elem_fc);
+       dst_fc->is_text = src_fc->is_text;
+}
+
+static inline
+struct ctf_field_class_array *_ctf_field_class_array_copy(
+               struct ctf_field_class_array *fc)
+{
+       struct ctf_field_class_array *copy_fc = ctf_field_class_array_create();
+
+       BT_ASSERT(copy_fc);
+       ctf_field_class_array_base_copy_content(&copy_fc->base, &fc->base);
+       copy_fc->length = fc->length;
+       return copy_fc;
+}
+
+static inline
+struct ctf_field_class_sequence *_ctf_field_class_sequence_copy(
+               struct ctf_field_class_sequence *fc)
+{
+       struct ctf_field_class_sequence *copy_fc =
+               ctf_field_class_sequence_create();
+
+       BT_ASSERT(copy_fc);
+       ctf_field_class_array_base_copy_content(&copy_fc->base, &fc->base);
+       ctf_field_path_copy_content(&copy_fc->length_path, &fc->length_path);
+       g_string_assign(copy_fc->length_ref, fc->length_ref->str);
+       copy_fc->stored_length_index = fc->stored_length_index;
+       return copy_fc;
+}
+
+static inline
+struct ctf_field_class *ctf_field_class_copy(struct ctf_field_class *fc)
+{
+       struct ctf_field_class *copy_fc = NULL;
+
+       if (!fc) {
+               goto end;
+       }
+
+       /*
+        * Translation should not have happened yet.
+        */
+       BT_ASSERT(!fc->ir_fc);
+
+       switch (fc->type) {
+       case CTF_FIELD_CLASS_TYPE_INT:
+               copy_fc = &_ctf_field_class_int_copy(ctf_field_class_as_int (fc))->base.base;
+               break;
+       case CTF_FIELD_CLASS_TYPE_ENUM:
+               copy_fc = &_ctf_field_class_enum_copy(ctf_field_class_as_enum (fc))->base.base.base;
+               break;
+       case CTF_FIELD_CLASS_TYPE_FLOAT:
+               copy_fc = &_ctf_field_class_float_copy(ctf_field_class_as_float (fc))->base.base;
+               break;
+       case CTF_FIELD_CLASS_TYPE_STRING:
+               copy_fc = &_ctf_field_class_string_copy(ctf_field_class_as_string (fc))->base;
+               break;
+       case CTF_FIELD_CLASS_TYPE_STRUCT:
+               copy_fc = &_ctf_field_class_struct_copy(ctf_field_class_as_struct (fc))->base;
+               break;
+       case CTF_FIELD_CLASS_TYPE_ARRAY:
+               copy_fc = &_ctf_field_class_array_copy(ctf_field_class_as_array(fc))->base.base;
+               break;
+       case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+               copy_fc = &_ctf_field_class_sequence_copy(ctf_field_class_as_sequence (fc))->base.base;
+               break;
+       case CTF_FIELD_CLASS_TYPE_VARIANT:
+               copy_fc = &_ctf_field_class_variant_copy(ctf_field_class_as_variant (fc))->base;
+               break;
+       default:
+               bt_common_abort();
+       }
+
+       copy_fc->type = fc->type;
+       copy_fc->alignment = fc->alignment;
+       copy_fc->in_ir = fc->in_ir;
+
+end:
+       return copy_fc;
+}
+
+static inline
+struct ctf_event_class *ctf_event_class_create(void)
+{
+       struct ctf_event_class *ec = g_new0(struct ctf_event_class, 1);
+
+       BT_ASSERT(ec);
+       ec->name = g_string_new(NULL);
+       BT_ASSERT(ec->name);
+       ec->emf_uri = g_string_new(NULL);
+       BT_ASSERT(ec->emf_uri);
+       ec->is_log_level_set = false;
+       return ec;
+}
+
+static inline
+void ctf_event_class_set_log_level(struct ctf_event_class *ec,
+               enum bt_event_class_log_level log_level)
+{
+       BT_ASSERT(ec);
+       ec->log_level = log_level;
+       ec->is_log_level_set = true;
+}
+
+static inline
+void ctf_event_class_destroy(struct ctf_event_class *ec)
+{
+       if (!ec) {
+               return;
+       }
+
+       if (ec->name) {
+               g_string_free(ec->name, TRUE);
+       }
+
+       if (ec->emf_uri) {
+               g_string_free(ec->emf_uri, TRUE);
+       }
+
+       ctf_field_class_destroy(ec->spec_context_fc);
+       ctf_field_class_destroy(ec->payload_fc);
+       g_free(ec);
+}
+
+static inline
+struct ctf_stream_class *ctf_stream_class_create(void)
+{
+       struct ctf_stream_class *sc = g_new0(struct ctf_stream_class, 1);
+
+       BT_ASSERT(sc);
+       sc->event_classes = g_ptr_array_new_with_free_func(
+               (GDestroyNotify) ctf_event_class_destroy);
+       BT_ASSERT(sc->event_classes);
+       sc->event_classes_by_id = g_hash_table_new(g_direct_hash,
+               g_direct_equal);
+       BT_ASSERT(sc->event_classes_by_id);
+       return sc;
+}
+
+static inline
+void ctf_stream_class_destroy(struct ctf_stream_class *sc)
+{
+       if (!sc) {
+               return;
+       }
+
+       if (sc->event_classes) {
+               g_ptr_array_free(sc->event_classes, TRUE);
+       }
+
+       if (sc->event_classes_by_id) {
+               g_hash_table_destroy(sc->event_classes_by_id);
+       }
+
+       ctf_field_class_destroy(sc->packet_context_fc);
+       ctf_field_class_destroy(sc->event_header_fc);
+       ctf_field_class_destroy(sc->event_common_context_fc);
+       g_free(sc);
+}
+
+static inline
+void ctf_stream_class_append_event_class(struct ctf_stream_class *sc,
+               struct ctf_event_class *ec)
+{
+       g_ptr_array_add(sc->event_classes, ec);
+       g_hash_table_insert(sc->event_classes_by_id,
+               GUINT_TO_POINTER((guint) ec->id), ec);
+}
+
+static inline
+struct ctf_event_class *ctf_stream_class_borrow_event_class_by_id(
+               struct ctf_stream_class *sc, uint64_t type)
+{
+       BT_ASSERT_DBG(sc);
+       return (struct ctf_event_class *) g_hash_table_lookup(sc->event_classes_by_id,
+               GUINT_TO_POINTER((guint) type));
+}
+
+static inline
+void _ctf_trace_class_env_entry_init(struct ctf_trace_class_env_entry *entry)
+{
+       BT_ASSERT(entry);
+       entry->name = g_string_new(NULL);
+       BT_ASSERT(entry->name);
+       entry->value.str = g_string_new(NULL);
+       BT_ASSERT(entry->value.str);
+}
+
+static inline
+void _ctf_trace_class_env_entry_fini(struct ctf_trace_class_env_entry *entry)
+{
+       BT_ASSERT(entry);
+
+       if (entry->name) {
+               g_string_free(entry->name, TRUE);
+       }
+
+       if (entry->value.str) {
+               g_string_free(entry->value.str, TRUE);
+       }
+}
+
+static inline
+struct ctf_clock_class *ctf_clock_class_create(void)
+{
+       struct ctf_clock_class *cc = g_new0(struct ctf_clock_class, 1);
+
+       BT_ASSERT(cc);
+       cc->name = g_string_new(NULL);
+       BT_ASSERT(cc->name);
+       cc->description = g_string_new(NULL);
+       BT_ASSERT(cc->description);
+       return cc;
+}
+
+static inline
+void ctf_clock_class_destroy(struct ctf_clock_class *cc)
+{
+       if (!cc) {
+               return;
+       }
+
+       if (cc->name) {
+               g_string_free(cc->name, TRUE);
+       }
+
+       if (cc->description) {
+               g_string_free(cc->description, TRUE);
+       }
+
+       bt_clock_class_put_ref(cc->ir_cc);
+       g_free(cc);
+}
+
+static inline
+struct ctf_trace_class *ctf_trace_class_create(void)
+{
+       struct ctf_trace_class *tc = g_new0(struct ctf_trace_class, 1);
+
+       BT_ASSERT(tc);
+       tc->default_byte_order = CTF_BYTE_ORDER_UNKNOWN;
+       tc->clock_classes = g_ptr_array_new_with_free_func(
+               (GDestroyNotify) ctf_clock_class_destroy);
+       BT_ASSERT(tc->clock_classes);
+       tc->stream_classes = g_ptr_array_new_with_free_func(
+               (GDestroyNotify) ctf_stream_class_destroy);
+       BT_ASSERT(tc->stream_classes);
+       tc->env_entries = g_array_new(FALSE, TRUE,
+               sizeof(struct ctf_trace_class_env_entry));
+       return tc;
+}
+
+static inline
+void ctf_trace_class_destroy(struct ctf_trace_class *tc)
+{
+       if (!tc) {
+               return;
+       }
+
+       ctf_field_class_destroy(tc->packet_header_fc);
+
+       if (tc->clock_classes) {
+               g_ptr_array_free(tc->clock_classes, TRUE);
+       }
+
+       if (tc->stream_classes) {
+               g_ptr_array_free(tc->stream_classes, TRUE);
+       }
+
+       if (tc->env_entries) {
+               uint64_t i;
+
+               for (i = 0; i < tc->env_entries->len; i++) {
+                       struct ctf_trace_class_env_entry *entry =
+                               &g_array_index(tc->env_entries,
+                                       struct ctf_trace_class_env_entry, i);
+
+                       _ctf_trace_class_env_entry_fini(entry);
+               }
+
+               g_array_free(tc->env_entries, TRUE);
+       }
+
+       g_free(tc);
+}
+
+static inline
+void ctf_trace_class_append_env_entry(struct ctf_trace_class *tc,
+               const char *name, enum ctf_trace_class_env_entry_type type,
+               const char *str_value, int64_t i_value)
+{
+       struct ctf_trace_class_env_entry *entry;
+
+       BT_ASSERT(tc);
+       BT_ASSERT(name);
+       g_array_set_size(tc->env_entries, tc->env_entries->len + 1);
+
+       entry = &g_array_index(tc->env_entries,
+               struct ctf_trace_class_env_entry, tc->env_entries->len - 1);
+       entry->type = type;
+       _ctf_trace_class_env_entry_init(entry);
+       g_string_assign(entry->name, name);
+
+       if (str_value) {
+               g_string_assign(entry->value.str, str_value);
+       }
+
+       entry->value.i = i_value;
+}
+
+static inline
+struct ctf_stream_class *ctf_trace_class_borrow_stream_class_by_id(
+               struct ctf_trace_class *tc, uint64_t id)
+{
+       uint64_t i;
+       struct ctf_stream_class *ret_sc = NULL;
+
+       BT_ASSERT_DBG(tc);
+
+       for (i = 0; i < tc->stream_classes->len; i++) {
+               struct ctf_stream_class *sc =
+                       (struct ctf_stream_class *) tc->stream_classes->pdata[i];
+
+               if (sc->id == id) {
+                       ret_sc = sc;
+                       goto end;
+               }
+       }
+
+end:
+       return ret_sc;
+}
+
+static inline
+struct ctf_clock_class *ctf_trace_class_borrow_clock_class_by_name(
+               struct ctf_trace_class *tc, const char *name)
+{
+       uint64_t i;
+       struct ctf_clock_class *ret_cc = NULL;
+
+       BT_ASSERT_DBG(tc);
+       BT_ASSERT_DBG(name);
+
+       for (i = 0; i < tc->clock_classes->len; i++) {
+               struct ctf_clock_class *cc =
+                       (struct ctf_clock_class *) tc->clock_classes->pdata[i];
+
+               BT_ASSERT_DBG(cc->name);
+               if (strcmp(cc->name->str, name) == 0) {
+                       ret_cc = cc;
+                       goto end;
+               }
+       }
+
+end:
+       return ret_cc;
+}
+
+static inline
+struct ctf_trace_class_env_entry *ctf_trace_class_borrow_env_entry_by_index(
+               struct ctf_trace_class *tc, uint64_t index)
+{
+       BT_ASSERT_DBG(tc);
+       BT_ASSERT_DBG(index < tc->env_entries->len);
+       return &g_array_index(tc->env_entries, struct ctf_trace_class_env_entry,
+               index);
+}
+
+static inline
+struct ctf_trace_class_env_entry *ctf_trace_class_borrow_env_entry_by_name(
+               struct ctf_trace_class *tc, const char *name)
+{
+       struct ctf_trace_class_env_entry *ret_entry = NULL;
+       uint64_t i;
+
+       BT_ASSERT_DBG(tc);
+       BT_ASSERT_DBG(name);
+
+       for (i = 0; i < tc->env_entries->len; i++) {
+               struct ctf_trace_class_env_entry *env_entry =
+                       ctf_trace_class_borrow_env_entry_by_index(tc, i);
+
+               if (strcmp(env_entry->name->str, name) == 0) {
+                       ret_entry = env_entry;
+                       goto end;
+               }
+       }
+
+end:
+       return ret_entry;
+}
+
+#endif /* _CTF_META_H */
diff --git a/src/plugins/ctf/common/metadata/decoder-packetized-file-stream-to-buf.c b/src/plugins/ctf/common/metadata/decoder-packetized-file-stream-to-buf.c
deleted file mode 100644 (file)
index 64fbb18..0000000
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2016-2017 Philippe Proulx <pproulx@efficios.com>
- */
-
-#define BT_COMP_LOG_SELF_COMP self_comp
-#define BT_COMP_LOG_SELF_COMP_CLASS self_comp_class
-#define BT_LOG_OUTPUT_LEVEL log_level
-#define BT_LOG_TAG "PLUGIN/CTF/META/DECODER-DECODE-PACKET"
-#include "logging/comp-logging.h"
-
-#include "decoder-packetized-file-stream-to-buf.h"
-
-#include <stdio.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <inttypes.h>
-#include "common/assert.h"
-#include "common/uuid.h"
-#include "compat/memstream.h"
-#include <babeltrace2/babeltrace.h>
-#include <glib.h>
-#include <string.h>
-
-#include "ast.h"
-#include "decoder.h"
-#include "scanner.h"
-#include "logging.h"
-
-#define TSDL_MAGIC     0x75d11d57
-
-struct ctf_metadata_decoder {
-       struct ctf_visitor_generate_ir *visitor;
-       bt_uuid_t uuid;
-       bool is_uuid_set;
-       int bo;
-       struct ctf_metadata_decoder_config config;
-};
-
-struct packet_header {
-       uint32_t magic;
-       bt_uuid_t  uuid;
-       uint32_t checksum;
-       uint32_t content_size;
-       uint32_t packet_size;
-       uint8_t  compression_scheme;
-       uint8_t  encryption_scheme;
-       uint8_t  checksum_scheme;
-       uint8_t  major;
-       uint8_t  minor;
-} __attribute__((__packed__));
-
-static
-int decode_packet(FILE *in_fp, FILE *out_fp,
-               int byte_order, bool *is_uuid_set, uint8_t *uuid,
-               bt_logging_level log_level, bt_self_component *self_comp,
-               bt_self_component_class *self_comp_class)
-{
-       struct packet_header header;
-       size_t readlen, writelen, toread;
-       uint8_t buf[512 + 1];   /* + 1 for debug-mode \0 */
-       int ret = 0;
-       const long offset = ftell(in_fp);
-
-       if (offset < 0) {
-               BT_COMP_LOGE_APPEND_CAUSE_ERRNO(BT_COMP_LOG_SELF_COMP,
-                       "Failed to get current metadata file position",
-                       ".");
-               goto error;
-       }
-       BT_COMP_LOGD("Decoding metadata packet: offset=%ld", offset);
-       readlen = fread(&header, sizeof(header), 1, in_fp);
-       if (feof(in_fp) != 0) {
-               BT_COMP_LOGI("Reached end of file: offset=%ld", ftell(in_fp));
-               goto end;
-       }
-       if (readlen < 1) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot decode metadata packet: offset=%ld", offset);
-               goto error;
-       }
-
-       if (byte_order != BYTE_ORDER) {
-               header.magic = GUINT32_SWAP_LE_BE(header.magic);
-               header.checksum = GUINT32_SWAP_LE_BE(header.checksum);
-               header.content_size = GUINT32_SWAP_LE_BE(header.content_size);
-               header.packet_size = GUINT32_SWAP_LE_BE(header.packet_size);
-       }
-
-       if (header.compression_scheme) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Metadata packet compression is not supported as of this version: "
-                       "compression-scheme=%u, offset=%ld",
-                       (unsigned int) header.compression_scheme, offset);
-               goto error;
-       }
-
-       if (header.encryption_scheme) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Metadata packet encryption is not supported as of this version: "
-                       "encryption-scheme=%u, offset=%ld",
-                       (unsigned int) header.encryption_scheme, offset);
-               goto error;
-       }
-
-       if (header.checksum || header.checksum_scheme) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Metadata packet checksum verification is not supported as of this version: "
-                       "checksum-scheme=%u, checksum=%x, offset=%ld",
-                       (unsigned int) header.checksum_scheme, header.checksum,
-                       offset);
-               goto error;
-       }
-
-       if (!ctf_metadata_decoder_is_packet_version_valid(header.major,
-                       header.minor)) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid metadata packet version: "
-                       "version=%u.%u, offset=%ld",
-                       header.major, header.minor, offset);
-               goto error;
-       }
-
-       /* Set expected trace UUID if not set; otherwise validate it */
-       if (is_uuid_set) {
-               if (!*is_uuid_set) {
-                       bt_uuid_copy(uuid, header.uuid);
-                       *is_uuid_set = true;
-               } else if (bt_uuid_compare(header.uuid, uuid)) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Metadata UUID mismatch between packets of the same stream: "
-                               "packet-uuid=\"" BT_UUID_FMT "\", "
-                               "expected-uuid=\"" BT_UUID_FMT "\", "
-                               "offset=%ld",
-                               BT_UUID_FMT_VALUES(header.uuid),
-                               BT_UUID_FMT_VALUES(uuid),
-                               offset);
-                       goto error;
-               }
-       }
-
-       if ((header.content_size / CHAR_BIT) < sizeof(header)) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Bad metadata packet content size: content-size=%u, "
-                       "offset=%ld", header.content_size, offset);
-               goto error;
-       }
-
-       toread = header.content_size / CHAR_BIT - sizeof(header);
-
-       for (;;) {
-               size_t loop_read;
-
-               loop_read = MIN(sizeof(buf) - 1, toread);
-               readlen = fread(buf, sizeof(uint8_t), loop_read, in_fp);
-               if (ferror(in_fp)) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot read metadata packet buffer: "
-                               "offset=%ld, read-size=%zu",
-                               ftell(in_fp), loop_read);
-                       goto error;
-               }
-               if (readlen > loop_read) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("fread returned more byte than expected: "
-                               "read-size-asked=%zu, read-size-returned=%zu",
-                               loop_read, readlen);
-                       goto error;
-               }
-
-               writelen = fwrite(buf, sizeof(uint8_t), readlen, out_fp);
-               if (writelen < readlen || ferror(out_fp)) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot write decoded metadata text to buffer: "
-                               "read-offset=%ld, write-size=%zu",
-                               ftell(in_fp), readlen);
-                       goto error;
-               }
-
-               toread -= readlen;
-               if (toread == 0) {
-                       int fseek_ret;
-
-                       /* Read leftover padding */
-                       toread = (header.packet_size - header.content_size) /
-                               CHAR_BIT;
-                       fseek_ret = fseek(in_fp, toread, SEEK_CUR);
-                       if (fseek_ret < 0) {
-                               BT_COMP_LOGW_STR("Missing padding at the end of the metadata stream.");
-                       }
-                       break;
-               }
-       }
-
-       goto end;
-
-error:
-       ret = -1;
-
-end:
-       return ret;
-}
-
-BT_HIDDEN
-int ctf_metadata_decoder_packetized_file_stream_to_buf(FILE *fp,
-               char **buf, int byte_order, bool *is_uuid_set,
-               uint8_t *uuid, bt_logging_level log_level,
-               bt_self_component *self_comp,
-               bt_self_component_class *self_comp_class)
-{
-       FILE *out_fp;
-       size_t size;
-       int ret = 0;
-       int tret;
-       size_t packet_index = 0;
-
-       out_fp = bt_open_memstream(buf, &size);
-       if (!out_fp) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot open memory stream: %s.",
-                       strerror(errno));
-               goto error;
-       }
-
-       for (;;) {
-               if (feof(fp) != 0) {
-                       break;
-               }
-
-               tret = decode_packet(fp, out_fp, byte_order, is_uuid_set,
-                       uuid, log_level, self_comp, self_comp_class);
-               if (tret) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot decode packet: index=%zu",
-                               packet_index);
-                       goto error;
-               }
-
-               packet_index++;
-       }
-
-       /* Make sure the whole string ends with a null character */
-       tret = fputc('\0', out_fp);
-       if (tret == EOF) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                       "Cannot append '\\0' to the decoded metadata buffer.");
-               goto error;
-       }
-
-       /* Close stream, which also flushes the buffer */
-       ret = bt_close_memstream(buf, &size, out_fp);
-       /*
-        * See fclose(3). Further access to out_fp after both success
-        * and error, even through another bt_close_memstream(), results
-        * in undefined behavior. Nullify out_fp to ensure we don't
-        * fclose it twice on error.
-        */
-       out_fp = NULL;
-       if (ret < 0) {
-               BT_COMP_LOGE_APPEND_CAUSE_ERRNO(BT_COMP_LOG_SELF_COMP, "Cannot close memory stream", ".");
-               goto error;
-       }
-
-       goto end;
-
-error:
-       ret = -1;
-
-       if (out_fp) {
-               if (bt_close_memstream(buf, &size, out_fp)) {
-                       BT_COMP_LOGE_ERRNO("Cannot close memory stream", ".");
-               }
-       }
-
-       if (*buf) {
-               free(*buf);
-               *buf = NULL;
-       }
-
-end:
-       return ret;
-}
diff --git a/src/plugins/ctf/common/metadata/decoder-packetized-file-stream-to-buf.cpp b/src/plugins/ctf/common/metadata/decoder-packetized-file-stream-to-buf.cpp
new file mode 100644 (file)
index 0000000..d0cd030
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2016-2017 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#define BT_COMP_LOG_SELF_COMP self_comp
+#define BT_COMP_LOG_SELF_COMP_CLASS self_comp_class
+#define BT_LOG_OUTPUT_LEVEL log_level
+#define BT_LOG_TAG "PLUGIN/CTF/META/DECODER-DECODE-PACKET"
+#include "logging/comp-logging.h"
+
+#include "decoder-packetized-file-stream-to-buf.hpp"
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include "common/assert.h"
+#include "common/uuid.h"
+#include "compat/memstream.h"
+#include <babeltrace2/babeltrace.h>
+#include <glib.h>
+#include <string.h>
+
+#include "ast.hpp"
+#include "decoder.hpp"
+#include "scanner.hpp"
+#include "logging.hpp"
+
+#define TSDL_MAGIC     0x75d11d57
+
+struct ctf_metadata_decoder {
+       struct ctf_visitor_generate_ir *visitor;
+       bt_uuid_t uuid;
+       bool is_uuid_set;
+       int bo;
+       struct ctf_metadata_decoder_config config;
+};
+
+struct packet_header {
+       uint32_t magic;
+       bt_uuid_t  uuid;
+       uint32_t checksum;
+       uint32_t content_size;
+       uint32_t packet_size;
+       uint8_t  compression_scheme;
+       uint8_t  encryption_scheme;
+       uint8_t  checksum_scheme;
+       uint8_t  major;
+       uint8_t  minor;
+} __attribute__((__packed__));
+
+static
+int decode_packet(FILE *in_fp, FILE *out_fp,
+               int byte_order, bool *is_uuid_set, uint8_t *uuid,
+               bt_logging_level log_level, bt_self_component *self_comp,
+               bt_self_component_class *self_comp_class)
+{
+       struct packet_header header;
+       size_t readlen, writelen, toread;
+       uint8_t buf[512 + 1];   /* + 1 for debug-mode \0 */
+       int ret = 0;
+       const long offset = ftell(in_fp);
+
+       if (offset < 0) {
+               BT_COMP_LOGE_APPEND_CAUSE_ERRNO(BT_COMP_LOG_SELF_COMP,
+                       "Failed to get current metadata file position",
+                       ".");
+               goto error;
+       }
+       BT_COMP_LOGD("Decoding metadata packet: offset=%ld", offset);
+       readlen = fread(&header, sizeof(header), 1, in_fp);
+       if (feof(in_fp) != 0) {
+               BT_COMP_LOGI("Reached end of file: offset=%ld", ftell(in_fp));
+               goto end;
+       }
+       if (readlen < 1) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot decode metadata packet: offset=%ld", offset);
+               goto error;
+       }
+
+       if (byte_order != BYTE_ORDER) {
+               header.magic = GUINT32_SWAP_LE_BE(header.magic);
+               header.checksum = GUINT32_SWAP_LE_BE(header.checksum);
+               header.content_size = GUINT32_SWAP_LE_BE(header.content_size);
+               header.packet_size = GUINT32_SWAP_LE_BE(header.packet_size);
+       }
+
+       if (header.compression_scheme) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Metadata packet compression is not supported as of this version: "
+                       "compression-scheme=%u, offset=%ld",
+                       (unsigned int) header.compression_scheme, offset);
+               goto error;
+       }
+
+       if (header.encryption_scheme) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Metadata packet encryption is not supported as of this version: "
+                       "encryption-scheme=%u, offset=%ld",
+                       (unsigned int) header.encryption_scheme, offset);
+               goto error;
+       }
+
+       if (header.checksum || header.checksum_scheme) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Metadata packet checksum verification is not supported as of this version: "
+                       "checksum-scheme=%u, checksum=%x, offset=%ld",
+                       (unsigned int) header.checksum_scheme, header.checksum,
+                       offset);
+               goto error;
+       }
+
+       if (!ctf_metadata_decoder_is_packet_version_valid(header.major,
+                       header.minor)) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid metadata packet version: "
+                       "version=%u.%u, offset=%ld",
+                       header.major, header.minor, offset);
+               goto error;
+       }
+
+       /* Set expected trace UUID if not set; otherwise validate it */
+       if (is_uuid_set) {
+               if (!*is_uuid_set) {
+                       bt_uuid_copy(uuid, header.uuid);
+                       *is_uuid_set = true;
+               } else if (bt_uuid_compare(header.uuid, uuid)) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Metadata UUID mismatch between packets of the same stream: "
+                               "packet-uuid=\"" BT_UUID_FMT "\", "
+                               "expected-uuid=\"" BT_UUID_FMT "\", "
+                               "offset=%ld",
+                               BT_UUID_FMT_VALUES(header.uuid),
+                               BT_UUID_FMT_VALUES(uuid),
+                               offset);
+                       goto error;
+               }
+       }
+
+       if ((header.content_size / CHAR_BIT) < sizeof(header)) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Bad metadata packet content size: content-size=%u, "
+                       "offset=%ld", header.content_size, offset);
+               goto error;
+       }
+
+       toread = header.content_size / CHAR_BIT - sizeof(header);
+
+       for (;;) {
+               size_t loop_read;
+
+               loop_read = MIN(sizeof(buf) - 1, toread);
+               readlen = fread(buf, sizeof(uint8_t), loop_read, in_fp);
+               if (ferror(in_fp)) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot read metadata packet buffer: "
+                               "offset=%ld, read-size=%zu",
+                               ftell(in_fp), loop_read);
+                       goto error;
+               }
+               if (readlen > loop_read) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("fread returned more byte than expected: "
+                               "read-size-asked=%zu, read-size-returned=%zu",
+                               loop_read, readlen);
+                       goto error;
+               }
+
+               writelen = fwrite(buf, sizeof(uint8_t), readlen, out_fp);
+               if (writelen < readlen || ferror(out_fp)) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot write decoded metadata text to buffer: "
+                               "read-offset=%ld, write-size=%zu",
+                               ftell(in_fp), readlen);
+                       goto error;
+               }
+
+               toread -= readlen;
+               if (toread == 0) {
+                       int fseek_ret;
+
+                       /* Read leftover padding */
+                       toread = (header.packet_size - header.content_size) /
+                               CHAR_BIT;
+                       fseek_ret = fseek(in_fp, toread, SEEK_CUR);
+                       if (fseek_ret < 0) {
+                               BT_COMP_LOGW_STR("Missing padding at the end of the metadata stream.");
+                       }
+                       break;
+               }
+       }
+
+       goto end;
+
+error:
+       ret = -1;
+
+end:
+       return ret;
+}
+
+BT_HIDDEN
+int ctf_metadata_decoder_packetized_file_stream_to_buf(FILE *fp,
+               char **buf, int byte_order, bool *is_uuid_set,
+               uint8_t *uuid, bt_logging_level log_level,
+               bt_self_component *self_comp,
+               bt_self_component_class *self_comp_class)
+{
+       FILE *out_fp;
+       size_t size;
+       int ret = 0;
+       int tret;
+       size_t packet_index = 0;
+
+       out_fp = bt_open_memstream(buf, &size);
+       if (!out_fp) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot open memory stream: %s.",
+                       strerror(errno));
+               goto error;
+       }
+
+       for (;;) {
+               if (feof(fp) != 0) {
+                       break;
+               }
+
+               tret = decode_packet(fp, out_fp, byte_order, is_uuid_set,
+                       uuid, log_level, self_comp, self_comp_class);
+               if (tret) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot decode packet: index=%zu",
+                               packet_index);
+                       goto error;
+               }
+
+               packet_index++;
+       }
+
+       /* Make sure the whole string ends with a null character */
+       tret = fputc('\0', out_fp);
+       if (tret == EOF) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
+                       "Cannot append '\\0' to the decoded metadata buffer.");
+               goto error;
+       }
+
+       /* Close stream, which also flushes the buffer */
+       ret = bt_close_memstream(buf, &size, out_fp);
+       /*
+        * See fclose(3). Further access to out_fp after both success
+        * and error, even through another bt_close_memstream(), results
+        * in undefined behavior. Nullify out_fp to ensure we don't
+        * fclose it twice on error.
+        */
+       out_fp = NULL;
+       if (ret < 0) {
+               BT_COMP_LOGE_APPEND_CAUSE_ERRNO(BT_COMP_LOG_SELF_COMP, "Cannot close memory stream", ".");
+               goto error;
+       }
+
+       goto end;
+
+error:
+       ret = -1;
+
+       if (out_fp) {
+               if (bt_close_memstream(buf, &size, out_fp)) {
+                       BT_COMP_LOGE_ERRNO("Cannot close memory stream", ".");
+               }
+       }
+
+       if (*buf) {
+               free(*buf);
+               *buf = NULL;
+       }
+
+end:
+       return ret;
+}
diff --git a/src/plugins/ctf/common/metadata/decoder-packetized-file-stream-to-buf.h b/src/plugins/ctf/common/metadata/decoder-packetized-file-stream-to-buf.h
deleted file mode 100644 (file)
index cc67124..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2019 Efficios Inc.
- */
-
-#ifndef SRC_PLUGINS_CTF_COMMON_METADATA_DECODER_PACKETIZED_FILE_STREAM_TO_BUF
-#define SRC_PLUGINS_CTF_COMMON_METADATA_DECODER_PACKETIZED_FILE_STREAM_TO_BUF
-
-#include <stdbool.h>
-#include <stdint.h>
-
-#include <babeltrace2/babeltrace.h>
-
-BT_HIDDEN
-int ctf_metadata_decoder_packetized_file_stream_to_buf(FILE *fp,
-               char **buf, int byte_order, bool *is_uuid_set,
-               uint8_t *uuid, bt_logging_level log_level,
-               bt_self_component *self_comp,
-               bt_self_component_class *self_comp_class);
-
-#endif /* SRC_PLUGINS_CTF_COMMON_METADATA_DECODER_PACKETIZED_FILE_STREAM_TO_BUF */
diff --git a/src/plugins/ctf/common/metadata/decoder-packetized-file-stream-to-buf.hpp b/src/plugins/ctf/common/metadata/decoder-packetized-file-stream-to-buf.hpp
new file mode 100644 (file)
index 0000000..cc67124
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2019 Efficios Inc.
+ */
+
+#ifndef SRC_PLUGINS_CTF_COMMON_METADATA_DECODER_PACKETIZED_FILE_STREAM_TO_BUF
+#define SRC_PLUGINS_CTF_COMMON_METADATA_DECODER_PACKETIZED_FILE_STREAM_TO_BUF
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <babeltrace2/babeltrace.h>
+
+BT_HIDDEN
+int ctf_metadata_decoder_packetized_file_stream_to_buf(FILE *fp,
+               char **buf, int byte_order, bool *is_uuid_set,
+               uint8_t *uuid, bt_logging_level log_level,
+               bt_self_component *self_comp,
+               bt_self_component_class *self_comp_class);
+
+#endif /* SRC_PLUGINS_CTF_COMMON_METADATA_DECODER_PACKETIZED_FILE_STREAM_TO_BUF */
diff --git a/src/plugins/ctf/common/metadata/decoder.c b/src/plugins/ctf/common/metadata/decoder.c
deleted file mode 100644 (file)
index f2a140b..0000000
+++ /dev/null
@@ -1,478 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2016-2017 Philippe Proulx <pproulx@efficios.com>
- */
-
-#define BT_COMP_LOG_SELF_COMP (mdec->config.self_comp)
-#define BT_COMP_LOG_SELF_COMP_CLASS (mdec->config.self_comp_class)
-#define BT_LOG_OUTPUT_LEVEL (mdec->config.log_level)
-#define BT_LOG_TAG "PLUGIN/CTF/META/DECODER"
-#include "logging/comp-logging.h"
-
-#include <stdio.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <inttypes.h>
-#include "common/assert.h"
-#include "common/uuid.h"
-#include "compat/memstream.h"
-#include <babeltrace2/babeltrace.h>
-#include <glib.h>
-#include <string.h>
-
-#include "ast.h"
-#include "decoder.h"
-#include "scanner.h"
-#include "logging.h"
-#include "parser-wrap.h"
-#include "decoder-packetized-file-stream-to-buf.h"
-
-#define TSDL_MAGIC     0x75d11d57
-
-struct ctf_metadata_decoder {
-       struct ctf_scanner *scanner;
-       GString *text;
-       struct ctf_visitor_generate_ir *visitor;
-       bt_uuid_t uuid;
-       bool is_uuid_set;
-       int bo;
-       struct ctf_metadata_decoder_config config;
-       struct meta_log_config log_cfg;
-};
-
-struct packet_header {
-       uint32_t magic;
-       bt_uuid_t  uuid;
-       uint32_t checksum;
-       uint32_t content_size;
-       uint32_t packet_size;
-       uint8_t  compression_scheme;
-       uint8_t  encryption_scheme;
-       uint8_t  checksum_scheme;
-       uint8_t  major;
-       uint8_t  minor;
-} __attribute__((__packed__));
-
-BT_HIDDEN
-int ctf_metadata_decoder_is_packetized(FILE *fp, bool *is_packetized,
-               int *byte_order, bt_logging_level log_level,
-               bt_self_component *self_comp)
-{
-       uint32_t magic;
-       size_t len;
-       int ret = 0;
-
-       *is_packetized = false;
-       len = fread(&magic, sizeof(magic), 1, fp);
-       if (len != 1) {
-               BT_COMP_LOG_CUR_LVL(BT_LOG_INFO, log_level, self_comp,
-                       "Cannot read first metadata packet header: assuming the stream is not packetized.");
-               ret = -1;
-               goto end;
-       }
-
-       if (byte_order) {
-               if (magic == TSDL_MAGIC) {
-                       *is_packetized = true;
-                       *byte_order = BYTE_ORDER;
-               } else if (magic == GUINT32_SWAP_LE_BE(TSDL_MAGIC)) {
-                       *is_packetized = true;
-                       *byte_order = BYTE_ORDER == BIG_ENDIAN ?
-                               LITTLE_ENDIAN : BIG_ENDIAN;
-               }
-       }
-
-end:
-       rewind(fp);
-
-       return ret;
-}
-
-BT_HIDDEN
-struct ctf_metadata_decoder *ctf_metadata_decoder_create(
-               const struct ctf_metadata_decoder_config *config)
-{
-       struct ctf_metadata_decoder *mdec =
-               g_new0(struct ctf_metadata_decoder, 1);
-
-       BT_ASSERT(config);
-       BT_COMP_LOG_CUR_LVL(BT_LOG_DEBUG, config->log_level, config->self_comp,
-               "Creating CTF metadata decoder: "
-               "clock-class-offset-s=%" PRId64 ", "
-               "clock-class-offset-ns=%" PRId64,
-               config->clock_class_offset_s, config->clock_class_offset_ns);
-
-       if (!mdec) {
-               BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, config->log_level,
-                       config->self_comp,
-                       "Failed to allocate one CTF metadata decoder.");
-               goto end;
-       }
-
-       mdec->log_cfg.log_level = config->log_level;
-       mdec->log_cfg.self_comp = config->self_comp;
-       mdec->log_cfg.self_comp_class = config->self_comp_class;
-       mdec->scanner = ctf_scanner_alloc();
-       if (!mdec->scanner) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot allocate a metadata lexical scanner: "
-                       "mdec-addr=%p", mdec);
-               goto error;
-       }
-
-       mdec->text = g_string_new(NULL);
-       if (!mdec->text) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Failed to allocate one GString: "
-                       "mdec-addr=%p", mdec);
-               goto error;
-       }
-
-       mdec->bo = -1;
-       mdec->config = *config;
-       mdec->visitor = ctf_visitor_generate_ir_create(config);
-       if (!mdec->visitor) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Failed to create a CTF IR metadata AST visitor: "
-                       "mdec-addr=%p", mdec);
-               goto error;
-       }
-
-       BT_COMP_LOGD("Creating CTF metadata decoder: "
-               "clock-class-offset-s=%" PRId64 ", "
-               "clock-class-offset-ns=%" PRId64 ", addr=%p",
-               config->clock_class_offset_s, config->clock_class_offset_ns,
-               mdec);
-       goto end;
-
-error:
-       ctf_metadata_decoder_destroy(mdec);
-       mdec = NULL;
-
-end:
-       return mdec;
-}
-
-BT_HIDDEN
-void ctf_metadata_decoder_destroy(struct ctf_metadata_decoder *mdec)
-{
-       if (!mdec) {
-               return;
-       }
-
-       if (mdec->scanner) {
-               ctf_scanner_free(mdec->scanner);
-       }
-
-       if (mdec->text) {
-               g_string_free(mdec->text, TRUE);
-       }
-
-       BT_COMP_LOGD("Destroying CTF metadata decoder: addr=%p", mdec);
-       ctf_visitor_generate_ir_destroy(mdec->visitor);
-       g_free(mdec);
-}
-
-BT_HIDDEN
-enum ctf_metadata_decoder_status ctf_metadata_decoder_append_content(
-               struct ctf_metadata_decoder *mdec, FILE *fp)
-{
-       enum ctf_metadata_decoder_status status =
-               CTF_METADATA_DECODER_STATUS_OK;
-       int ret;
-       char *buf = NULL;
-       bool close_fp = false;
-       long start_pos = -1;
-       bool is_packetized;
-
-       BT_ASSERT(mdec);
-       ret = ctf_metadata_decoder_is_packetized(fp, &is_packetized, &mdec->bo,
-                       mdec->config.log_level, mdec->config.self_comp);
-       if (ret) {
-               status = CTF_METADATA_DECODER_STATUS_ERROR;
-               goto end;
-       }
-
-       if (is_packetized) {
-               BT_COMP_LOGI("Metadata stream is packetized: mdec-addr=%p", mdec);
-               ret = ctf_metadata_decoder_packetized_file_stream_to_buf(fp,
-                       &buf, mdec->bo, &mdec->is_uuid_set,
-                       mdec->uuid, mdec->config.log_level,
-                       mdec->config.self_comp,
-                       mdec->config.self_comp_class);
-               if (ret) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot decode packetized metadata packets to metadata text: "
-                               "mdec-addr=%p, ret=%d", mdec, ret);
-                       status = CTF_METADATA_DECODER_STATUS_ERROR;
-                       goto end;
-               }
-
-               if (strlen(buf) == 0) {
-                       /* An empty metadata packet is OK. */
-                       goto end;
-               }
-
-               /* Convert the real file pointer to a memory file pointer */
-               fp = bt_fmemopen(buf, strlen(buf), "rb");
-               close_fp = true;
-               if (!fp) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot memory-open metadata buffer: %s: "
-                               "mdec-addr=%p", strerror(errno), mdec);
-                       status = CTF_METADATA_DECODER_STATUS_ERROR;
-                       goto end;
-               }
-       } else {
-               unsigned int major, minor;
-               ssize_t nr_items;
-               const long init_pos = ftell(fp);
-
-               BT_COMP_LOGI("Metadata stream is plain text: mdec-addr=%p", mdec);
-
-               if (init_pos < 0) {
-                       BT_COMP_LOGE_APPEND_CAUSE_ERRNO(BT_COMP_LOG_SELF_COMP,
-                               "Failed to get current file position", ".");
-                       status = CTF_METADATA_DECODER_STATUS_ERROR;
-                       goto end;
-               }
-
-               /* Check text-only metadata header and version */
-               nr_items = fscanf(fp, "/* CTF %10u.%10u", &major, &minor);
-               if (nr_items < 2) {
-                       BT_COMP_LOGW("Missing \"/* CTF major.minor\" signature in plain text metadata file stream: "
-                               "mdec-addr=%p", mdec);
-               }
-
-               BT_COMP_LOGI("Found metadata stream version in signature: version=%u.%u", major, minor);
-
-               if (!ctf_metadata_decoder_is_packet_version_valid(major,
-                               minor)) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid metadata version found in plain text signature: "
-                               "version=%u.%u, mdec-addr=%p", major, minor,
-                               mdec);
-                       status = CTF_METADATA_DECODER_STATUS_INVAL_VERSION;
-                       goto end;
-               }
-
-               if (fseek(fp, init_pos, SEEK_SET)) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot seek metadata file stream to initial position: %s: "
-                               "mdec-addr=%p", strerror(errno), mdec);
-                       status = CTF_METADATA_DECODER_STATUS_ERROR;
-                       goto end;
-               }
-       }
-
-#if YYDEBUG
-       if (BT_LOG_ON_TRACE) {
-               yydebug = 1;
-       }
-#endif
-
-       /* Save the file's position: we'll seek back to append the plain text */
-       BT_ASSERT(fp);
-
-       if (mdec->config.keep_plain_text) {
-               start_pos = ftell(fp);
-       }
-
-       /* Append the metadata text content */
-       ret = ctf_scanner_append_ast(mdec->scanner, fp);
-       if (ret) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot create the metadata AST out of the metadata text: "
-                       "mdec-addr=%p", mdec);
-               status = CTF_METADATA_DECODER_STATUS_INCOMPLETE;
-               goto end;
-       }
-
-       /* We know it's complete: append plain text */
-       if (mdec->config.keep_plain_text) {
-               BT_ASSERT(start_pos != -1);
-               ret = fseek(fp, start_pos, SEEK_SET);
-               if (ret) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Failed to seek file: ret=%d, mdec-addr=%p",
-                               ret, mdec);
-                       status = CTF_METADATA_DECODER_STATUS_ERROR;
-                       goto end;
-               }
-
-               ret = bt_common_append_file_content_to_g_string(mdec->text, fp);
-               if (ret) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Failed to append to current plain text: "
-                               "ret=%d, mdec-addr=%p", ret, mdec);
-                       status = CTF_METADATA_DECODER_STATUS_ERROR;
-                       goto end;
-               }
-       }
-
-       ret = ctf_visitor_semantic_check(0, &mdec->scanner->ast->root,
-               &mdec->log_cfg);
-       if (ret) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Validation of the metadata semantics failed: "
-                       "mdec-addr=%p", mdec);
-               status = CTF_METADATA_DECODER_STATUS_ERROR;
-               goto end;
-       }
-
-       if (mdec->config.create_trace_class) {
-               ret = ctf_visitor_generate_ir_visit_node(mdec->visitor,
-                       &mdec->scanner->ast->root);
-               switch (ret) {
-               case 0:
-                       /* Success */
-                       break;
-               case -EINCOMPLETE:
-                       BT_COMP_LOGD("While visiting metadata AST: incomplete data: "
-                               "mdec-addr=%p", mdec);
-                       status = CTF_METADATA_DECODER_STATUS_INCOMPLETE;
-                       goto end;
-               default:
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Failed to visit AST node to create CTF IR objects: "
-                               "mdec-addr=%p, ret=%d", mdec, ret);
-                       status = CTF_METADATA_DECODER_STATUS_IR_VISITOR_ERROR;
-                       goto end;
-               }
-       }
-
-end:
-#if YYDEBUG
-       yydebug = 0;
-#endif
-
-       if (fp && close_fp) {
-               if (fclose(fp)) {
-                       BT_COMP_LOGE("Cannot close metadata file stream: "
-                               "mdec-addr=%p", mdec);
-               }
-       }
-
-       free(buf);
-
-       return status;
-}
-
-BT_HIDDEN
-bt_trace_class *ctf_metadata_decoder_get_ir_trace_class(
-               struct ctf_metadata_decoder *mdec)
-{
-       BT_ASSERT_DBG(mdec);
-       BT_ASSERT_DBG(mdec->config.create_trace_class);
-       return ctf_visitor_generate_ir_get_ir_trace_class(mdec->visitor);
-}
-
-BT_HIDDEN
-struct ctf_trace_class *ctf_metadata_decoder_borrow_ctf_trace_class(
-               struct ctf_metadata_decoder *mdec)
-{
-       BT_ASSERT_DBG(mdec);
-       BT_ASSERT_DBG(mdec->config.create_trace_class);
-       return ctf_visitor_generate_ir_borrow_ctf_trace_class(mdec->visitor);
-}
-
-BT_HIDDEN
-const char *ctf_metadata_decoder_get_text(struct ctf_metadata_decoder *mdec)
-{
-       BT_ASSERT_DBG(mdec);
-       BT_ASSERT_DBG(mdec->config.keep_plain_text);
-       return mdec->text->str;
-}
-
-BT_HIDDEN
-int ctf_metadata_decoder_get_byte_order(struct ctf_metadata_decoder *mdec)
-{
-       BT_ASSERT_DBG(mdec);
-       return mdec->bo;
-}
-
-BT_HIDDEN
-int ctf_metadata_decoder_get_uuid(struct ctf_metadata_decoder *mdec,
-               bt_uuid_t uuid)
-{
-       int ret = 0;
-
-       BT_ASSERT_DBG(mdec);
-
-       if (!mdec->is_uuid_set) {
-               ret = -1;
-               goto end;
-       }
-
-       bt_uuid_copy(uuid, mdec->uuid);
-
-end:
-       return ret;
-}
-
-static
-enum ctf_metadata_decoder_status find_uuid_in_trace_decl(
-               struct ctf_metadata_decoder *mdec, struct ctf_node *trace_node,
-               bt_uuid_t uuid)
-{
-       enum ctf_metadata_decoder_status status =
-               CTF_METADATA_DECODER_STATUS_OK;
-       struct ctf_node *entry_node;
-       struct bt_list_head *decl_list = &trace_node->u.trace.declaration_list;
-       char *left = NULL;
-
-       bt_list_for_each_entry(entry_node, decl_list, siblings) {
-               if (entry_node->type == NODE_CTF_EXPRESSION) {
-                       int ret;
-
-                       left = ctf_ast_concatenate_unary_strings(
-                               &entry_node->u.ctf_expression.left);
-                       if (!left) {
-                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot concatenate unary strings.");
-                               status = CTF_METADATA_DECODER_STATUS_ERROR;
-                               goto end;
-                       }
-
-                       if (strcmp(left, "uuid") == 0) {
-                               ret = ctf_ast_get_unary_uuid(
-                                       &entry_node->u.ctf_expression.right,
-                                       uuid, mdec->config.log_level,
-                                       mdec->config.self_comp);
-                               if (ret) {
-                                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid trace's `uuid` attribute.");
-                                       status = CTF_METADATA_DECODER_STATUS_ERROR;
-                                       goto end;
-                               }
-
-                               goto end;
-                       }
-
-                       g_free(left);
-                       left = NULL;
-               }
-       }
-
-       status = CTF_METADATA_DECODER_STATUS_NONE;
-
-end:
-       g_free(left);
-       return status;
-}
-
-BT_HIDDEN
-enum ctf_metadata_decoder_status ctf_metadata_decoder_get_trace_class_uuid(
-               struct ctf_metadata_decoder *mdec, bt_uuid_t uuid)
-{
-       enum ctf_metadata_decoder_status status =
-               CTF_METADATA_DECODER_STATUS_INCOMPLETE;
-       struct ctf_node *root_node = &mdec->scanner->ast->root;
-       struct ctf_node *trace_node;
-
-       if (!root_node) {
-               status = CTF_METADATA_DECODER_STATUS_INCOMPLETE;
-               goto end;
-       }
-
-       trace_node =
-               bt_list_entry(root_node->u.root.trace.next,
-                       struct ctf_node, siblings);
-       if (!trace_node) {
-               status = CTF_METADATA_DECODER_STATUS_INCOMPLETE;
-               goto end;
-       }
-
-       status = find_uuid_in_trace_decl(mdec, trace_node, uuid);
-
-end:
-       return status;
-}
diff --git a/src/plugins/ctf/common/metadata/decoder.cpp b/src/plugins/ctf/common/metadata/decoder.cpp
new file mode 100644 (file)
index 0000000..31019dd
--- /dev/null
@@ -0,0 +1,478 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2016-2017 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#define BT_COMP_LOG_SELF_COMP (mdec->config.self_comp)
+#define BT_COMP_LOG_SELF_COMP_CLASS (mdec->config.self_comp_class)
+#define BT_LOG_OUTPUT_LEVEL (mdec->config.log_level)
+#define BT_LOG_TAG "PLUGIN/CTF/META/DECODER"
+#include "logging/comp-logging.h"
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include "common/assert.h"
+#include "common/uuid.h"
+#include "compat/memstream.h"
+#include <babeltrace2/babeltrace.h>
+#include <glib.h>
+#include <string.h>
+
+#include "ast.hpp"
+#include "decoder.hpp"
+#include "scanner.hpp"
+#include "logging.hpp"
+#include "parser-wrap.hpp"
+#include "decoder-packetized-file-stream-to-buf.hpp"
+
+#define TSDL_MAGIC     0x75d11d57
+
+struct ctf_metadata_decoder {
+       struct ctf_scanner *scanner;
+       GString *text;
+       struct ctf_visitor_generate_ir *visitor;
+       bt_uuid_t uuid;
+       bool is_uuid_set;
+       int bo;
+       struct ctf_metadata_decoder_config config;
+       struct meta_log_config log_cfg;
+};
+
+struct packet_header {
+       uint32_t magic;
+       bt_uuid_t  uuid;
+       uint32_t checksum;
+       uint32_t content_size;
+       uint32_t packet_size;
+       uint8_t  compression_scheme;
+       uint8_t  encryption_scheme;
+       uint8_t  checksum_scheme;
+       uint8_t  major;
+       uint8_t  minor;
+} __attribute__((__packed__));
+
+BT_HIDDEN
+int ctf_metadata_decoder_is_packetized(FILE *fp, bool *is_packetized,
+               int *byte_order, bt_logging_level log_level,
+               bt_self_component *self_comp)
+{
+       uint32_t magic;
+       size_t len;
+       int ret = 0;
+
+       *is_packetized = false;
+       len = fread(&magic, sizeof(magic), 1, fp);
+       if (len != 1) {
+               BT_COMP_LOG_CUR_LVL(BT_LOG_INFO, log_level, self_comp,
+                       "Cannot read first metadata packet header: assuming the stream is not packetized.");
+               ret = -1;
+               goto end;
+       }
+
+       if (byte_order) {
+               if (magic == TSDL_MAGIC) {
+                       *is_packetized = true;
+                       *byte_order = BYTE_ORDER;
+               } else if (magic == GUINT32_SWAP_LE_BE(TSDL_MAGIC)) {
+                       *is_packetized = true;
+                       *byte_order = BYTE_ORDER == BIG_ENDIAN ?
+                               LITTLE_ENDIAN : BIG_ENDIAN;
+               }
+       }
+
+end:
+       rewind(fp);
+
+       return ret;
+}
+
+BT_HIDDEN
+struct ctf_metadata_decoder *ctf_metadata_decoder_create(
+               const struct ctf_metadata_decoder_config *config)
+{
+       struct ctf_metadata_decoder *mdec =
+               g_new0(struct ctf_metadata_decoder, 1);
+
+       BT_ASSERT(config);
+       BT_COMP_LOG_CUR_LVL(BT_LOG_DEBUG, config->log_level, config->self_comp,
+               "Creating CTF metadata decoder: "
+               "clock-class-offset-s=%" PRId64 ", "
+               "clock-class-offset-ns=%" PRId64,
+               config->clock_class_offset_s, config->clock_class_offset_ns);
+
+       if (!mdec) {
+               BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, config->log_level,
+                       config->self_comp,
+                       "Failed to allocate one CTF metadata decoder.");
+               goto end;
+       }
+
+       mdec->log_cfg.log_level = config->log_level;
+       mdec->log_cfg.self_comp = config->self_comp;
+       mdec->log_cfg.self_comp_class = config->self_comp_class;
+       mdec->scanner = ctf_scanner_alloc();
+       if (!mdec->scanner) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot allocate a metadata lexical scanner: "
+                       "mdec-addr=%p", mdec);
+               goto error;
+       }
+
+       mdec->text = g_string_new(NULL);
+       if (!mdec->text) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Failed to allocate one GString: "
+                       "mdec-addr=%p", mdec);
+               goto error;
+       }
+
+       mdec->bo = -1;
+       mdec->config = *config;
+       mdec->visitor = ctf_visitor_generate_ir_create(config);
+       if (!mdec->visitor) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Failed to create a CTF IR metadata AST visitor: "
+                       "mdec-addr=%p", mdec);
+               goto error;
+       }
+
+       BT_COMP_LOGD("Creating CTF metadata decoder: "
+               "clock-class-offset-s=%" PRId64 ", "
+               "clock-class-offset-ns=%" PRId64 ", addr=%p",
+               config->clock_class_offset_s, config->clock_class_offset_ns,
+               mdec);
+       goto end;
+
+error:
+       ctf_metadata_decoder_destroy(mdec);
+       mdec = NULL;
+
+end:
+       return mdec;
+}
+
+BT_HIDDEN
+void ctf_metadata_decoder_destroy(struct ctf_metadata_decoder *mdec)
+{
+       if (!mdec) {
+               return;
+       }
+
+       if (mdec->scanner) {
+               ctf_scanner_free(mdec->scanner);
+       }
+
+       if (mdec->text) {
+               g_string_free(mdec->text, TRUE);
+       }
+
+       BT_COMP_LOGD("Destroying CTF metadata decoder: addr=%p", mdec);
+       ctf_visitor_generate_ir_destroy(mdec->visitor);
+       g_free(mdec);
+}
+
+BT_HIDDEN
+enum ctf_metadata_decoder_status ctf_metadata_decoder_append_content(
+               struct ctf_metadata_decoder *mdec, FILE *fp)
+{
+       enum ctf_metadata_decoder_status status =
+               CTF_METADATA_DECODER_STATUS_OK;
+       int ret;
+       char *buf = NULL;
+       bool close_fp = false;
+       long start_pos = -1;
+       bool is_packetized;
+
+       BT_ASSERT(mdec);
+       ret = ctf_metadata_decoder_is_packetized(fp, &is_packetized, &mdec->bo,
+                       mdec->config.log_level, mdec->config.self_comp);
+       if (ret) {
+               status = CTF_METADATA_DECODER_STATUS_ERROR;
+               goto end;
+       }
+
+       if (is_packetized) {
+               BT_COMP_LOGI("Metadata stream is packetized: mdec-addr=%p", mdec);
+               ret = ctf_metadata_decoder_packetized_file_stream_to_buf(fp,
+                       &buf, mdec->bo, &mdec->is_uuid_set,
+                       mdec->uuid, mdec->config.log_level,
+                       mdec->config.self_comp,
+                       mdec->config.self_comp_class);
+               if (ret) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot decode packetized metadata packets to metadata text: "
+                               "mdec-addr=%p, ret=%d", mdec, ret);
+                       status = CTF_METADATA_DECODER_STATUS_ERROR;
+                       goto end;
+               }
+
+               if (strlen(buf) == 0) {
+                       /* An empty metadata packet is OK. */
+                       goto end;
+               }
+
+               /* Convert the real file pointer to a memory file pointer */
+               fp = bt_fmemopen(buf, strlen(buf), "rb");
+               close_fp = true;
+               if (!fp) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot memory-open metadata buffer: %s: "
+                               "mdec-addr=%p", strerror(errno), mdec);
+                       status = CTF_METADATA_DECODER_STATUS_ERROR;
+                       goto end;
+               }
+       } else {
+               unsigned int major, minor;
+               ssize_t nr_items;
+               const long init_pos = ftell(fp);
+
+               BT_COMP_LOGI("Metadata stream is plain text: mdec-addr=%p", mdec);
+
+               if (init_pos < 0) {
+                       BT_COMP_LOGE_APPEND_CAUSE_ERRNO(BT_COMP_LOG_SELF_COMP,
+                               "Failed to get current file position", ".");
+                       status = CTF_METADATA_DECODER_STATUS_ERROR;
+                       goto end;
+               }
+
+               /* Check text-only metadata header and version */
+               nr_items = fscanf(fp, "/* CTF %10u.%10u", &major, &minor);
+               if (nr_items < 2) {
+                       BT_COMP_LOGW("Missing \"/* CTF major.minor\" signature in plain text metadata file stream: "
+                               "mdec-addr=%p", mdec);
+               }
+
+               BT_COMP_LOGI("Found metadata stream version in signature: version=%u.%u", major, minor);
+
+               if (!ctf_metadata_decoder_is_packet_version_valid(major,
+                               minor)) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid metadata version found in plain text signature: "
+                               "version=%u.%u, mdec-addr=%p", major, minor,
+                               mdec);
+                       status = CTF_METADATA_DECODER_STATUS_INVAL_VERSION;
+                       goto end;
+               }
+
+               if (fseek(fp, init_pos, SEEK_SET)) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot seek metadata file stream to initial position: %s: "
+                               "mdec-addr=%p", strerror(errno), mdec);
+                       status = CTF_METADATA_DECODER_STATUS_ERROR;
+                       goto end;
+               }
+       }
+
+#if YYDEBUG
+       if (BT_LOG_ON_TRACE) {
+               yydebug = 1;
+       }
+#endif
+
+       /* Save the file's position: we'll seek back to append the plain text */
+       BT_ASSERT(fp);
+
+       if (mdec->config.keep_plain_text) {
+               start_pos = ftell(fp);
+       }
+
+       /* Append the metadata text content */
+       ret = ctf_scanner_append_ast(mdec->scanner, fp);
+       if (ret) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot create the metadata AST out of the metadata text: "
+                       "mdec-addr=%p", mdec);
+               status = CTF_METADATA_DECODER_STATUS_INCOMPLETE;
+               goto end;
+       }
+
+       /* We know it's complete: append plain text */
+       if (mdec->config.keep_plain_text) {
+               BT_ASSERT(start_pos != -1);
+               ret = fseek(fp, start_pos, SEEK_SET);
+               if (ret) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Failed to seek file: ret=%d, mdec-addr=%p",
+                               ret, mdec);
+                       status = CTF_METADATA_DECODER_STATUS_ERROR;
+                       goto end;
+               }
+
+               ret = bt_common_append_file_content_to_g_string(mdec->text, fp);
+               if (ret) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Failed to append to current plain text: "
+                               "ret=%d, mdec-addr=%p", ret, mdec);
+                       status = CTF_METADATA_DECODER_STATUS_ERROR;
+                       goto end;
+               }
+       }
+
+       ret = ctf_visitor_semantic_check(0, &mdec->scanner->ast->root,
+               &mdec->log_cfg);
+       if (ret) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Validation of the metadata semantics failed: "
+                       "mdec-addr=%p", mdec);
+               status = CTF_METADATA_DECODER_STATUS_ERROR;
+               goto end;
+       }
+
+       if (mdec->config.create_trace_class) {
+               ret = ctf_visitor_generate_ir_visit_node(mdec->visitor,
+                       &mdec->scanner->ast->root);
+               switch (ret) {
+               case 0:
+                       /* Success */
+                       break;
+               case -EINCOMPLETE:
+                       BT_COMP_LOGD("While visiting metadata AST: incomplete data: "
+                               "mdec-addr=%p", mdec);
+                       status = CTF_METADATA_DECODER_STATUS_INCOMPLETE;
+                       goto end;
+               default:
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Failed to visit AST node to create CTF IR objects: "
+                               "mdec-addr=%p, ret=%d", mdec, ret);
+                       status = CTF_METADATA_DECODER_STATUS_IR_VISITOR_ERROR;
+                       goto end;
+               }
+       }
+
+end:
+#if YYDEBUG
+       yydebug = 0;
+#endif
+
+       if (fp && close_fp) {
+               if (fclose(fp)) {
+                       BT_COMP_LOGE("Cannot close metadata file stream: "
+                               "mdec-addr=%p", mdec);
+               }
+       }
+
+       free(buf);
+
+       return status;
+}
+
+BT_HIDDEN
+bt_trace_class *ctf_metadata_decoder_get_ir_trace_class(
+               struct ctf_metadata_decoder *mdec)
+{
+       BT_ASSERT_DBG(mdec);
+       BT_ASSERT_DBG(mdec->config.create_trace_class);
+       return ctf_visitor_generate_ir_get_ir_trace_class(mdec->visitor);
+}
+
+BT_HIDDEN
+struct ctf_trace_class *ctf_metadata_decoder_borrow_ctf_trace_class(
+               struct ctf_metadata_decoder *mdec)
+{
+       BT_ASSERT_DBG(mdec);
+       BT_ASSERT_DBG(mdec->config.create_trace_class);
+       return ctf_visitor_generate_ir_borrow_ctf_trace_class(mdec->visitor);
+}
+
+BT_HIDDEN
+const char *ctf_metadata_decoder_get_text(struct ctf_metadata_decoder *mdec)
+{
+       BT_ASSERT_DBG(mdec);
+       BT_ASSERT_DBG(mdec->config.keep_plain_text);
+       return mdec->text->str;
+}
+
+BT_HIDDEN
+int ctf_metadata_decoder_get_byte_order(struct ctf_metadata_decoder *mdec)
+{
+       BT_ASSERT_DBG(mdec);
+       return mdec->bo;
+}
+
+BT_HIDDEN
+int ctf_metadata_decoder_get_uuid(struct ctf_metadata_decoder *mdec,
+               bt_uuid_t uuid)
+{
+       int ret = 0;
+
+       BT_ASSERT_DBG(mdec);
+
+       if (!mdec->is_uuid_set) {
+               ret = -1;
+               goto end;
+       }
+
+       bt_uuid_copy(uuid, mdec->uuid);
+
+end:
+       return ret;
+}
+
+static
+enum ctf_metadata_decoder_status find_uuid_in_trace_decl(
+               struct ctf_metadata_decoder *mdec, struct ctf_node *trace_node,
+               bt_uuid_t uuid)
+{
+       enum ctf_metadata_decoder_status status =
+               CTF_METADATA_DECODER_STATUS_OK;
+       struct ctf_node *entry_node;
+       struct bt_list_head *decl_list = &trace_node->u.trace.declaration_list;
+       char *left = NULL;
+
+       bt_list_for_each_entry(entry_node, decl_list, siblings) {
+               if (entry_node->type == NODE_CTF_EXPRESSION) {
+                       int ret;
+
+                       left = ctf_ast_concatenate_unary_strings(
+                               &entry_node->u.ctf_expression.left);
+                       if (!left) {
+                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot concatenate unary strings.");
+                               status = CTF_METADATA_DECODER_STATUS_ERROR;
+                               goto end;
+                       }
+
+                       if (strcmp(left, "uuid") == 0) {
+                               ret = ctf_ast_get_unary_uuid(
+                                       &entry_node->u.ctf_expression.right,
+                                       uuid, mdec->config.log_level,
+                                       mdec->config.self_comp);
+                               if (ret) {
+                                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Invalid trace's `uuid` attribute.");
+                                       status = CTF_METADATA_DECODER_STATUS_ERROR;
+                                       goto end;
+                               }
+
+                               goto end;
+                       }
+
+                       g_free(left);
+                       left = NULL;
+               }
+       }
+
+       status = CTF_METADATA_DECODER_STATUS_NONE;
+
+end:
+       g_free(left);
+       return status;
+}
+
+BT_HIDDEN
+enum ctf_metadata_decoder_status ctf_metadata_decoder_get_trace_class_uuid(
+               struct ctf_metadata_decoder *mdec, bt_uuid_t uuid)
+{
+       enum ctf_metadata_decoder_status status =
+               CTF_METADATA_DECODER_STATUS_INCOMPLETE;
+       struct ctf_node *root_node = &mdec->scanner->ast->root;
+       struct ctf_node *trace_node;
+
+       if (!root_node) {
+               status = CTF_METADATA_DECODER_STATUS_INCOMPLETE;
+               goto end;
+       }
+
+       trace_node =
+               bt_list_entry(root_node->u.root.trace.next,
+                       struct ctf_node, siblings);
+       if (!trace_node) {
+               status = CTF_METADATA_DECODER_STATUS_INCOMPLETE;
+               goto end;
+       }
+
+       status = find_uuid_in_trace_decl(mdec, trace_node, uuid);
+
+end:
+       return status;
+}
diff --git a/src/plugins/ctf/common/metadata/decoder.h b/src/plugins/ctf/common/metadata/decoder.h
deleted file mode 100644 (file)
index 7c5a8b7..0000000
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2016-2017 Philippe Proulx <pproulx@efficios.com>
- */
-
-#ifndef _METADATA_DECODER_H
-#define _METADATA_DECODER_H
-
-#include <stdint.h>
-#include <stdbool.h>
-
-#include <babeltrace2/babeltrace.h>
-
-#include "common/macros.h"
-#include "common/uuid.h"
-
-struct ctf_trace_class;
-
-/* A CTF metadata decoder object */
-struct ctf_metadata_decoder;
-
-/* CTF metadata decoder status */
-enum ctf_metadata_decoder_status {
-       CTF_METADATA_DECODER_STATUS_OK                  = 0,
-       CTF_METADATA_DECODER_STATUS_NONE                = 1,
-       CTF_METADATA_DECODER_STATUS_ERROR               = -1,
-       CTF_METADATA_DECODER_STATUS_INCOMPLETE          = -2,
-       CTF_METADATA_DECODER_STATUS_INVAL_VERSION       = -3,
-       CTF_METADATA_DECODER_STATUS_IR_VISITOR_ERROR    = -4,
-};
-
-/* Decoding configuration */
-struct ctf_metadata_decoder_config {
-       /* Active log level to use */
-       bt_logging_level log_level;
-
-       /*
-        * Component or component class to use for logging (exactly one of
-        * them must be non-`NULL`); weak
-        */
-       bt_self_component *self_comp;
-       bt_self_component_class *self_comp_class;
-
-       /* Additional clock class offset to apply */
-       int64_t clock_class_offset_s;
-       int64_t clock_class_offset_ns;
-       bool force_clock_class_origin_unix_epoch;
-
-       /* True to create trace class objects */
-       bool create_trace_class;
-
-       /*
-        * True to keep the plain text when content is appended with
-        * ctf_metadata_decoder_append_content().
-        */
-       bool keep_plain_text;
-};
-
-/*
- * Creates a CTF metadata decoder.
- *
- * Returns `NULL` on error.
- */
-BT_HIDDEN
-struct ctf_metadata_decoder *ctf_metadata_decoder_create(
-               const struct ctf_metadata_decoder_config *config);
-
-/*
- * Destroys a CTF metadata decoder that you created with
- * ctf_metadata_decoder_create().
- */
-BT_HIDDEN
-void ctf_metadata_decoder_destroy(
-               struct ctf_metadata_decoder *metadata_decoder);
-
-/*
- * Appends content to the metadata decoder.
- *
- * This function reads the metadata from the current position of `fp`
- * until the end of this file stream.
- *
- * The metadata can be packetized or not.
- *
- * The metadata chunk needs to be complete and lexically scannable, that
- * is, zero or more complete top-level blocks. If it's incomplete, this
- * function returns `CTF_METADATA_DECODER_STATUS_INCOMPLETE`. If this
- * function returns `CTF_METADATA_DECODER_STATUS_INCOMPLETE`, then you
- * need to call it again with the _same_ metadata and more to make it
- * complete. For example:
- *
- *     First call:  event { name = hell
- *     Second call: event { name = hello_world; ... };
- *
- * If everything goes as expected, this function returns
- * `CTF_METADATA_DECODER_STATUS_OK`.
- */
-BT_HIDDEN
-enum ctf_metadata_decoder_status ctf_metadata_decoder_append_content(
-               struct ctf_metadata_decoder *metadata_decoder, FILE *fp);
-
-/*
- * Returns the trace IR trace class of this metadata decoder (new
- * reference).
- *
- * Returns `NULL` if there's none yet or if the metadata decoder is not
- * configured to create trace classes.
- */
-BT_HIDDEN
-bt_trace_class *ctf_metadata_decoder_get_ir_trace_class(
-               struct ctf_metadata_decoder *mdec);
-
-/*
- * Returns the CTF IR trace class of this metadata decoder.
- *
- * Returns `NULL` if there's none yet or if the metadata decoder is not
- * configured to create trace classes.
- */
-BT_HIDDEN
-struct ctf_trace_class *ctf_metadata_decoder_borrow_ctf_trace_class(
-               struct ctf_metadata_decoder *mdec);
-
-/*
- * Checks whether or not a given metadata file stream `fp` is
- * packetized, setting `is_packetized` accordingly on success. On
- * success, also sets `*byte_order` to the byte order of the first
- * packet.
- *
- * This function uses `log_level` and `self_comp` for logging purposes.
- * `self_comp` can be `NULL` if not available.
- */
-BT_HIDDEN
-int ctf_metadata_decoder_is_packetized(FILE *fp, bool *is_packetized,
-               int *byte_order, bt_logging_level log_level,
-               bt_self_component *self_comp);
-
-/*
- * Returns the byte order of the decoder's metadata stream as set by the
- * last call to ctf_metadata_decoder_append_content().
- *
- * Returns -1 if unknown (plain text content).
- */
-BT_HIDDEN
-int ctf_metadata_decoder_get_byte_order(struct ctf_metadata_decoder *mdec);
-
-/*
- * Returns the UUID of the decoder's metadata stream as set by the last
- * call to ctf_metadata_decoder_append_content().
- */
-BT_HIDDEN
-int ctf_metadata_decoder_get_uuid(
-               struct ctf_metadata_decoder *mdec, bt_uuid_t uuid);
-
-/*
- * Returns the UUID of the decoder's trace class, if available.
- *
- * Returns:
- *
- * * `CTF_METADATA_DECODER_STATUS_OK`: success.
- * * `CTF_METADATA_DECODER_STATUS_NONE`: no UUID.
- * * `CTF_METADATA_DECODER_STATUS_INCOMPLETE`: missing metadata content.
- */
-BT_HIDDEN
-enum ctf_metadata_decoder_status ctf_metadata_decoder_get_trace_class_uuid(
-               struct ctf_metadata_decoder *mdec, bt_uuid_t uuid);
-
-/*
- * Returns the metadata decoder's current metadata text.
- */
-BT_HIDDEN
-const char *ctf_metadata_decoder_get_text(struct ctf_metadata_decoder *mdec);
-
-static inline
-bool ctf_metadata_decoder_is_packet_version_valid(unsigned int major,
-               unsigned int minor)
-{
-       return major == 1 && minor == 8;
-}
-
-#endif /* _METADATA_DECODER_H */
diff --git a/src/plugins/ctf/common/metadata/decoder.hpp b/src/plugins/ctf/common/metadata/decoder.hpp
new file mode 100644 (file)
index 0000000..d8803d8
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2016-2017 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#ifndef _METADATA_DECODER_H
+#define _METADATA_DECODER_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#include <babeltrace2/babeltrace.h>
+
+#include "common/macros.h"
+#include "common/uuid.h"
+
+struct ctf_trace_class;
+
+/* A CTF metadata decoder object */
+struct ctf_metadata_decoder;
+
+/* CTF metadata decoder status */
+enum ctf_metadata_decoder_status {
+       CTF_METADATA_DECODER_STATUS_OK                  = 0,
+       CTF_METADATA_DECODER_STATUS_NONE                = 1,
+       CTF_METADATA_DECODER_STATUS_ERROR               = -1,
+       CTF_METADATA_DECODER_STATUS_INCOMPLETE          = -2,
+       CTF_METADATA_DECODER_STATUS_INVAL_VERSION       = -3,
+       CTF_METADATA_DECODER_STATUS_IR_VISITOR_ERROR    = -4,
+};
+
+/* Decoding configuration */
+struct ctf_metadata_decoder_config {
+       /* Active log level to use */
+       bt_logging_level log_level;
+
+       /*
+        * Component or component class to use for logging (exactly one of
+        * them must be non-`NULL`); weak
+        */
+       bt_self_component *self_comp;
+       bt_self_component_class *self_comp_class;
+
+       /* Additional clock class offset to apply */
+       int64_t clock_class_offset_s;
+       int64_t clock_class_offset_ns;
+       bool force_clock_class_origin_unix_epoch;
+
+       /* True to create trace class objects */
+       bool create_trace_class;
+
+       /*
+        * True to keep the plain text when content is appended with
+        * ctf_metadata_decoder_append_content().
+        */
+       bool keep_plain_text;
+};
+
+/*
+ * Creates a CTF metadata decoder.
+ *
+ * Returns `NULL` on error.
+ */
+BT_HIDDEN
+struct ctf_metadata_decoder *ctf_metadata_decoder_create(
+               const struct ctf_metadata_decoder_config *config);
+
+/*
+ * Destroys a CTF metadata decoder that you created with
+ * ctf_metadata_decoder_create().
+ */
+BT_HIDDEN
+void ctf_metadata_decoder_destroy(
+               struct ctf_metadata_decoder *metadata_decoder);
+
+/*
+ * Appends content to the metadata decoder.
+ *
+ * This function reads the metadata from the current position of `fp`
+ * until the end of this file stream.
+ *
+ * The metadata can be packetized or not.
+ *
+ * The metadata chunk needs to be complete and lexically scannable, that
+ * is, zero or more complete top-level blocks. If it's incomplete, this
+ * function returns `CTF_METADATA_DECODER_STATUS_INCOMPLETE`. If this
+ * function returns `CTF_METADATA_DECODER_STATUS_INCOMPLETE`, then you
+ * need to call it again with the _same_ metadata and more to make it
+ * complete. For example:
+ *
+ *     First call:  event { name = hell
+ *     Second call: event { name = hello_world; ... };
+ *
+ * If everything goes as expected, this function returns
+ * `CTF_METADATA_DECODER_STATUS_OK`.
+ */
+BT_HIDDEN
+enum ctf_metadata_decoder_status ctf_metadata_decoder_append_content(
+               struct ctf_metadata_decoder *metadata_decoder, FILE *fp);
+
+/*
+ * Returns the trace IR trace class of this metadata decoder (new
+ * reference).
+ *
+ * Returns `NULL` if there's none yet or if the metadata decoder is not
+ * configured to create trace classes.
+ */
+BT_HIDDEN
+bt_trace_class *ctf_metadata_decoder_get_ir_trace_class(
+               struct ctf_metadata_decoder *mdec);
+
+/*
+ * Returns the CTF IR trace class of this metadata decoder.
+ *
+ * Returns `NULL` if there's none yet or if the metadata decoder is not
+ * configured to create trace classes.
+ */
+BT_HIDDEN
+struct ctf_trace_class *ctf_metadata_decoder_borrow_ctf_trace_class(
+               struct ctf_metadata_decoder *mdec);
+
+/*
+ * Checks whether or not a given metadata file stream `fp` is
+ * packetized, setting `is_packetized` accordingly on success. On
+ * success, also sets `*byte_order` to the byte order of the first
+ * packet.
+ *
+ * This function uses `log_level` and `self_comp` for logging purposes.
+ * `self_comp` can be `NULL` if not available.
+ */
+BT_HIDDEN
+int ctf_metadata_decoder_is_packetized(FILE *fp, bool *is_packetized,
+               int *byte_order, bt_logging_level log_level,
+               bt_self_component *self_comp);
+
+/*
+ * Returns the byte order of the decoder's metadata stream as set by the
+ * last call to ctf_metadata_decoder_append_content().
+ *
+ * Returns -1 if unknown (plain text content).
+ */
+BT_HIDDEN
+int ctf_metadata_decoder_get_byte_order(struct ctf_metadata_decoder *mdec);
+
+/*
+ * Returns the UUID of the decoder's metadata stream as set by the last
+ * call to ctf_metadata_decoder_append_content().
+ */
+BT_HIDDEN
+int ctf_metadata_decoder_get_uuid(
+               struct ctf_metadata_decoder *mdec, bt_uuid_t uuid);
+
+/*
+ * Returns the UUID of the decoder's trace class, if available.
+ *
+ * Returns:
+ *
+ * * `CTF_METADATA_DECODER_STATUS_OK`: success.
+ * * `CTF_METADATA_DECODER_STATUS_NONE`: no UUID.
+ * * `CTF_METADATA_DECODER_STATUS_INCOMPLETE`: missing metadata content.
+ */
+BT_HIDDEN
+enum ctf_metadata_decoder_status ctf_metadata_decoder_get_trace_class_uuid(
+               struct ctf_metadata_decoder *mdec, bt_uuid_t uuid);
+
+/*
+ * Returns the metadata decoder's current metadata text.
+ */
+BT_HIDDEN
+const char *ctf_metadata_decoder_get_text(struct ctf_metadata_decoder *mdec);
+
+static inline
+bool ctf_metadata_decoder_is_packet_version_valid(unsigned int major,
+               unsigned int minor)
+{
+       return major == 1 && minor == 8;
+}
+
+#endif /* _METADATA_DECODER_H */
diff --git a/src/plugins/ctf/common/metadata/lexer.l b/src/plugins/ctf/common/metadata/lexer.l
deleted file mode 100644 (file)
index 849d016..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-%{
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2010 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * Common Trace Formal Lexer
- */
-
-#define BT_LOG_OUTPUT_LEVEL ctf_plugin_metadata_log_level
-#define BT_LOG_TAG "PLUGIN-CTF-METADATA-LEXER"
-#include "logging.h"
-
-#include <stdio.h>
-#include <ctype.h>
-#include "scanner.h"
-#include "parser-wrap.h"
-#include "ast.h"
-
-#define YY_FATAL_ERROR(_msg)   BT_LOGF_STR(_msg)
-
-#define PARSE_INTEGER_LITERAL(base)                                    \
-       do {                                                            \
-               errno = 0;                                              \
-               yylval->ull = strtoull(yytext, NULL, base);             \
-               if (errno) {                                            \
-                       _BT_LOGE_APPEND_CAUSE_LINENO(yylineno,                  \
-                               "Cannot parser constant integer: "      \
-                               "base=%d, text=\"%s\"", base, yytext);  \
-                       return CTF_ERROR;                               \
-               }                                                       \
-       } while (0)
-%}
-
-%x comment_ml comment_sl string_lit char_const
-%option reentrant yylineno noyywrap bison-bridge
-%option extra-type="struct ctf_scanner *"
-       /* bison-locations */
-INTEGER_SUFFIX                 (U|UL|ULL|LU|LLU|Ul|Ull|lU|llU|u|uL|uLL|Lu|LLu|ul|ull|lu|llu)
-DIGIT                          [0-9]
-NONDIGIT                       [a-zA-Z_]
-HEXDIGIT                       [0-9A-Fa-f]
-OCTALDIGIT                     [0-7]
-UCHARLOWERCASE                 \\u{HEXDIGIT}{4}
-UCHARUPPERCASE                 \\U{HEXDIGIT}{8}
-ID_NONDIGIT                    {NONDIGIT}|{UCHARLOWERCASE}|{UCHARUPPERCASE}
-IDENTIFIER                     {ID_NONDIGIT}({ID_NONDIGIT}|{DIGIT})*
-%%
-
-                               /*
-                                * Using start conditions to deal with comments
-                                * and strings.
-                                */
-
-"/*"                           BEGIN(comment_ml);
-<comment_ml>[^*\n]*            /* eat anything that's not a '*' */
-<comment_ml>"*"+[^*/\n]*       /* eat up '*'s not followed by '/'s */
-<comment_ml>\n
-<comment_ml>"*"+"/"            BEGIN(INITIAL);
-
-"//"[^\n]*\n                   /* skip comment */
-
-L?\"(\\.|[^\\"])*\"            { if (import_string(yyextra, yylval, yytext, '\"') < 0) return CTF_ERROR; else return CTF_STRING_LITERAL; }
-L?\'(\\.|[^\\'])*\'            { if (import_string(yyextra, yylval, yytext, '\'') < 0) return CTF_ERROR; else return CTF_CHARACTER_LITERAL; }
-
-"["                            return CTF_LSBRAC;
-"]"                            return CTF_RSBRAC;
-"("                            return CTF_LPAREN;
-")"                            return CTF_RPAREN;
-"{"                            return CTF_LBRAC;
-"}"                            return CTF_RBRAC;
-"->"                           return CTF_RARROW;
-"*"                            return CTF_STAR;
-"+"                            return CTF_PLUS;
-"-"                            return CTF_MINUS;
-"<"                            return CTF_LT;
-">"                            return CTF_GT;
-:=                             return CTF_TYPEASSIGN;
-:                              return CTF_COLON;
-;                              return CTF_SEMICOLON;
-"..."                          return CTF_DOTDOTDOT;
-"."                            return CTF_DOT;
-=                              return CTF_EQUAL;
-","                            return CTF_COMMA;
-align                          setstring(yyextra, yylval, yytext); return CTF_TOK_ALIGN;
-const                          setstring(yyextra, yylval, yytext); return CTF_CONST;
-char                           setstring(yyextra, yylval, yytext); return CTF_CHAR;
-clock                          setstring(yyextra, yylval, yytext); return CTF_CLOCK;
-double                         setstring(yyextra, yylval, yytext); return CTF_DOUBLE;
-enum                           setstring(yyextra, yylval, yytext); return CTF_ENUM;
-env                            setstring(yyextra, yylval, yytext); return CTF_ENV;
-event                          setstring(yyextra, yylval, yytext); return CTF_EVENT;
-floating_point                 setstring(yyextra, yylval, yytext); return CTF_FLOATING_POINT;
-float                          setstring(yyextra, yylval, yytext); return CTF_FLOAT;
-integer                                setstring(yyextra, yylval, yytext); return CTF_INTEGER;
-int                            setstring(yyextra, yylval, yytext); return CTF_INT;
-long                           setstring(yyextra, yylval, yytext); return CTF_LONG;
-short                          setstring(yyextra, yylval, yytext); return CTF_SHORT;
-signed                         setstring(yyextra, yylval, yytext); return CTF_SIGNED;
-stream                         setstring(yyextra, yylval, yytext); return CTF_STREAM;
-string                         setstring(yyextra, yylval, yytext); return CTF_STRING;
-struct                         setstring(yyextra, yylval, yytext); return CTF_STRUCT;
-trace                          setstring(yyextra, yylval, yytext); return CTF_TRACE;
-callsite                       setstring(yyextra, yylval, yytext); return CTF_CALLSITE;
-typealias                      setstring(yyextra, yylval, yytext); return CTF_TYPEALIAS;
-typedef                                setstring(yyextra, yylval, yytext); return CTF_TYPEDEF;
-unsigned                       setstring(yyextra, yylval, yytext); return CTF_UNSIGNED;
-variant                                setstring(yyextra, yylval, yytext); return CTF_VARIANT;
-void                           setstring(yyextra, yylval, yytext); return CTF_VOID;
-_Bool                          setstring(yyextra, yylval, yytext); return CTF_BOOL;
-_Complex                       setstring(yyextra, yylval, yytext); return CTF_COMPLEX;
-_Imaginary                     setstring(yyextra, yylval, yytext); return CTF_IMAGINARY;
-[1-9]{DIGIT}*{INTEGER_SUFFIX}? PARSE_INTEGER_LITERAL(10); return CTF_INTEGER_LITERAL;
-0{OCTALDIGIT}*{INTEGER_SUFFIX}?        PARSE_INTEGER_LITERAL(8); return CTF_INTEGER_LITERAL;
-0[xX]{HEXDIGIT}+{INTEGER_SUFFIX}?      PARSE_INTEGER_LITERAL(16); return CTF_INTEGER_LITERAL;
-
-{IDENTIFIER}                   BT_LOGT("Got identifier: id=\"%s\"", yytext); setstring(yyextra, yylval, yytext); if (is_type(yyextra, yytext)) return ID_TYPE; else return IDENTIFIER;
-[ \t\r\n]                      ; /* ignore */
-.                              _BT_LOGE_APPEND_CAUSE_LINENO(yylineno, "Invalid character: char=\"%c\", val=0x%02x", isprint((unsigned char) yytext[0]) ? yytext[0] : '\0', yytext[0]); return CTF_ERROR;
-%%
diff --git a/src/plugins/ctf/common/metadata/lexer.lpp b/src/plugins/ctf/common/metadata/lexer.lpp
new file mode 100644 (file)
index 0000000..27fea43
--- /dev/null
@@ -0,0 +1,120 @@
+%{
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2010 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * Common Trace Formal Lexer
+ */
+
+#define BT_LOG_OUTPUT_LEVEL ctf_plugin_metadata_log_level
+#define BT_LOG_TAG "PLUGIN-CTF-METADATA-LEXER"
+#include "logging.hpp"
+
+#include <stdio.h>
+#include <ctype.h>
+#include "scanner.hpp"
+#include "parser-wrap.hpp"
+#include "ast.hpp"
+
+#define YY_FATAL_ERROR(_msg)   BT_LOGF_STR(_msg)
+
+#define PARSE_INTEGER_LITERAL(base)                                    \
+       do {                                                            \
+               errno = 0;                                              \
+               yylval->ull = strtoull(yytext, NULL, base);             \
+               if (errno) {                                            \
+                       _BT_LOGE_APPEND_CAUSE_LINENO(yylineno,                  \
+                               "Cannot parser constant integer: "      \
+                               "base=%d, text=\"%s\"", base, yytext);  \
+                       return CTF_ERROR;                               \
+               }                                                       \
+       } while (0)
+%}
+
+%x comment_ml comment_sl string_lit char_const
+%option reentrant yylineno noyywrap bison-bridge
+%option extra-type="struct ctf_scanner *"
+       /* bison-locations */
+INTEGER_SUFFIX                 (U|UL|ULL|LU|LLU|Ul|Ull|lU|llU|u|uL|uLL|Lu|LLu|ul|ull|lu|llu)
+DIGIT                          [0-9]
+NONDIGIT                       [a-zA-Z_]
+HEXDIGIT                       [0-9A-Fa-f]
+OCTALDIGIT                     [0-7]
+UCHARLOWERCASE                 \\u{HEXDIGIT}{4}
+UCHARUPPERCASE                 \\U{HEXDIGIT}{8}
+ID_NONDIGIT                    {NONDIGIT}|{UCHARLOWERCASE}|{UCHARUPPERCASE}
+IDENTIFIER                     {ID_NONDIGIT}({ID_NONDIGIT}|{DIGIT})*
+%%
+
+                               /*
+                                * Using start conditions to deal with comments
+                                * and strings.
+                                */
+
+"/*"                           BEGIN(comment_ml);
+<comment_ml>[^*\n]*            /* eat anything that's not a '*' */
+<comment_ml>"*"+[^*/\n]*       /* eat up '*'s not followed by '/'s */
+<comment_ml>\n
+<comment_ml>"*"+"/"            BEGIN(INITIAL);
+
+"//"[^\n]*\n                   /* skip comment */
+
+L?\"(\\.|[^\\"])*\"            { if (import_string(yyextra, yylval, yytext, '\"') < 0) return CTF_ERROR; else return CTF_STRING_LITERAL; }
+L?\'(\\.|[^\\'])*\'            { if (import_string(yyextra, yylval, yytext, '\'') < 0) return CTF_ERROR; else return CTF_CHARACTER_LITERAL; }
+
+"["                            return CTF_LSBRAC;
+"]"                            return CTF_RSBRAC;
+"("                            return CTF_LPAREN;
+")"                            return CTF_RPAREN;
+"{"                            return CTF_LBRAC;
+"}"                            return CTF_RBRAC;
+"->"                           return CTF_RARROW;
+"*"                            return CTF_STAR;
+"+"                            return CTF_PLUS;
+"-"                            return CTF_MINUS;
+"<"                            return CTF_LT;
+">"                            return CTF_GT;
+:=                             return CTF_TYPEASSIGN;
+:                              return CTF_COLON;
+;                              return CTF_SEMICOLON;
+"..."                          return CTF_DOTDOTDOT;
+"."                            return CTF_DOT;
+=                              return CTF_EQUAL;
+","                            return CTF_COMMA;
+align                          setstring(yyextra, yylval, yytext); return CTF_TOK_ALIGN;
+const                          setstring(yyextra, yylval, yytext); return CTF_CONST;
+char                           setstring(yyextra, yylval, yytext); return CTF_CHAR;
+clock                          setstring(yyextra, yylval, yytext); return CTF_CLOCK;
+double                         setstring(yyextra, yylval, yytext); return CTF_DOUBLE;
+enum                           setstring(yyextra, yylval, yytext); return CTF_ENUM;
+env                            setstring(yyextra, yylval, yytext); return CTF_ENV;
+event                          setstring(yyextra, yylval, yytext); return CTF_EVENT;
+floating_point                 setstring(yyextra, yylval, yytext); return CTF_FLOATING_POINT;
+float                          setstring(yyextra, yylval, yytext); return CTF_FLOAT;
+integer                                setstring(yyextra, yylval, yytext); return CTF_INTEGER;
+int                            setstring(yyextra, yylval, yytext); return CTF_INT;
+long                           setstring(yyextra, yylval, yytext); return CTF_LONG;
+short                          setstring(yyextra, yylval, yytext); return CTF_SHORT;
+signed                         setstring(yyextra, yylval, yytext); return CTF_SIGNED;
+stream                         setstring(yyextra, yylval, yytext); return CTF_STREAM;
+string                         setstring(yyextra, yylval, yytext); return CTF_STRING;
+struct                         setstring(yyextra, yylval, yytext); return CTF_STRUCT;
+trace                          setstring(yyextra, yylval, yytext); return CTF_TRACE;
+callsite                       setstring(yyextra, yylval, yytext); return CTF_CALLSITE;
+typealias                      setstring(yyextra, yylval, yytext); return CTF_TYPEALIAS;
+typedef                                setstring(yyextra, yylval, yytext); return CTF_TYPEDEF;
+unsigned                       setstring(yyextra, yylval, yytext); return CTF_UNSIGNED;
+variant                                setstring(yyextra, yylval, yytext); return CTF_VARIANT;
+void                           setstring(yyextra, yylval, yytext); return CTF_VOID;
+_Bool                          setstring(yyextra, yylval, yytext); return CTF_BOOL;
+_Complex                       setstring(yyextra, yylval, yytext); return CTF_COMPLEX;
+_Imaginary                     setstring(yyextra, yylval, yytext); return CTF_IMAGINARY;
+[1-9]{DIGIT}*{INTEGER_SUFFIX}? PARSE_INTEGER_LITERAL(10); return CTF_INTEGER_LITERAL;
+0{OCTALDIGIT}*{INTEGER_SUFFIX}?        PARSE_INTEGER_LITERAL(8); return CTF_INTEGER_LITERAL;
+0[xX]{HEXDIGIT}+{INTEGER_SUFFIX}?      PARSE_INTEGER_LITERAL(16); return CTF_INTEGER_LITERAL;
+
+{IDENTIFIER}                   BT_LOGT("Got identifier: id=\"%s\"", yytext); setstring(yyextra, yylval, yytext); if (is_type(yyextra, yytext)) return ID_TYPE; else return IDENTIFIER;
+[ \t\r\n]                      ; /* ignore */
+.                              _BT_LOGE_APPEND_CAUSE_LINENO(yylineno, "Invalid character: char=\"%c\", val=0x%02x", isprint((unsigned char) yytext[0]) ? yytext[0] : '\0', yytext[0]); return CTF_ERROR;
+%%
diff --git a/src/plugins/ctf/common/metadata/logging.c b/src/plugins/ctf/common/metadata/logging.c
deleted file mode 100644 (file)
index 6689e76..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright (c) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- */
-
-#define BT_LOG_OUTPUT_LEVEL ctf_plugin_metadata_log_level
-#include "logging/log.h"
-
-BT_LOG_INIT_LOG_LEVEL(ctf_plugin_metadata_log_level, "BABELTRACE_PLUGIN_CTF_METADATA_LOG_LEVEL");
diff --git a/src/plugins/ctf/common/metadata/logging.cpp b/src/plugins/ctf/common/metadata/logging.cpp
new file mode 100644 (file)
index 0000000..6689e76
--- /dev/null
@@ -0,0 +1,10 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright (c) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ */
+
+#define BT_LOG_OUTPUT_LEVEL ctf_plugin_metadata_log_level
+#include "logging/log.h"
+
+BT_LOG_INIT_LOG_LEVEL(ctf_plugin_metadata_log_level, "BABELTRACE_PLUGIN_CTF_METADATA_LOG_LEVEL");
diff --git a/src/plugins/ctf/common/metadata/logging.h b/src/plugins/ctf/common/metadata/logging.h
deleted file mode 100644 (file)
index 4488b62..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright (c) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- */
-
-#ifndef CTF_METADATA_LOGGING_H
-#define CTF_METADATA_LOGGING_H
-
-#include <babeltrace2/babeltrace.h>
-#include "logging/log.h"
-
-/*
- * This global log level is for the generated lexer and parser: we can't
- * use a contextual log level for their "tracing", so they rely on this.
- */
-BT_LOG_LEVEL_EXTERN_SYMBOL(ctf_plugin_metadata_log_level);
-
-/*
- * To be used by functions without a context structure to pass all the
- * logging configuration at once.
- */
-struct meta_log_config {
-       bt_logging_level log_level;
-
-       /* Weak, exactly one of these must be set */
-       bt_self_component *self_comp;
-       bt_self_component_class *self_comp_class;
-};
-
-#define _BT_LOGT_LINENO(_lineno, _msg, args...) \
-       BT_LOGT("At line %u in metadata stream: " _msg, _lineno, ## args)
-
-#define _BT_LOGW_LINENO(_lineno, _msg, args...) \
-       BT_LOGW("At line %u in metadata stream: " _msg, _lineno, ## args)
-
-#define _BT_LOGE_APPEND_CAUSE_LINENO(_lineno, _msg, args...) \
-       do { \
-               BT_LOGE("At line %u in metadata stream: " _msg, _lineno, ## args); \
-               (void) BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_UNKNOWN( \
-                       "CTF metadata parser", "At line %u in metadata stream: " _msg, _lineno, ## args); \
-       } while (0)
-
-#define _BT_COMP_LOGT_LINENO(_lineno, _msg, args...) \
-       BT_COMP_LOGT("At line %u in metadata stream: " _msg, _lineno, ## args)
-
-#define _BT_COMP_LOGW_LINENO(_lineno, _msg, args...) \
-       BT_COMP_LOGW("At line %u in metadata stream: " _msg, _lineno, ## args)
-
-#define _BT_COMP_LOGE_LINENO(_lineno, _msg, args...) \
-       BT_COMP_LOGE("At line %u in metadata stream: " _msg, _lineno, ## args)
-
-#define _BT_COMP_LOGE_APPEND_CAUSE_LINENO(_lineno, _msg, args...) \
-       BT_COMP_LOGE_APPEND_CAUSE(BT_COMP_LOG_SELF_COMP, "At line %u in metadata stream: " _msg, _lineno, ## args)
-
-#define _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(_msg, args...) \
-       BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE( \
-               BT_COMP_LOG_SELF_COMP, \
-               BT_COMP_LOG_SELF_COMP_CLASS, \
-               _msg, ## args)
-
-#endif /* CTF_METADATA_LOGGING_H */
diff --git a/src/plugins/ctf/common/metadata/logging.hpp b/src/plugins/ctf/common/metadata/logging.hpp
new file mode 100644 (file)
index 0000000..4488b62
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright (c) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ */
+
+#ifndef CTF_METADATA_LOGGING_H
+#define CTF_METADATA_LOGGING_H
+
+#include <babeltrace2/babeltrace.h>
+#include "logging/log.h"
+
+/*
+ * This global log level is for the generated lexer and parser: we can't
+ * use a contextual log level for their "tracing", so they rely on this.
+ */
+BT_LOG_LEVEL_EXTERN_SYMBOL(ctf_plugin_metadata_log_level);
+
+/*
+ * To be used by functions without a context structure to pass all the
+ * logging configuration at once.
+ */
+struct meta_log_config {
+       bt_logging_level log_level;
+
+       /* Weak, exactly one of these must be set */
+       bt_self_component *self_comp;
+       bt_self_component_class *self_comp_class;
+};
+
+#define _BT_LOGT_LINENO(_lineno, _msg, args...) \
+       BT_LOGT("At line %u in metadata stream: " _msg, _lineno, ## args)
+
+#define _BT_LOGW_LINENO(_lineno, _msg, args...) \
+       BT_LOGW("At line %u in metadata stream: " _msg, _lineno, ## args)
+
+#define _BT_LOGE_APPEND_CAUSE_LINENO(_lineno, _msg, args...) \
+       do { \
+               BT_LOGE("At line %u in metadata stream: " _msg, _lineno, ## args); \
+               (void) BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_UNKNOWN( \
+                       "CTF metadata parser", "At line %u in metadata stream: " _msg, _lineno, ## args); \
+       } while (0)
+
+#define _BT_COMP_LOGT_LINENO(_lineno, _msg, args...) \
+       BT_COMP_LOGT("At line %u in metadata stream: " _msg, _lineno, ## args)
+
+#define _BT_COMP_LOGW_LINENO(_lineno, _msg, args...) \
+       BT_COMP_LOGW("At line %u in metadata stream: " _msg, _lineno, ## args)
+
+#define _BT_COMP_LOGE_LINENO(_lineno, _msg, args...) \
+       BT_COMP_LOGE("At line %u in metadata stream: " _msg, _lineno, ## args)
+
+#define _BT_COMP_LOGE_APPEND_CAUSE_LINENO(_lineno, _msg, args...) \
+       BT_COMP_LOGE_APPEND_CAUSE(BT_COMP_LOG_SELF_COMP, "At line %u in metadata stream: " _msg, _lineno, ## args)
+
+#define _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(_msg, args...) \
+       BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE( \
+               BT_COMP_LOG_SELF_COMP, \
+               BT_COMP_LOG_SELF_COMP_CLASS, \
+               _msg, ## args)
+
+#endif /* CTF_METADATA_LOGGING_H */
diff --git a/src/plugins/ctf/common/metadata/objstack.c b/src/plugins/ctf/common/metadata/objstack.c
deleted file mode 100644 (file)
index a730589..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2013 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * Common Trace Format Object Stack.
- */
-
-#define BT_LOG_OUTPUT_LEVEL ctf_plugin_metadata_log_level
-#define BT_LOG_TAG "PLUGIN/CTF/META/OBJSTACK"
-#include "logging.h"
-
-#include "objstack.h"
-
-#include <stdlib.h>
-#include "common/list.h"
-#include "common/macros.h"
-#include "common/align.h"
-
-#define OBJSTACK_ALIGN                 8       /* Object stack alignment */
-#define OBJSTACK_INIT_LEN              128
-#define OBJSTACK_POISON                        0xcc
-
-struct objstack {
-       struct bt_list_head head;       /* list of struct objstack_node */
-};
-
-struct objstack_node {
-       struct bt_list_head node;
-       size_t len;
-       size_t used_len;
-       char __attribute__ ((aligned (OBJSTACK_ALIGN))) data[];
-};
-
-BT_HIDDEN
-struct objstack *objstack_create(void)
-{
-       struct objstack *objstack;
-       struct objstack_node *node;
-
-       objstack = calloc(1, sizeof(*objstack));
-       if (!objstack) {
-               BT_LOGE_STR("Failed to allocate one object stack.");
-               return NULL;
-       }
-       node = calloc(sizeof(struct objstack_node) + OBJSTACK_INIT_LEN,
-                       sizeof(char));
-       if (!node) {
-               BT_LOGE_STR("Failed to allocate one object stack node.");
-               free(objstack);
-               return NULL;
-       }
-       BT_INIT_LIST_HEAD(&objstack->head);
-       bt_list_add_tail(&node->node, &objstack->head);
-       node->len = OBJSTACK_INIT_LEN;
-       return objstack;
-}
-
-static
-void objstack_node_free(struct objstack_node *node)
-{
-       size_t offset, len;
-       char *p;
-
-       if (!node)
-               return;
-       p = (char *) node;
-       len = sizeof(*node) + node->len;
-       for (offset = 0; offset < len; offset++)
-               p[offset] = OBJSTACK_POISON;
-       free(node);
-}
-
-BT_HIDDEN
-void objstack_destroy(struct objstack *objstack)
-{
-       struct objstack_node *node, *p;
-
-       if (!objstack)
-               return;
-       bt_list_for_each_entry_safe(node, p, &objstack->head, node) {
-               bt_list_del(&node->node);
-               objstack_node_free(node);
-       }
-       free(objstack);
-}
-
-static
-struct objstack_node *objstack_append_node(struct objstack *objstack)
-{
-       struct objstack_node *last_node, *new_node;
-
-       /* Get last node */
-       last_node = bt_list_entry(objstack->head.prev,
-                       struct objstack_node, node);
-
-       /* Allocate new node with double of size of last node */
-       new_node = calloc(sizeof(struct objstack_node) + (last_node->len << 1),
-                       sizeof(char));
-       if (!new_node) {
-               BT_LOGE_STR("Failed to allocate one object stack node.");
-               return NULL;
-       }
-       bt_list_add_tail(&new_node->node, &objstack->head);
-       new_node->len = last_node->len << 1;
-       return new_node;
-}
-
-BT_HIDDEN
-void *objstack_alloc(struct objstack *objstack, size_t len)
-{
-       struct objstack_node *last_node;
-       void *p;
-
-       len = BT_ALIGN(len, OBJSTACK_ALIGN);
-
-       /* Get last node */
-       last_node = bt_list_entry(objstack->head.prev,
-                       struct objstack_node, node);
-       while (last_node->len - last_node->used_len < len) {
-               last_node = objstack_append_node(objstack);
-               if (!last_node) {
-                       return NULL;
-               }
-       }
-       p = &last_node->data[last_node->used_len];
-       last_node->used_len += len;
-       return p;
-}
diff --git a/src/plugins/ctf/common/metadata/objstack.cpp b/src/plugins/ctf/common/metadata/objstack.cpp
new file mode 100644 (file)
index 0000000..0be8dbf
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2013 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * Common Trace Format Object Stack.
+ */
+
+#define BT_LOG_OUTPUT_LEVEL ctf_plugin_metadata_log_level
+#define BT_LOG_TAG "PLUGIN/CTF/META/OBJSTACK"
+#include "logging.hpp"
+
+#include "objstack.hpp"
+
+#include <stdlib.h>
+#include "common/list.h"
+#include "common/macros.h"
+#include "common/align.h"
+
+#define OBJSTACK_ALIGN                 8       /* Object stack alignment */
+#define OBJSTACK_INIT_LEN              128
+#define OBJSTACK_POISON                        0xcc
+
+struct objstack {
+       struct bt_list_head head;       /* list of struct objstack_node */
+};
+
+struct objstack_node {
+       struct bt_list_head node;
+       size_t len;
+       size_t used_len;
+       char __attribute__ ((aligned (OBJSTACK_ALIGN))) data[];
+};
+
+BT_HIDDEN
+struct objstack *objstack_create(void)
+{
+       struct objstack *objstack;
+       struct objstack_node *node;
+
+       objstack = (struct objstack *) calloc(1, sizeof(*objstack));
+       if (!objstack) {
+               BT_LOGE_STR("Failed to allocate one object stack.");
+               return NULL;
+       }
+       node = (objstack_node *) calloc(sizeof(struct objstack_node) + OBJSTACK_INIT_LEN,
+                       sizeof(char));
+       if (!node) {
+               BT_LOGE_STR("Failed to allocate one object stack node.");
+               free(objstack);
+               return NULL;
+       }
+       BT_INIT_LIST_HEAD(&objstack->head);
+       bt_list_add_tail(&node->node, &objstack->head);
+       node->len = OBJSTACK_INIT_LEN;
+       return objstack;
+}
+
+static
+void objstack_node_free(struct objstack_node *node)
+{
+       size_t offset, len;
+       char *p;
+
+       if (!node)
+               return;
+       p = (char *) node;
+       len = sizeof(*node) + node->len;
+       for (offset = 0; offset < len; offset++)
+               p[offset] = OBJSTACK_POISON;
+       free(node);
+}
+
+BT_HIDDEN
+void objstack_destroy(struct objstack *objstack)
+{
+       struct objstack_node *node, *p;
+
+       if (!objstack)
+               return;
+       bt_list_for_each_entry_safe(node, p, &objstack->head, node) {
+               bt_list_del(&node->node);
+               objstack_node_free(node);
+       }
+       free(objstack);
+}
+
+static
+struct objstack_node *objstack_append_node(struct objstack *objstack)
+{
+       struct objstack_node *last_node, *new_node;
+
+       /* Get last node */
+       last_node = bt_list_entry(objstack->head.prev,
+                       struct objstack_node, node);
+
+       /* Allocate new node with double of size of last node */
+       new_node = (objstack_node *) calloc(sizeof(struct objstack_node) + (last_node->len << 1),
+                       sizeof(char));
+       if (!new_node) {
+               BT_LOGE_STR("Failed to allocate one object stack node.");
+               return NULL;
+       }
+       bt_list_add_tail(&new_node->node, &objstack->head);
+       new_node->len = last_node->len << 1;
+       return new_node;
+}
+
+BT_HIDDEN
+void *objstack_alloc(struct objstack *objstack, size_t len)
+{
+       struct objstack_node *last_node;
+       void *p;
+
+       len = BT_ALIGN(len, OBJSTACK_ALIGN);
+
+       /* Get last node */
+       last_node = bt_list_entry(objstack->head.prev,
+                       struct objstack_node, node);
+       while (last_node->len - last_node->used_len < len) {
+               last_node = objstack_append_node(objstack);
+               if (!last_node) {
+                       return NULL;
+               }
+       }
+       p = &last_node->data[last_node->used_len];
+       last_node->used_len += len;
+       return p;
+}
diff --git a/src/plugins/ctf/common/metadata/objstack.h b/src/plugins/ctf/common/metadata/objstack.h
deleted file mode 100644 (file)
index f6c655f..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2013 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * Common Trace Format Object Stack.
- */
-
-#ifndef _OBJSTACK_H
-#define _OBJSTACK_H
-
-#include "common/macros.h"
-
-struct objstack;
-
-BT_HIDDEN
-struct objstack *objstack_create(void);
-BT_HIDDEN
-void objstack_destroy(struct objstack *objstack);
-
-/*
- * Allocate len bytes of zeroed memory.
- * Return NULL on error.
- */
-BT_HIDDEN
-void *objstack_alloc(struct objstack *objstack, size_t len);
-
-#endif /* _OBJSTACK_H */
diff --git a/src/plugins/ctf/common/metadata/objstack.hpp b/src/plugins/ctf/common/metadata/objstack.hpp
new file mode 100644 (file)
index 0000000..f6c655f
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2013 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * Common Trace Format Object Stack.
+ */
+
+#ifndef _OBJSTACK_H
+#define _OBJSTACK_H
+
+#include "common/macros.h"
+
+struct objstack;
+
+BT_HIDDEN
+struct objstack *objstack_create(void);
+BT_HIDDEN
+void objstack_destroy(struct objstack *objstack);
+
+/*
+ * Allocate len bytes of zeroed memory.
+ * Return NULL on error.
+ */
+BT_HIDDEN
+void *objstack_alloc(struct objstack *objstack, size_t len);
+
+#endif /* _OBJSTACK_H */
diff --git a/src/plugins/ctf/common/metadata/parser-wrap.h b/src/plugins/ctf/common/metadata/parser-wrap.h
deleted file mode 100644 (file)
index 4976264..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2019 EfficiOS Inc.
- */
-
-#ifndef BABELTRACE_PLUGINS_CTF_COMMON_METADATA_PARSER_WRAP_H
-#define BABELTRACE_PLUGINS_CTF_COMMON_METADATA_PARSER_WRAP_H
-
-/*
- * Small wrapper around the bison-generated parser.h to conditionally define
- * YYDEBUG (and therefore the yydebug declaration).
- */
-
-#include "logging/log.h"
-
-#if BT_LOG_ENABLED_TRACE
-# define YYDEBUG 1
-# define YYFPRINTF(_stream, _fmt, args...) BT_LOGT(_fmt, ## args)
-#else
-# define YYDEBUG 0
-#endif
-
-#define ALLOW_INCLUDE_PARSER_H
-#include "parser.h"
-#undef ALLOW_INCLUDE_PARSER_H
-
-#endif
diff --git a/src/plugins/ctf/common/metadata/parser-wrap.hpp b/src/plugins/ctf/common/metadata/parser-wrap.hpp
new file mode 100644 (file)
index 0000000..6300b25
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2019 EfficiOS Inc.
+ */
+
+#ifndef BABELTRACE_PLUGINS_CTF_COMMON_METADATA_PARSER_WRAP_H
+#define BABELTRACE_PLUGINS_CTF_COMMON_METADATA_PARSER_WRAP_H
+
+/*
+ * Small wrapper around the bison-generated parser.h to conditionally define
+ * YYDEBUG (and therefore the yydebug declaration).
+ */
+
+#include "logging/log.h"
+
+#if BT_LOG_ENABLED_TRACE
+# define YYDEBUG 1
+# define YYFPRINTF(_stream, _fmt, args...) BT_LOGT(_fmt, ## args)
+#else
+# define YYDEBUG 0
+#endif
+
+#define ALLOW_INCLUDE_PARSER_H
+#include "parser.hpp"
+#undef ALLOW_INCLUDE_PARSER_H
+
+#endif
diff --git a/src/plugins/ctf/common/metadata/parser.y b/src/plugins/ctf/common/metadata/parser.y
deleted file mode 100644 (file)
index cb4eb74..0000000
+++ /dev/null
@@ -1,2607 +0,0 @@
-%{
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2010 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * Common Trace Format Metadata Grammar.
- */
-
-#define BT_LOG_OUTPUT_LEVEL ctf_plugin_metadata_log_level
-#define BT_LOG_TAG "PLUGIN/CTF/META/PARSER"
-#include "logging.h"
-
-#include <stdio.h>
-#include <ctype.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <glib.h>
-#include <errno.h>
-#include <inttypes.h>
-#include "common/list.h"
-#include "common/assert.h"
-#include "scanner.h"
-#include "ast.h"
-#include "objstack.h"
-
-#include "parser-wrap.h"
-
-/* Join two lists, put "add" at the end of "head".  */
-static inline void
-_bt_list_splice_tail (struct bt_list_head *add, struct bt_list_head *head)
-{
-       /* Do nothing if the list which gets added is empty.  */
-       if (add != add->next) {
-               add->next->prev = head->prev;
-               add->prev->next = head;
-               head->prev->next = add->next;
-               head->prev = add->prev;
-       }
-}
-
-BT_HIDDEN
-int yylex(union YYSTYPE *yyval, yyscan_t yyscanner);
-BT_HIDDEN
-int yylex_init_extra(struct ctf_scanner *scanner, yyscan_t * ptr_yy_globals);
-BT_HIDDEN
-int yylex_destroy(yyscan_t yyscanner);
-BT_HIDDEN
-void yyrestart(FILE * in_str, yyscan_t yyscanner);
-BT_HIDDEN
-int yyget_lineno(yyscan_t yyscanner);
-BT_HIDDEN
-char *yyget_text(yyscan_t yyscanner);
-
-static const char *node_type_to_str[] = {
-#define ENTRY(S)       [S] = #S,
-       FOREACH_CTF_NODES(ENTRY)
-#undef ENTRY
-};
-
-/*
- * Static node for out of memory errors. Only "type" is used. lineno is
- * always left at 0. The rest of the node content can be overwritten,
- * but is never used.
- */
-static struct ctf_node error_node = {
-       .type = NODE_ERROR,
-};
-
-BT_HIDDEN
-const char *node_type(struct ctf_node *node)
-{
-       if (node->type < NR_NODE_TYPES)
-               return node_type_to_str[node->type];
-       else
-               return NULL;
-}
-
-void setstring(struct ctf_scanner *scanner, YYSTYPE *lvalp, const char *src)
-{
-       lvalp->s = objstack_alloc(scanner->objstack, strlen(src) + 1);
-       strcpy(lvalp->s, src);
-}
-
-static
-int str_check(size_t str_len, size_t offset, size_t len)
-{
-       /* check overflow */
-       if (offset + len < offset)
-               return -1;
-       if (offset + len > str_len)
-               return -1;
-       return 0;
-}
-
-static
-int bt_isodigit(int c)
-{
-       switch (c) {
-       case '0':
-       case '1':
-       case '2':
-       case '3':
-       case '4':
-       case '5':
-       case '6':
-       case '7':
-               return 1;
-       default:
-               return 0;
-       }
-}
-
-static
-int parse_base_sequence(const char *src, size_t len, size_t pos,
-               char *buffer, size_t *buf_len, int base)
-{
-       const size_t max_char = 3;
-       int nr_char = 0;
-
-       while (!str_check(len, pos, 1) && nr_char < max_char) {
-               char c = src[pos++];
-
-               if (base == 8) {
-                       if (bt_isodigit(c))
-                               buffer[nr_char++] = c;
-                       else
-                               break;
-               } else if (base == 16) {
-                       if (isxdigit(c))
-                               buffer[nr_char++] = c;
-                       else
-                               break;
-
-               } else {
-                       /* Unsupported base */
-                       return -1;
-               }
-       }
-       BT_ASSERT_DBG(nr_char > 0);
-       buffer[nr_char] = '\0';
-       *buf_len = nr_char;
-       return 0;
-}
-
-static
-int import_basic_string(struct ctf_scanner *scanner, YYSTYPE *lvalp,
-               size_t len, const char *src, char delim)
-{
-       size_t pos = 0, dpos = 0;
-
-       if (str_check(len, pos, 1))
-               return -1;
-       if (src[pos++] != delim)
-               return -1;
-
-       while (src[pos] != delim) {
-               char c;
-
-               if (str_check(len, pos, 1))
-                       return -1;
-               c = src[pos++];
-               if (c == '\\') {
-                       if (str_check(len, pos, 1))
-                               return -1;
-                       c = src[pos++];
-
-                       switch (c) {
-                       case 'a':
-                               c = '\a';
-                               break;
-                       case 'b':
-                               c = '\b';
-                               break;
-                       case 'f':
-                               c = '\f';
-                               break;
-                       case 'n':
-                               c = '\n';
-                               break;
-                       case 'r':
-                               c = '\r';
-                               break;
-                       case 't':
-                               c = '\t';
-                               break;
-                       case 'v':
-                               c = '\v';
-                               break;
-                       case '\\':
-                               c = '\\';
-                               break;
-                       case '\'':
-                               c = '\'';
-                               break;
-                       case '\"':
-                               c = '\"';
-                               break;
-                       case '?':
-                               c = '?';
-                               break;
-                       case '0':
-                       case '1':
-                       case '2':
-                       case '3':
-                       case '4':
-                       case '5':
-                       case '6':
-                       case '7':
-                       {
-                               char oct_buffer[4];
-                               size_t oct_len;
-
-                               if (parse_base_sequence(src, len, pos - 1,
-                                               oct_buffer, &oct_len, 8))
-                                       return -1;
-                               c = strtoul(&oct_buffer[0], NULL, 8);
-                               pos += oct_len - 1;
-                               break;
-                       }
-                       case 'x':
-                       {
-                               char hex_buffer[4];
-                               size_t hex_len;
-
-                               if (parse_base_sequence(src, len, pos,
-                                               hex_buffer, &hex_len, 16))
-                                       return -1;
-                               c = strtoul(&hex_buffer[0], NULL, 16);
-                               pos += hex_len;
-                               break;
-                       }
-                       default:
-                               return -1;
-                       }
-               }
-               if (str_check(len, dpos, 1))
-                       return -1;
-               lvalp->s[dpos++] = c;
-       }
-
-       if (str_check(len, dpos, 1))
-               return -1;
-       lvalp->s[dpos++] = '\0';
-
-       if (str_check(len, pos, 1))
-               return -1;
-       if (src[pos++] != delim)
-               return -1;
-
-       if (str_check(len, pos, 1))
-               return -1;
-       if (src[pos] != '\0')
-               return -1;
-       return 0;
-}
-
-int import_string(struct ctf_scanner *scanner, YYSTYPE *lvalp,
-               const char *src, char delim)
-{
-       size_t len;
-
-       len = strlen(src) + 1;
-       lvalp->s = objstack_alloc(scanner->objstack, len);
-       if (src[0] == 'L') {
-               // TODO: import wide string
-               _BT_LOGE_APPEND_CAUSE_LINENO(yyget_lineno(scanner),
-                       "wide characters are not supported as of this version: "
-                       "scanner-addr=%p", scanner);
-               return -1;
-       } else {
-               return import_basic_string(scanner, lvalp, len, src, delim);
-       }
-}
-
-static void init_scope(struct ctf_scanner_scope *scope,
-                      struct ctf_scanner_scope *parent)
-{
-       scope->parent = parent;
-       scope->classes = g_hash_table_new_full(g_str_hash, g_str_equal,
-                                            NULL, NULL);
-}
-
-static void finalize_scope(struct ctf_scanner_scope *scope)
-{
-       g_hash_table_destroy(scope->classes);
-}
-
-static void push_scope(struct ctf_scanner *scanner)
-{
-       struct ctf_scanner_scope *ns;
-
-       BT_LOGT("Pushing scope: scanner-addr=%p", scanner);
-       ns = malloc(sizeof(struct ctf_scanner_scope));
-       init_scope(ns, scanner->cs);
-       scanner->cs = ns;
-}
-
-static void pop_scope(struct ctf_scanner *scanner)
-{
-       struct ctf_scanner_scope *os;
-
-       BT_LOGT("Popping scope: scanner-addr=%p", scanner);
-       os = scanner->cs;
-       scanner->cs = os->parent;
-       finalize_scope(os);
-       free(os);
-}
-
-static int lookup_type(struct ctf_scanner_scope *s, const char *id)
-{
-       int ret;
-
-       ret = GPOINTER_TO_INT(g_hash_table_lookup(s->classes, id));
-       BT_LOGT("Looked up type: scanner-addr=%p, id=\"%s\", ret=%d",
-               s, id, ret);
-       return ret;
-}
-
-BT_HIDDEN
-int is_type(struct ctf_scanner *scanner, const char *id)
-{
-       struct ctf_scanner_scope *it;
-       int ret = 0;
-
-       for (it = scanner->cs; it; it = it->parent) {
-               if (lookup_type(it, id)) {
-                       ret = 1;
-                       break;
-               }
-       }
-       BT_LOGT("Found if ID is type: scanner-addr=%p, id=\"%s\", ret=%d",
-               scanner, id, ret);
-       return ret;
-}
-
-static void add_type(struct ctf_scanner *scanner, char *id)
-{
-       BT_LOGT("Adding type: scanner-addr=%p, id=\"%s\"",
-               scanner, id);
-       if (lookup_type(scanner->cs, id))
-               return;
-       g_hash_table_insert(scanner->cs->classes, id, id);
-}
-
-static struct ctf_node *make_node(struct ctf_scanner *scanner,
-                                 enum node_type type)
-{
-       struct ctf_node *node;
-
-       node = objstack_alloc(scanner->objstack, sizeof(*node));
-       if (!node) {
-               _BT_LOGE_APPEND_CAUSE_LINENO(yyget_lineno(scanner->scanner),
-                       "failed to allocate one stack entry: "
-                       "scanner-addr=%p", scanner);
-               return &error_node;
-       }
-       node->type = type;
-       node->lineno = yyget_lineno(scanner->scanner);
-       BT_INIT_LIST_HEAD(&node->tmp_head);
-       bt_list_add(&node->siblings, &node->tmp_head);
-
-       switch (type) {
-       case NODE_ROOT:
-               node->type = NODE_ERROR;
-               BT_LOGE("Trying to create root node: scanner-addr=%p",
-                       scanner);
-               break;
-       case NODE_EVENT:
-               BT_INIT_LIST_HEAD(&node->u.event.declaration_list);
-               break;
-       case NODE_STREAM:
-               BT_INIT_LIST_HEAD(&node->u.stream.declaration_list);
-               break;
-       case NODE_ENV:
-               BT_INIT_LIST_HEAD(&node->u.env.declaration_list);
-               break;
-       case NODE_TRACE:
-               BT_INIT_LIST_HEAD(&node->u.trace.declaration_list);
-               break;
-       case NODE_CLOCK:
-               BT_INIT_LIST_HEAD(&node->u.clock.declaration_list);
-               break;
-       case NODE_CALLSITE:
-               BT_INIT_LIST_HEAD(&node->u.callsite.declaration_list);
-               break;
-       case NODE_CTF_EXPRESSION:
-               BT_INIT_LIST_HEAD(&node->u.ctf_expression.left);
-               BT_INIT_LIST_HEAD(&node->u.ctf_expression.right);
-               break;
-       case NODE_UNARY_EXPRESSION:
-               break;
-       case NODE_TYPEDEF:
-               BT_INIT_LIST_HEAD(&node->u.field_class_def.field_class_declarators);
-               break;
-       case NODE_TYPEALIAS_TARGET:
-               BT_INIT_LIST_HEAD(&node->u.field_class_alias_target.field_class_declarators);
-               break;
-       case NODE_TYPEALIAS_ALIAS:
-               BT_INIT_LIST_HEAD(&node->u.field_class_alias_name.field_class_declarators);
-               break;
-       case NODE_TYPEALIAS:
-               break;
-       case NODE_TYPE_SPECIFIER:
-               break;
-       case NODE_TYPE_SPECIFIER_LIST:
-               BT_INIT_LIST_HEAD(&node->u.field_class_specifier_list.head);
-               break;
-       case NODE_POINTER:
-               break;
-       case NODE_TYPE_DECLARATOR:
-               BT_INIT_LIST_HEAD(&node->u.field_class_declarator.pointers);
-               break;
-       case NODE_FLOATING_POINT:
-               BT_INIT_LIST_HEAD(&node->u.floating_point.expressions);
-               break;
-       case NODE_INTEGER:
-               BT_INIT_LIST_HEAD(&node->u.integer.expressions);
-               break;
-       case NODE_STRING:
-               BT_INIT_LIST_HEAD(&node->u.string.expressions);
-               break;
-       case NODE_ENUMERATOR:
-               BT_INIT_LIST_HEAD(&node->u.enumerator.values);
-               break;
-       case NODE_ENUM:
-               BT_INIT_LIST_HEAD(&node->u._enum.enumerator_list);
-               break;
-       case NODE_STRUCT_OR_VARIANT_DECLARATION:
-               BT_INIT_LIST_HEAD(&node->u.struct_or_variant_declaration.field_class_declarators);
-               break;
-       case NODE_VARIANT:
-               BT_INIT_LIST_HEAD(&node->u.variant.declaration_list);
-               break;
-       case NODE_STRUCT:
-               BT_INIT_LIST_HEAD(&node->u._struct.declaration_list);
-               BT_INIT_LIST_HEAD(&node->u._struct.min_align);
-               break;
-       case NODE_UNKNOWN:
-       default:
-               node->type = NODE_ERROR;
-               BT_LOGE("Unknown node type: scanner-addr=%p, node-type=%d",
-                       scanner, type);
-               break;
-       }
-
-       return node;
-}
-
-static int reparent_ctf_expression(struct ctf_node *node,
-                                  struct ctf_node *parent)
-{
-       switch (parent->type) {
-       case NODE_EVENT:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.event.declaration_list);
-               break;
-       case NODE_STREAM:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.stream.declaration_list);
-               break;
-       case NODE_ENV:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.env.declaration_list);
-               break;
-       case NODE_TRACE:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.trace.declaration_list);
-               break;
-       case NODE_CLOCK:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.clock.declaration_list);
-               break;
-       case NODE_CALLSITE:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.callsite.declaration_list);
-               break;
-       case NODE_FLOATING_POINT:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.floating_point.expressions);
-               break;
-       case NODE_INTEGER:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.integer.expressions);
-               break;
-       case NODE_STRING:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.string.expressions);
-               break;
-
-       case NODE_ROOT:
-       case NODE_CTF_EXPRESSION:
-       case NODE_TYPEDEF:
-       case NODE_TYPEALIAS_TARGET:
-       case NODE_TYPEALIAS_ALIAS:
-       case NODE_TYPEALIAS:
-       case NODE_TYPE_SPECIFIER:
-       case NODE_TYPE_SPECIFIER_LIST:
-       case NODE_POINTER:
-       case NODE_TYPE_DECLARATOR:
-       case NODE_ENUMERATOR:
-       case NODE_ENUM:
-       case NODE_STRUCT_OR_VARIANT_DECLARATION:
-       case NODE_VARIANT:
-       case NODE_STRUCT:
-       case NODE_UNARY_EXPRESSION:
-               return -EPERM;
-
-       case NODE_UNKNOWN:
-       default:
-               BT_LOGE("Unknown node type: node-type=%d", parent->type);
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static int reparent_typedef(struct ctf_node *node, struct ctf_node *parent)
-{
-       switch (parent->type) {
-       case NODE_ROOT:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.root.declaration_list);
-               break;
-       case NODE_EVENT:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.event.declaration_list);
-               break;
-       case NODE_STREAM:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.stream.declaration_list);
-               break;
-       case NODE_ENV:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.env.declaration_list);
-               break;
-       case NODE_TRACE:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.trace.declaration_list);
-               break;
-       case NODE_CLOCK:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.clock.declaration_list);
-               break;
-       case NODE_CALLSITE:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.callsite.declaration_list);
-               break;
-       case NODE_VARIANT:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.variant.declaration_list);
-               break;
-       case NODE_STRUCT:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u._struct.declaration_list);
-               break;
-
-       case NODE_FLOATING_POINT:
-       case NODE_INTEGER:
-       case NODE_STRING:
-       case NODE_CTF_EXPRESSION:
-       case NODE_TYPEDEF:
-       case NODE_TYPEALIAS_TARGET:
-       case NODE_TYPEALIAS_ALIAS:
-       case NODE_TYPEALIAS:
-       case NODE_TYPE_SPECIFIER:
-       case NODE_TYPE_SPECIFIER_LIST:
-       case NODE_POINTER:
-       case NODE_TYPE_DECLARATOR:
-       case NODE_ENUMERATOR:
-       case NODE_ENUM:
-       case NODE_STRUCT_OR_VARIANT_DECLARATION:
-       case NODE_UNARY_EXPRESSION:
-               return -EPERM;
-
-       case NODE_UNKNOWN:
-       default:
-               BT_LOGE("Unknown node type: node-type=%d", parent->type);
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static int reparent_field_class_alias(struct ctf_node *node, struct ctf_node *parent)
-{
-       switch (parent->type) {
-       case NODE_ROOT:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.root.declaration_list);
-               break;
-       case NODE_EVENT:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.event.declaration_list);
-               break;
-       case NODE_STREAM:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.stream.declaration_list);
-               break;
-       case NODE_ENV:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.env.declaration_list);
-               break;
-       case NODE_TRACE:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.trace.declaration_list);
-               break;
-       case NODE_CLOCK:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.clock.declaration_list);
-               break;
-       case NODE_CALLSITE:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.callsite.declaration_list);
-               break;
-       case NODE_VARIANT:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.variant.declaration_list);
-               break;
-       case NODE_STRUCT:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u._struct.declaration_list);
-               break;
-
-       case NODE_FLOATING_POINT:
-       case NODE_INTEGER:
-       case NODE_STRING:
-       case NODE_CTF_EXPRESSION:
-       case NODE_TYPEDEF:
-       case NODE_TYPEALIAS_TARGET:
-       case NODE_TYPEALIAS_ALIAS:
-       case NODE_TYPEALIAS:
-       case NODE_TYPE_SPECIFIER:
-       case NODE_TYPE_SPECIFIER_LIST:
-       case NODE_POINTER:
-       case NODE_TYPE_DECLARATOR:
-       case NODE_ENUMERATOR:
-       case NODE_ENUM:
-       case NODE_STRUCT_OR_VARIANT_DECLARATION:
-       case NODE_UNARY_EXPRESSION:
-               return -EPERM;
-
-       case NODE_UNKNOWN:
-       default:
-               BT_LOGE("Unknown node type: node-type=%d", parent->type);
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static int reparent_field_class_specifier(struct ctf_node *node,
-                                  struct ctf_node *parent)
-{
-       switch (parent->type) {
-       case NODE_TYPE_SPECIFIER_LIST:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.field_class_specifier_list.head);
-               break;
-
-       case NODE_TYPE_SPECIFIER:
-       case NODE_EVENT:
-       case NODE_STREAM:
-       case NODE_ENV:
-       case NODE_TRACE:
-       case NODE_CLOCK:
-       case NODE_CALLSITE:
-       case NODE_VARIANT:
-       case NODE_STRUCT:
-       case NODE_TYPEDEF:
-       case NODE_TYPEALIAS_TARGET:
-       case NODE_TYPEALIAS_ALIAS:
-       case NODE_TYPE_DECLARATOR:
-       case NODE_ENUM:
-       case NODE_STRUCT_OR_VARIANT_DECLARATION:
-       case NODE_TYPEALIAS:
-       case NODE_FLOATING_POINT:
-       case NODE_INTEGER:
-       case NODE_STRING:
-       case NODE_CTF_EXPRESSION:
-       case NODE_POINTER:
-       case NODE_ENUMERATOR:
-       case NODE_UNARY_EXPRESSION:
-               return -EPERM;
-
-       case NODE_UNKNOWN:
-       default:
-               BT_LOGE("Unknown node type: node-type=%d", parent->type);
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static int reparent_field_class_specifier_list(struct ctf_node *node,
-                                       struct ctf_node *parent)
-{
-       switch (parent->type) {
-       case NODE_ROOT:
-               bt_list_add_tail(&node->siblings, &parent->u.root.declaration_list);
-               break;
-       case NODE_EVENT:
-               bt_list_add_tail(&node->siblings, &parent->u.event.declaration_list);
-               break;
-       case NODE_STREAM:
-               bt_list_add_tail(&node->siblings, &parent->u.stream.declaration_list);
-               break;
-       case NODE_ENV:
-               bt_list_add_tail(&node->siblings, &parent->u.env.declaration_list);
-               break;
-       case NODE_TRACE:
-               bt_list_add_tail(&node->siblings, &parent->u.trace.declaration_list);
-               break;
-       case NODE_CLOCK:
-               bt_list_add_tail(&node->siblings, &parent->u.clock.declaration_list);
-               break;
-       case NODE_CALLSITE:
-               bt_list_add_tail(&node->siblings, &parent->u.callsite.declaration_list);
-               break;
-       case NODE_VARIANT:
-               bt_list_add_tail(&node->siblings, &parent->u.variant.declaration_list);
-               break;
-       case NODE_STRUCT:
-               bt_list_add_tail(&node->siblings, &parent->u._struct.declaration_list);
-               break;
-       case NODE_TYPEDEF:
-               parent->u.field_class_def.field_class_specifier_list = node;
-               break;
-       case NODE_TYPEALIAS_TARGET:
-               parent->u.field_class_alias_target.field_class_specifier_list = node;
-               break;
-       case NODE_TYPEALIAS_ALIAS:
-               parent->u.field_class_alias_name.field_class_specifier_list = node;
-               break;
-       case NODE_ENUM:
-               parent->u._enum.container_field_class = node;
-               break;
-       case NODE_STRUCT_OR_VARIANT_DECLARATION:
-               parent->u.struct_or_variant_declaration.field_class_specifier_list = node;
-               break;
-       case NODE_TYPE_DECLARATOR:
-       case NODE_TYPE_SPECIFIER:
-       case NODE_TYPEALIAS:
-       case NODE_FLOATING_POINT:
-       case NODE_INTEGER:
-       case NODE_STRING:
-       case NODE_CTF_EXPRESSION:
-       case NODE_POINTER:
-       case NODE_ENUMERATOR:
-       case NODE_UNARY_EXPRESSION:
-               return -EPERM;
-
-       case NODE_UNKNOWN:
-       default:
-               BT_LOGE("Unknown node type: node-type=%d", parent->type);
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static int reparent_field_class_declarator(struct ctf_node *node,
-                                   struct ctf_node *parent)
-{
-       switch (parent->type) {
-       case NODE_TYPE_DECLARATOR:
-               parent->u.field_class_declarator.type = TYPEDEC_NESTED;
-               parent->u.field_class_declarator.u.nested.field_class_declarator = node;
-               break;
-       case NODE_STRUCT_OR_VARIANT_DECLARATION:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.struct_or_variant_declaration.field_class_declarators);
-               break;
-       case NODE_TYPEDEF:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.field_class_def.field_class_declarators);
-               break;
-       case NODE_TYPEALIAS_TARGET:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.field_class_alias_target.field_class_declarators);
-               break;
-       case NODE_TYPEALIAS_ALIAS:
-               _bt_list_splice_tail(&node->tmp_head, &parent->u.field_class_alias_name.field_class_declarators);
-               break;
-
-       case NODE_ROOT:
-       case NODE_EVENT:
-       case NODE_STREAM:
-       case NODE_ENV:
-       case NODE_TRACE:
-       case NODE_CLOCK:
-       case NODE_CALLSITE:
-       case NODE_VARIANT:
-       case NODE_STRUCT:
-       case NODE_TYPEALIAS:
-       case NODE_ENUM:
-       case NODE_FLOATING_POINT:
-       case NODE_INTEGER:
-       case NODE_STRING:
-       case NODE_CTF_EXPRESSION:
-       case NODE_TYPE_SPECIFIER:
-       case NODE_TYPE_SPECIFIER_LIST:
-       case NODE_POINTER:
-       case NODE_ENUMERATOR:
-       case NODE_UNARY_EXPRESSION:
-               return -EPERM;
-
-       case NODE_UNKNOWN:
-       default:
-               BT_LOGE("Unknown node type: node-type=%d", parent->type);
-               return -EINVAL;
-       }
-       return 0;
-}
-
-/*
- * set_parent_node
- *
- * Link node to parent. Returns 0 on success, -EPERM if it is not permitted to
- * create the link declared by the input, -ENOENT if node or parent is NULL,
- * -EINVAL if there is an internal structure problem.
- */
-static int set_parent_node(struct ctf_node *node,
-                        struct ctf_node *parent)
-{
-       if (!node || !parent)
-               return -ENOENT;
-
-       /* Note: Linking to parent will be done only by an external visitor */
-
-       switch (node->type) {
-       case NODE_ROOT:
-               BT_LOGE_STR("Trying to reparent root node.");
-               return -EINVAL;
-
-       case NODE_EVENT:
-               if (parent->type == NODE_ROOT) {
-                       _bt_list_splice_tail(&node->tmp_head, &parent->u.root.event);
-               } else {
-                       return -EPERM;
-               }
-               break;
-       case NODE_STREAM:
-               if (parent->type == NODE_ROOT) {
-                       _bt_list_splice_tail(&node->tmp_head, &parent->u.root.stream);
-               } else {
-                       return -EPERM;
-               }
-               break;
-       case NODE_ENV:
-               if (parent->type == NODE_ROOT) {
-                       _bt_list_splice_tail(&node->tmp_head, &parent->u.root.env);
-               } else {
-                       return -EPERM;
-               }
-               break;
-       case NODE_TRACE:
-               if (parent->type == NODE_ROOT) {
-                       _bt_list_splice_tail(&node->tmp_head, &parent->u.root.trace);
-               } else {
-                       return -EPERM;
-               }
-               break;
-       case NODE_CLOCK:
-               if (parent->type == NODE_ROOT) {
-                       _bt_list_splice_tail(&node->tmp_head, &parent->u.root.clock);
-               } else {
-                       return -EPERM;
-               }
-               break;
-       case NODE_CALLSITE:
-               if (parent->type == NODE_ROOT) {
-                       _bt_list_splice_tail(&node->tmp_head, &parent->u.root.callsite);
-               } else {
-                       return -EPERM;
-               }
-               break;
-
-       case NODE_CTF_EXPRESSION:
-               return reparent_ctf_expression(node, parent);
-       case NODE_UNARY_EXPRESSION:
-               if (parent->type == NODE_TYPE_DECLARATOR)
-                       parent->u.field_class_declarator.bitfield_len = node;
-               else
-                       return -EPERM;
-               break;
-
-       case NODE_TYPEDEF:
-               return reparent_typedef(node, parent);
-       case NODE_TYPEALIAS_TARGET:
-               if (parent->type == NODE_TYPEALIAS)
-                       parent->u.field_class_alias.target = node;
-               else
-                       return -EINVAL;
-               /* fall-through */
-       case NODE_TYPEALIAS_ALIAS:
-               if (parent->type == NODE_TYPEALIAS)
-                       parent->u.field_class_alias.alias = node;
-               else
-                       return -EINVAL;
-               /* fall-through */
-       case NODE_TYPEALIAS:
-               return reparent_field_class_alias(node, parent);
-
-       case NODE_POINTER:
-               if (parent->type == NODE_TYPE_DECLARATOR) {
-                       _bt_list_splice_tail(&node->tmp_head, &parent->u.field_class_declarator.pointers);
-               } else
-                       return -EPERM;
-               break;
-       case NODE_TYPE_DECLARATOR:
-               return reparent_field_class_declarator(node, parent);
-
-       case NODE_TYPE_SPECIFIER_LIST:
-               return reparent_field_class_specifier_list(node, parent);
-
-       case NODE_TYPE_SPECIFIER:
-               return reparent_field_class_specifier(node, parent);
-
-       case NODE_FLOATING_POINT:
-       case NODE_INTEGER:
-       case NODE_STRING:
-       case NODE_ENUM:
-       case NODE_VARIANT:
-       case NODE_STRUCT:
-               return -EINVAL; /* Dealt with internally within grammar */
-
-       case NODE_ENUMERATOR:
-               if (parent->type == NODE_ENUM) {
-                       _bt_list_splice_tail(&node->tmp_head, &parent->u._enum.enumerator_list);
-               } else {
-                       return -EPERM;
-               }
-               break;
-       case NODE_STRUCT_OR_VARIANT_DECLARATION:
-               switch (parent->type) {
-               case NODE_STRUCT:
-                       _bt_list_splice_tail(&node->tmp_head, &parent->u._struct.declaration_list);
-                       break;
-               case NODE_VARIANT:
-                       _bt_list_splice_tail(&node->tmp_head, &parent->u.variant.declaration_list);
-                       break;
-               default:
-                       return -EINVAL;
-               }
-               break;
-
-       case NODE_UNKNOWN:
-       default:
-               BT_LOGE("Unknown node type: node-type=%d", parent->type);
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static
-void yyerror(struct ctf_scanner *scanner, yyscan_t yyscanner, const char *str)
-{
-       _BT_LOGE_APPEND_CAUSE_LINENO(yyget_lineno(scanner->scanner),
-               "%s: token=\"%s\"", str, yyget_text(scanner->scanner));
-}
-
-#define reparent_error(scanner, str)                           \
-do {                                                           \
-       yyerror(scanner, scanner->scanner, YY_("reparent_error: " str)); \
-       YYERROR;                                                \
-} while (0)
-
-static struct ctf_ast *ctf_ast_alloc(struct ctf_scanner *scanner)
-{
-       struct ctf_ast *ast;
-
-       ast = objstack_alloc(scanner->objstack, sizeof(*ast));
-       if (!ast)
-               return NULL;
-       ast->root.type = NODE_ROOT;
-       BT_INIT_LIST_HEAD(&ast->root.tmp_head);
-       BT_INIT_LIST_HEAD(&ast->root.u.root.declaration_list);
-       BT_INIT_LIST_HEAD(&ast->root.u.root.trace);
-       BT_INIT_LIST_HEAD(&ast->root.u.root.env);
-       BT_INIT_LIST_HEAD(&ast->root.u.root.stream);
-       BT_INIT_LIST_HEAD(&ast->root.u.root.event);
-       BT_INIT_LIST_HEAD(&ast->root.u.root.clock);
-       BT_INIT_LIST_HEAD(&ast->root.u.root.callsite);
-       return ast;
-}
-
-int ctf_scanner_append_ast(struct ctf_scanner *scanner, FILE *input)
-{
-       /* Start processing new stream */
-       yyrestart(input, scanner->scanner);
-       return yyparse(scanner, scanner->scanner);
-}
-
-struct ctf_scanner *ctf_scanner_alloc(void)
-{
-       struct ctf_scanner *scanner;
-       int ret;
-
-       scanner = malloc(sizeof(*scanner));
-       if (!scanner)
-               return NULL;
-       memset(scanner, 0, sizeof(*scanner));
-       ret = yylex_init_extra(scanner, &scanner->scanner);
-       if (ret) {
-               BT_LOGE("yylex_init_extra() failed: ret=%d", ret);
-               goto cleanup_scanner;
-       }
-       scanner->objstack = objstack_create();
-       if (!scanner->objstack)
-               goto cleanup_lexer;
-       scanner->ast = ctf_ast_alloc(scanner);
-       if (!scanner->ast)
-               goto cleanup_objstack;
-       init_scope(&scanner->root_scope, NULL);
-       scanner->cs = &scanner->root_scope;
-
-       return scanner;
-
-cleanup_objstack:
-       objstack_destroy(scanner->objstack);
-cleanup_lexer:
-       ret = yylex_destroy(scanner->scanner);
-       if (!ret)
-               BT_LOGE("yylex_destroy() failed: scanner-addr=%p, ret=%d",
-                       scanner, ret);
-cleanup_scanner:
-       free(scanner);
-       return NULL;
-}
-
-void ctf_scanner_free(struct ctf_scanner *scanner)
-{
-       int ret;
-
-       if (!scanner)
-               return;
-
-       struct ctf_scanner_scope *scope = scanner->cs;
-
-       do {
-               struct ctf_scanner_scope *parent = scope->parent;
-               finalize_scope(scope);
-
-               /*
-                * The root scope is allocated within the ctf_scanner structure,
-                * do doesn't need freeing.  All others are allocated on their
-                * own.
-                */
-               if (scope != &scanner->root_scope)
-                       free(scope);
-
-               scope = parent;
-       } while (scope);
-
-       objstack_destroy(scanner->objstack);
-       ret = yylex_destroy(scanner->scanner);
-       if (ret)
-               BT_LOGE("yylex_destroy() failed: scanner-addr=%p, ret=%d",
-                       scanner, ret);
-       free(scanner);
-}
-
-/*
- * The bison-provided version of strlen (yystrlen) generates a benign
- * -Wnull-dereference warning.  That version is used when building on cygwin,
- * for example, but you can also enable it by hand (to test) by removing the
- * preprocessor conditional around it.
- *
- * Define yystrlen such that it will always use strlen.  As far as we know,
- * strlen provided by all the platforms we use is reliable.
- */
-#define yystrlen strlen
-
-%}
-
-/*
- * This ends up in parser.h and makes sure those who want to include it pass
- * through parser-wrap.h.
- */
-%code requires {
-#ifndef ALLOW_INCLUDE_PARSER_H
-# error "Don't include parser.h directly, include parser-wrap.h instead."
-#endif
-}
-
-%code provides {
-       BT_HIDDEN
-       void setstring(struct ctf_scanner *scanner, YYSTYPE *lvalp, const char *src);
-
-       BT_HIDDEN
-       int import_string(struct ctf_scanner *scanner, YYSTYPE *lvalp, const char *src, char delim);
-}
-
-%define api.pure
-       /* %locations */
-%error-verbose
-%parse-param {struct ctf_scanner *scanner}
-%parse-param {yyscan_t yyscanner}
-%lex-param {yyscan_t yyscanner}
-/*
- * Expect two shift-reduce conflicts. Caused by enum name-opt : type {}
- * vs struct { int :value; } (unnamed bit-field). The default is to
- * shift, so whenever we encounter an enumeration, we are doing the
- * proper thing (shift). It is illegal to declare an enumeration
- * "bit-field", so it is OK if this situation ends up in a parsing
- * error.
- */
-%expect 2
-%start file
-%token CTF_INTEGER_LITERAL CTF_STRING_LITERAL CTF_CHARACTER_LITERAL CTF_LSBRAC CTF_RSBRAC CTF_LPAREN CTF_RPAREN CTF_LBRAC CTF_RBRAC CTF_RARROW CTF_STAR CTF_PLUS CTF_MINUS CTF_LT CTF_GT CTF_TYPEASSIGN CTF_COLON CTF_SEMICOLON CTF_DOTDOTDOT CTF_DOT CTF_EQUAL CTF_COMMA CTF_CONST CTF_CHAR CTF_DOUBLE CTF_ENUM CTF_ENV CTF_EVENT CTF_FLOATING_POINT CTF_FLOAT CTF_INTEGER CTF_INT CTF_LONG CTF_SHORT CTF_SIGNED CTF_STREAM CTF_STRING CTF_STRUCT CTF_TRACE CTF_CALLSITE CTF_CLOCK CTF_TYPEALIAS CTF_TYPEDEF CTF_UNSIGNED CTF_VARIANT CTF_VOID CTF_BOOL CTF_COMPLEX CTF_IMAGINARY CTF_TOK_ALIGN
-%token <s> IDENTIFIER ID_TYPE
-%token CTF_ERROR
-%union
-{
-       long long ll;
-       unsigned long long ull;
-       char c;
-       char *s;
-       struct ctf_node *n;
-}
-
-%type <s> CTF_STRING_LITERAL CTF_CHARACTER_LITERAL
-
-%type <s> keywords
-
-%type <ull> CTF_INTEGER_LITERAL
-%type <n> postfix_expression unary_expression unary_expression_or_range
-
-%type <n> declaration
-%type <n> event_declaration
-%type <n> stream_declaration
-%type <n> env_declaration
-%type <n> trace_declaration
-%type <n> clock_declaration
-%type <n> callsite_declaration
-%type <n> integer_declaration_specifiers
-%type <n> declaration_specifiers
-%type <n> alias_declaration_specifiers
-
-%type <n> field_class_declarator_list
-%type <n> integer_field_class_specifier
-%type <n> field_class_specifier
-%type <n> struct_class_specifier
-%type <n> variant_field_class_specifier
-%type <n> enum_field_class_specifier
-%type <n> struct_or_variant_declaration_list
-%type <n> struct_or_variant_declaration
-%type <n> struct_or_variant_declarator_list
-%type <n> struct_or_variant_declarator
-%type <n> enumerator_list
-%type <n> enumerator
-%type <n> abstract_declarator_list
-%type <n> abstract_declarator
-%type <n> direct_abstract_declarator
-%type <n> alias_abstract_declarator_list
-%type <n> alias_abstract_declarator
-%type <n> direct_alias_abstract_declarator
-%type <n> declarator
-%type <n> direct_declarator
-%type <n> field_class_declarator
-%type <n> direct_field_class_declarator
-%type <n> pointer
-%type <n> ctf_assignment_expression_list
-%type <n> ctf_assignment_expression
-
-%%
-
-file:
-               declaration
-               {
-                       if (set_parent_node($1, &ctf_scanner_get_ast(scanner)->root))
-                               reparent_error(scanner, "error reparenting to root");
-               }
-       |       file declaration
-               {
-                       if (set_parent_node($2, &ctf_scanner_get_ast(scanner)->root))
-                               reparent_error(scanner, "error reparenting to root");
-               }
-       ;
-
-keywords:
-               CTF_VOID
-               {       $$ = yylval.s;          }
-       |       CTF_CHAR
-               {       $$ = yylval.s;          }
-       |       CTF_SHORT
-               {       $$ = yylval.s;          }
-       |       CTF_INT
-               {       $$ = yylval.s;          }
-       |       CTF_LONG
-               {       $$ = yylval.s;          }
-       |       CTF_FLOAT
-               {       $$ = yylval.s;          }
-       |       CTF_DOUBLE
-               {       $$ = yylval.s;          }
-       |       CTF_SIGNED
-               {       $$ = yylval.s;          }
-       |       CTF_UNSIGNED
-               {       $$ = yylval.s;          }
-       |       CTF_BOOL
-               {       $$ = yylval.s;          }
-       |       CTF_COMPLEX
-               {       $$ = yylval.s;          }
-       |       CTF_IMAGINARY
-               {       $$ = yylval.s;          }
-       |       CTF_FLOATING_POINT
-               {       $$ = yylval.s;          }
-       |       CTF_INTEGER
-               {       $$ = yylval.s;          }
-       |       CTF_STRING
-               {       $$ = yylval.s;          }
-       |       CTF_ENUM
-               {       $$ = yylval.s;          }
-       |       CTF_VARIANT
-               {       $$ = yylval.s;          }
-       |       CTF_STRUCT
-               {       $$ = yylval.s;          }
-       |       CTF_CONST
-               {       $$ = yylval.s;          }
-       |       CTF_TYPEDEF
-               {       $$ = yylval.s;          }
-       |       CTF_EVENT
-               {       $$ = yylval.s;          }
-       |       CTF_STREAM
-               {       $$ = yylval.s;          }
-       |       CTF_ENV
-               {       $$ = yylval.s;          }
-       |       CTF_TRACE
-               {       $$ = yylval.s;          }
-       |       CTF_CLOCK
-               {       $$ = yylval.s;          }
-       |       CTF_CALLSITE
-               {       $$ = yylval.s;          }
-       |       CTF_TOK_ALIGN
-               {       $$ = yylval.s;          }
-       ;
-
-
-/* 2: Phrase structure grammar */
-
-postfix_expression:
-               IDENTIFIER
-               {
-                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
-                       $$->u.unary_expression.type = UNARY_STRING;
-                       $$->u.unary_expression.u.string = yylval.s;
-               }
-       |       ID_TYPE
-               {
-                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
-                       $$->u.unary_expression.type = UNARY_STRING;
-                       $$->u.unary_expression.u.string = yylval.s;
-               }
-       |       keywords
-               {
-                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
-                       $$->u.unary_expression.type = UNARY_STRING;
-                       $$->u.unary_expression.u.string = yylval.s;
-               }
-       |       CTF_INTEGER_LITERAL
-               {
-                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
-                       $$->u.unary_expression.type = UNARY_UNSIGNED_CONSTANT;
-                       $$->u.unary_expression.u.unsigned_constant = $1;
-               }
-       |       CTF_STRING_LITERAL
-               {
-                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
-                       $$->u.unary_expression.type = UNARY_STRING;
-                       $$->u.unary_expression.u.string = $1;
-               }
-       |       CTF_CHARACTER_LITERAL
-               {
-                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
-                       $$->u.unary_expression.type = UNARY_STRING;
-                       $$->u.unary_expression.u.string = $1;
-               }
-       |       CTF_LPAREN unary_expression CTF_RPAREN
-               {
-                       $$ = $2;
-               }
-       |       postfix_expression CTF_LSBRAC unary_expression CTF_RSBRAC
-               {
-                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
-                       $$->u.unary_expression.type = UNARY_SBRAC;
-                       $$->u.unary_expression.u.sbrac_exp = $3;
-                       bt_list_splice(&($1)->tmp_head, &($$)->tmp_head);
-                       bt_list_add_tail(&($$)->siblings, &($$)->tmp_head);
-               }
-       |       postfix_expression CTF_DOT IDENTIFIER
-               {
-                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
-                       $$->u.unary_expression.type = UNARY_STRING;
-                       $$->u.unary_expression.u.string = yylval.s;
-                       $$->u.unary_expression.link = UNARY_DOTLINK;
-                       bt_list_splice(&($1)->tmp_head, &($$)->tmp_head);
-                       bt_list_add_tail(&($$)->siblings, &($$)->tmp_head);
-               }
-       |       postfix_expression CTF_DOT ID_TYPE
-               {
-                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
-                       $$->u.unary_expression.type = UNARY_STRING;
-                       $$->u.unary_expression.u.string = yylval.s;
-                       $$->u.unary_expression.link = UNARY_DOTLINK;
-                       bt_list_splice(&($1)->tmp_head, &($$)->tmp_head);
-                       bt_list_add_tail(&($$)->siblings, &($$)->tmp_head);
-               }
-       |       postfix_expression CTF_DOT keywords
-               {
-                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
-                       $$->u.unary_expression.type = UNARY_STRING;
-                       $$->u.unary_expression.u.string = yylval.s;
-                       $$->u.unary_expression.link = UNARY_DOTLINK;
-                       bt_list_splice(&($1)->tmp_head, &($$)->tmp_head);
-                       bt_list_add_tail(&($$)->siblings, &($$)->tmp_head);
-               }
-       |       postfix_expression CTF_RARROW IDENTIFIER
-               {
-                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
-                       $$->u.unary_expression.type = UNARY_STRING;
-                       $$->u.unary_expression.u.string = yylval.s;
-                       $$->u.unary_expression.link = UNARY_ARROWLINK;
-                       bt_list_splice(&($1)->tmp_head, &($$)->tmp_head);
-                       bt_list_add_tail(&($$)->siblings, &($$)->tmp_head);
-               }
-       |       postfix_expression CTF_RARROW ID_TYPE
-               {
-                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
-                       $$->u.unary_expression.type = UNARY_STRING;
-                       $$->u.unary_expression.u.string = yylval.s;
-                       $$->u.unary_expression.link = UNARY_ARROWLINK;
-                       bt_list_splice(&($1)->tmp_head, &($$)->tmp_head);
-                       bt_list_add_tail(&($$)->siblings, &($$)->tmp_head);
-               }
-       ;
-
-unary_expression:
-               postfix_expression
-               {       $$ = $1;                                }
-       |       CTF_PLUS postfix_expression
-               {
-                       $$ = $2;
-                       if ($$->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT
-                               && $$->u.unary_expression.type != UNARY_SIGNED_CONSTANT) {
-                               reparent_error(scanner, "expecting numeric constant");
-                       }
-               }
-       |       CTF_MINUS postfix_expression
-               {
-                       $$ = $2;
-                       if ($$->u.unary_expression.type == UNARY_UNSIGNED_CONSTANT) {
-                               $$->u.unary_expression.type = UNARY_SIGNED_CONSTANT;
-                               $$->u.unary_expression.u.signed_constant =
-                                       -($$->u.unary_expression.u.unsigned_constant);
-                       } else if ($$->u.unary_expression.type == UNARY_SIGNED_CONSTANT) {
-                               $$->u.unary_expression.u.signed_constant =
-                                       -($$->u.unary_expression.u.signed_constant);
-                       } else {
-                               reparent_error(scanner, "expecting numeric constant");
-                       }
-               }
-       ;
-
-unary_expression_or_range:
-               unary_expression CTF_DOTDOTDOT unary_expression
-               {
-                       $$ = $1;
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->tmp_head);
-                       $3->u.unary_expression.link = UNARY_DOTDOTDOT;
-               }
-       |       unary_expression
-               {       $$ = $1;                }
-       ;
-
-/* 2.2: Declarations */
-
-declaration:
-               declaration_specifiers CTF_SEMICOLON
-               {       $$ = $1;        }
-       |       event_declaration
-               {       $$ = $1;        }
-       |       stream_declaration
-               {       $$ = $1;        }
-       |       env_declaration
-               {       $$ = $1;        }
-       |       trace_declaration
-               {       $$ = $1;        }
-       |       clock_declaration
-               {       $$ = $1;        }
-       |       callsite_declaration
-               {       $$ = $1;        }
-       |       declaration_specifiers CTF_TYPEDEF declaration_specifiers field_class_declarator_list CTF_SEMICOLON
-               {
-                       struct ctf_node *list;
-
-                       $$ = make_node(scanner, NODE_TYPEDEF);
-                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       $$->u.field_class_def.field_class_specifier_list = list;
-                       _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       _bt_list_splice_tail(&($3)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       _bt_list_splice_tail(&($4)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
-               }
-       |       CTF_TYPEDEF declaration_specifiers field_class_declarator_list CTF_SEMICOLON
-               {
-                       struct ctf_node *list;
-
-                       $$ = make_node(scanner, NODE_TYPEDEF);
-                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       $$->u.field_class_def.field_class_specifier_list = list;
-                       _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
-               }
-       |       declaration_specifiers CTF_TYPEDEF field_class_declarator_list CTF_SEMICOLON
-               {
-                       struct ctf_node *list;
-
-                       $$ = make_node(scanner, NODE_TYPEDEF);
-                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       $$->u.field_class_def.field_class_specifier_list = list;
-                       _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
-               }
-       |       CTF_TYPEALIAS declaration_specifiers abstract_declarator_list CTF_TYPEASSIGN alias_declaration_specifiers alias_abstract_declarator_list CTF_SEMICOLON
-               {
-                       struct ctf_node *list;
-
-                       $$ = make_node(scanner, NODE_TYPEALIAS);
-                       $$->u.field_class_alias.target = make_node(scanner, NODE_TYPEALIAS_TARGET);
-                       $$->u.field_class_alias.alias = make_node(scanner, NODE_TYPEALIAS_ALIAS);
-
-                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       $$->u.field_class_alias.target->u.field_class_alias_target.field_class_specifier_list = list;
-                       _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_alias.target->u.field_class_alias_target.field_class_declarators);
-
-                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       $$->u.field_class_alias.alias->u.field_class_alias_name.field_class_specifier_list = list;
-                       _bt_list_splice_tail(&($5)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       _bt_list_splice_tail(&($6)->tmp_head, &($$)->u.field_class_alias.alias->u.field_class_alias_name.field_class_declarators);
-               }
-       ;
-
-event_declaration:
-               event_declaration_begin event_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_EVENT);
-               }
-       |       event_declaration_begin ctf_assignment_expression_list event_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_EVENT);
-                       if (set_parent_node($2, $$))
-                               reparent_error(scanner, "event_declaration");
-               }
-       ;
-
-event_declaration_begin:
-               CTF_EVENT CTF_LBRAC
-               {       push_scope(scanner);    }
-       ;
-
-event_declaration_end:
-               CTF_RBRAC CTF_SEMICOLON
-               {       pop_scope(scanner);     }
-       ;
-
-
-stream_declaration:
-               stream_declaration_begin stream_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_STREAM);
-               }
-       |       stream_declaration_begin ctf_assignment_expression_list stream_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_STREAM);
-                       if (set_parent_node($2, $$))
-                               reparent_error(scanner, "stream_declaration");
-               }
-       ;
-
-stream_declaration_begin:
-               CTF_STREAM CTF_LBRAC
-               {       push_scope(scanner);    }
-       ;
-
-stream_declaration_end:
-               CTF_RBRAC CTF_SEMICOLON
-               {       pop_scope(scanner);     }
-       ;
-
-env_declaration:
-               env_declaration_begin env_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_ENV);
-               }
-       |       env_declaration_begin ctf_assignment_expression_list env_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_ENV);
-                       if (set_parent_node($2, $$))
-                               reparent_error(scanner, "env declaration");
-               }
-       ;
-
-env_declaration_begin:
-               CTF_ENV CTF_LBRAC
-               {       push_scope(scanner);    }
-       ;
-
-env_declaration_end:
-               CTF_RBRAC CTF_SEMICOLON
-               {       pop_scope(scanner);     }
-       ;
-
-trace_declaration:
-               trace_declaration_begin trace_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_TRACE);
-               }
-       |       trace_declaration_begin ctf_assignment_expression_list trace_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_TRACE);
-                       if (set_parent_node($2, $$))
-                               reparent_error(scanner, "trace_declaration");
-               }
-       ;
-
-trace_declaration_begin:
-               CTF_TRACE CTF_LBRAC
-               {       push_scope(scanner);    }
-       ;
-
-trace_declaration_end:
-               CTF_RBRAC CTF_SEMICOLON
-               {       pop_scope(scanner);     }
-       ;
-
-clock_declaration:
-               CTF_CLOCK clock_declaration_begin clock_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_CLOCK);
-               }
-       |       CTF_CLOCK clock_declaration_begin ctf_assignment_expression_list clock_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_CLOCK);
-                       if (set_parent_node($3, $$))
-                               reparent_error(scanner, "trace_declaration");
-               }
-       ;
-
-clock_declaration_begin:
-               CTF_LBRAC
-               {       push_scope(scanner);    }
-       ;
-
-clock_declaration_end:
-               CTF_RBRAC CTF_SEMICOLON
-               {       pop_scope(scanner);     }
-       ;
-
-callsite_declaration:
-               CTF_CALLSITE callsite_declaration_begin callsite_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_CALLSITE);
-               }
-       |       CTF_CALLSITE callsite_declaration_begin ctf_assignment_expression_list callsite_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_CALLSITE);
-                       if (set_parent_node($3, $$))
-                               reparent_error(scanner, "trace_declaration");
-               }
-       ;
-
-callsite_declaration_begin:
-               CTF_LBRAC
-               {       push_scope(scanner);    }
-       ;
-
-callsite_declaration_end:
-               CTF_RBRAC CTF_SEMICOLON
-               {       pop_scope(scanner);     }
-       ;
-
-integer_declaration_specifiers:
-               CTF_CONST
-               {
-                       struct ctf_node *node;
-
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       node->u.field_class_specifier.type = TYPESPEC_CONST;
-                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
-               }
-       |       integer_field_class_specifier
-               {
-                       struct ctf_node *node;
-
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       node = $1;
-                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
-               }
-       |       integer_declaration_specifiers CTF_CONST
-               {
-                       struct ctf_node *node;
-
-                       $$ = $1;
-                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       node->u.field_class_specifier.type = TYPESPEC_CONST;
-                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
-               }
-       |       integer_declaration_specifiers integer_field_class_specifier
-               {
-                       $$ = $1;
-                       bt_list_add_tail(&($2)->siblings, &($$)->u.field_class_specifier_list.head);
-               }
-       ;
-
-declaration_specifiers:
-               CTF_CONST
-               {
-                       struct ctf_node *node;
-
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       node->u.field_class_specifier.type = TYPESPEC_CONST;
-                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
-               }
-       |       field_class_specifier
-               {
-                       struct ctf_node *node;
-
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       node = $1;
-                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
-               }
-       |       declaration_specifiers CTF_CONST
-               {
-                       struct ctf_node *node;
-
-                       $$ = $1;
-                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       node->u.field_class_specifier.type = TYPESPEC_CONST;
-                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
-               }
-       |       declaration_specifiers field_class_specifier
-               {
-                       $$ = $1;
-                       bt_list_add_tail(&($2)->siblings, &($$)->u.field_class_specifier_list.head);
-               }
-       ;
-
-field_class_declarator_list:
-               field_class_declarator
-               {       $$ = $1;        }
-       |       field_class_declarator_list CTF_COMMA field_class_declarator
-               {
-                       $$ = $1;
-                       bt_list_add_tail(&($3)->siblings, &($$)->tmp_head);
-               }
-       ;
-
-integer_field_class_specifier:
-               CTF_CHAR
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_CHAR;
-               }
-       |       CTF_SHORT
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_SHORT;
-               }
-       |       CTF_INT
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_INT;
-               }
-       |       CTF_LONG
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_LONG;
-               }
-       |       CTF_SIGNED
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_SIGNED;
-               }
-       |       CTF_UNSIGNED
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_UNSIGNED;
-               }
-       |       CTF_BOOL
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_BOOL;
-               }
-       |       ID_TYPE
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_ID_TYPE;
-                       $$->u.field_class_specifier.id_type = yylval.s;
-               }
-       |       CTF_INTEGER CTF_LBRAC CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_INTEGER;
-                       $$->u.field_class_specifier.node = make_node(scanner, NODE_INTEGER);
-               }
-       |       CTF_INTEGER CTF_LBRAC ctf_assignment_expression_list CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_INTEGER;
-                       $$->u.field_class_specifier.node = make_node(scanner, NODE_INTEGER);
-                       if (set_parent_node($3, $$->u.field_class_specifier.node))
-                               reparent_error(scanner, "integer reparent error");
-               }
-       ;
-
-field_class_specifier:
-               CTF_VOID
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_VOID;
-               }
-       |       CTF_CHAR
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_CHAR;
-               }
-       |       CTF_SHORT
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_SHORT;
-               }
-       |       CTF_INT
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_INT;
-               }
-       |       CTF_LONG
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_LONG;
-               }
-       |       CTF_FLOAT
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_FLOAT;
-               }
-       |       CTF_DOUBLE
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_DOUBLE;
-               }
-       |       CTF_SIGNED
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_SIGNED;
-               }
-       |       CTF_UNSIGNED
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_UNSIGNED;
-               }
-       |       CTF_BOOL
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_BOOL;
-               }
-       |       CTF_COMPLEX
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_COMPLEX;
-               }
-       |       CTF_IMAGINARY
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_IMAGINARY;
-               }
-       |       ID_TYPE
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_ID_TYPE;
-                       $$->u.field_class_specifier.id_type = yylval.s;
-               }
-       |       CTF_FLOATING_POINT CTF_LBRAC CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_FLOATING_POINT;
-                       $$->u.field_class_specifier.node = make_node(scanner, NODE_FLOATING_POINT);
-               }
-       |       CTF_FLOATING_POINT CTF_LBRAC ctf_assignment_expression_list CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_FLOATING_POINT;
-                       $$->u.field_class_specifier.node = make_node(scanner, NODE_FLOATING_POINT);
-                       if (set_parent_node($3, $$->u.field_class_specifier.node))
-                               reparent_error(scanner, "floating point reparent error");
-               }
-       |       CTF_INTEGER CTF_LBRAC CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_INTEGER;
-                       $$->u.field_class_specifier.node = make_node(scanner, NODE_INTEGER);
-               }
-       |       CTF_INTEGER CTF_LBRAC ctf_assignment_expression_list CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_INTEGER;
-                       $$->u.field_class_specifier.node = make_node(scanner, NODE_INTEGER);
-                       if (set_parent_node($3, $$->u.field_class_specifier.node))
-                               reparent_error(scanner, "integer reparent error");
-               }
-       |       CTF_STRING
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_STRING;
-                       $$->u.field_class_specifier.node = make_node(scanner, NODE_STRING);
-               }
-       |       CTF_STRING CTF_LBRAC CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_STRING;
-                       $$->u.field_class_specifier.node = make_node(scanner, NODE_STRING);
-               }
-       |       CTF_STRING CTF_LBRAC ctf_assignment_expression_list CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_STRING;
-                       $$->u.field_class_specifier.node = make_node(scanner, NODE_STRING);
-                       if (set_parent_node($3, $$->u.field_class_specifier.node))
-                               reparent_error(scanner, "string reparent error");
-               }
-       |       CTF_ENUM enum_field_class_specifier
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_ENUM;
-                       $$->u.field_class_specifier.node = $2;
-               }
-       |       CTF_VARIANT variant_field_class_specifier
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_VARIANT;
-                       $$->u.field_class_specifier.node = $2;
-               }
-       |       CTF_STRUCT struct_class_specifier
-               {
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       $$->u.field_class_specifier.type = TYPESPEC_STRUCT;
-                       $$->u.field_class_specifier.node = $2;
-               }
-       ;
-
-struct_class_specifier:
-               struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_STRUCT);
-                       $$->u._struct.has_body = 1;
-                       if ($2 && set_parent_node($2, $$))
-                               reparent_error(scanner, "struct reparent error");
-               }
-       |       IDENTIFIER struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_STRUCT);
-                       $$->u._struct.has_body = 1;
-                       $$->u._struct.name = $1;
-                       if ($3 && set_parent_node($3, $$))
-                               reparent_error(scanner, "struct reparent error");
-               }
-       |       ID_TYPE struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_STRUCT);
-                       $$->u._struct.has_body = 1;
-                       $$->u._struct.name = $1;
-                       if ($3 && set_parent_node($3, $$))
-                               reparent_error(scanner, "struct reparent error");
-               }
-       |       IDENTIFIER
-               {
-                       $$ = make_node(scanner, NODE_STRUCT);
-                       $$->u._struct.has_body = 0;
-                       $$->u._struct.name = $1;
-               }
-       |       ID_TYPE
-               {
-                       $$ = make_node(scanner, NODE_STRUCT);
-                       $$->u._struct.has_body = 0;
-                       $$->u._struct.name = $1;
-               }
-       |       struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end CTF_TOK_ALIGN CTF_LPAREN unary_expression CTF_RPAREN
-               {
-                       $$ = make_node(scanner, NODE_STRUCT);
-                       $$->u._struct.has_body = 1;
-                       bt_list_add_tail(&($6)->siblings, &$$->u._struct.min_align);
-                       if ($2 && set_parent_node($2, $$))
-                               reparent_error(scanner, "struct reparent error");
-               }
-       |       IDENTIFIER struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end CTF_TOK_ALIGN CTF_LPAREN unary_expression CTF_RPAREN
-               {
-                       $$ = make_node(scanner, NODE_STRUCT);
-                       $$->u._struct.has_body = 1;
-                       $$->u._struct.name = $1;
-                       bt_list_add_tail(&($7)->siblings, &$$->u._struct.min_align);
-                       if ($3 && set_parent_node($3, $$))
-                               reparent_error(scanner, "struct reparent error");
-               }
-       |       ID_TYPE struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end CTF_TOK_ALIGN CTF_LPAREN unary_expression CTF_RPAREN
-               {
-                       $$ = make_node(scanner, NODE_STRUCT);
-                       $$->u._struct.has_body = 1;
-                       $$->u._struct.name = $1;
-                       bt_list_add_tail(&($7)->siblings, &$$->u._struct.min_align);
-                       if ($3 && set_parent_node($3, $$))
-                               reparent_error(scanner, "struct reparent error");
-               }
-       ;
-
-struct_declaration_begin:
-               CTF_LBRAC
-               {       push_scope(scanner);    }
-       ;
-
-struct_declaration_end:
-               CTF_RBRAC
-               {       pop_scope(scanner);     }
-       ;
-
-variant_field_class_specifier:
-               variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_VARIANT);
-                       $$->u.variant.has_body = 1;
-                       if ($2 && set_parent_node($2, $$))
-                               reparent_error(scanner, "variant reparent error");
-               }
-       |       CTF_LT IDENTIFIER CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_VARIANT);
-                       $$->u.variant.has_body = 1;
-                       $$->u.variant.choice = $2;
-                       if ($5 && set_parent_node($5, $$))
-                               reparent_error(scanner, "variant reparent error");
-               }
-       |       CTF_LT ID_TYPE CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_VARIANT);
-                       $$->u.variant.has_body = 1;
-                       $$->u.variant.choice = $2;
-                       if ($5 && set_parent_node($5, $$))
-                               reparent_error(scanner, "variant reparent error");
-               }
-       |       IDENTIFIER variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_VARIANT);
-                       $$->u.variant.has_body = 1;
-                       $$->u.variant.name = $1;
-                       if ($3 && set_parent_node($3, $$))
-                               reparent_error(scanner, "variant reparent error");
-               }
-       |       IDENTIFIER CTF_LT IDENTIFIER CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_VARIANT);
-                       $$->u.variant.has_body = 1;
-                       $$->u.variant.name = $1;
-                       $$->u.variant.choice = $3;
-                       if ($6 && set_parent_node($6, $$))
-                               reparent_error(scanner, "variant reparent error");
-               }
-       |       IDENTIFIER CTF_LT IDENTIFIER CTF_GT
-               {
-                       $$ = make_node(scanner, NODE_VARIANT);
-                       $$->u.variant.has_body = 0;
-                       $$->u.variant.name = $1;
-                       $$->u.variant.choice = $3;
-               }
-       |       IDENTIFIER CTF_LT ID_TYPE CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_VARIANT);
-                       $$->u.variant.has_body = 1;
-                       $$->u.variant.name = $1;
-                       $$->u.variant.choice = $3;
-                       if ($6 && set_parent_node($6, $$))
-                               reparent_error(scanner, "variant reparent error");
-               }
-       |       IDENTIFIER CTF_LT ID_TYPE CTF_GT
-               {
-                       $$ = make_node(scanner, NODE_VARIANT);
-                       $$->u.variant.has_body = 0;
-                       $$->u.variant.name = $1;
-                       $$->u.variant.choice = $3;
-               }
-       |       ID_TYPE variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_VARIANT);
-                       $$->u.variant.has_body = 1;
-                       $$->u.variant.name = $1;
-                       if ($3 && set_parent_node($3, $$))
-                               reparent_error(scanner, "variant reparent error");
-               }
-       |       ID_TYPE CTF_LT IDENTIFIER CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_VARIANT);
-                       $$->u.variant.has_body = 1;
-                       $$->u.variant.name = $1;
-                       $$->u.variant.choice = $3;
-                       if ($6 && set_parent_node($6, $$))
-                               reparent_error(scanner, "variant reparent error");
-               }
-       |       ID_TYPE CTF_LT IDENTIFIER CTF_GT
-               {
-                       $$ = make_node(scanner, NODE_VARIANT);
-                       $$->u.variant.has_body = 0;
-                       $$->u.variant.name = $1;
-                       $$->u.variant.choice = $3;
-               }
-       |       ID_TYPE CTF_LT ID_TYPE CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
-               {
-                       $$ = make_node(scanner, NODE_VARIANT);
-                       $$->u.variant.has_body = 1;
-                       $$->u.variant.name = $1;
-                       $$->u.variant.choice = $3;
-                       if ($6 && set_parent_node($6, $$))
-                               reparent_error(scanner, "variant reparent error");
-               }
-       |       ID_TYPE CTF_LT ID_TYPE CTF_GT
-               {
-                       $$ = make_node(scanner, NODE_VARIANT);
-                       $$->u.variant.has_body = 0;
-                       $$->u.variant.name = $1;
-                       $$->u.variant.choice = $3;
-               }
-       ;
-
-variant_declaration_begin:
-               CTF_LBRAC
-               {       push_scope(scanner);    }
-       ;
-
-variant_declaration_end:
-               CTF_RBRAC
-               {       pop_scope(scanner);     }
-       ;
-
-enum_field_class_specifier:
-               CTF_LBRAC enumerator_list CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_ENUM);
-                       $$->u._enum.has_body = 1;
-                       _bt_list_splice_tail(&($2)->tmp_head, &($$)->u._enum.enumerator_list);
-               }
-       |       CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_ENUM);
-                       $$->u._enum.has_body = 1;
-                       ($$)->u._enum.container_field_class = $2;
-                       _bt_list_splice_tail(&($4)->tmp_head, &($$)->u._enum.enumerator_list);
-               }
-       |       IDENTIFIER CTF_LBRAC enumerator_list CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_ENUM);
-                       $$->u._enum.has_body = 1;
-                       $$->u._enum.enum_id = $1;
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._enum.enumerator_list);
-               }
-       |       IDENTIFIER CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_ENUM);
-                       $$->u._enum.has_body = 1;
-                       $$->u._enum.enum_id = $1;
-                       ($$)->u._enum.container_field_class = $3;
-                       _bt_list_splice_tail(&($5)->tmp_head, &($$)->u._enum.enumerator_list);
-               }
-       |       ID_TYPE CTF_LBRAC enumerator_list CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_ENUM);
-                       $$->u._enum.has_body = 1;
-                       $$->u._enum.enum_id = $1;
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._enum.enumerator_list);
-               }
-       |       ID_TYPE CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_ENUM);
-                       $$->u._enum.has_body = 1;
-                       $$->u._enum.enum_id = $1;
-                       ($$)->u._enum.container_field_class = $3;
-                       _bt_list_splice_tail(&($5)->tmp_head, &($$)->u._enum.enumerator_list);
-               }
-       |       CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_ENUM);
-                       $$->u._enum.has_body = 1;
-                       _bt_list_splice_tail(&($2)->tmp_head, &($$)->u._enum.enumerator_list);
-               }
-       |       CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_ENUM);
-                       $$->u._enum.has_body = 1;
-                       ($$)->u._enum.container_field_class = $2;
-                       _bt_list_splice_tail(&($4)->tmp_head, &($$)->u._enum.enumerator_list);
-               }
-       |       IDENTIFIER CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_ENUM);
-                       $$->u._enum.has_body = 1;
-                       $$->u._enum.enum_id = $1;
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._enum.enumerator_list);
-               }
-       |       IDENTIFIER CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_ENUM);
-                       $$->u._enum.has_body = 1;
-                       $$->u._enum.enum_id = $1;
-                       ($$)->u._enum.container_field_class = $3;
-                       _bt_list_splice_tail(&($5)->tmp_head, &($$)->u._enum.enumerator_list);
-               }
-       |       IDENTIFIER
-               {
-                       $$ = make_node(scanner, NODE_ENUM);
-                       $$->u._enum.has_body = 0;
-                       $$->u._enum.enum_id = $1;
-               }
-       |       ID_TYPE CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_ENUM);
-                       $$->u._enum.has_body = 1;
-                       $$->u._enum.enum_id = $1;
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._enum.enumerator_list);
-               }
-       |       ID_TYPE CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC
-               {
-                       $$ = make_node(scanner, NODE_ENUM);
-                       $$->u._enum.has_body = 1;
-                       $$->u._enum.enum_id = $1;
-                       ($$)->u._enum.container_field_class = $3;
-                       _bt_list_splice_tail(&($5)->tmp_head, &($$)->u._enum.enumerator_list);
-               }
-       |       ID_TYPE
-               {
-                       $$ = make_node(scanner, NODE_ENUM);
-                       $$->u._enum.has_body = 0;
-                       $$->u._enum.enum_id = $1;
-               }
-       ;
-
-struct_or_variant_declaration_list:
-               /* empty */
-               {       $$ = NULL;      }
-       |       struct_or_variant_declaration_list struct_or_variant_declaration
-               {
-                       if ($1) {
-                               $$ = $1;
-                               bt_list_add_tail(&($2)->siblings, &($$)->tmp_head);
-                       } else {
-                               $$ = $2;
-                               bt_list_add_tail(&($$)->siblings, &($$)->tmp_head);
-                       }
-               }
-       ;
-
-struct_or_variant_declaration:
-               declaration_specifiers struct_or_variant_declarator_list CTF_SEMICOLON
-               {
-                       struct ctf_node *list;
-
-                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       $$ = make_node(scanner, NODE_STRUCT_OR_VARIANT_DECLARATION);
-                       ($$)->u.struct_or_variant_declaration.field_class_specifier_list = list;
-                       _bt_list_splice_tail(&($2)->tmp_head, &($$)->u.struct_or_variant_declaration.field_class_declarators);
-               }
-       |       declaration_specifiers CTF_TYPEDEF declaration_specifiers field_class_declarator_list CTF_SEMICOLON
-               {
-                       struct ctf_node *list;
-
-                       $$ = make_node(scanner, NODE_TYPEDEF);
-                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       $$->u.field_class_def.field_class_specifier_list = list;
-                       _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       _bt_list_splice_tail(&($3)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       _bt_list_splice_tail(&($4)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
-               }
-       |       CTF_TYPEDEF declaration_specifiers field_class_declarator_list CTF_SEMICOLON
-               {
-                       struct ctf_node *list;
-
-                       $$ = make_node(scanner, NODE_TYPEDEF);
-                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       $$->u.field_class_def.field_class_specifier_list = list;
-                       _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
-               }
-       |       declaration_specifiers CTF_TYPEDEF field_class_declarator_list CTF_SEMICOLON
-               {
-                       struct ctf_node *list;
-
-                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       $$ = make_node(scanner, NODE_TYPEDEF);
-                       ($$)->u.struct_or_variant_declaration.field_class_specifier_list = list;
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
-               }
-       |       CTF_TYPEALIAS declaration_specifiers abstract_declarator_list CTF_TYPEASSIGN alias_declaration_specifiers alias_abstract_declarator_list CTF_SEMICOLON
-               {
-                       struct ctf_node *list;
-
-                       $$ = make_node(scanner, NODE_TYPEALIAS);
-                       $$->u.field_class_alias.target = make_node(scanner, NODE_TYPEALIAS_TARGET);
-                       $$->u.field_class_alias.alias = make_node(scanner, NODE_TYPEALIAS_ALIAS);
-
-                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       $$->u.field_class_alias.target->u.field_class_alias_target.field_class_specifier_list = list;
-                       _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_alias.target->u.field_class_alias_target.field_class_declarators);
-
-                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       $$->u.field_class_alias.alias->u.field_class_alias_name.field_class_specifier_list = list;
-                       _bt_list_splice_tail(&($5)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       _bt_list_splice_tail(&($6)->tmp_head, &($$)->u.field_class_alias.alias->u.field_class_alias_name.field_class_declarators);
-               }
-       ;
-
-alias_declaration_specifiers:
-               CTF_CONST
-               {
-                       struct ctf_node *node;
-
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       node->u.field_class_specifier.type = TYPESPEC_CONST;
-                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
-               }
-       |       field_class_specifier
-               {
-                       struct ctf_node *node;
-
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       node = $1;
-                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
-               }
-       |       IDENTIFIER
-               {
-                       struct ctf_node *node;
-
-                       add_type(scanner, $1);
-                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       node->u.field_class_specifier.type = TYPESPEC_ID_TYPE;
-                       node->u.field_class_specifier.id_type = yylval.s;
-                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
-               }
-       |       alias_declaration_specifiers CTF_CONST
-               {
-                       struct ctf_node *node;
-
-                       $$ = $1;
-                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       node->u.field_class_specifier.type = TYPESPEC_CONST;
-                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
-               }
-       |       alias_declaration_specifiers field_class_specifier
-               {
-                       $$ = $1;
-                       bt_list_add_tail(&($2)->siblings, &($$)->u.field_class_specifier_list.head);
-               }
-       |       alias_declaration_specifiers IDENTIFIER
-               {
-                       struct ctf_node *node;
-
-                       add_type(scanner, $2);
-                       $$ = $1;
-                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
-                       node->u.field_class_specifier.type = TYPESPEC_ID_TYPE;
-                       node->u.field_class_specifier.id_type = yylval.s;
-                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
-               }
-       ;
-
-struct_or_variant_declarator_list:
-               struct_or_variant_declarator
-               {       $$ = $1;        }
-       |       struct_or_variant_declarator_list CTF_COMMA struct_or_variant_declarator
-               {
-                       $$ = $1;
-                       bt_list_add_tail(&($3)->siblings, &($$)->tmp_head);
-               }
-       ;
-
-struct_or_variant_declarator:
-               declarator
-               {       $$ = $1;        }
-       |       CTF_COLON unary_expression
-               {       $$ = $2;        }
-       |       declarator CTF_COLON unary_expression
-               {
-                       $$ = $1;
-                       if (set_parent_node($3, $1))
-                               reparent_error(scanner, "struct_or_variant_declarator");
-               }
-       ;
-
-enumerator_list:
-               enumerator
-               {       $$ = $1;        }
-       |       enumerator_list CTF_COMMA enumerator
-               {
-                       $$ = $1;
-                       bt_list_add_tail(&($3)->siblings, &($$)->tmp_head);
-               }
-       ;
-
-enumerator:
-               IDENTIFIER
-               {
-                       $$ = make_node(scanner, NODE_ENUMERATOR);
-                       $$->u.enumerator.id = $1;
-               }
-       |       ID_TYPE
-               {
-                       $$ = make_node(scanner, NODE_ENUMERATOR);
-                       $$->u.enumerator.id = $1;
-               }
-       |       keywords
-               {
-                       $$ = make_node(scanner, NODE_ENUMERATOR);
-                       $$->u.enumerator.id = $1;
-               }
-       |       CTF_STRING_LITERAL
-               {
-                       $$ = make_node(scanner, NODE_ENUMERATOR);
-                       $$->u.enumerator.id = $1;
-               }
-       |       IDENTIFIER CTF_EQUAL unary_expression_or_range
-               {
-                       $$ = make_node(scanner, NODE_ENUMERATOR);
-                       $$->u.enumerator.id = $1;
-                       bt_list_splice(&($3)->tmp_head, &($$)->u.enumerator.values);
-               }
-       |       ID_TYPE CTF_EQUAL unary_expression_or_range
-               {
-                       $$ = make_node(scanner, NODE_ENUMERATOR);
-                       $$->u.enumerator.id = $1;
-                       bt_list_splice(&($3)->tmp_head, &($$)->u.enumerator.values);
-               }
-       |       keywords CTF_EQUAL unary_expression_or_range
-               {
-                       $$ = make_node(scanner, NODE_ENUMERATOR);
-                       $$->u.enumerator.id = $1;
-                       bt_list_splice(&($3)->tmp_head, &($$)->u.enumerator.values);
-               }
-       |       CTF_STRING_LITERAL CTF_EQUAL unary_expression_or_range
-               {
-                       $$ = make_node(scanner, NODE_ENUMERATOR);
-                       $$->u.enumerator.id = $1;
-                       bt_list_splice(&($3)->tmp_head, &($$)->u.enumerator.values);
-               }
-       ;
-
-abstract_declarator_list:
-               abstract_declarator
-               {       $$ = $1;        }
-       |       abstract_declarator_list CTF_COMMA abstract_declarator
-               {
-                       $$ = $1;
-                       bt_list_add_tail(&($3)->siblings, &($$)->tmp_head);
-               }
-       ;
-
-abstract_declarator:
-               direct_abstract_declarator
-               {       $$ = $1;        }
-       |       pointer direct_abstract_declarator
-               {
-                       $$ = $2;
-                       bt_list_splice(&($1)->tmp_head, &($$)->u.field_class_declarator.pointers);
-               }
-       ;
-
-direct_abstract_declarator:
-               /* empty */
-               {
-                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
-                        $$->u.field_class_declarator.type = TYPEDEC_ID;
-                       /* id is NULL */
-               }
-       |       IDENTIFIER
-               {
-                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
-                       $$->u.field_class_declarator.type = TYPEDEC_ID;
-                       $$->u.field_class_declarator.u.id = $1;
-               }
-       |       CTF_LPAREN abstract_declarator CTF_RPAREN
-               {
-                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
-                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
-                       $$->u.field_class_declarator.u.nested.field_class_declarator = $2;
-               }
-       |       direct_abstract_declarator CTF_LSBRAC unary_expression CTF_RSBRAC
-               {
-                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
-                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
-                       $$->u.field_class_declarator.u.nested.field_class_declarator = $1;
-                       BT_INIT_LIST_HEAD(&($$)->u.field_class_declarator.u.nested.length);
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_declarator.u.nested.length);
-               }
-       |       direct_abstract_declarator CTF_LSBRAC CTF_RSBRAC
-               {
-                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
-                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
-                       $$->u.field_class_declarator.u.nested.field_class_declarator = $1;
-                       $$->u.field_class_declarator.u.nested.abstract_array = 1;
-               }
-       ;
-
-alias_abstract_declarator_list:
-               alias_abstract_declarator
-               {       $$ = $1;        }
-       |       alias_abstract_declarator_list CTF_COMMA alias_abstract_declarator
-               {
-                       $$ = $1;
-                       bt_list_add_tail(&($3)->siblings, &($$)->tmp_head);
-               }
-       ;
-
-alias_abstract_declarator:
-               direct_alias_abstract_declarator
-               {       $$ = $1;        }
-       |       pointer direct_alias_abstract_declarator
-               {
-                       $$ = $2;
-                       bt_list_splice(&($1)->tmp_head, &($$)->u.field_class_declarator.pointers);
-               }
-       ;
-
-direct_alias_abstract_declarator:
-               /* empty */
-               {
-                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
-                        $$->u.field_class_declarator.type = TYPEDEC_ID;
-                       /* id is NULL */
-               }
-       |       CTF_LPAREN alias_abstract_declarator CTF_RPAREN
-               {
-                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
-                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
-                       $$->u.field_class_declarator.u.nested.field_class_declarator = $2;
-               }
-       |       direct_alias_abstract_declarator CTF_LSBRAC unary_expression CTF_RSBRAC
-               {
-                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
-                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
-                       $$->u.field_class_declarator.u.nested.field_class_declarator = $1;
-                       BT_INIT_LIST_HEAD(&($$)->u.field_class_declarator.u.nested.length);
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_declarator.u.nested.length);
-               }
-       |       direct_alias_abstract_declarator CTF_LSBRAC CTF_RSBRAC
-               {
-                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
-                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
-                       $$->u.field_class_declarator.u.nested.field_class_declarator = $1;
-                       $$->u.field_class_declarator.u.nested.abstract_array = 1;
-               }
-       ;
-
-declarator:
-               direct_declarator
-               {       $$ = $1;        }
-       |       pointer direct_declarator
-               {
-                       $$ = $2;
-                       bt_list_splice(&($1)->tmp_head, &($$)->u.field_class_declarator.pointers);
-               }
-       ;
-
-direct_declarator:
-               IDENTIFIER
-               {
-                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
-                       $$->u.field_class_declarator.type = TYPEDEC_ID;
-                       $$->u.field_class_declarator.u.id = $1;
-               }
-       |       CTF_LPAREN declarator CTF_RPAREN
-               {
-                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
-                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
-                       $$->u.field_class_declarator.u.nested.field_class_declarator = $2;
-               }
-       |       direct_declarator CTF_LSBRAC unary_expression CTF_RSBRAC
-               {
-                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
-                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
-                       $$->u.field_class_declarator.u.nested.field_class_declarator = $1;
-                       BT_INIT_LIST_HEAD(&($$)->u.field_class_declarator.u.nested.length);
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_declarator.u.nested.length);
-               }
-       ;
-
-field_class_declarator:
-               direct_field_class_declarator
-               {       $$ = $1;        }
-       |       pointer direct_field_class_declarator
-               {
-                       $$ = $2;
-                       bt_list_splice(&($1)->tmp_head, &($$)->u.field_class_declarator.pointers);
-               }
-       ;
-
-direct_field_class_declarator:
-               IDENTIFIER
-               {
-                       add_type(scanner, $1);
-                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
-                       $$->u.field_class_declarator.type = TYPEDEC_ID;
-                       $$->u.field_class_declarator.u.id = $1;
-               }
-       |       CTF_LPAREN field_class_declarator CTF_RPAREN
-               {
-                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
-                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
-                       $$->u.field_class_declarator.u.nested.field_class_declarator = $2;
-               }
-       |       direct_field_class_declarator CTF_LSBRAC unary_expression CTF_RSBRAC
-               {
-                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
-                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
-                       $$->u.field_class_declarator.u.nested.field_class_declarator = $1;
-                       BT_INIT_LIST_HEAD(&($$)->u.field_class_declarator.u.nested.length);
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_declarator.u.nested.length);
-               }
-       ;
-
-pointer:
-               CTF_STAR
-               {
-                       $$ = make_node(scanner, NODE_POINTER);
-               }
-       |       CTF_STAR pointer
-               {
-                       $$ = make_node(scanner, NODE_POINTER);
-                       bt_list_splice(&($2)->tmp_head, &($$)->tmp_head);
-               }
-       |       CTF_STAR type_qualifier_list pointer
-               {
-                       $$ = make_node(scanner, NODE_POINTER);
-                       $$->u.pointer.const_qualifier = 1;
-                       bt_list_splice(&($3)->tmp_head, &($$)->tmp_head);
-               }
-       ;
-
-type_qualifier_list:
-               /* pointer assumes only const type qualifier */
-               CTF_CONST
-       |       type_qualifier_list CTF_CONST
-       ;
-
-/* 2.3: CTF-specific declarations */
-
-ctf_assignment_expression_list:
-               ctf_assignment_expression CTF_SEMICOLON
-               {       $$ = $1;        }
-       |       ctf_assignment_expression_list ctf_assignment_expression CTF_SEMICOLON
-               {
-                       $$ = $1;
-                       bt_list_add_tail(&($2)->siblings, &($$)->tmp_head);
-               }
-       ;
-
-ctf_assignment_expression:
-               unary_expression CTF_EQUAL unary_expression
-               {
-                       /*
-                        * Because we have left and right, cannot use
-                        * set_parent_node.
-                        */
-                       $$ = make_node(scanner, NODE_CTF_EXPRESSION);
-                       _bt_list_splice_tail(&($1)->tmp_head, &($$)->u.ctf_expression.left);
-                       if ($1->u.unary_expression.type != UNARY_STRING)
-                               reparent_error(scanner, "ctf_assignment_expression left expects string");
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.ctf_expression.right);
-               }
-       |       unary_expression CTF_TYPEASSIGN declaration_specifiers  /* Only allow struct */
-               {
-                       /*
-                        * Because we have left and right, cannot use
-                        * set_parent_node.
-                        */
-                       $$ = make_node(scanner, NODE_CTF_EXPRESSION);
-                       _bt_list_splice_tail(&($1)->tmp_head, &($$)->u.ctf_expression.left);
-                       if ($1->u.unary_expression.type != UNARY_STRING)
-                               reparent_error(scanner, "ctf_assignment_expression left expects string");
-                       bt_list_add_tail(&($3)->siblings, &($$)->u.ctf_expression.right);
-               }
-       |       declaration_specifiers CTF_TYPEDEF declaration_specifiers field_class_declarator_list
-               {
-                       struct ctf_node *list;
-
-                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       _bt_list_splice_tail(&($3)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       $$ = make_node(scanner, NODE_TYPEDEF);
-                       ($$)->u.struct_or_variant_declaration.field_class_specifier_list = list;
-                       _bt_list_splice_tail(&($4)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
-               }
-       |       CTF_TYPEDEF declaration_specifiers field_class_declarator_list
-               {
-                       struct ctf_node *list;
-
-                       $$ = make_node(scanner, NODE_TYPEDEF);
-                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       $$->u.field_class_def.field_class_specifier_list = list;
-                       _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
-               }
-       |       declaration_specifiers CTF_TYPEDEF field_class_declarator_list
-               {
-                       struct ctf_node *list;
-
-                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       $$ = make_node(scanner, NODE_TYPEDEF);
-                       ($$)->u.struct_or_variant_declaration.field_class_specifier_list = list;
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
-               }
-       |       CTF_TYPEALIAS declaration_specifiers abstract_declarator_list CTF_TYPEASSIGN alias_declaration_specifiers alias_abstract_declarator_list
-               {
-                       struct ctf_node *list;
-
-                       $$ = make_node(scanner, NODE_TYPEALIAS);
-                       $$->u.field_class_alias.target = make_node(scanner, NODE_TYPEALIAS_TARGET);
-                       $$->u.field_class_alias.alias = make_node(scanner, NODE_TYPEALIAS_ALIAS);
-
-                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       $$->u.field_class_alias.target->u.field_class_alias_target.field_class_specifier_list = list;
-                       _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_alias.target->u.field_class_alias_target.field_class_declarators);
-
-                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
-                       $$->u.field_class_alias.alias->u.field_class_alias_name.field_class_specifier_list = list;
-                       _bt_list_splice_tail(&($5)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
-                       _bt_list_splice_tail(&($6)->tmp_head, &($$)->u.field_class_alias.alias->u.field_class_alias_name.field_class_declarators);
-               }
-       ;
diff --git a/src/plugins/ctf/common/metadata/parser.ypp b/src/plugins/ctf/common/metadata/parser.ypp
new file mode 100644 (file)
index 0000000..b644b5d
--- /dev/null
@@ -0,0 +1,2609 @@
+%{
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2010 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * Common Trace Format Metadata Grammar.
+ */
+
+#define BT_LOG_OUTPUT_LEVEL ctf_plugin_metadata_log_level
+#define BT_LOG_TAG "PLUGIN/CTF/META/PARSER"
+#include "logging.hpp"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <glib.h>
+#include <errno.h>
+#include <inttypes.h>
+#include "common/list.h"
+#include "common/assert.h"
+#include "scanner.hpp"
+#include "ast.hpp"
+#include "objstack.hpp"
+
+#include "parser-wrap.hpp"
+
+/* Join two lists, put "add" at the end of "head".  */
+static inline void
+_bt_list_splice_tail (struct bt_list_head *add, struct bt_list_head *head)
+{
+       /* Do nothing if the list which gets added is empty.  */
+       if (add != add->next) {
+               add->next->prev = head->prev;
+               add->prev->next = head;
+               head->prev->next = add->next;
+               head->prev = add->prev;
+       }
+}
+
+BT_HIDDEN
+int yylex(union YYSTYPE *yyval, yyscan_t yyscanner);
+BT_HIDDEN
+int yylex_init_extra(struct ctf_scanner *scanner, yyscan_t * ptr_yy_globals);
+BT_HIDDEN
+int yylex_destroy(yyscan_t yyscanner);
+BT_HIDDEN
+void yyrestart(FILE * in_str, yyscan_t yyscanner);
+BT_HIDDEN
+int yyget_lineno(yyscan_t yyscanner);
+BT_HIDDEN
+char *yyget_text(yyscan_t yyscanner);
+
+/*
+ * Static node for out of memory errors. Only "type" is used. lineno is
+ * always left at 0. The rest of the node content can be overwritten,
+ * but is never used.
+ */
+static struct ctf_node error_node = {
+       .parent = nullptr,
+       .siblings = {},
+       .tmp_head = {},
+       .lineno = 0,
+       .visited = 0,
+       .type = NODE_ERROR,
+};
+
+BT_HIDDEN
+const char *node_type(struct ctf_node *node)
+{
+       switch (node->type) {
+#define ENTRY(S) case S: return #S;
+       FOREACH_CTF_NODES(ENTRY)
+#undef ENTRY
+       };
+
+       bt_common_abort();
+}
+
+void setstring(struct ctf_scanner *scanner, YYSTYPE *lvalp, const char *src)
+{
+       lvalp->s = (char *) objstack_alloc(scanner->objstack, strlen(src) + 1);
+       strcpy(lvalp->s, src);
+}
+
+static
+int str_check(size_t str_len, size_t offset, size_t len)
+{
+       /* check overflow */
+       if (offset + len < offset)
+               return -1;
+       if (offset + len > str_len)
+               return -1;
+       return 0;
+}
+
+static
+int bt_isodigit(int c)
+{
+       switch (c) {
+       case '0':
+       case '1':
+       case '2':
+       case '3':
+       case '4':
+       case '5':
+       case '6':
+       case '7':
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+static
+int parse_base_sequence(const char *src, size_t len, size_t pos,
+               char *buffer, size_t *buf_len, int base)
+{
+       const size_t max_char = 3;
+       int nr_char = 0;
+
+       while (!str_check(len, pos, 1) && nr_char < max_char) {
+               char c = src[pos++];
+
+               if (base == 8) {
+                       if (bt_isodigit(c))
+                               buffer[nr_char++] = c;
+                       else
+                               break;
+               } else if (base == 16) {
+                       if (isxdigit(c))
+                               buffer[nr_char++] = c;
+                       else
+                               break;
+
+               } else {
+                       /* Unsupported base */
+                       return -1;
+               }
+       }
+       BT_ASSERT_DBG(nr_char > 0);
+       buffer[nr_char] = '\0';
+       *buf_len = nr_char;
+       return 0;
+}
+
+static
+int import_basic_string(struct ctf_scanner *scanner, YYSTYPE *lvalp,
+               size_t len, const char *src, char delim)
+{
+       size_t pos = 0, dpos = 0;
+
+       if (str_check(len, pos, 1))
+               return -1;
+       if (src[pos++] != delim)
+               return -1;
+
+       while (src[pos] != delim) {
+               char c;
+
+               if (str_check(len, pos, 1))
+                       return -1;
+               c = src[pos++];
+               if (c == '\\') {
+                       if (str_check(len, pos, 1))
+                               return -1;
+                       c = src[pos++];
+
+                       switch (c) {
+                       case 'a':
+                               c = '\a';
+                               break;
+                       case 'b':
+                               c = '\b';
+                               break;
+                       case 'f':
+                               c = '\f';
+                               break;
+                       case 'n':
+                               c = '\n';
+                               break;
+                       case 'r':
+                               c = '\r';
+                               break;
+                       case 't':
+                               c = '\t';
+                               break;
+                       case 'v':
+                               c = '\v';
+                               break;
+                       case '\\':
+                               c = '\\';
+                               break;
+                       case '\'':
+                               c = '\'';
+                               break;
+                       case '\"':
+                               c = '\"';
+                               break;
+                       case '?':
+                               c = '?';
+                               break;
+                       case '0':
+                       case '1':
+                       case '2':
+                       case '3':
+                       case '4':
+                       case '5':
+                       case '6':
+                       case '7':
+                       {
+                               char oct_buffer[4];
+                               size_t oct_len;
+
+                               if (parse_base_sequence(src, len, pos - 1,
+                                               oct_buffer, &oct_len, 8))
+                                       return -1;
+                               c = strtoul(&oct_buffer[0], NULL, 8);
+                               pos += oct_len - 1;
+                               break;
+                       }
+                       case 'x':
+                       {
+                               char hex_buffer[4];
+                               size_t hex_len;
+
+                               if (parse_base_sequence(src, len, pos,
+                                               hex_buffer, &hex_len, 16))
+                                       return -1;
+                               c = strtoul(&hex_buffer[0], NULL, 16);
+                               pos += hex_len;
+                               break;
+                       }
+                       default:
+                               return -1;
+                       }
+               }
+               if (str_check(len, dpos, 1))
+                       return -1;
+               lvalp->s[dpos++] = c;
+       }
+
+       if (str_check(len, dpos, 1))
+               return -1;
+       lvalp->s[dpos++] = '\0';
+
+       if (str_check(len, pos, 1))
+               return -1;
+       if (src[pos++] != delim)
+               return -1;
+
+       if (str_check(len, pos, 1))
+               return -1;
+       if (src[pos] != '\0')
+               return -1;
+       return 0;
+}
+
+int import_string(struct ctf_scanner *scanner, YYSTYPE *lvalp,
+               const char *src, char delim)
+{
+       size_t len;
+
+       len = strlen(src) + 1;
+       lvalp->s = (char *) objstack_alloc(scanner->objstack, len);
+       if (src[0] == 'L') {
+               // TODO: import wide string
+               _BT_LOGE_APPEND_CAUSE_LINENO(yyget_lineno(scanner),
+                       "wide characters are not supported as of this version: "
+                       "scanner-addr=%p", scanner);
+               return -1;
+       } else {
+               return import_basic_string(scanner, lvalp, len, src, delim);
+       }
+}
+
+static void init_scope(struct ctf_scanner_scope *scope,
+                      struct ctf_scanner_scope *parent)
+{
+       scope->parent = parent;
+       scope->classes = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                            NULL, NULL);
+}
+
+static void finalize_scope(struct ctf_scanner_scope *scope)
+{
+       g_hash_table_destroy(scope->classes);
+}
+
+static void push_scope(struct ctf_scanner *scanner)
+{
+       struct ctf_scanner_scope *ns;
+
+       BT_LOGT("Pushing scope: scanner-addr=%p", scanner);
+       ns = (ctf_scanner_scope *) malloc(sizeof(struct ctf_scanner_scope));
+       init_scope(ns, scanner->cs);
+       scanner->cs = ns;
+}
+
+static void pop_scope(struct ctf_scanner *scanner)
+{
+       struct ctf_scanner_scope *os;
+
+       BT_LOGT("Popping scope: scanner-addr=%p", scanner);
+       os = scanner->cs;
+       scanner->cs = os->parent;
+       finalize_scope(os);
+       free(os);
+}
+
+static int lookup_type(struct ctf_scanner_scope *s, const char *id)
+{
+       int ret;
+
+       ret = GPOINTER_TO_INT(g_hash_table_lookup(s->classes, id));
+       BT_LOGT("Looked up type: scanner-addr=%p, id=\"%s\", ret=%d",
+               s, id, ret);
+       return ret;
+}
+
+BT_HIDDEN
+int is_type(struct ctf_scanner *scanner, const char *id)
+{
+       struct ctf_scanner_scope *it;
+       int ret = 0;
+
+       for (it = scanner->cs; it; it = it->parent) {
+               if (lookup_type(it, id)) {
+                       ret = 1;
+                       break;
+               }
+       }
+       BT_LOGT("Found if ID is type: scanner-addr=%p, id=\"%s\", ret=%d",
+               scanner, id, ret);
+       return ret;
+}
+
+static void add_type(struct ctf_scanner *scanner, char *id)
+{
+       BT_LOGT("Adding type: scanner-addr=%p, id=\"%s\"",
+               scanner, id);
+       if (lookup_type(scanner->cs, id))
+               return;
+       g_hash_table_insert(scanner->cs->classes, id, id);
+}
+
+static struct ctf_node *make_node(struct ctf_scanner *scanner,
+                                 enum node_type type)
+{
+       struct ctf_node *node;
+
+       node = (ctf_node *) objstack_alloc(scanner->objstack, sizeof(*node));
+       if (!node) {
+               _BT_LOGE_APPEND_CAUSE_LINENO(yyget_lineno(scanner->scanner),
+                       "failed to allocate one stack entry: "
+                       "scanner-addr=%p", scanner);
+               return &error_node;
+       }
+       node->type = type;
+       node->lineno = yyget_lineno(scanner->scanner);
+       BT_INIT_LIST_HEAD(&node->tmp_head);
+       bt_list_add(&node->siblings, &node->tmp_head);
+
+       switch (type) {
+       case NODE_ROOT:
+               node->type = NODE_ERROR;
+               BT_LOGE("Trying to create root node: scanner-addr=%p",
+                       scanner);
+               break;
+       case NODE_EVENT:
+               BT_INIT_LIST_HEAD(&node->u.event.declaration_list);
+               break;
+       case NODE_STREAM:
+               BT_INIT_LIST_HEAD(&node->u.stream.declaration_list);
+               break;
+       case NODE_ENV:
+               BT_INIT_LIST_HEAD(&node->u.env.declaration_list);
+               break;
+       case NODE_TRACE:
+               BT_INIT_LIST_HEAD(&node->u.trace.declaration_list);
+               break;
+       case NODE_CLOCK:
+               BT_INIT_LIST_HEAD(&node->u.clock.declaration_list);
+               break;
+       case NODE_CALLSITE:
+               BT_INIT_LIST_HEAD(&node->u.callsite.declaration_list);
+               break;
+       case NODE_CTF_EXPRESSION:
+               BT_INIT_LIST_HEAD(&node->u.ctf_expression.left);
+               BT_INIT_LIST_HEAD(&node->u.ctf_expression.right);
+               break;
+       case NODE_UNARY_EXPRESSION:
+               break;
+       case NODE_TYPEDEF:
+               BT_INIT_LIST_HEAD(&node->u.field_class_def.field_class_declarators);
+               break;
+       case NODE_TYPEALIAS_TARGET:
+               BT_INIT_LIST_HEAD(&node->u.field_class_alias_target.field_class_declarators);
+               break;
+       case NODE_TYPEALIAS_ALIAS:
+               BT_INIT_LIST_HEAD(&node->u.field_class_alias_name.field_class_declarators);
+               break;
+       case NODE_TYPEALIAS:
+               break;
+       case NODE_TYPE_SPECIFIER:
+               break;
+       case NODE_TYPE_SPECIFIER_LIST:
+               BT_INIT_LIST_HEAD(&node->u.field_class_specifier_list.head);
+               break;
+       case NODE_POINTER:
+               break;
+       case NODE_TYPE_DECLARATOR:
+               BT_INIT_LIST_HEAD(&node->u.field_class_declarator.pointers);
+               break;
+       case NODE_FLOATING_POINT:
+               BT_INIT_LIST_HEAD(&node->u.floating_point.expressions);
+               break;
+       case NODE_INTEGER:
+               BT_INIT_LIST_HEAD(&node->u.integer.expressions);
+               break;
+       case NODE_STRING:
+               BT_INIT_LIST_HEAD(&node->u.string.expressions);
+               break;
+       case NODE_ENUMERATOR:
+               BT_INIT_LIST_HEAD(&node->u.enumerator.values);
+               break;
+       case NODE_ENUM:
+               BT_INIT_LIST_HEAD(&node->u._enum.enumerator_list);
+               break;
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+               BT_INIT_LIST_HEAD(&node->u.struct_or_variant_declaration.field_class_declarators);
+               break;
+       case NODE_VARIANT:
+               BT_INIT_LIST_HEAD(&node->u.variant.declaration_list);
+               break;
+       case NODE_STRUCT:
+               BT_INIT_LIST_HEAD(&node->u._struct.declaration_list);
+               BT_INIT_LIST_HEAD(&node->u._struct.min_align);
+               break;
+       case NODE_UNKNOWN:
+       default:
+               node->type = NODE_ERROR;
+               BT_LOGE("Unknown node type: scanner-addr=%p, node-type=%d",
+                       scanner, type);
+               break;
+       }
+
+       return node;
+}
+
+static int reparent_ctf_expression(struct ctf_node *node,
+                                  struct ctf_node *parent)
+{
+       switch (parent->type) {
+       case NODE_EVENT:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.event.declaration_list);
+               break;
+       case NODE_STREAM:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.stream.declaration_list);
+               break;
+       case NODE_ENV:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.env.declaration_list);
+               break;
+       case NODE_TRACE:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.trace.declaration_list);
+               break;
+       case NODE_CLOCK:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.clock.declaration_list);
+               break;
+       case NODE_CALLSITE:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.callsite.declaration_list);
+               break;
+       case NODE_FLOATING_POINT:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.floating_point.expressions);
+               break;
+       case NODE_INTEGER:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.integer.expressions);
+               break;
+       case NODE_STRING:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.string.expressions);
+               break;
+
+       case NODE_ROOT:
+       case NODE_CTF_EXPRESSION:
+       case NODE_TYPEDEF:
+       case NODE_TYPEALIAS_TARGET:
+       case NODE_TYPEALIAS_ALIAS:
+       case NODE_TYPEALIAS:
+       case NODE_TYPE_SPECIFIER:
+       case NODE_TYPE_SPECIFIER_LIST:
+       case NODE_POINTER:
+       case NODE_TYPE_DECLARATOR:
+       case NODE_ENUMERATOR:
+       case NODE_ENUM:
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+       case NODE_VARIANT:
+       case NODE_STRUCT:
+       case NODE_UNARY_EXPRESSION:
+               return -EPERM;
+
+       case NODE_UNKNOWN:
+       default:
+               BT_LOGE("Unknown node type: node-type=%d", parent->type);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int reparent_typedef(struct ctf_node *node, struct ctf_node *parent)
+{
+       switch (parent->type) {
+       case NODE_ROOT:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.root.declaration_list);
+               break;
+       case NODE_EVENT:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.event.declaration_list);
+               break;
+       case NODE_STREAM:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.stream.declaration_list);
+               break;
+       case NODE_ENV:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.env.declaration_list);
+               break;
+       case NODE_TRACE:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.trace.declaration_list);
+               break;
+       case NODE_CLOCK:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.clock.declaration_list);
+               break;
+       case NODE_CALLSITE:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.callsite.declaration_list);
+               break;
+       case NODE_VARIANT:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.variant.declaration_list);
+               break;
+       case NODE_STRUCT:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u._struct.declaration_list);
+               break;
+
+       case NODE_FLOATING_POINT:
+       case NODE_INTEGER:
+       case NODE_STRING:
+       case NODE_CTF_EXPRESSION:
+       case NODE_TYPEDEF:
+       case NODE_TYPEALIAS_TARGET:
+       case NODE_TYPEALIAS_ALIAS:
+       case NODE_TYPEALIAS:
+       case NODE_TYPE_SPECIFIER:
+       case NODE_TYPE_SPECIFIER_LIST:
+       case NODE_POINTER:
+       case NODE_TYPE_DECLARATOR:
+       case NODE_ENUMERATOR:
+       case NODE_ENUM:
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+       case NODE_UNARY_EXPRESSION:
+               return -EPERM;
+
+       case NODE_UNKNOWN:
+       default:
+               BT_LOGE("Unknown node type: node-type=%d", parent->type);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int reparent_field_class_alias(struct ctf_node *node, struct ctf_node *parent)
+{
+       switch (parent->type) {
+       case NODE_ROOT:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.root.declaration_list);
+               break;
+       case NODE_EVENT:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.event.declaration_list);
+               break;
+       case NODE_STREAM:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.stream.declaration_list);
+               break;
+       case NODE_ENV:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.env.declaration_list);
+               break;
+       case NODE_TRACE:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.trace.declaration_list);
+               break;
+       case NODE_CLOCK:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.clock.declaration_list);
+               break;
+       case NODE_CALLSITE:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.callsite.declaration_list);
+               break;
+       case NODE_VARIANT:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.variant.declaration_list);
+               break;
+       case NODE_STRUCT:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u._struct.declaration_list);
+               break;
+
+       case NODE_FLOATING_POINT:
+       case NODE_INTEGER:
+       case NODE_STRING:
+       case NODE_CTF_EXPRESSION:
+       case NODE_TYPEDEF:
+       case NODE_TYPEALIAS_TARGET:
+       case NODE_TYPEALIAS_ALIAS:
+       case NODE_TYPEALIAS:
+       case NODE_TYPE_SPECIFIER:
+       case NODE_TYPE_SPECIFIER_LIST:
+       case NODE_POINTER:
+       case NODE_TYPE_DECLARATOR:
+       case NODE_ENUMERATOR:
+       case NODE_ENUM:
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+       case NODE_UNARY_EXPRESSION:
+               return -EPERM;
+
+       case NODE_UNKNOWN:
+       default:
+               BT_LOGE("Unknown node type: node-type=%d", parent->type);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int reparent_field_class_specifier(struct ctf_node *node,
+                                  struct ctf_node *parent)
+{
+       switch (parent->type) {
+       case NODE_TYPE_SPECIFIER_LIST:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.field_class_specifier_list.head);
+               break;
+
+       case NODE_TYPE_SPECIFIER:
+       case NODE_EVENT:
+       case NODE_STREAM:
+       case NODE_ENV:
+       case NODE_TRACE:
+       case NODE_CLOCK:
+       case NODE_CALLSITE:
+       case NODE_VARIANT:
+       case NODE_STRUCT:
+       case NODE_TYPEDEF:
+       case NODE_TYPEALIAS_TARGET:
+       case NODE_TYPEALIAS_ALIAS:
+       case NODE_TYPE_DECLARATOR:
+       case NODE_ENUM:
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+       case NODE_TYPEALIAS:
+       case NODE_FLOATING_POINT:
+       case NODE_INTEGER:
+       case NODE_STRING:
+       case NODE_CTF_EXPRESSION:
+       case NODE_POINTER:
+       case NODE_ENUMERATOR:
+       case NODE_UNARY_EXPRESSION:
+               return -EPERM;
+
+       case NODE_UNKNOWN:
+       default:
+               BT_LOGE("Unknown node type: node-type=%d", parent->type);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int reparent_field_class_specifier_list(struct ctf_node *node,
+                                       struct ctf_node *parent)
+{
+       switch (parent->type) {
+       case NODE_ROOT:
+               bt_list_add_tail(&node->siblings, &parent->u.root.declaration_list);
+               break;
+       case NODE_EVENT:
+               bt_list_add_tail(&node->siblings, &parent->u.event.declaration_list);
+               break;
+       case NODE_STREAM:
+               bt_list_add_tail(&node->siblings, &parent->u.stream.declaration_list);
+               break;
+       case NODE_ENV:
+               bt_list_add_tail(&node->siblings, &parent->u.env.declaration_list);
+               break;
+       case NODE_TRACE:
+               bt_list_add_tail(&node->siblings, &parent->u.trace.declaration_list);
+               break;
+       case NODE_CLOCK:
+               bt_list_add_tail(&node->siblings, &parent->u.clock.declaration_list);
+               break;
+       case NODE_CALLSITE:
+               bt_list_add_tail(&node->siblings, &parent->u.callsite.declaration_list);
+               break;
+       case NODE_VARIANT:
+               bt_list_add_tail(&node->siblings, &parent->u.variant.declaration_list);
+               break;
+       case NODE_STRUCT:
+               bt_list_add_tail(&node->siblings, &parent->u._struct.declaration_list);
+               break;
+       case NODE_TYPEDEF:
+               parent->u.field_class_def.field_class_specifier_list = node;
+               break;
+       case NODE_TYPEALIAS_TARGET:
+               parent->u.field_class_alias_target.field_class_specifier_list = node;
+               break;
+       case NODE_TYPEALIAS_ALIAS:
+               parent->u.field_class_alias_name.field_class_specifier_list = node;
+               break;
+       case NODE_ENUM:
+               parent->u._enum.container_field_class = node;
+               break;
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+               parent->u.struct_or_variant_declaration.field_class_specifier_list = node;
+               break;
+       case NODE_TYPE_DECLARATOR:
+       case NODE_TYPE_SPECIFIER:
+       case NODE_TYPEALIAS:
+       case NODE_FLOATING_POINT:
+       case NODE_INTEGER:
+       case NODE_STRING:
+       case NODE_CTF_EXPRESSION:
+       case NODE_POINTER:
+       case NODE_ENUMERATOR:
+       case NODE_UNARY_EXPRESSION:
+               return -EPERM;
+
+       case NODE_UNKNOWN:
+       default:
+               BT_LOGE("Unknown node type: node-type=%d", parent->type);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int reparent_field_class_declarator(struct ctf_node *node,
+                                   struct ctf_node *parent)
+{
+       switch (parent->type) {
+       case NODE_TYPE_DECLARATOR:
+               parent->u.field_class_declarator.type = TYPEDEC_NESTED;
+               parent->u.field_class_declarator.u.nested.field_class_declarator = node;
+               break;
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.struct_or_variant_declaration.field_class_declarators);
+               break;
+       case NODE_TYPEDEF:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.field_class_def.field_class_declarators);
+               break;
+       case NODE_TYPEALIAS_TARGET:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.field_class_alias_target.field_class_declarators);
+               break;
+       case NODE_TYPEALIAS_ALIAS:
+               _bt_list_splice_tail(&node->tmp_head, &parent->u.field_class_alias_name.field_class_declarators);
+               break;
+
+       case NODE_ROOT:
+       case NODE_EVENT:
+       case NODE_STREAM:
+       case NODE_ENV:
+       case NODE_TRACE:
+       case NODE_CLOCK:
+       case NODE_CALLSITE:
+       case NODE_VARIANT:
+       case NODE_STRUCT:
+       case NODE_TYPEALIAS:
+       case NODE_ENUM:
+       case NODE_FLOATING_POINT:
+       case NODE_INTEGER:
+       case NODE_STRING:
+       case NODE_CTF_EXPRESSION:
+       case NODE_TYPE_SPECIFIER:
+       case NODE_TYPE_SPECIFIER_LIST:
+       case NODE_POINTER:
+       case NODE_ENUMERATOR:
+       case NODE_UNARY_EXPRESSION:
+               return -EPERM;
+
+       case NODE_UNKNOWN:
+       default:
+               BT_LOGE("Unknown node type: node-type=%d", parent->type);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+/*
+ * set_parent_node
+ *
+ * Link node to parent. Returns 0 on success, -EPERM if it is not permitted to
+ * create the link declared by the input, -ENOENT if node or parent is NULL,
+ * -EINVAL if there is an internal structure problem.
+ */
+static int set_parent_node(struct ctf_node *node,
+                        struct ctf_node *parent)
+{
+       if (!node || !parent)
+               return -ENOENT;
+
+       /* Note: Linking to parent will be done only by an external visitor */
+
+       switch (node->type) {
+       case NODE_ROOT:
+               BT_LOGE_STR("Trying to reparent root node.");
+               return -EINVAL;
+
+       case NODE_EVENT:
+               if (parent->type == NODE_ROOT) {
+                       _bt_list_splice_tail(&node->tmp_head, &parent->u.root.event);
+               } else {
+                       return -EPERM;
+               }
+               break;
+       case NODE_STREAM:
+               if (parent->type == NODE_ROOT) {
+                       _bt_list_splice_tail(&node->tmp_head, &parent->u.root.stream);
+               } else {
+                       return -EPERM;
+               }
+               break;
+       case NODE_ENV:
+               if (parent->type == NODE_ROOT) {
+                       _bt_list_splice_tail(&node->tmp_head, &parent->u.root.env);
+               } else {
+                       return -EPERM;
+               }
+               break;
+       case NODE_TRACE:
+               if (parent->type == NODE_ROOT) {
+                       _bt_list_splice_tail(&node->tmp_head, &parent->u.root.trace);
+               } else {
+                       return -EPERM;
+               }
+               break;
+       case NODE_CLOCK:
+               if (parent->type == NODE_ROOT) {
+                       _bt_list_splice_tail(&node->tmp_head, &parent->u.root.clock);
+               } else {
+                       return -EPERM;
+               }
+               break;
+       case NODE_CALLSITE:
+               if (parent->type == NODE_ROOT) {
+                       _bt_list_splice_tail(&node->tmp_head, &parent->u.root.callsite);
+               } else {
+                       return -EPERM;
+               }
+               break;
+
+       case NODE_CTF_EXPRESSION:
+               return reparent_ctf_expression(node, parent);
+       case NODE_UNARY_EXPRESSION:
+               if (parent->type == NODE_TYPE_DECLARATOR)
+                       parent->u.field_class_declarator.bitfield_len = node;
+               else
+                       return -EPERM;
+               break;
+
+       case NODE_TYPEDEF:
+               return reparent_typedef(node, parent);
+       case NODE_TYPEALIAS_TARGET:
+               if (parent->type == NODE_TYPEALIAS)
+                       parent->u.field_class_alias.target = node;
+               else
+                       return -EINVAL;
+               /* fall-through */
+       case NODE_TYPEALIAS_ALIAS:
+               if (parent->type == NODE_TYPEALIAS)
+                       parent->u.field_class_alias.alias = node;
+               else
+                       return -EINVAL;
+               /* fall-through */
+       case NODE_TYPEALIAS:
+               return reparent_field_class_alias(node, parent);
+
+       case NODE_POINTER:
+               if (parent->type == NODE_TYPE_DECLARATOR) {
+                       _bt_list_splice_tail(&node->tmp_head, &parent->u.field_class_declarator.pointers);
+               } else
+                       return -EPERM;
+               break;
+       case NODE_TYPE_DECLARATOR:
+               return reparent_field_class_declarator(node, parent);
+
+       case NODE_TYPE_SPECIFIER_LIST:
+               return reparent_field_class_specifier_list(node, parent);
+
+       case NODE_TYPE_SPECIFIER:
+               return reparent_field_class_specifier(node, parent);
+
+       case NODE_FLOATING_POINT:
+       case NODE_INTEGER:
+       case NODE_STRING:
+       case NODE_ENUM:
+       case NODE_VARIANT:
+       case NODE_STRUCT:
+               return -EINVAL; /* Dealt with internally within grammar */
+
+       case NODE_ENUMERATOR:
+               if (parent->type == NODE_ENUM) {
+                       _bt_list_splice_tail(&node->tmp_head, &parent->u._enum.enumerator_list);
+               } else {
+                       return -EPERM;
+               }
+               break;
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+               switch (parent->type) {
+               case NODE_STRUCT:
+                       _bt_list_splice_tail(&node->tmp_head, &parent->u._struct.declaration_list);
+                       break;
+               case NODE_VARIANT:
+                       _bt_list_splice_tail(&node->tmp_head, &parent->u.variant.declaration_list);
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
+
+       case NODE_UNKNOWN:
+       default:
+               BT_LOGE("Unknown node type: node-type=%d", parent->type);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static
+void yyerror(struct ctf_scanner *scanner, yyscan_t yyscanner, const char *str)
+{
+       _BT_LOGE_APPEND_CAUSE_LINENO(yyget_lineno(scanner->scanner),
+               "%s: token=\"%s\"", str, yyget_text(scanner->scanner));
+}
+
+#define reparent_error(scanner, str)                           \
+do {                                                           \
+       yyerror(scanner, scanner->scanner, YY_("reparent_error: " str)); \
+       YYERROR;                                                \
+} while (0)
+
+static struct ctf_ast *ctf_ast_alloc(struct ctf_scanner *scanner)
+{
+       struct ctf_ast *ast;
+
+       ast = (ctf_ast *) objstack_alloc(scanner->objstack, sizeof(*ast));
+       if (!ast)
+               return NULL;
+       ast->root.type = NODE_ROOT;
+       BT_INIT_LIST_HEAD(&ast->root.tmp_head);
+       BT_INIT_LIST_HEAD(&ast->root.u.root.declaration_list);
+       BT_INIT_LIST_HEAD(&ast->root.u.root.trace);
+       BT_INIT_LIST_HEAD(&ast->root.u.root.env);
+       BT_INIT_LIST_HEAD(&ast->root.u.root.stream);
+       BT_INIT_LIST_HEAD(&ast->root.u.root.event);
+       BT_INIT_LIST_HEAD(&ast->root.u.root.clock);
+       BT_INIT_LIST_HEAD(&ast->root.u.root.callsite);
+       return ast;
+}
+
+int ctf_scanner_append_ast(struct ctf_scanner *scanner, FILE *input)
+{
+       /* Start processing new stream */
+       yyrestart(input, scanner->scanner);
+       return yyparse(scanner, scanner->scanner);
+}
+
+struct ctf_scanner *ctf_scanner_alloc(void)
+{
+       struct ctf_scanner *scanner;
+       int ret;
+
+       scanner = (ctf_scanner *) malloc(sizeof(*scanner));
+       if (!scanner)
+               return NULL;
+       memset(scanner, 0, sizeof(*scanner));
+       ret = yylex_init_extra(scanner, &scanner->scanner);
+       if (ret) {
+               BT_LOGE("yylex_init_extra() failed: ret=%d", ret);
+               goto cleanup_scanner;
+       }
+       scanner->objstack = objstack_create();
+       if (!scanner->objstack)
+               goto cleanup_lexer;
+       scanner->ast = ctf_ast_alloc(scanner);
+       if (!scanner->ast)
+               goto cleanup_objstack;
+       init_scope(&scanner->root_scope, NULL);
+       scanner->cs = &scanner->root_scope;
+
+       return scanner;
+
+cleanup_objstack:
+       objstack_destroy(scanner->objstack);
+cleanup_lexer:
+       ret = yylex_destroy(scanner->scanner);
+       if (!ret)
+               BT_LOGE("yylex_destroy() failed: scanner-addr=%p, ret=%d",
+                       scanner, ret);
+cleanup_scanner:
+       free(scanner);
+       return NULL;
+}
+
+void ctf_scanner_free(struct ctf_scanner *scanner)
+{
+       int ret;
+
+       if (!scanner)
+               return;
+
+       struct ctf_scanner_scope *scope = scanner->cs;
+
+       do {
+               struct ctf_scanner_scope *parent = scope->parent;
+               finalize_scope(scope);
+
+               /*
+                * The root scope is allocated within the ctf_scanner structure,
+                * do doesn't need freeing.  All others are allocated on their
+                * own.
+                */
+               if (scope != &scanner->root_scope)
+                       free(scope);
+
+               scope = parent;
+       } while (scope);
+
+       objstack_destroy(scanner->objstack);
+       ret = yylex_destroy(scanner->scanner);
+       if (ret)
+               BT_LOGE("yylex_destroy() failed: scanner-addr=%p, ret=%d",
+                       scanner, ret);
+       free(scanner);
+}
+
+/*
+ * The bison-provided version of strlen (yystrlen) generates a benign
+ * -Wnull-dereference warning.  That version is used when building on cygwin,
+ * for example, but you can also enable it by hand (to test) by removing the
+ * preprocessor conditional around it.
+ *
+ * Define yystrlen such that it will always use strlen.  As far as we know,
+ * strlen provided by all the platforms we use is reliable.
+ */
+#define yystrlen strlen
+
+%}
+
+/*
+ * This ends up in parser.h and makes sure those who want to include it pass
+ * through parser-wrap.h.
+ */
+%code requires {
+#ifndef ALLOW_INCLUDE_PARSER_H
+# error "Don't include parser.h directly, include parser-wrap.h instead."
+#endif
+}
+
+%code provides {
+       BT_HIDDEN
+       void setstring(struct ctf_scanner *scanner, YYSTYPE *lvalp, const char *src);
+
+       BT_HIDDEN
+       int import_string(struct ctf_scanner *scanner, YYSTYPE *lvalp, const char *src, char delim);
+}
+
+%define api.pure
+       /* %locations */
+%error-verbose
+%parse-param {struct ctf_scanner *scanner}
+%parse-param {yyscan_t yyscanner}
+%lex-param {yyscan_t yyscanner}
+/*
+ * Expect two shift-reduce conflicts. Caused by enum name-opt : type {}
+ * vs struct { int :value; } (unnamed bit-field). The default is to
+ * shift, so whenever we encounter an enumeration, we are doing the
+ * proper thing (shift). It is illegal to declare an enumeration
+ * "bit-field", so it is OK if this situation ends up in a parsing
+ * error.
+ */
+%expect 2
+%start file
+%token CTF_INTEGER_LITERAL CTF_STRING_LITERAL CTF_CHARACTER_LITERAL CTF_LSBRAC CTF_RSBRAC CTF_LPAREN CTF_RPAREN CTF_LBRAC CTF_RBRAC CTF_RARROW CTF_STAR CTF_PLUS CTF_MINUS CTF_LT CTF_GT CTF_TYPEASSIGN CTF_COLON CTF_SEMICOLON CTF_DOTDOTDOT CTF_DOT CTF_EQUAL CTF_COMMA CTF_CONST CTF_CHAR CTF_DOUBLE CTF_ENUM CTF_ENV CTF_EVENT CTF_FLOATING_POINT CTF_FLOAT CTF_INTEGER CTF_INT CTF_LONG CTF_SHORT CTF_SIGNED CTF_STREAM CTF_STRING CTF_STRUCT CTF_TRACE CTF_CALLSITE CTF_CLOCK CTF_TYPEALIAS CTF_TYPEDEF CTF_UNSIGNED CTF_VARIANT CTF_VOID CTF_BOOL CTF_COMPLEX CTF_IMAGINARY CTF_TOK_ALIGN
+%token <s> IDENTIFIER ID_TYPE
+%token CTF_ERROR
+%union
+{
+       long long ll;
+       unsigned long long ull;
+       char c;
+       char *s;
+       struct ctf_node *n;
+}
+
+%type <s> CTF_STRING_LITERAL CTF_CHARACTER_LITERAL
+
+%type <s> keywords
+
+%type <ull> CTF_INTEGER_LITERAL
+%type <n> postfix_expression unary_expression unary_expression_or_range
+
+%type <n> declaration
+%type <n> event_declaration
+%type <n> stream_declaration
+%type <n> env_declaration
+%type <n> trace_declaration
+%type <n> clock_declaration
+%type <n> callsite_declaration
+%type <n> integer_declaration_specifiers
+%type <n> declaration_specifiers
+%type <n> alias_declaration_specifiers
+
+%type <n> field_class_declarator_list
+%type <n> integer_field_class_specifier
+%type <n> field_class_specifier
+%type <n> struct_class_specifier
+%type <n> variant_field_class_specifier
+%type <n> enum_field_class_specifier
+%type <n> struct_or_variant_declaration_list
+%type <n> struct_or_variant_declaration
+%type <n> struct_or_variant_declarator_list
+%type <n> struct_or_variant_declarator
+%type <n> enumerator_list
+%type <n> enumerator
+%type <n> abstract_declarator_list
+%type <n> abstract_declarator
+%type <n> direct_abstract_declarator
+%type <n> alias_abstract_declarator_list
+%type <n> alias_abstract_declarator
+%type <n> direct_alias_abstract_declarator
+%type <n> declarator
+%type <n> direct_declarator
+%type <n> field_class_declarator
+%type <n> direct_field_class_declarator
+%type <n> pointer
+%type <n> ctf_assignment_expression_list
+%type <n> ctf_assignment_expression
+
+%%
+
+file:
+               declaration
+               {
+                       if (set_parent_node($1, &ctf_scanner_get_ast(scanner)->root))
+                               reparent_error(scanner, "error reparenting to root");
+               }
+       |       file declaration
+               {
+                       if (set_parent_node($2, &ctf_scanner_get_ast(scanner)->root))
+                               reparent_error(scanner, "error reparenting to root");
+               }
+       ;
+
+keywords:
+               CTF_VOID
+               {       $$ = yylval.s;          }
+       |       CTF_CHAR
+               {       $$ = yylval.s;          }
+       |       CTF_SHORT
+               {       $$ = yylval.s;          }
+       |       CTF_INT
+               {       $$ = yylval.s;          }
+       |       CTF_LONG
+               {       $$ = yylval.s;          }
+       |       CTF_FLOAT
+               {       $$ = yylval.s;          }
+       |       CTF_DOUBLE
+               {       $$ = yylval.s;          }
+       |       CTF_SIGNED
+               {       $$ = yylval.s;          }
+       |       CTF_UNSIGNED
+               {       $$ = yylval.s;          }
+       |       CTF_BOOL
+               {       $$ = yylval.s;          }
+       |       CTF_COMPLEX
+               {       $$ = yylval.s;          }
+       |       CTF_IMAGINARY
+               {       $$ = yylval.s;          }
+       |       CTF_FLOATING_POINT
+               {       $$ = yylval.s;          }
+       |       CTF_INTEGER
+               {       $$ = yylval.s;          }
+       |       CTF_STRING
+               {       $$ = yylval.s;          }
+       |       CTF_ENUM
+               {       $$ = yylval.s;          }
+       |       CTF_VARIANT
+               {       $$ = yylval.s;          }
+       |       CTF_STRUCT
+               {       $$ = yylval.s;          }
+       |       CTF_CONST
+               {       $$ = yylval.s;          }
+       |       CTF_TYPEDEF
+               {       $$ = yylval.s;          }
+       |       CTF_EVENT
+               {       $$ = yylval.s;          }
+       |       CTF_STREAM
+               {       $$ = yylval.s;          }
+       |       CTF_ENV
+               {       $$ = yylval.s;          }
+       |       CTF_TRACE
+               {       $$ = yylval.s;          }
+       |       CTF_CLOCK
+               {       $$ = yylval.s;          }
+       |       CTF_CALLSITE
+               {       $$ = yylval.s;          }
+       |       CTF_TOK_ALIGN
+               {       $$ = yylval.s;          }
+       ;
+
+
+/* 2: Phrase structure grammar */
+
+postfix_expression:
+               IDENTIFIER
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_STRING;
+                       $$->u.unary_expression.u.string = yylval.s;
+               }
+       |       ID_TYPE
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_STRING;
+                       $$->u.unary_expression.u.string = yylval.s;
+               }
+       |       keywords
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_STRING;
+                       $$->u.unary_expression.u.string = yylval.s;
+               }
+       |       CTF_INTEGER_LITERAL
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_UNSIGNED_CONSTANT;
+                       $$->u.unary_expression.u.unsigned_constant = $1;
+               }
+       |       CTF_STRING_LITERAL
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_STRING;
+                       $$->u.unary_expression.u.string = $1;
+               }
+       |       CTF_CHARACTER_LITERAL
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_STRING;
+                       $$->u.unary_expression.u.string = $1;
+               }
+       |       CTF_LPAREN unary_expression CTF_RPAREN
+               {
+                       $$ = $2;
+               }
+       |       postfix_expression CTF_LSBRAC unary_expression CTF_RSBRAC
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_SBRAC;
+                       $$->u.unary_expression.u.sbrac_exp = $3;
+                       bt_list_splice(&($1)->tmp_head, &($$)->tmp_head);
+                       bt_list_add_tail(&($$)->siblings, &($$)->tmp_head);
+               }
+       |       postfix_expression CTF_DOT IDENTIFIER
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_STRING;
+                       $$->u.unary_expression.u.string = yylval.s;
+                       $$->u.unary_expression.link = UNARY_DOTLINK;
+                       bt_list_splice(&($1)->tmp_head, &($$)->tmp_head);
+                       bt_list_add_tail(&($$)->siblings, &($$)->tmp_head);
+               }
+       |       postfix_expression CTF_DOT ID_TYPE
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_STRING;
+                       $$->u.unary_expression.u.string = yylval.s;
+                       $$->u.unary_expression.link = UNARY_DOTLINK;
+                       bt_list_splice(&($1)->tmp_head, &($$)->tmp_head);
+                       bt_list_add_tail(&($$)->siblings, &($$)->tmp_head);
+               }
+       |       postfix_expression CTF_DOT keywords
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_STRING;
+                       $$->u.unary_expression.u.string = yylval.s;
+                       $$->u.unary_expression.link = UNARY_DOTLINK;
+                       bt_list_splice(&($1)->tmp_head, &($$)->tmp_head);
+                       bt_list_add_tail(&($$)->siblings, &($$)->tmp_head);
+               }
+       |       postfix_expression CTF_RARROW IDENTIFIER
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_STRING;
+                       $$->u.unary_expression.u.string = yylval.s;
+                       $$->u.unary_expression.link = UNARY_ARROWLINK;
+                       bt_list_splice(&($1)->tmp_head, &($$)->tmp_head);
+                       bt_list_add_tail(&($$)->siblings, &($$)->tmp_head);
+               }
+       |       postfix_expression CTF_RARROW ID_TYPE
+               {
+                       $$ = make_node(scanner, NODE_UNARY_EXPRESSION);
+                       $$->u.unary_expression.type = UNARY_STRING;
+                       $$->u.unary_expression.u.string = yylval.s;
+                       $$->u.unary_expression.link = UNARY_ARROWLINK;
+                       bt_list_splice(&($1)->tmp_head, &($$)->tmp_head);
+                       bt_list_add_tail(&($$)->siblings, &($$)->tmp_head);
+               }
+       ;
+
+unary_expression:
+               postfix_expression
+               {       $$ = $1;                                }
+       |       CTF_PLUS postfix_expression
+               {
+                       $$ = $2;
+                       if ($$->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT
+                               && $$->u.unary_expression.type != UNARY_SIGNED_CONSTANT) {
+                               reparent_error(scanner, "expecting numeric constant");
+                       }
+               }
+       |       CTF_MINUS postfix_expression
+               {
+                       $$ = $2;
+                       if ($$->u.unary_expression.type == UNARY_UNSIGNED_CONSTANT) {
+                               $$->u.unary_expression.type = UNARY_SIGNED_CONSTANT;
+                               $$->u.unary_expression.u.signed_constant =
+                                       -($$->u.unary_expression.u.unsigned_constant);
+                       } else if ($$->u.unary_expression.type == UNARY_SIGNED_CONSTANT) {
+                               $$->u.unary_expression.u.signed_constant =
+                                       -($$->u.unary_expression.u.signed_constant);
+                       } else {
+                               reparent_error(scanner, "expecting numeric constant");
+                       }
+               }
+       ;
+
+unary_expression_or_range:
+               unary_expression CTF_DOTDOTDOT unary_expression
+               {
+                       $$ = $1;
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->tmp_head);
+                       $3->u.unary_expression.link = UNARY_DOTDOTDOT;
+               }
+       |       unary_expression
+               {       $$ = $1;                }
+       ;
+
+/* 2.2: Declarations */
+
+declaration:
+               declaration_specifiers CTF_SEMICOLON
+               {       $$ = $1;        }
+       |       event_declaration
+               {       $$ = $1;        }
+       |       stream_declaration
+               {       $$ = $1;        }
+       |       env_declaration
+               {       $$ = $1;        }
+       |       trace_declaration
+               {       $$ = $1;        }
+       |       clock_declaration
+               {       $$ = $1;        }
+       |       callsite_declaration
+               {       $$ = $1;        }
+       |       declaration_specifiers CTF_TYPEDEF declaration_specifiers field_class_declarator_list CTF_SEMICOLON
+               {
+                       struct ctf_node *list;
+
+                       $$ = make_node(scanner, NODE_TYPEDEF);
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u.field_class_def.field_class_specifier_list = list;
+                       _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       _bt_list_splice_tail(&($3)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       _bt_list_splice_tail(&($4)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
+               }
+       |       CTF_TYPEDEF declaration_specifiers field_class_declarator_list CTF_SEMICOLON
+               {
+                       struct ctf_node *list;
+
+                       $$ = make_node(scanner, NODE_TYPEDEF);
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u.field_class_def.field_class_specifier_list = list;
+                       _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
+               }
+       |       declaration_specifiers CTF_TYPEDEF field_class_declarator_list CTF_SEMICOLON
+               {
+                       struct ctf_node *list;
+
+                       $$ = make_node(scanner, NODE_TYPEDEF);
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u.field_class_def.field_class_specifier_list = list;
+                       _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
+               }
+       |       CTF_TYPEALIAS declaration_specifiers abstract_declarator_list CTF_TYPEASSIGN alias_declaration_specifiers alias_abstract_declarator_list CTF_SEMICOLON
+               {
+                       struct ctf_node *list;
+
+                       $$ = make_node(scanner, NODE_TYPEALIAS);
+                       $$->u.field_class_alias.target = make_node(scanner, NODE_TYPEALIAS_TARGET);
+                       $$->u.field_class_alias.alias = make_node(scanner, NODE_TYPEALIAS_ALIAS);
+
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u.field_class_alias.target->u.field_class_alias_target.field_class_specifier_list = list;
+                       _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_alias.target->u.field_class_alias_target.field_class_declarators);
+
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u.field_class_alias.alias->u.field_class_alias_name.field_class_specifier_list = list;
+                       _bt_list_splice_tail(&($5)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       _bt_list_splice_tail(&($6)->tmp_head, &($$)->u.field_class_alias.alias->u.field_class_alias_name.field_class_declarators);
+               }
+       ;
+
+event_declaration:
+               event_declaration_begin event_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_EVENT);
+               }
+       |       event_declaration_begin ctf_assignment_expression_list event_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_EVENT);
+                       if (set_parent_node($2, $$))
+                               reparent_error(scanner, "event_declaration");
+               }
+       ;
+
+event_declaration_begin:
+               CTF_EVENT CTF_LBRAC
+               {       push_scope(scanner);    }
+       ;
+
+event_declaration_end:
+               CTF_RBRAC CTF_SEMICOLON
+               {       pop_scope(scanner);     }
+       ;
+
+
+stream_declaration:
+               stream_declaration_begin stream_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_STREAM);
+               }
+       |       stream_declaration_begin ctf_assignment_expression_list stream_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_STREAM);
+                       if (set_parent_node($2, $$))
+                               reparent_error(scanner, "stream_declaration");
+               }
+       ;
+
+stream_declaration_begin:
+               CTF_STREAM CTF_LBRAC
+               {       push_scope(scanner);    }
+       ;
+
+stream_declaration_end:
+               CTF_RBRAC CTF_SEMICOLON
+               {       pop_scope(scanner);     }
+       ;
+
+env_declaration:
+               env_declaration_begin env_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_ENV);
+               }
+       |       env_declaration_begin ctf_assignment_expression_list env_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_ENV);
+                       if (set_parent_node($2, $$))
+                               reparent_error(scanner, "env declaration");
+               }
+       ;
+
+env_declaration_begin:
+               CTF_ENV CTF_LBRAC
+               {       push_scope(scanner);    }
+       ;
+
+env_declaration_end:
+               CTF_RBRAC CTF_SEMICOLON
+               {       pop_scope(scanner);     }
+       ;
+
+trace_declaration:
+               trace_declaration_begin trace_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_TRACE);
+               }
+       |       trace_declaration_begin ctf_assignment_expression_list trace_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_TRACE);
+                       if (set_parent_node($2, $$))
+                               reparent_error(scanner, "trace_declaration");
+               }
+       ;
+
+trace_declaration_begin:
+               CTF_TRACE CTF_LBRAC
+               {       push_scope(scanner);    }
+       ;
+
+trace_declaration_end:
+               CTF_RBRAC CTF_SEMICOLON
+               {       pop_scope(scanner);     }
+       ;
+
+clock_declaration:
+               CTF_CLOCK clock_declaration_begin clock_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_CLOCK);
+               }
+       |       CTF_CLOCK clock_declaration_begin ctf_assignment_expression_list clock_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_CLOCK);
+                       if (set_parent_node($3, $$))
+                               reparent_error(scanner, "trace_declaration");
+               }
+       ;
+
+clock_declaration_begin:
+               CTF_LBRAC
+               {       push_scope(scanner);    }
+       ;
+
+clock_declaration_end:
+               CTF_RBRAC CTF_SEMICOLON
+               {       pop_scope(scanner);     }
+       ;
+
+callsite_declaration:
+               CTF_CALLSITE callsite_declaration_begin callsite_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_CALLSITE);
+               }
+       |       CTF_CALLSITE callsite_declaration_begin ctf_assignment_expression_list callsite_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_CALLSITE);
+                       if (set_parent_node($3, $$))
+                               reparent_error(scanner, "trace_declaration");
+               }
+       ;
+
+callsite_declaration_begin:
+               CTF_LBRAC
+               {       push_scope(scanner);    }
+       ;
+
+callsite_declaration_end:
+               CTF_RBRAC CTF_SEMICOLON
+               {       pop_scope(scanner);     }
+       ;
+
+integer_declaration_specifiers:
+               CTF_CONST
+               {
+                       struct ctf_node *node;
+
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       node->u.field_class_specifier.type = TYPESPEC_CONST;
+                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
+               }
+       |       integer_field_class_specifier
+               {
+                       struct ctf_node *node;
+
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       node = $1;
+                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
+               }
+       |       integer_declaration_specifiers CTF_CONST
+               {
+                       struct ctf_node *node;
+
+                       $$ = $1;
+                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       node->u.field_class_specifier.type = TYPESPEC_CONST;
+                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
+               }
+       |       integer_declaration_specifiers integer_field_class_specifier
+               {
+                       $$ = $1;
+                       bt_list_add_tail(&($2)->siblings, &($$)->u.field_class_specifier_list.head);
+               }
+       ;
+
+declaration_specifiers:
+               CTF_CONST
+               {
+                       struct ctf_node *node;
+
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       node->u.field_class_specifier.type = TYPESPEC_CONST;
+                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
+               }
+       |       field_class_specifier
+               {
+                       struct ctf_node *node;
+
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       node = $1;
+                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
+               }
+       |       declaration_specifiers CTF_CONST
+               {
+                       struct ctf_node *node;
+
+                       $$ = $1;
+                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       node->u.field_class_specifier.type = TYPESPEC_CONST;
+                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
+               }
+       |       declaration_specifiers field_class_specifier
+               {
+                       $$ = $1;
+                       bt_list_add_tail(&($2)->siblings, &($$)->u.field_class_specifier_list.head);
+               }
+       ;
+
+field_class_declarator_list:
+               field_class_declarator
+               {       $$ = $1;        }
+       |       field_class_declarator_list CTF_COMMA field_class_declarator
+               {
+                       $$ = $1;
+                       bt_list_add_tail(&($3)->siblings, &($$)->tmp_head);
+               }
+       ;
+
+integer_field_class_specifier:
+               CTF_CHAR
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_CHAR;
+               }
+       |       CTF_SHORT
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_SHORT;
+               }
+       |       CTF_INT
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_INT;
+               }
+       |       CTF_LONG
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_LONG;
+               }
+       |       CTF_SIGNED
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_SIGNED;
+               }
+       |       CTF_UNSIGNED
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_UNSIGNED;
+               }
+       |       CTF_BOOL
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_BOOL;
+               }
+       |       ID_TYPE
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_ID_TYPE;
+                       $$->u.field_class_specifier.id_type = yylval.s;
+               }
+       |       CTF_INTEGER CTF_LBRAC CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_INTEGER;
+                       $$->u.field_class_specifier.node = make_node(scanner, NODE_INTEGER);
+               }
+       |       CTF_INTEGER CTF_LBRAC ctf_assignment_expression_list CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_INTEGER;
+                       $$->u.field_class_specifier.node = make_node(scanner, NODE_INTEGER);
+                       if (set_parent_node($3, $$->u.field_class_specifier.node))
+                               reparent_error(scanner, "integer reparent error");
+               }
+       ;
+
+field_class_specifier:
+               CTF_VOID
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_VOID;
+               }
+       |       CTF_CHAR
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_CHAR;
+               }
+       |       CTF_SHORT
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_SHORT;
+               }
+       |       CTF_INT
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_INT;
+               }
+       |       CTF_LONG
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_LONG;
+               }
+       |       CTF_FLOAT
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_FLOAT;
+               }
+       |       CTF_DOUBLE
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_DOUBLE;
+               }
+       |       CTF_SIGNED
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_SIGNED;
+               }
+       |       CTF_UNSIGNED
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_UNSIGNED;
+               }
+       |       CTF_BOOL
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_BOOL;
+               }
+       |       CTF_COMPLEX
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_COMPLEX;
+               }
+       |       CTF_IMAGINARY
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_IMAGINARY;
+               }
+       |       ID_TYPE
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_ID_TYPE;
+                       $$->u.field_class_specifier.id_type = yylval.s;
+               }
+       |       CTF_FLOATING_POINT CTF_LBRAC CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_FLOATING_POINT;
+                       $$->u.field_class_specifier.node = make_node(scanner, NODE_FLOATING_POINT);
+               }
+       |       CTF_FLOATING_POINT CTF_LBRAC ctf_assignment_expression_list CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_FLOATING_POINT;
+                       $$->u.field_class_specifier.node = make_node(scanner, NODE_FLOATING_POINT);
+                       if (set_parent_node($3, $$->u.field_class_specifier.node))
+                               reparent_error(scanner, "floating point reparent error");
+               }
+       |       CTF_INTEGER CTF_LBRAC CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_INTEGER;
+                       $$->u.field_class_specifier.node = make_node(scanner, NODE_INTEGER);
+               }
+       |       CTF_INTEGER CTF_LBRAC ctf_assignment_expression_list CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_INTEGER;
+                       $$->u.field_class_specifier.node = make_node(scanner, NODE_INTEGER);
+                       if (set_parent_node($3, $$->u.field_class_specifier.node))
+                               reparent_error(scanner, "integer reparent error");
+               }
+       |       CTF_STRING
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_STRING;
+                       $$->u.field_class_specifier.node = make_node(scanner, NODE_STRING);
+               }
+       |       CTF_STRING CTF_LBRAC CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_STRING;
+                       $$->u.field_class_specifier.node = make_node(scanner, NODE_STRING);
+               }
+       |       CTF_STRING CTF_LBRAC ctf_assignment_expression_list CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_STRING;
+                       $$->u.field_class_specifier.node = make_node(scanner, NODE_STRING);
+                       if (set_parent_node($3, $$->u.field_class_specifier.node))
+                               reparent_error(scanner, "string reparent error");
+               }
+       |       CTF_ENUM enum_field_class_specifier
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_ENUM;
+                       $$->u.field_class_specifier.node = $2;
+               }
+       |       CTF_VARIANT variant_field_class_specifier
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_VARIANT;
+                       $$->u.field_class_specifier.node = $2;
+               }
+       |       CTF_STRUCT struct_class_specifier
+               {
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       $$->u.field_class_specifier.type = TYPESPEC_STRUCT;
+                       $$->u.field_class_specifier.node = $2;
+               }
+       ;
+
+struct_class_specifier:
+               struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_STRUCT);
+                       $$->u._struct.has_body = 1;
+                       if ($2 && set_parent_node($2, $$))
+                               reparent_error(scanner, "struct reparent error");
+               }
+       |       IDENTIFIER struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_STRUCT);
+                       $$->u._struct.has_body = 1;
+                       $$->u._struct.name = $1;
+                       if ($3 && set_parent_node($3, $$))
+                               reparent_error(scanner, "struct reparent error");
+               }
+       |       ID_TYPE struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_STRUCT);
+                       $$->u._struct.has_body = 1;
+                       $$->u._struct.name = $1;
+                       if ($3 && set_parent_node($3, $$))
+                               reparent_error(scanner, "struct reparent error");
+               }
+       |       IDENTIFIER
+               {
+                       $$ = make_node(scanner, NODE_STRUCT);
+                       $$->u._struct.has_body = 0;
+                       $$->u._struct.name = $1;
+               }
+       |       ID_TYPE
+               {
+                       $$ = make_node(scanner, NODE_STRUCT);
+                       $$->u._struct.has_body = 0;
+                       $$->u._struct.name = $1;
+               }
+       |       struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end CTF_TOK_ALIGN CTF_LPAREN unary_expression CTF_RPAREN
+               {
+                       $$ = make_node(scanner, NODE_STRUCT);
+                       $$->u._struct.has_body = 1;
+                       bt_list_add_tail(&($6)->siblings, &$$->u._struct.min_align);
+                       if ($2 && set_parent_node($2, $$))
+                               reparent_error(scanner, "struct reparent error");
+               }
+       |       IDENTIFIER struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end CTF_TOK_ALIGN CTF_LPAREN unary_expression CTF_RPAREN
+               {
+                       $$ = make_node(scanner, NODE_STRUCT);
+                       $$->u._struct.has_body = 1;
+                       $$->u._struct.name = $1;
+                       bt_list_add_tail(&($7)->siblings, &$$->u._struct.min_align);
+                       if ($3 && set_parent_node($3, $$))
+                               reparent_error(scanner, "struct reparent error");
+               }
+       |       ID_TYPE struct_declaration_begin struct_or_variant_declaration_list struct_declaration_end CTF_TOK_ALIGN CTF_LPAREN unary_expression CTF_RPAREN
+               {
+                       $$ = make_node(scanner, NODE_STRUCT);
+                       $$->u._struct.has_body = 1;
+                       $$->u._struct.name = $1;
+                       bt_list_add_tail(&($7)->siblings, &$$->u._struct.min_align);
+                       if ($3 && set_parent_node($3, $$))
+                               reparent_error(scanner, "struct reparent error");
+               }
+       ;
+
+struct_declaration_begin:
+               CTF_LBRAC
+               {       push_scope(scanner);    }
+       ;
+
+struct_declaration_end:
+               CTF_RBRAC
+               {       pop_scope(scanner);     }
+       ;
+
+variant_field_class_specifier:
+               variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 1;
+                       if ($2 && set_parent_node($2, $$))
+                               reparent_error(scanner, "variant reparent error");
+               }
+       |       CTF_LT IDENTIFIER CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 1;
+                       $$->u.variant.choice = $2;
+                       if ($5 && set_parent_node($5, $$))
+                               reparent_error(scanner, "variant reparent error");
+               }
+       |       CTF_LT ID_TYPE CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 1;
+                       $$->u.variant.choice = $2;
+                       if ($5 && set_parent_node($5, $$))
+                               reparent_error(scanner, "variant reparent error");
+               }
+       |       IDENTIFIER variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 1;
+                       $$->u.variant.name = $1;
+                       if ($3 && set_parent_node($3, $$))
+                               reparent_error(scanner, "variant reparent error");
+               }
+       |       IDENTIFIER CTF_LT IDENTIFIER CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 1;
+                       $$->u.variant.name = $1;
+                       $$->u.variant.choice = $3;
+                       if ($6 && set_parent_node($6, $$))
+                               reparent_error(scanner, "variant reparent error");
+               }
+       |       IDENTIFIER CTF_LT IDENTIFIER CTF_GT
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 0;
+                       $$->u.variant.name = $1;
+                       $$->u.variant.choice = $3;
+               }
+       |       IDENTIFIER CTF_LT ID_TYPE CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 1;
+                       $$->u.variant.name = $1;
+                       $$->u.variant.choice = $3;
+                       if ($6 && set_parent_node($6, $$))
+                               reparent_error(scanner, "variant reparent error");
+               }
+       |       IDENTIFIER CTF_LT ID_TYPE CTF_GT
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 0;
+                       $$->u.variant.name = $1;
+                       $$->u.variant.choice = $3;
+               }
+       |       ID_TYPE variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 1;
+                       $$->u.variant.name = $1;
+                       if ($3 && set_parent_node($3, $$))
+                               reparent_error(scanner, "variant reparent error");
+               }
+       |       ID_TYPE CTF_LT IDENTIFIER CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 1;
+                       $$->u.variant.name = $1;
+                       $$->u.variant.choice = $3;
+                       if ($6 && set_parent_node($6, $$))
+                               reparent_error(scanner, "variant reparent error");
+               }
+       |       ID_TYPE CTF_LT IDENTIFIER CTF_GT
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 0;
+                       $$->u.variant.name = $1;
+                       $$->u.variant.choice = $3;
+               }
+       |       ID_TYPE CTF_LT ID_TYPE CTF_GT variant_declaration_begin struct_or_variant_declaration_list variant_declaration_end
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 1;
+                       $$->u.variant.name = $1;
+                       $$->u.variant.choice = $3;
+                       if ($6 && set_parent_node($6, $$))
+                               reparent_error(scanner, "variant reparent error");
+               }
+       |       ID_TYPE CTF_LT ID_TYPE CTF_GT
+               {
+                       $$ = make_node(scanner, NODE_VARIANT);
+                       $$->u.variant.has_body = 0;
+                       $$->u.variant.name = $1;
+                       $$->u.variant.choice = $3;
+               }
+       ;
+
+variant_declaration_begin:
+               CTF_LBRAC
+               {       push_scope(scanner);    }
+       ;
+
+variant_declaration_end:
+               CTF_RBRAC
+               {       pop_scope(scanner);     }
+       ;
+
+enum_field_class_specifier:
+               CTF_LBRAC enumerator_list CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       _bt_list_splice_tail(&($2)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       ($$)->u._enum.container_field_class = $2;
+                       _bt_list_splice_tail(&($4)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       IDENTIFIER CTF_LBRAC enumerator_list CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       $$->u._enum.enum_id = $1;
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       IDENTIFIER CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       $$->u._enum.enum_id = $1;
+                       ($$)->u._enum.container_field_class = $3;
+                       _bt_list_splice_tail(&($5)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       ID_TYPE CTF_LBRAC enumerator_list CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       $$->u._enum.enum_id = $1;
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       ID_TYPE CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       $$->u._enum.enum_id = $1;
+                       ($$)->u._enum.container_field_class = $3;
+                       _bt_list_splice_tail(&($5)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       _bt_list_splice_tail(&($2)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       ($$)->u._enum.container_field_class = $2;
+                       _bt_list_splice_tail(&($4)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       IDENTIFIER CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       $$->u._enum.enum_id = $1;
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       IDENTIFIER CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       $$->u._enum.enum_id = $1;
+                       ($$)->u._enum.container_field_class = $3;
+                       _bt_list_splice_tail(&($5)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       IDENTIFIER
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 0;
+                       $$->u._enum.enum_id = $1;
+               }
+       |       ID_TYPE CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       $$->u._enum.enum_id = $1;
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       ID_TYPE CTF_COLON integer_declaration_specifiers CTF_LBRAC enumerator_list CTF_COMMA CTF_RBRAC
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 1;
+                       $$->u._enum.enum_id = $1;
+                       ($$)->u._enum.container_field_class = $3;
+                       _bt_list_splice_tail(&($5)->tmp_head, &($$)->u._enum.enumerator_list);
+               }
+       |       ID_TYPE
+               {
+                       $$ = make_node(scanner, NODE_ENUM);
+                       $$->u._enum.has_body = 0;
+                       $$->u._enum.enum_id = $1;
+               }
+       ;
+
+struct_or_variant_declaration_list:
+               /* empty */
+               {       $$ = NULL;      }
+       |       struct_or_variant_declaration_list struct_or_variant_declaration
+               {
+                       if ($1) {
+                               $$ = $1;
+                               bt_list_add_tail(&($2)->siblings, &($$)->tmp_head);
+                       } else {
+                               $$ = $2;
+                               bt_list_add_tail(&($$)->siblings, &($$)->tmp_head);
+                       }
+               }
+       ;
+
+struct_or_variant_declaration:
+               declaration_specifiers struct_or_variant_declarator_list CTF_SEMICOLON
+               {
+                       struct ctf_node *list;
+
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       $$ = make_node(scanner, NODE_STRUCT_OR_VARIANT_DECLARATION);
+                       ($$)->u.struct_or_variant_declaration.field_class_specifier_list = list;
+                       _bt_list_splice_tail(&($2)->tmp_head, &($$)->u.struct_or_variant_declaration.field_class_declarators);
+               }
+       |       declaration_specifiers CTF_TYPEDEF declaration_specifiers field_class_declarator_list CTF_SEMICOLON
+               {
+                       struct ctf_node *list;
+
+                       $$ = make_node(scanner, NODE_TYPEDEF);
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u.field_class_def.field_class_specifier_list = list;
+                       _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       _bt_list_splice_tail(&($3)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       _bt_list_splice_tail(&($4)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
+               }
+       |       CTF_TYPEDEF declaration_specifiers field_class_declarator_list CTF_SEMICOLON
+               {
+                       struct ctf_node *list;
+
+                       $$ = make_node(scanner, NODE_TYPEDEF);
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u.field_class_def.field_class_specifier_list = list;
+                       _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
+               }
+       |       declaration_specifiers CTF_TYPEDEF field_class_declarator_list CTF_SEMICOLON
+               {
+                       struct ctf_node *list;
+
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       $$ = make_node(scanner, NODE_TYPEDEF);
+                       ($$)->u.struct_or_variant_declaration.field_class_specifier_list = list;
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
+               }
+       |       CTF_TYPEALIAS declaration_specifiers abstract_declarator_list CTF_TYPEASSIGN alias_declaration_specifiers alias_abstract_declarator_list CTF_SEMICOLON
+               {
+                       struct ctf_node *list;
+
+                       $$ = make_node(scanner, NODE_TYPEALIAS);
+                       $$->u.field_class_alias.target = make_node(scanner, NODE_TYPEALIAS_TARGET);
+                       $$->u.field_class_alias.alias = make_node(scanner, NODE_TYPEALIAS_ALIAS);
+
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u.field_class_alias.target->u.field_class_alias_target.field_class_specifier_list = list;
+                       _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_alias.target->u.field_class_alias_target.field_class_declarators);
+
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u.field_class_alias.alias->u.field_class_alias_name.field_class_specifier_list = list;
+                       _bt_list_splice_tail(&($5)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       _bt_list_splice_tail(&($6)->tmp_head, &($$)->u.field_class_alias.alias->u.field_class_alias_name.field_class_declarators);
+               }
+       ;
+
+alias_declaration_specifiers:
+               CTF_CONST
+               {
+                       struct ctf_node *node;
+
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       node->u.field_class_specifier.type = TYPESPEC_CONST;
+                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
+               }
+       |       field_class_specifier
+               {
+                       struct ctf_node *node;
+
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       node = $1;
+                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
+               }
+       |       IDENTIFIER
+               {
+                       struct ctf_node *node;
+
+                       add_type(scanner, $1);
+                       $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       node->u.field_class_specifier.type = TYPESPEC_ID_TYPE;
+                       node->u.field_class_specifier.id_type = yylval.s;
+                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
+               }
+       |       alias_declaration_specifiers CTF_CONST
+               {
+                       struct ctf_node *node;
+
+                       $$ = $1;
+                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       node->u.field_class_specifier.type = TYPESPEC_CONST;
+                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
+               }
+       |       alias_declaration_specifiers field_class_specifier
+               {
+                       $$ = $1;
+                       bt_list_add_tail(&($2)->siblings, &($$)->u.field_class_specifier_list.head);
+               }
+       |       alias_declaration_specifiers IDENTIFIER
+               {
+                       struct ctf_node *node;
+
+                       add_type(scanner, $2);
+                       $$ = $1;
+                       node = make_node(scanner, NODE_TYPE_SPECIFIER);
+                       node->u.field_class_specifier.type = TYPESPEC_ID_TYPE;
+                       node->u.field_class_specifier.id_type = yylval.s;
+                       bt_list_add_tail(&node->siblings, &($$)->u.field_class_specifier_list.head);
+               }
+       ;
+
+struct_or_variant_declarator_list:
+               struct_or_variant_declarator
+               {       $$ = $1;        }
+       |       struct_or_variant_declarator_list CTF_COMMA struct_or_variant_declarator
+               {
+                       $$ = $1;
+                       bt_list_add_tail(&($3)->siblings, &($$)->tmp_head);
+               }
+       ;
+
+struct_or_variant_declarator:
+               declarator
+               {       $$ = $1;        }
+       |       CTF_COLON unary_expression
+               {       $$ = $2;        }
+       |       declarator CTF_COLON unary_expression
+               {
+                       $$ = $1;
+                       if (set_parent_node($3, $1))
+                               reparent_error(scanner, "struct_or_variant_declarator");
+               }
+       ;
+
+enumerator_list:
+               enumerator
+               {       $$ = $1;        }
+       |       enumerator_list CTF_COMMA enumerator
+               {
+                       $$ = $1;
+                       bt_list_add_tail(&($3)->siblings, &($$)->tmp_head);
+               }
+       ;
+
+enumerator:
+               IDENTIFIER
+               {
+                       $$ = make_node(scanner, NODE_ENUMERATOR);
+                       $$->u.enumerator.id = $1;
+               }
+       |       ID_TYPE
+               {
+                       $$ = make_node(scanner, NODE_ENUMERATOR);
+                       $$->u.enumerator.id = $1;
+               }
+       |       keywords
+               {
+                       $$ = make_node(scanner, NODE_ENUMERATOR);
+                       $$->u.enumerator.id = $1;
+               }
+       |       CTF_STRING_LITERAL
+               {
+                       $$ = make_node(scanner, NODE_ENUMERATOR);
+                       $$->u.enumerator.id = $1;
+               }
+       |       IDENTIFIER CTF_EQUAL unary_expression_or_range
+               {
+                       $$ = make_node(scanner, NODE_ENUMERATOR);
+                       $$->u.enumerator.id = $1;
+                       bt_list_splice(&($3)->tmp_head, &($$)->u.enumerator.values);
+               }
+       |       ID_TYPE CTF_EQUAL unary_expression_or_range
+               {
+                       $$ = make_node(scanner, NODE_ENUMERATOR);
+                       $$->u.enumerator.id = $1;
+                       bt_list_splice(&($3)->tmp_head, &($$)->u.enumerator.values);
+               }
+       |       keywords CTF_EQUAL unary_expression_or_range
+               {
+                       $$ = make_node(scanner, NODE_ENUMERATOR);
+                       $$->u.enumerator.id = $1;
+                       bt_list_splice(&($3)->tmp_head, &($$)->u.enumerator.values);
+               }
+       |       CTF_STRING_LITERAL CTF_EQUAL unary_expression_or_range
+               {
+                       $$ = make_node(scanner, NODE_ENUMERATOR);
+                       $$->u.enumerator.id = $1;
+                       bt_list_splice(&($3)->tmp_head, &($$)->u.enumerator.values);
+               }
+       ;
+
+abstract_declarator_list:
+               abstract_declarator
+               {       $$ = $1;        }
+       |       abstract_declarator_list CTF_COMMA abstract_declarator
+               {
+                       $$ = $1;
+                       bt_list_add_tail(&($3)->siblings, &($$)->tmp_head);
+               }
+       ;
+
+abstract_declarator:
+               direct_abstract_declarator
+               {       $$ = $1;        }
+       |       pointer direct_abstract_declarator
+               {
+                       $$ = $2;
+                       bt_list_splice(&($1)->tmp_head, &($$)->u.field_class_declarator.pointers);
+               }
+       ;
+
+direct_abstract_declarator:
+               /* empty */
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                        $$->u.field_class_declarator.type = TYPEDEC_ID;
+                       /* id is NULL */
+               }
+       |       IDENTIFIER
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.field_class_declarator.type = TYPEDEC_ID;
+                       $$->u.field_class_declarator.u.id = $1;
+               }
+       |       CTF_LPAREN abstract_declarator CTF_RPAREN
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
+                       $$->u.field_class_declarator.u.nested.field_class_declarator = $2;
+               }
+       |       direct_abstract_declarator CTF_LSBRAC unary_expression CTF_RSBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
+                       $$->u.field_class_declarator.u.nested.field_class_declarator = $1;
+                       BT_INIT_LIST_HEAD(&($$)->u.field_class_declarator.u.nested.length);
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_declarator.u.nested.length);
+               }
+       |       direct_abstract_declarator CTF_LSBRAC CTF_RSBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
+                       $$->u.field_class_declarator.u.nested.field_class_declarator = $1;
+                       $$->u.field_class_declarator.u.nested.abstract_array = 1;
+               }
+       ;
+
+alias_abstract_declarator_list:
+               alias_abstract_declarator
+               {       $$ = $1;        }
+       |       alias_abstract_declarator_list CTF_COMMA alias_abstract_declarator
+               {
+                       $$ = $1;
+                       bt_list_add_tail(&($3)->siblings, &($$)->tmp_head);
+               }
+       ;
+
+alias_abstract_declarator:
+               direct_alias_abstract_declarator
+               {       $$ = $1;        }
+       |       pointer direct_alias_abstract_declarator
+               {
+                       $$ = $2;
+                       bt_list_splice(&($1)->tmp_head, &($$)->u.field_class_declarator.pointers);
+               }
+       ;
+
+direct_alias_abstract_declarator:
+               /* empty */
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                        $$->u.field_class_declarator.type = TYPEDEC_ID;
+                       /* id is NULL */
+               }
+       |       CTF_LPAREN alias_abstract_declarator CTF_RPAREN
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
+                       $$->u.field_class_declarator.u.nested.field_class_declarator = $2;
+               }
+       |       direct_alias_abstract_declarator CTF_LSBRAC unary_expression CTF_RSBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
+                       $$->u.field_class_declarator.u.nested.field_class_declarator = $1;
+                       BT_INIT_LIST_HEAD(&($$)->u.field_class_declarator.u.nested.length);
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_declarator.u.nested.length);
+               }
+       |       direct_alias_abstract_declarator CTF_LSBRAC CTF_RSBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
+                       $$->u.field_class_declarator.u.nested.field_class_declarator = $1;
+                       $$->u.field_class_declarator.u.nested.abstract_array = 1;
+               }
+       ;
+
+declarator:
+               direct_declarator
+               {       $$ = $1;        }
+       |       pointer direct_declarator
+               {
+                       $$ = $2;
+                       bt_list_splice(&($1)->tmp_head, &($$)->u.field_class_declarator.pointers);
+               }
+       ;
+
+direct_declarator:
+               IDENTIFIER
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.field_class_declarator.type = TYPEDEC_ID;
+                       $$->u.field_class_declarator.u.id = $1;
+               }
+       |       CTF_LPAREN declarator CTF_RPAREN
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
+                       $$->u.field_class_declarator.u.nested.field_class_declarator = $2;
+               }
+       |       direct_declarator CTF_LSBRAC unary_expression CTF_RSBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
+                       $$->u.field_class_declarator.u.nested.field_class_declarator = $1;
+                       BT_INIT_LIST_HEAD(&($$)->u.field_class_declarator.u.nested.length);
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_declarator.u.nested.length);
+               }
+       ;
+
+field_class_declarator:
+               direct_field_class_declarator
+               {       $$ = $1;        }
+       |       pointer direct_field_class_declarator
+               {
+                       $$ = $2;
+                       bt_list_splice(&($1)->tmp_head, &($$)->u.field_class_declarator.pointers);
+               }
+       ;
+
+direct_field_class_declarator:
+               IDENTIFIER
+               {
+                       add_type(scanner, $1);
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.field_class_declarator.type = TYPEDEC_ID;
+                       $$->u.field_class_declarator.u.id = $1;
+               }
+       |       CTF_LPAREN field_class_declarator CTF_RPAREN
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
+                       $$->u.field_class_declarator.u.nested.field_class_declarator = $2;
+               }
+       |       direct_field_class_declarator CTF_LSBRAC unary_expression CTF_RSBRAC
+               {
+                       $$ = make_node(scanner, NODE_TYPE_DECLARATOR);
+                       $$->u.field_class_declarator.type = TYPEDEC_NESTED;
+                       $$->u.field_class_declarator.u.nested.field_class_declarator = $1;
+                       BT_INIT_LIST_HEAD(&($$)->u.field_class_declarator.u.nested.length);
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_declarator.u.nested.length);
+               }
+       ;
+
+pointer:
+               CTF_STAR
+               {
+                       $$ = make_node(scanner, NODE_POINTER);
+               }
+       |       CTF_STAR pointer
+               {
+                       $$ = make_node(scanner, NODE_POINTER);
+                       bt_list_splice(&($2)->tmp_head, &($$)->tmp_head);
+               }
+       |       CTF_STAR type_qualifier_list pointer
+               {
+                       $$ = make_node(scanner, NODE_POINTER);
+                       $$->u.pointer.const_qualifier = 1;
+                       bt_list_splice(&($3)->tmp_head, &($$)->tmp_head);
+               }
+       ;
+
+type_qualifier_list:
+               /* pointer assumes only const type qualifier */
+               CTF_CONST
+       |       type_qualifier_list CTF_CONST
+       ;
+
+/* 2.3: CTF-specific declarations */
+
+ctf_assignment_expression_list:
+               ctf_assignment_expression CTF_SEMICOLON
+               {       $$ = $1;        }
+       |       ctf_assignment_expression_list ctf_assignment_expression CTF_SEMICOLON
+               {
+                       $$ = $1;
+                       bt_list_add_tail(&($2)->siblings, &($$)->tmp_head);
+               }
+       ;
+
+ctf_assignment_expression:
+               unary_expression CTF_EQUAL unary_expression
+               {
+                       /*
+                        * Because we have left and right, cannot use
+                        * set_parent_node.
+                        */
+                       $$ = make_node(scanner, NODE_CTF_EXPRESSION);
+                       _bt_list_splice_tail(&($1)->tmp_head, &($$)->u.ctf_expression.left);
+                       if ($1->u.unary_expression.type != UNARY_STRING)
+                               reparent_error(scanner, "ctf_assignment_expression left expects string");
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.ctf_expression.right);
+               }
+       |       unary_expression CTF_TYPEASSIGN declaration_specifiers  /* Only allow struct */
+               {
+                       /*
+                        * Because we have left and right, cannot use
+                        * set_parent_node.
+                        */
+                       $$ = make_node(scanner, NODE_CTF_EXPRESSION);
+                       _bt_list_splice_tail(&($1)->tmp_head, &($$)->u.ctf_expression.left);
+                       if ($1->u.unary_expression.type != UNARY_STRING)
+                               reparent_error(scanner, "ctf_assignment_expression left expects string");
+                       bt_list_add_tail(&($3)->siblings, &($$)->u.ctf_expression.right);
+               }
+       |       declaration_specifiers CTF_TYPEDEF declaration_specifiers field_class_declarator_list
+               {
+                       struct ctf_node *list;
+
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       _bt_list_splice_tail(&($3)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       $$ = make_node(scanner, NODE_TYPEDEF);
+                       ($$)->u.struct_or_variant_declaration.field_class_specifier_list = list;
+                       _bt_list_splice_tail(&($4)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
+               }
+       |       CTF_TYPEDEF declaration_specifiers field_class_declarator_list
+               {
+                       struct ctf_node *list;
+
+                       $$ = make_node(scanner, NODE_TYPEDEF);
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u.field_class_def.field_class_specifier_list = list;
+                       _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
+               }
+       |       declaration_specifiers CTF_TYPEDEF field_class_declarator_list
+               {
+                       struct ctf_node *list;
+
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       _bt_list_splice_tail(&($1)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       $$ = make_node(scanner, NODE_TYPEDEF);
+                       ($$)->u.struct_or_variant_declaration.field_class_specifier_list = list;
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_def.field_class_declarators);
+               }
+       |       CTF_TYPEALIAS declaration_specifiers abstract_declarator_list CTF_TYPEASSIGN alias_declaration_specifiers alias_abstract_declarator_list
+               {
+                       struct ctf_node *list;
+
+                       $$ = make_node(scanner, NODE_TYPEALIAS);
+                       $$->u.field_class_alias.target = make_node(scanner, NODE_TYPEALIAS_TARGET);
+                       $$->u.field_class_alias.alias = make_node(scanner, NODE_TYPEALIAS_ALIAS);
+
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u.field_class_alias.target->u.field_class_alias_target.field_class_specifier_list = list;
+                       _bt_list_splice_tail(&($2)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.field_class_alias.target->u.field_class_alias_target.field_class_declarators);
+
+                       list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST);
+                       $$->u.field_class_alias.alias->u.field_class_alias_name.field_class_specifier_list = list;
+                       _bt_list_splice_tail(&($5)->u.field_class_specifier_list.head, &list->u.field_class_specifier_list.head);
+                       _bt_list_splice_tail(&($6)->tmp_head, &($$)->u.field_class_alias.alias->u.field_class_alias_name.field_class_declarators);
+               }
+       ;
diff --git a/src/plugins/ctf/common/metadata/scanner-symbols.h b/src/plugins/ctf/common/metadata/scanner-symbols.h
deleted file mode 100644 (file)
index 8b2ee45..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2011-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- */
-
-#ifndef _CTF_SCANNER_SYMBOLS
-#define _CTF_SCANNER_SYMBOLS
-
-#define yy_create_buffer bt_yy_create_buffer
-#define yy_delete_buffer bt_yy_delete_buffer
-#define yy_flush_buffer bt_yy_flush_buffer
-#define yy_scan_buffer bt_yy_scan_buffer
-#define yy_scan_bytes bt_yy_scan_bytes
-#define yy_scan_string bt_yy_scan_string
-#define yy_switch_to_buffer bt_yy_switch_to_buffer
-#define yyalloc bt_yyalloc
-#define yyfree bt_yyfree
-#define yyget_column bt_yyget_column
-#define yyget_debug bt_yyget_debug
-#define yyget_extra bt_yyget_extra
-#define yyget_in bt_yyget_in
-#define yyget_leng bt_yyget_leng
-#define yyget_lineno bt_yyget_lineno
-#define yyget_lval bt_yyget_lval
-#define yyget_out bt_yyget_out
-#define yyget_text bt_yyget_text
-#define yylex_init bt_yylex_init
-#define yypop_buffer_state bt_yypop_buffer_state
-#define yypush_buffer_state bt_yypush_buffer_state
-#define yyrealloc bt_yyrealloc
-#define yyset_column bt_yyset_column
-#define yyset_debug bt_yyset_debug
-#define yyset_extra bt_yyset_extra
-#define yyset_in bt_yyset_in
-#define yyset_lineno bt_yyset_lineno
-#define yyset_lval bt_yyset_lval
-#define yyset_out bt_yyset_out
-
-#endif /* _CTF_SCANNER_SYMBOLS */
diff --git a/src/plugins/ctf/common/metadata/scanner-symbols.hpp b/src/plugins/ctf/common/metadata/scanner-symbols.hpp
new file mode 100644 (file)
index 0000000..8b2ee45
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2011-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ */
+
+#ifndef _CTF_SCANNER_SYMBOLS
+#define _CTF_SCANNER_SYMBOLS
+
+#define yy_create_buffer bt_yy_create_buffer
+#define yy_delete_buffer bt_yy_delete_buffer
+#define yy_flush_buffer bt_yy_flush_buffer
+#define yy_scan_buffer bt_yy_scan_buffer
+#define yy_scan_bytes bt_yy_scan_bytes
+#define yy_scan_string bt_yy_scan_string
+#define yy_switch_to_buffer bt_yy_switch_to_buffer
+#define yyalloc bt_yyalloc
+#define yyfree bt_yyfree
+#define yyget_column bt_yyget_column
+#define yyget_debug bt_yyget_debug
+#define yyget_extra bt_yyget_extra
+#define yyget_in bt_yyget_in
+#define yyget_leng bt_yyget_leng
+#define yyget_lineno bt_yyget_lineno
+#define yyget_lval bt_yyget_lval
+#define yyget_out bt_yyget_out
+#define yyget_text bt_yyget_text
+#define yylex_init bt_yylex_init
+#define yypop_buffer_state bt_yypop_buffer_state
+#define yypush_buffer_state bt_yypush_buffer_state
+#define yyrealloc bt_yyrealloc
+#define yyset_column bt_yyset_column
+#define yyset_debug bt_yyset_debug
+#define yyset_extra bt_yyset_extra
+#define yyset_in bt_yyset_in
+#define yyset_lineno bt_yyset_lineno
+#define yyset_lval bt_yyset_lval
+#define yyset_out bt_yyset_out
+
+#endif /* _CTF_SCANNER_SYMBOLS */
diff --git a/src/plugins/ctf/common/metadata/scanner.h b/src/plugins/ctf/common/metadata/scanner.h
deleted file mode 100644 (file)
index 1a1d9d9..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2011-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- */
-
-#ifndef _CTF_SCANNER_H
-#define _CTF_SCANNER_H
-
-#include <stdio.h>
-#include "common/macros.h"
-#include "ast.h"
-
-#ifndef YY_TYPEDEF_YY_SCANNER_T
-#define YY_TYPEDEF_YY_SCANNER_T
-typedef void* yyscan_t;
-#endif
-
-struct ctf_scanner_scope;
-struct ctf_scanner_scope {
-       struct ctf_scanner_scope *parent;
-       GHashTable *classes;
-};
-
-struct ctf_scanner {
-       yyscan_t scanner;
-       struct ctf_ast *ast;
-       struct ctf_scanner_scope root_scope;
-       struct ctf_scanner_scope *cs;
-       struct objstack *objstack;
-};
-
-BT_HIDDEN
-struct ctf_scanner *ctf_scanner_alloc(void);
-
-BT_HIDDEN
-void ctf_scanner_free(struct ctf_scanner *scanner);
-
-BT_HIDDEN
-int ctf_scanner_append_ast(struct ctf_scanner *scanner, FILE *input);
-
-static inline
-struct ctf_ast *ctf_scanner_get_ast(struct ctf_scanner *scanner)
-{
-       return scanner->ast;
-}
-
-BT_HIDDEN
-int is_type(struct ctf_scanner *scanner, const char *id);
-
-#endif /* _CTF_SCANNER_H */
diff --git a/src/plugins/ctf/common/metadata/scanner.hpp b/src/plugins/ctf/common/metadata/scanner.hpp
new file mode 100644 (file)
index 0000000..d1ab1d9
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2011-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ */
+
+#ifndef _CTF_SCANNER_H
+#define _CTF_SCANNER_H
+
+#include <stdio.h>
+#include "common/macros.h"
+#include "ast.hpp"
+
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+struct ctf_scanner_scope;
+struct ctf_scanner_scope {
+       struct ctf_scanner_scope *parent;
+       GHashTable *classes;
+};
+
+struct ctf_scanner {
+       yyscan_t scanner;
+       struct ctf_ast *ast;
+       struct ctf_scanner_scope root_scope;
+       struct ctf_scanner_scope *cs;
+       struct objstack *objstack;
+};
+
+BT_HIDDEN
+struct ctf_scanner *ctf_scanner_alloc(void);
+
+BT_HIDDEN
+void ctf_scanner_free(struct ctf_scanner *scanner);
+
+BT_HIDDEN
+int ctf_scanner_append_ast(struct ctf_scanner *scanner, FILE *input);
+
+static inline
+struct ctf_ast *ctf_scanner_get_ast(struct ctf_scanner *scanner)
+{
+       return scanner->ast;
+}
+
+BT_HIDDEN
+int is_type(struct ctf_scanner *scanner, const char *id);
+
+#endif /* _CTF_SCANNER_H */
diff --git a/src/plugins/ctf/common/metadata/visitor-generate-ir.c b/src/plugins/ctf/common/metadata/visitor-generate-ir.c
deleted file mode 100644 (file)
index ca87aad..0000000
+++ /dev/null
@@ -1,4943 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2010 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- * Copyright 2015-2018 Philippe Proulx <philippe.proulx@efficios.com>
- *
- * Common Trace Format metadata visitor (generates CTF IR objects).
- */
-
-#define BT_COMP_LOG_SELF_COMP (ctx->log_cfg.self_comp)
-#define BT_COMP_LOG_SELF_COMP_CLASS (ctx->log_cfg.self_comp_class)
-#define BT_LOG_OUTPUT_LEVEL (ctx->log_cfg.log_level)
-#define BT_LOG_TAG "PLUGIN/CTF/META/IR-VISITOR"
-#include "logging/comp-logging.h"
-
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include "common/assert.h"
-#include <glib.h>
-#include <inttypes.h>
-#include <errno.h>
-#include "common/common.h"
-#include "common/uuid.h"
-#include "compat/endian.h"
-#include <babeltrace2/babeltrace.h>
-
-#include "logging.h"
-#include "scanner.h"
-#include "ast.h"
-#include "decoder.h"
-#include "ctf-meta.h"
-#include "ctf-meta-visitors.h"
-
-/* Bit value (left shift) */
-#define _BV(_val)              (1 << (_val))
-
-/* Bit is set in a set of bits */
-#define _IS_SET(_set, _mask)   (*(_set) & (_mask))
-
-/* Set bit in a set of bits */
-#define _SET(_set, _mask)      (*(_set) |= (_mask))
-
-/* Try to push scope, or go to the `error` label */
-#define _TRY_PUSH_SCOPE_OR_GOTO_ERROR()                                        \
-       do {                                                            \
-               ret = ctx_push_scope(ctx);                              \
-               if (ret) {                                              \
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot push scope.");         \
-                       goto error;                                     \
-               }                                                       \
-       } while (0)
-
-/* Bits for verifying existing attributes in various declarations */
-enum {
-       _CLOCK_NAME_SET =               _BV(0),
-       _CLOCK_UUID_SET =               _BV(1),
-       _CLOCK_FREQ_SET =               _BV(2),
-       _CLOCK_PRECISION_SET =          _BV(3),
-       _CLOCK_OFFSET_S_SET =           _BV(4),
-       _CLOCK_OFFSET_SET =             _BV(5),
-       _CLOCK_ABSOLUTE_SET =           _BV(6),
-       _CLOCK_DESCRIPTION_SET =        _BV(7),
-};
-
-enum {
-       _INTEGER_ALIGN_SET =            _BV(0),
-       _INTEGER_SIZE_SET =             _BV(1),
-       _INTEGER_BASE_SET =             _BV(2),
-       _INTEGER_ENCODING_SET =         _BV(3),
-       _INTEGER_BYTE_ORDER_SET =       _BV(4),
-       _INTEGER_SIGNED_SET =           _BV(5),
-       _INTEGER_MAP_SET =              _BV(6),
-};
-
-enum {
-       _FLOAT_ALIGN_SET =              _BV(0),
-       _FLOAT_MANT_DIG_SET =           _BV(1),
-       _FLOAT_EXP_DIG_SET =            _BV(2),
-       _FLOAT_BYTE_ORDER_SET =         _BV(3),
-};
-
-enum {
-       _STRING_ENCODING_SET =          _BV(0),
-};
-
-enum {
-       _TRACE_MINOR_SET =              _BV(0),
-       _TRACE_MAJOR_SET =              _BV(1),
-       _TRACE_BYTE_ORDER_SET =         _BV(2),
-       _TRACE_UUID_SET =               _BV(3),
-       _TRACE_PACKET_HEADER_SET =      _BV(4),
-};
-
-enum {
-       _STREAM_ID_SET =                _BV(0),
-       _STREAM_PACKET_CONTEXT_SET =    _BV(1),
-       _STREAM_EVENT_HEADER_SET =      _BV(2),
-       _STREAM_EVENT_CONTEXT_SET =     _BV(3),
-};
-
-enum {
-       _EVENT_NAME_SET =               _BV(0),
-       _EVENT_ID_SET =                 _BV(1),
-       _EVENT_MODEL_EMF_URI_SET =      _BV(2),
-       _EVENT_STREAM_ID_SET =          _BV(3),
-       _EVENT_LOG_LEVEL_SET =          _BV(4),
-       _EVENT_CONTEXT_SET =            _BV(5),
-       _EVENT_FIELDS_SET =             _BV(6),
-};
-
-enum loglevel {
-        LOG_LEVEL_EMERG                  = 0,
-        LOG_LEVEL_ALERT                  = 1,
-        LOG_LEVEL_CRIT                   = 2,
-        LOG_LEVEL_ERR                    = 3,
-        LOG_LEVEL_WARNING                = 4,
-        LOG_LEVEL_NOTICE                 = 5,
-        LOG_LEVEL_INFO                   = 6,
-        LOG_LEVEL_DEBUG_SYSTEM           = 7,
-        LOG_LEVEL_DEBUG_PROGRAM          = 8,
-        LOG_LEVEL_DEBUG_PROCESS          = 9,
-        LOG_LEVEL_DEBUG_MODULE           = 10,
-        LOG_LEVEL_DEBUG_UNIT             = 11,
-        LOG_LEVEL_DEBUG_FUNCTION         = 12,
-        LOG_LEVEL_DEBUG_LINE             = 13,
-        LOG_LEVEL_DEBUG                  = 14,
-       _NR_LOGLEVELS                   = 15,
-};
-
-/* Prefixes of class aliases */
-#define _PREFIX_ALIAS                  'a'
-#define _PREFIX_ENUM                   'e'
-#define _PREFIX_STRUCT                 's'
-#define _PREFIX_VARIANT                        'v'
-
-/* First entry in a BT list */
-#define _BT_LIST_FIRST_ENTRY(_ptr, _class, _member)                    \
-       bt_list_entry((_ptr)->next, _class, _member)
-
-#define _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(_node, _attr, _entity)                     \
-       _BT_COMP_LOGE_APPEND_CAUSE_LINENO((_node)->lineno,                              \
-               "Duplicate attribute in %s: attr-name=\"%s\"",          \
-               _entity, _attr)
-
-#define _BT_COMP_LOGE_NODE(_node, _msg, args...)                               \
-       _BT_COMP_LOGE_LINENO((_node)->lineno, _msg, ## args)
-
-#define _BT_COMP_LOGE_APPEND_CAUSE_NODE(_node, _msg, args...)                          \
-       _BT_COMP_LOGE_APPEND_CAUSE_LINENO((_node)->lineno, _msg, ## args)
-
-#define _BT_COMP_LOGW_NODE(_node, _msg, args...)                               \
-       _BT_COMP_LOGW_LINENO((_node)->lineno, _msg, ## args)
-
-#define _BT_COMP_LOGT_NODE(_node, _msg, args...)                               \
-       _BT_COMP_LOGT_LINENO((_node)->lineno, _msg, ## args)
-
-/*
- * Declaration scope of a visitor context. This represents a TSDL
- * lexical scope, so that aliases and named structures, variants,
- * and enumerations may be registered and looked up hierarchically.
- */
-struct ctx_decl_scope {
-       /*
-        * Alias name to field class.
-        *
-        * GQuark -> struct ctf_field_class * (owned by this)
-        */
-       GHashTable *decl_map;
-
-       /* Parent scope; NULL if this is the root declaration scope */
-       struct ctx_decl_scope *parent_scope;
-};
-
-/*
- * Visitor context (private).
- */
-struct ctf_visitor_generate_ir {
-       struct meta_log_config log_cfg;
-
-       /* Trace IR trace class being filled (owned by this) */
-       bt_trace_class *trace_class;
-
-       /* CTF meta trace being filled (owned by this) */
-       struct ctf_trace_class *ctf_tc;
-
-       /* Current declaration scope (top of the stack) (owned by this) */
-       struct ctx_decl_scope *current_scope;
-
-       /* True if trace declaration is visited */
-       bool is_trace_visited;
-
-       /* True if this is an LTTng trace */
-       bool is_lttng;
-
-       /* Config passed by the user */
-       struct ctf_metadata_decoder_config decoder_config;
-};
-
-/*
- * Visitor (public).
- */
-struct ctf_visitor_generate_ir;
-
-/**
- * Creates a new declaration scope.
- *
- * @param par_scope    Parent scope (NULL if creating a root scope)
- * @returns            New declaration scope, or NULL on error
- */
-static
-struct ctx_decl_scope *ctx_decl_scope_create(struct ctf_visitor_generate_ir *ctx,
-               struct ctx_decl_scope *par_scope)
-{
-       struct ctx_decl_scope *scope;
-
-       scope = g_new(struct ctx_decl_scope, 1);
-       if (!scope) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Failed to allocate one declaration scope.");
-               goto end;
-       }
-
-       scope->decl_map = g_hash_table_new_full(g_direct_hash, g_direct_equal,
-               NULL, (GDestroyNotify) ctf_field_class_destroy);
-       scope->parent_scope = par_scope;
-
-end:
-       return scope;
-}
-
-/**
- * Destroys a declaration scope.
- *
- * This function does not destroy the parent scope.
- *
- * @param scope        Scope to destroy
- */
-static
-void ctx_decl_scope_destroy(struct ctx_decl_scope *scope)
-{
-       if (!scope) {
-               goto end;
-       }
-
-       g_hash_table_destroy(scope->decl_map);
-       g_free(scope);
-
-end:
-       return;
-}
-
-/**
- * Returns the GQuark of a prefixed alias.
- *
- * @param prefix       Prefix character
- * @param name         Name
- * @returns            Associated GQuark, or 0 on error
- */
-static
-GQuark get_prefixed_named_quark(struct ctf_visitor_generate_ir *ctx, char prefix,
-               const char *name)
-{
-       GQuark qname = 0;
-
-       BT_ASSERT(name);
-
-       /* Prefix character + original string + '\0' */
-       char *prname = g_new(char, strlen(name) + 2);
-       if (!prname) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Failed to allocate a string.");
-               goto end;
-       }
-
-       sprintf(prname, "%c%s", prefix, name);
-       qname = g_quark_from_string(prname);
-       g_free(prname);
-
-end:
-       return qname;
-}
-
-/**
- * Looks up a prefixed class alias within a declaration scope.
- *
- * @param scope                Declaration scope
- * @param prefix       Prefix character
- * @param name         Alias name
- * @param levels       Number of levels to dig into (-1 means infinite)
- * @param copy         True to return a copy
- * @returns            Declaration (owned by caller if \p copy is true),
- *                     or NULL if not found
- */
-static
-struct ctf_field_class *ctx_decl_scope_lookup_prefix_alias(
-               struct ctf_visitor_generate_ir *ctx, struct ctx_decl_scope *scope, char prefix,
-               const char *name, int levels, bool copy)
-{
-       GQuark qname = 0;
-       int cur_levels = 0;
-       struct ctf_field_class *decl = NULL;
-       struct ctx_decl_scope *cur_scope = scope;
-
-       BT_ASSERT(scope);
-       BT_ASSERT(name);
-       qname = get_prefixed_named_quark(ctx, prefix, name);
-       if (!qname) {
-               goto end;
-       }
-
-       if (levels < 0) {
-               levels = INT_MAX;
-       }
-
-       while (cur_scope && cur_levels < levels) {
-               decl = g_hash_table_lookup(cur_scope->decl_map,
-                       (gconstpointer) GUINT_TO_POINTER(qname));
-               if (decl) {
-                       /* Caller's reference */
-                       if (copy) {
-                               decl = ctf_field_class_copy(decl);
-                               BT_ASSERT(decl);
-                       }
-
-                       goto end;
-               }
-
-               cur_scope = cur_scope->parent_scope;
-               cur_levels++;
-       }
-
-end:
-       return decl;
-}
-
-/**
- * Looks up a class alias within a declaration scope.
- *
- * @param scope                Declaration scope
- * @param name         Alias name
- * @param levels       Number of levels to dig into (-1 means infinite)
- * @param copy         True to return a copy
- * @returns            Declaration (owned by caller if \p copy is true),
- *                     or NULL if not found
- */
-static
-struct ctf_field_class *ctx_decl_scope_lookup_alias(struct ctf_visitor_generate_ir *ctx,
-               struct ctx_decl_scope *scope, const char *name, int levels,
-               bool copy)
-{
-       return ctx_decl_scope_lookup_prefix_alias(ctx, scope, _PREFIX_ALIAS,
-               name, levels, copy);
-}
-
-/**
- * Looks up an enumeration within a declaration scope.
- *
- * @param scope                Declaration scope
- * @param name         Enumeration name
- * @param levels       Number of levels to dig into (-1 means infinite)
- * @param copy         True to return a copy
- * @returns            Declaration (owned by caller if \p copy is true),
- *                     or NULL if not found
- */
-static
-struct ctf_field_class_enum *ctx_decl_scope_lookup_enum(struct ctf_visitor_generate_ir *ctx,
-               struct ctx_decl_scope *scope, const char *name, int levels,
-               bool copy)
-{
-       return (void *) ctx_decl_scope_lookup_prefix_alias(ctx, scope,
-               _PREFIX_ENUM, name, levels, copy);
-}
-
-/**
- * Looks up a structure within a declaration scope.
- *
- * @param scope                Declaration scope
- * @param name         Structure name
- * @param levels       Number of levels to dig into (-1 means infinite)
- * @param copy         True to return a copy
- * @returns            Declaration (owned by caller if \p copy is true),
- *                     or NULL if not found
- */
-static
-struct ctf_field_class_struct *ctx_decl_scope_lookup_struct(struct ctf_visitor_generate_ir *ctx,
-               struct ctx_decl_scope *scope, const char *name, int levels,
-               bool copy)
-{
-       return (void *) ctx_decl_scope_lookup_prefix_alias(ctx, scope,
-               _PREFIX_STRUCT, name, levels, copy);
-}
-
-/**
- * Looks up a variant within a declaration scope.
- *
- * @param scope                Declaration scope
- * @param name         Variant name
- * @param levels       Number of levels to dig into (-1 means infinite)
- * @param copy         True to return a copy
- * @returns            Declaration (owned by caller if \p copy is true),
- *                     or NULL if not found
- */
-static
-struct ctf_field_class_variant *ctx_decl_scope_lookup_variant(struct ctf_visitor_generate_ir *ctx,
-               struct ctx_decl_scope *scope, const char *name, int levels,
-               bool copy)
-{
-       return (void *) ctx_decl_scope_lookup_prefix_alias(ctx, scope,
-               _PREFIX_VARIANT, name, levels, copy);
-}
-
-/**
- * Registers a prefixed class alias within a declaration scope.
- *
- * @param scope                Declaration scope
- * @param prefix       Prefix character
- * @param name         Alias name (non-NULL)
- * @param decl         Field class to register (copied)
- * @returns            0 if registration went okay, negative value otherwise
- */
-static
-int ctx_decl_scope_register_prefix_alias(struct ctf_visitor_generate_ir *ctx,
-               struct ctx_decl_scope *scope, char prefix, const char *name,
-               struct ctf_field_class *decl)
-{
-       int ret = 0;
-       GQuark qname = 0;
-
-       BT_ASSERT(scope);
-       BT_ASSERT(name);
-       BT_ASSERT(decl);
-       qname = get_prefixed_named_quark(ctx, prefix, name);
-       if (!qname) {
-               ret = -ENOMEM;
-               goto end;
-       }
-
-       /* Make sure alias does not exist in local scope */
-       if (ctx_decl_scope_lookup_prefix_alias(ctx, scope, prefix, name, 1,
-                       false)) {
-               ret = -EEXIST;
-               goto end;
-       }
-
-       decl = ctf_field_class_copy(decl);
-       BT_ASSERT(decl);
-       g_hash_table_insert(scope->decl_map, GUINT_TO_POINTER(qname), decl);
-
-end:
-       return ret;
-}
-
-/**
- * Registers a class alias within a declaration scope.
- *
- * @param scope        Declaration scope
- * @param name Alias name (non-NULL)
- * @param decl Field class to register (copied)
- * @returns    0 if registration went okay, negative value otherwise
- */
-static
-int ctx_decl_scope_register_alias(struct ctf_visitor_generate_ir *ctx,
-               struct ctx_decl_scope *scope, const char *name, struct ctf_field_class *decl)
-{
-       return ctx_decl_scope_register_prefix_alias(ctx, scope, _PREFIX_ALIAS,
-               name, (void *) decl);
-}
-
-/**
- * Registers an enumeration declaration within a declaration scope.
- *
- * @param scope        Declaration scope
- * @param name Enumeration name (non-NULL)
- * @param decl Enumeration field class to register (copied)
- * @returns    0 if registration went okay, negative value otherwise
- */
-static
-int ctx_decl_scope_register_enum(struct ctf_visitor_generate_ir *ctx,
-               struct ctx_decl_scope *scope, const char *name,
-               struct ctf_field_class_enum *decl)
-{
-       return ctx_decl_scope_register_prefix_alias(ctx, scope, _PREFIX_ENUM,
-               name, (void *) decl);
-}
-
-/**
- * Registers a structure declaration within a declaration scope.
- *
- * @param scope        Declaration scope
- * @param name Structure name (non-NULL)
- * @param decl Structure field class to register (copied)
- * @returns    0 if registration went okay, negative value otherwise
- */
-static
-int ctx_decl_scope_register_struct(struct ctf_visitor_generate_ir *ctx,
-               struct ctx_decl_scope *scope, const char *name,
-               struct ctf_field_class_struct *decl)
-{
-       return ctx_decl_scope_register_prefix_alias(ctx, scope, _PREFIX_STRUCT,
-               name, (void *) decl);
-}
-
-/**
- * Registers a variant declaration within a declaration scope.
- *
- * @param scope        Declaration scope
- * @param name Variant name (non-NULL)
- * @param decl Variant field class to register
- * @returns    0 if registration went okay, negative value otherwise
- */
-static
-int ctx_decl_scope_register_variant(struct ctf_visitor_generate_ir *ctx,
-               struct ctx_decl_scope *scope, const char *name,
-               struct ctf_field_class_variant *decl)
-{
-       return ctx_decl_scope_register_prefix_alias(ctx, scope, _PREFIX_VARIANT,
-               name, (void *) decl);
-}
-
-/**
- * Destroys a visitor context.
- *
- * @param ctx  Visitor context to destroy
- */
-static
-void ctx_destroy(struct ctf_visitor_generate_ir *ctx)
-{
-       struct ctx_decl_scope *scope;
-
-       if (!ctx) {
-               goto end;
-       }
-
-       scope = ctx->current_scope;
-
-       /*
-        * Destroy all scopes, from current one to the root scope.
-        */
-       while (scope) {
-               struct ctx_decl_scope *parent_scope = scope->parent_scope;
-
-               ctx_decl_scope_destroy(scope);
-               scope = parent_scope;
-       }
-
-       bt_trace_class_put_ref(ctx->trace_class);
-
-       if (ctx->ctf_tc) {
-               ctf_trace_class_destroy(ctx->ctf_tc);
-       }
-
-       g_free(ctx);
-
-end:
-       return;
-}
-
-/**
- * Creates a new visitor context.
- *
- * @param trace        Associated trace
- * @returns    New visitor context, or NULL on error
- */
-static
-struct ctf_visitor_generate_ir *ctx_create(
-               const struct ctf_metadata_decoder_config *decoder_config)
-{
-       struct ctf_visitor_generate_ir *ctx = NULL;
-
-       BT_ASSERT(decoder_config);
-
-       ctx = g_new0(struct ctf_visitor_generate_ir, 1);
-       if (!ctx) {
-               BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, decoder_config->log_level,
-                       decoder_config->self_comp,
-                       "Failed to allocate one visitor context.");
-               goto error;
-       }
-
-       ctx->log_cfg.log_level = decoder_config->log_level;
-       ctx->log_cfg.self_comp = decoder_config->self_comp;
-       ctx->log_cfg.self_comp_class = decoder_config->self_comp_class;
-
-       if (decoder_config->self_comp) {
-               ctx->trace_class = bt_trace_class_create(
-                       decoder_config->self_comp);
-               if (!ctx->trace_class) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot create empty trace class.");
-                       goto error;
-               }
-       }
-
-       ctx->ctf_tc = ctf_trace_class_create();
-       if (!ctx->ctf_tc) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot create CTF trace class.");
-               goto error;
-       }
-
-       /* Root declaration scope */
-       ctx->current_scope = ctx_decl_scope_create(ctx, NULL);
-       if (!ctx->current_scope) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot create declaration scope.");
-               goto error;
-       }
-
-       ctx->decoder_config = *decoder_config;
-       goto end;
-
-error:
-       ctx_destroy(ctx);
-       ctx = NULL;
-
-end:
-       return ctx;
-}
-
-/**
- * Pushes a new declaration scope on top of a visitor context's
- * declaration scope stack.
- *
- * @param ctx  Visitor context
- * @returns    0 on success, or a negative value on error
- */
-static
-int ctx_push_scope(struct ctf_visitor_generate_ir *ctx)
-{
-       int ret = 0;
-       struct ctx_decl_scope *new_scope;
-
-       BT_ASSERT(ctx);
-       new_scope = ctx_decl_scope_create(ctx, ctx->current_scope);
-       if (!new_scope) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot create declaration scope.");
-               ret = -ENOMEM;
-               goto end;
-       }
-
-       ctx->current_scope = new_scope;
-
-end:
-       return ret;
-}
-
-static
-void ctx_pop_scope(struct ctf_visitor_generate_ir *ctx)
-{
-       struct ctx_decl_scope *parent_scope = NULL;
-
-       BT_ASSERT(ctx);
-
-       if (!ctx->current_scope) {
-               goto end;
-       }
-
-       parent_scope = ctx->current_scope->parent_scope;
-       ctx_decl_scope_destroy(ctx->current_scope);
-       ctx->current_scope = parent_scope;
-
-end:
-       return;
-}
-
-static
-int visit_field_class_specifier_list(struct ctf_visitor_generate_ir *ctx,
-               struct ctf_node *ts_list, struct ctf_field_class **decl);
-
-static
-int is_unary_string(struct bt_list_head *head)
-{
-       int ret = TRUE;
-       struct ctf_node *node;
-
-       bt_list_for_each_entry(node, head, siblings) {
-               if (node->type != NODE_UNARY_EXPRESSION) {
-                       ret = FALSE;
-               }
-
-               if (node->u.unary_expression.type != UNARY_STRING) {
-                       ret = FALSE;
-               }
-       }
-
-       return ret;
-}
-
-static
-const char *get_map_clock_name_value(struct bt_list_head *head)
-{
-       int i = 0;
-       struct ctf_node *node;
-       const char *name = NULL;
-
-       bt_list_for_each_entry(node, head, siblings) {
-               char *src_string;
-               int uexpr_type = node->u.unary_expression.type;
-               int uexpr_link = node->u.unary_expression.link;
-               int cond = node->type != NODE_UNARY_EXPRESSION ||
-                       uexpr_type != UNARY_STRING ||
-                       !((uexpr_link != UNARY_LINK_UNKNOWN) ^ (i == 0));
-               if (cond) {
-                       goto error;
-               }
-
-               /* Needs to be chained with . */
-               switch (node->u.unary_expression.link) {
-               case UNARY_DOTLINK:
-                       break;
-               case UNARY_ARROWLINK:
-               case UNARY_DOTDOTDOT:
-                       goto error;
-               default:
-                       break;
-               }
-
-               src_string = node->u.unary_expression.u.string;
-
-               switch (i) {
-               case 0:
-                       if (strcmp("clock", src_string)) {
-                               goto error;
-                       }
-                       break;
-               case 1:
-                       name = src_string;
-                       break;
-               case 2:
-                       if (strcmp("value", src_string)) {
-                               goto error;
-                       }
-                       break;
-               default:
-                       /* Extra identifier, unknown */
-                       goto error;
-               }
-
-               i++;
-       }
-
-       return name;
-
-error:
-       return NULL;
-}
-
-static
-int is_unary_unsigned(struct bt_list_head *head)
-{
-       int ret = TRUE;
-       struct ctf_node *node;
-
-       bt_list_for_each_entry(node, head, siblings) {
-               if (node->type != NODE_UNARY_EXPRESSION) {
-                       ret = FALSE;
-               }
-
-               if (node->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT) {
-                       ret = FALSE;
-               }
-       }
-
-       return ret;
-}
-
-static
-int get_unary_unsigned(struct ctf_visitor_generate_ir *ctx, struct bt_list_head *head,
-               uint64_t *value)
-{
-       int i = 0;
-       int ret = 0;
-       struct ctf_node *node;
-
-       *value = 0;
-
-       if (bt_list_empty(head)) {
-               ret = -1;
-               goto end;
-       }
-
-       bt_list_for_each_entry(node, head, siblings) {
-               int uexpr_type = node->u.unary_expression.type;
-               int uexpr_link = node->u.unary_expression.link;
-               int cond = node->type != NODE_UNARY_EXPRESSION ||
-                       uexpr_type != UNARY_UNSIGNED_CONSTANT ||
-                       uexpr_link != UNARY_LINK_UNKNOWN || i != 0;
-               if (cond) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Invalid constant unsigned integer.");
-                       ret = -EINVAL;
-                       goto end;
-               }
-
-               *value = node->u.unary_expression.u.unsigned_constant;
-               i++;
-       }
-
-end:
-       return ret;
-}
-
-static
-int is_unary_signed(struct bt_list_head *head)
-{
-       int ret = TRUE;
-       struct ctf_node *node;
-
-       bt_list_for_each_entry(node, head, siblings) {
-               if (node->type != NODE_UNARY_EXPRESSION) {
-                       ret = FALSE;
-               }
-
-               if (node->u.unary_expression.type != UNARY_SIGNED_CONSTANT) {
-                       ret = FALSE;
-               }
-       }
-
-       return ret;
-}
-
-static
-int get_unary_signed(struct bt_list_head *head, int64_t *value)
-{
-       int i = 0;
-       int ret = 0;
-       struct ctf_node *node;
-
-       bt_list_for_each_entry(node, head, siblings) {
-               int uexpr_type = node->u.unary_expression.type;
-               int uexpr_link = node->u.unary_expression.link;
-               int cond = node->type != NODE_UNARY_EXPRESSION ||
-                       (uexpr_type != UNARY_UNSIGNED_CONSTANT &&
-                               uexpr_type != UNARY_SIGNED_CONSTANT) ||
-                       uexpr_link != UNARY_LINK_UNKNOWN || i != 0;
-               if (cond) {
-                       ret = -EINVAL;
-                       goto end;
-               }
-
-               switch (uexpr_type) {
-               case UNARY_UNSIGNED_CONSTANT:
-                       *value = (int64_t)
-                               node->u.unary_expression.u.unsigned_constant;
-                       break;
-               case UNARY_SIGNED_CONSTANT:
-                       *value = node->u.unary_expression.u.signed_constant;
-                       break;
-               default:
-                       ret = -EINVAL;
-                       goto end;
-               }
-
-               i++;
-       }
-
-end:
-       return ret;
-}
-
-static
-int get_unary_uuid(struct ctf_visitor_generate_ir *ctx, struct bt_list_head *head,
-               bt_uuid_t uuid)
-{
-       return ctf_ast_get_unary_uuid(head, uuid, ctx->log_cfg.log_level,
-               ctx->log_cfg.self_comp);
-}
-
-static
-int get_boolean(struct ctf_visitor_generate_ir *ctx, struct ctf_node *unary_expr)
-{
-       int ret = 0;
-
-       if (unary_expr->type != NODE_UNARY_EXPRESSION) {
-               _BT_COMP_LOGE_APPEND_CAUSE_NODE(unary_expr,
-                       "Expecting unary expression: node-type=%d",
-                       unary_expr->type);
-               ret = -EINVAL;
-               goto end;
-       }
-
-       switch (unary_expr->u.unary_expression.type) {
-       case UNARY_UNSIGNED_CONSTANT:
-               ret = (unary_expr->u.unary_expression.u.unsigned_constant != 0);
-               break;
-       case UNARY_SIGNED_CONSTANT:
-               ret = (unary_expr->u.unary_expression.u.signed_constant != 0);
-               break;
-       case UNARY_STRING:
-       {
-               const char *str = unary_expr->u.unary_expression.u.string;
-
-               if (strcmp(str, "true") == 0 || strcmp(str, "TRUE") == 0) {
-                       ret = TRUE;
-               } else if (strcmp(str, "false") == 0 || strcmp(str, "FALSE") == 0) {
-                       ret = FALSE;
-               } else {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(unary_expr,
-                               "Unexpected boolean value: value=\"%s\"", str);
-                       ret = -EINVAL;
-                       goto end;
-               }
-               break;
-       }
-       default:
-               _BT_COMP_LOGE_APPEND_CAUSE_NODE(unary_expr,
-                       "Unexpected unary expression type: node-type=%d",
-                       unary_expr->u.unary_expression.type);
-               ret = -EINVAL;
-               goto end;
-       }
-
-end:
-       return ret;
-}
-
-static
-enum ctf_byte_order byte_order_from_unary_expr(struct ctf_visitor_generate_ir *ctx,
-               struct ctf_node *unary_expr)
-{
-       const char *str;
-       enum ctf_byte_order bo = CTF_BYTE_ORDER_UNKNOWN;
-
-       if (unary_expr->u.unary_expression.type != UNARY_STRING) {
-               _BT_COMP_LOGE_APPEND_CAUSE_NODE(unary_expr,
-                       "\"byte_order\" attribute: expecting `be`, `le`, `network`, or `native`.");
-               goto end;
-       }
-
-       str = unary_expr->u.unary_expression.u.string;
-
-       if (strcmp(str, "be") == 0 || strcmp(str, "network") == 0) {
-               bo = CTF_BYTE_ORDER_BIG;
-       } else if (strcmp(str, "le") == 0) {
-               bo = CTF_BYTE_ORDER_LITTLE;
-       } else if (strcmp(str, "native") == 0) {
-               bo = CTF_BYTE_ORDER_DEFAULT;
-       } else {
-               _BT_COMP_LOGE_APPEND_CAUSE_NODE(unary_expr,
-                       "Unexpected \"byte_order\" attribute value: "
-                       "expecting `be`, `le`, `network`, or `native`: value=\"%s\"",
-                       str);
-               goto end;
-       }
-
-end:
-       return bo;
-}
-
-static
-enum ctf_byte_order get_real_byte_order(struct ctf_visitor_generate_ir *ctx,
-               struct ctf_node *uexpr)
-{
-       enum ctf_byte_order bo = byte_order_from_unary_expr(ctx, uexpr);
-
-       if (bo == CTF_BYTE_ORDER_DEFAULT) {
-               bo = ctx->ctf_tc->default_byte_order;
-       }
-
-       return bo;
-}
-
-static
-int is_align_valid(uint64_t align)
-{
-       return (align != 0) && !(align & (align - UINT64_C(1)));
-}
-
-static
-int get_class_specifier_name(struct ctf_visitor_generate_ir *ctx,
-               struct ctf_node *cls_specifier, GString *str)
-{
-       int ret = 0;
-
-       if (cls_specifier->type != NODE_TYPE_SPECIFIER) {
-               _BT_COMP_LOGE_APPEND_CAUSE_NODE(cls_specifier,
-                       "Unexpected node type: node-type=%d",
-                       cls_specifier->type);
-               ret = -EINVAL;
-               goto end;
-       }
-
-       switch (cls_specifier->u.field_class_specifier.type) {
-       case TYPESPEC_VOID:
-               g_string_append(str, "void");
-               break;
-       case TYPESPEC_CHAR:
-               g_string_append(str, "char");
-               break;
-       case TYPESPEC_SHORT:
-               g_string_append(str, "short");
-               break;
-       case TYPESPEC_INT:
-               g_string_append(str, "int");
-               break;
-       case TYPESPEC_LONG:
-               g_string_append(str, "long");
-               break;
-       case TYPESPEC_FLOAT:
-               g_string_append(str, "float");
-               break;
-       case TYPESPEC_DOUBLE:
-               g_string_append(str, "double");
-               break;
-       case TYPESPEC_SIGNED:
-               g_string_append(str, "signed");
-               break;
-       case TYPESPEC_UNSIGNED:
-               g_string_append(str, "unsigned");
-               break;
-       case TYPESPEC_BOOL:
-               g_string_append(str, "bool");
-               break;
-       case TYPESPEC_COMPLEX:
-               g_string_append(str, "_Complex");
-               break;
-       case TYPESPEC_IMAGINARY:
-               g_string_append(str, "_Imaginary");
-               break;
-       case TYPESPEC_CONST:
-               g_string_append(str, "const");
-               break;
-       case TYPESPEC_ID_TYPE:
-               if (cls_specifier->u.field_class_specifier.id_type) {
-                       g_string_append(str,
-                               cls_specifier->u.field_class_specifier.id_type);
-               }
-               break;
-       case TYPESPEC_STRUCT:
-       {
-               struct ctf_node *node = cls_specifier->u.field_class_specifier.node;
-
-               if (!node->u._struct.name) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Unexpected empty structure field class name.");
-                       ret = -EINVAL;
-                       goto end;
-               }
-
-               g_string_append(str, "struct ");
-               g_string_append(str, node->u._struct.name);
-               break;
-       }
-       case TYPESPEC_VARIANT:
-       {
-               struct ctf_node *node = cls_specifier->u.field_class_specifier.node;
-
-               if (!node->u.variant.name) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Unexpected empty variant field class name.");
-                       ret = -EINVAL;
-                       goto end;
-               }
-
-               g_string_append(str, "variant ");
-               g_string_append(str, node->u.variant.name);
-               break;
-       }
-       case TYPESPEC_ENUM:
-       {
-               struct ctf_node *node = cls_specifier->u.field_class_specifier.node;
-
-               if (!node->u._enum.enum_id) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                               "Unexpected empty enumeration field class (`enum`) name.");
-                       ret = -EINVAL;
-                       goto end;
-               }
-
-               g_string_append(str, "enum ");
-               g_string_append(str, node->u._enum.enum_id);
-               break;
-       }
-       case TYPESPEC_FLOATING_POINT:
-       case TYPESPEC_INTEGER:
-       case TYPESPEC_STRING:
-       default:
-               _BT_COMP_LOGE_APPEND_CAUSE_NODE(cls_specifier->u.field_class_specifier.node,
-                       "Unexpected field class specifier type: %d",
-                       cls_specifier->u.field_class_specifier.type);
-               ret = -EINVAL;
-               goto end;
-       }
-
-end:
-       return ret;
-}
-
-static
-int get_class_specifier_list_name(struct ctf_visitor_generate_ir *ctx,
-               struct ctf_node *cls_specifier_list, GString *str)
-{
-       int ret = 0;
-       struct ctf_node *iter;
-       int alias_item_nr = 0;
-       struct bt_list_head *head =
-               &cls_specifier_list->u.field_class_specifier_list.head;
-
-       bt_list_for_each_entry(iter, head, siblings) {
-               if (alias_item_nr != 0) {
-                       g_string_append(str, " ");
-               }
-
-               alias_item_nr++;
-               ret = get_class_specifier_name(ctx, iter, str);
-               if (ret) {
-                       goto end;
-               }
-       }
-
-end:
-       return ret;
-}
-
-static
-GQuark create_class_alias_identifier(struct ctf_visitor_generate_ir *ctx,
-               struct ctf_node *cls_specifier_list,
-               struct ctf_node *node_field_class_declarator)
-{
-       int ret;
-       char *str_c;
-       GString *str;
-       GQuark qalias = 0;
-       struct ctf_node *iter;
-       struct bt_list_head *pointers =
-               &node_field_class_declarator->u.field_class_declarator.pointers;
-
-       str = g_string_new("");
-       ret = get_class_specifier_list_name(ctx, cls_specifier_list, str);
-       if (ret) {
-               g_string_free(str, TRUE);
-               goto end;
-       }
-
-       bt_list_for_each_entry(iter, pointers, siblings) {
-               g_string_append(str, " *");
-
-               if (iter->u.pointer.const_qualifier) {
-                       g_string_append(str, " const");
-               }
-       }
-
-       str_c = g_string_free(str, FALSE);
-       qalias = g_quark_from_string(str_c);
-       g_free(str_c);
-
-end:
-       return qalias;
-}
-
-static
-int visit_field_class_declarator(struct ctf_visitor_generate_ir *ctx,
-               struct ctf_node *cls_specifier_list,
-               GQuark *field_name, struct ctf_node *node_field_class_declarator,
-               struct ctf_field_class **field_decl,
-               struct ctf_field_class *nested_decl)
-{
-       /*
-        * During this whole function, nested_decl is always OURS,
-        * whereas field_decl is an output which we create, but
-        * belongs to the caller (it is moved).
-        */
-       int ret = 0;
-       *field_decl = NULL;
-
-       /* Validate field class declarator node */
-       if (node_field_class_declarator) {
-               if (node_field_class_declarator->u.field_class_declarator.type ==
-                               TYPEDEC_UNKNOWN) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
-                               "Unexpected field class declarator type: type=%d",
-                               node_field_class_declarator->u.field_class_declarator.type);
-                       ret = -EINVAL;
-                       goto error;
-               }
-
-               /* TODO: GCC bitfields not supported yet */
-               if (node_field_class_declarator->u.field_class_declarator.bitfield_len !=
-                               NULL) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
-                               "GCC bitfields are not supported as of this version.");
-                       ret = -EPERM;
-                       goto error;
-               }
-       }
-
-       /* Find the right nested declaration if not provided */
-       if (!nested_decl) {
-               struct bt_list_head *pointers =
-                       &node_field_class_declarator->u.field_class_declarator.pointers;
-
-               if (node_field_class_declarator && !bt_list_empty(pointers)) {
-                       GQuark qalias;
-
-                       /*
-                        * If we have a pointer declarator, it HAS to
-                        * be present in the field class aliases (else
-                        * fail).
-                        */
-                       qalias = create_class_alias_identifier(ctx,
-                               cls_specifier_list, node_field_class_declarator);
-                       nested_decl =
-                               ctx_decl_scope_lookup_alias(ctx,
-                                       ctx->current_scope,
-                                       g_quark_to_string(qalias), -1, true);
-                       if (!nested_decl) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
-                                       "Cannot find class alias: name=\"%s\"",
-                                       g_quark_to_string(qalias));
-                               ret = -EINVAL;
-                               goto error;
-                       }
-
-                       if (nested_decl->type == CTF_FIELD_CLASS_TYPE_INT) {
-                               /* Pointer: force integer's base to 16 */
-                               struct ctf_field_class_int *int_fc =
-                                       (void *) nested_decl;
-
-                               int_fc->disp_base =
-                                       BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL;
-                       }
-               } else {
-                       ret = visit_field_class_specifier_list(ctx,
-                               cls_specifier_list, &nested_decl);
-                       if (ret) {
-                               BT_ASSERT(!nested_decl);
-                               goto error;
-                       }
-               }
-       }
-
-       BT_ASSERT(nested_decl);
-
-       if (!node_field_class_declarator) {
-               *field_decl = nested_decl;
-               nested_decl = NULL;
-               goto end;
-       }
-
-       if (node_field_class_declarator->u.field_class_declarator.type == TYPEDEC_ID) {
-               if (node_field_class_declarator->u.field_class_declarator.u.id) {
-                       const char *id =
-                               node_field_class_declarator->u.field_class_declarator.u.id;
-
-                       *field_name = g_quark_from_string(id);
-               } else {
-                       *field_name = 0;
-               }
-
-               *field_decl = nested_decl;
-               nested_decl = NULL;
-               goto end;
-       } else {
-               struct ctf_node *first;
-               struct ctf_field_class *decl = NULL;
-               struct ctf_field_class *outer_field_decl = NULL;
-               struct bt_list_head *length =
-                       &node_field_class_declarator->
-                               u.field_class_declarator.u.nested.length;
-
-               /* Create array/sequence, pass nested_decl as child */
-               if (bt_list_empty(length)) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
-                               "Expecting length field reference or value.");
-                       ret = -EINVAL;
-                       goto error;
-               }
-
-               first = _BT_LIST_FIRST_ENTRY(length, struct ctf_node, siblings);
-               if (first->type != NODE_UNARY_EXPRESSION) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(first,
-                               "Unexpected node type: node-type=%d",
-                               first->type);
-                       ret = -EINVAL;
-                       goto error;
-               }
-
-               switch (first->u.unary_expression.type) {
-               case UNARY_UNSIGNED_CONSTANT:
-               {
-                       struct ctf_field_class_array *array_decl = NULL;
-
-                       array_decl = ctf_field_class_array_create();
-                       BT_ASSERT(array_decl);
-                       array_decl->length =
-                               first->u.unary_expression.u.unsigned_constant;
-                       array_decl->base.elem_fc = nested_decl;
-                       nested_decl = NULL;
-                       decl = (void *) array_decl;
-                       break;
-               }
-               case UNARY_STRING:
-               {
-                       /* Lookup unsigned integer definition, create seq. */
-                       struct ctf_field_class_sequence *seq_decl = NULL;
-                       char *length_name = ctf_ast_concatenate_unary_strings(length);
-
-                       if (!length_name) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
-                                       "Cannot concatenate unary strings.");
-                               ret = -EINVAL;
-                               goto error;
-                       }
-
-                       if (strncmp(length_name, "env.", 4) == 0) {
-                               /* This is, in fact, an array */
-                               const char *env_entry_name = &length_name[4];
-                               struct ctf_trace_class_env_entry *env_entry =
-                                       ctf_trace_class_borrow_env_entry_by_name(
-                                               ctx->ctf_tc, env_entry_name);
-                               struct ctf_field_class_array *array_decl;
-
-                               if (!env_entry) {
-                                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
-                                               "Cannot find environment entry: "
-                                               "name=\"%s\"", env_entry_name);
-                                       ret = -EINVAL;
-                                       goto error;
-                               }
-
-                               if (env_entry->type != CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT) {
-                                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
-                                               "Wrong environment entry type "
-                                               "(expecting integer): "
-                                               "name=\"%s\"", env_entry_name);
-                                       ret = -EINVAL;
-                                       goto error;
-                               }
-
-                               if (env_entry->value.i < 0) {
-                                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
-                                               "Invalid, negative array length: "
-                                               "env-entry-name=\"%s\", "
-                                               "value=%" PRId64,
-                                               env_entry_name,
-                                               env_entry->value.i);
-                                       ret = -EINVAL;
-                                       goto error;
-                               }
-
-                               array_decl = ctf_field_class_array_create();
-                               BT_ASSERT(array_decl);
-                               array_decl->length =
-                                       (uint64_t) env_entry->value.i;
-                               array_decl->base.elem_fc = nested_decl;
-                               nested_decl = NULL;
-                               decl = (void *) array_decl;
-                       } else {
-                               seq_decl = ctf_field_class_sequence_create();
-                               BT_ASSERT(seq_decl);
-                               seq_decl->base.elem_fc = nested_decl;
-                               nested_decl = NULL;
-                               g_string_assign(seq_decl->length_ref,
-                                       length_name);
-                               decl = (void *) seq_decl;
-                       }
-
-                       g_free(length_name);
-                       break;
-               }
-               default:
-                       ret = -EINVAL;
-                       goto error;
-               }
-
-               BT_ASSERT(!nested_decl);
-               BT_ASSERT(decl);
-               BT_ASSERT(!*field_decl);
-
-               /*
-                * At this point, we found the next nested declaration.
-                * We currently own this (and lost the ownership of
-                * nested_decl in the meantime). Pass this next
-                * nested declaration as the content of the outer
-                * container, MOVING its ownership.
-                */
-               ret = visit_field_class_declarator(ctx, cls_specifier_list,
-                       field_name,
-                       node_field_class_declarator->
-                               u.field_class_declarator.u.nested.field_class_declarator,
-                       &outer_field_decl, decl);
-               decl = NULL;
-               if (ret) {
-                       BT_ASSERT(!outer_field_decl);
-                       ret = -EINVAL;
-                       goto error;
-               }
-
-               BT_ASSERT(outer_field_decl);
-               *field_decl = outer_field_decl;
-               outer_field_decl = NULL;
-       }
-
-       BT_ASSERT(*field_decl);
-       goto end;
-
-error:
-       ctf_field_class_destroy(*field_decl);
-       *field_decl = NULL;
-
-       if (ret >= 0) {
-               ret = -1;
-       }
-
-end:
-       ctf_field_class_destroy(nested_decl);
-       nested_decl = NULL;
-       return ret;
-}
-
-static
-int visit_struct_decl_field(struct ctf_visitor_generate_ir *ctx,
-               struct ctf_field_class_struct *struct_decl,
-               struct ctf_node *cls_specifier_list,
-               struct bt_list_head *field_class_declarators)
-{
-       int ret = 0;
-       struct ctf_node *iter;
-       struct ctf_field_class *field_decl = NULL;
-
-       bt_list_for_each_entry(iter, field_class_declarators, siblings) {
-               field_decl = NULL;
-               GQuark qfield_name;
-               const char *field_name;
-
-               ret = visit_field_class_declarator(ctx, cls_specifier_list,
-                       &qfield_name, iter, &field_decl, NULL);
-               if (ret) {
-                       BT_ASSERT(!field_decl);
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(cls_specifier_list,
-                               "Cannot visit field class declarator: ret=%d", ret);
-                       goto error;
-               }
-
-               BT_ASSERT(field_decl);
-               field_name = g_quark_to_string(qfield_name);
-
-               /* Check if field with same name already exists */
-               if (ctf_field_class_struct_borrow_member_by_name(
-                               struct_decl, field_name)) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(cls_specifier_list,
-                               "Duplicate field in structure field class: "
-                               "field-name=\"%s\"", field_name);
-                       ret = -EINVAL;
-                       goto error;
-               }
-
-               /* Add field to structure */
-               ctf_field_class_struct_append_member(struct_decl,
-                       field_name, field_decl);
-               field_decl = NULL;
-       }
-
-       return 0;
-
-error:
-       ctf_field_class_destroy(field_decl);
-       field_decl = NULL;
-       return ret;
-}
-
-static
-int visit_variant_decl_field(struct ctf_visitor_generate_ir *ctx,
-               struct ctf_field_class_variant *variant_decl,
-               struct ctf_node *cls_specifier_list,
-               struct bt_list_head *field_class_declarators)
-{
-       int ret = 0;
-       struct ctf_node *iter;
-       struct ctf_field_class *field_decl = NULL;
-
-       bt_list_for_each_entry(iter, field_class_declarators, siblings) {
-               field_decl = NULL;
-               GQuark qfield_name;
-               const char *field_name;
-
-               ret = visit_field_class_declarator(ctx, cls_specifier_list,
-                       &qfield_name, iter, &field_decl, NULL);
-               if (ret) {
-                       BT_ASSERT(!field_decl);
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(cls_specifier_list,
-                               "Cannot visit field class declarator: ret=%d", ret);
-                       goto error;
-               }
-
-               BT_ASSERT(field_decl);
-               field_name = g_quark_to_string(qfield_name);
-
-               /* Check if field with same name already exists */
-               if (ctf_field_class_variant_borrow_option_by_name(
-                               variant_decl, field_name)) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(cls_specifier_list,
-                               "Duplicate field in variant field class: "
-                               "field-name=\"%s\"", field_name);
-                       ret = -EINVAL;
-                       goto error;
-               }
-
-               /* Add field to structure */
-               ctf_field_class_variant_append_option(variant_decl,
-                       field_name, field_decl);
-               field_decl = NULL;
-       }
-
-       return 0;
-
-error:
-       ctf_field_class_destroy(field_decl);
-       field_decl = NULL;
-       return ret;
-}
-
-static
-int visit_field_class_def(struct ctf_visitor_generate_ir *ctx,
-               struct ctf_node *cls_specifier_list,
-               struct bt_list_head *field_class_declarators)
-{
-       int ret = 0;
-       GQuark qidentifier;
-       struct ctf_node *iter;
-       struct ctf_field_class *class_decl = NULL;
-
-       bt_list_for_each_entry(iter, field_class_declarators, siblings) {
-               ret = visit_field_class_declarator(ctx, cls_specifier_list,
-                       &qidentifier, iter, &class_decl, NULL);
-               if (ret) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
-                               "Cannot visit field class declarator: ret=%d", ret);
-                       ret = -EINVAL;
-                       goto end;
-               }
-
-               /* Do not allow field class def and alias of untagged variants */
-               if (class_decl->type == CTF_FIELD_CLASS_TYPE_VARIANT) {
-                       struct ctf_field_class_variant *var_fc =
-                               (void *) class_decl;
-
-                       if (var_fc->tag_path.path->len == 0) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
-                                       "Type definition of untagged variant field class is not allowed.");
-                               ret = -EPERM;
-                               goto end;
-                       }
-               }
-
-               ret = ctx_decl_scope_register_alias(ctx, ctx->current_scope,
-                       g_quark_to_string(qidentifier), class_decl);
-               if (ret) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
-                               "Cannot register field class alias: name=\"%s\"",
-                               g_quark_to_string(qidentifier));
-                       goto end;
-               }
-       }
-
-end:
-       ctf_field_class_destroy(class_decl);
-       class_decl = NULL;
-       return ret;
-}
-
-static
-int visit_field_class_alias(struct ctf_visitor_generate_ir *ctx, struct ctf_node *target,
-               struct ctf_node *alias)
-{
-       int ret = 0;
-       GQuark qalias;
-       struct ctf_node *node;
-       GQuark qdummy_field_name;
-       struct ctf_field_class *class_decl = NULL;
-
-       /* Create target field class */
-       if (bt_list_empty(&target->u.field_class_alias_target.field_class_declarators)) {
-               node = NULL;
-       } else {
-               node = _BT_LIST_FIRST_ENTRY(
-                       &target->u.field_class_alias_target.field_class_declarators,
-                       struct ctf_node, siblings);
-       }
-
-       ret = visit_field_class_declarator(ctx,
-               target->u.field_class_alias_target.field_class_specifier_list,
-               &qdummy_field_name, node, &class_decl, NULL);
-       if (ret) {
-               BT_ASSERT(!class_decl);
-               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                       "Cannot visit field class declarator: ret=%d", ret);
-               goto end;
-       }
-
-       /* Do not allow field class def and alias of untagged variants */
-       if (class_decl->type == CTF_FIELD_CLASS_TYPE_VARIANT) {
-               struct ctf_field_class_variant *var_fc = (void *) class_decl;
-
-               if (var_fc->tag_path.path->len == 0) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(target,
-                               "Type definition of untagged variant field class is not allowed.");
-                       ret = -EPERM;
-                       goto end;
-               }
-       }
-
-       /*
-        * The semantic validator does not check whether the target is
-        * abstract or not (if it has an identifier). Check it here.
-        */
-       if (qdummy_field_name != 0) {
-               _BT_COMP_LOGE_APPEND_CAUSE_NODE(target,
-                       "Expecting empty identifier: id=\"%s\"",
-                       g_quark_to_string(qdummy_field_name));
-               ret = -EINVAL;
-               goto end;
-       }
-
-       /* Create alias identifier */
-       node = _BT_LIST_FIRST_ENTRY(&alias->u.field_class_alias_name.field_class_declarators,
-               struct ctf_node, siblings);
-       qalias = create_class_alias_identifier(ctx,
-               alias->u.field_class_alias_name.field_class_specifier_list, node);
-       ret = ctx_decl_scope_register_alias(ctx, ctx->current_scope,
-               g_quark_to_string(qalias), class_decl);
-       if (ret) {
-               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                       "Cannot register class alias: name=\"%s\"",
-                       g_quark_to_string(qalias));
-               goto end;
-       }
-
-end:
-       ctf_field_class_destroy(class_decl);
-       class_decl = NULL;
-       return ret;
-}
-
-static
-int visit_struct_decl_entry(struct ctf_visitor_generate_ir *ctx, struct ctf_node *entry_node,
-               struct ctf_field_class_struct *struct_decl)
-{
-       int ret = 0;
-
-       switch (entry_node->type) {
-       case NODE_TYPEDEF:
-               ret = visit_field_class_def(ctx,
-                       entry_node->u.field_class_def.field_class_specifier_list,
-                       &entry_node->u.field_class_def.field_class_declarators);
-               if (ret) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
-                               "Cannot add field class found in structure field class: ret=%d",
-                               ret);
-                       goto end;
-               }
-               break;
-       case NODE_TYPEALIAS:
-               ret = visit_field_class_alias(ctx, entry_node->u.field_class_alias.target,
-                       entry_node->u.field_class_alias.alias);
-               if (ret) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
-                               "Cannot add field class alias found in structure field class: ret=%d",
-                               ret);
-                       goto end;
-               }
-               break;
-       case NODE_STRUCT_OR_VARIANT_DECLARATION:
-               /* Field */
-               ret = visit_struct_decl_field(ctx, struct_decl,
-                       entry_node->u.struct_or_variant_declaration.
-                               field_class_specifier_list,
-                       &entry_node->u.struct_or_variant_declaration.
-                               field_class_declarators);
-               if (ret) {
-                       goto end;
-               }
-               break;
-       default:
-               _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
-                       "Unexpected node type: node-type=%d", entry_node->type);
-               ret = -EINVAL;
-               goto end;
-       }
-
-end:
-       return ret;
-}
-
-static
-int visit_variant_decl_entry(struct ctf_visitor_generate_ir *ctx, struct ctf_node *entry_node,
-               struct ctf_field_class_variant *variant_decl)
-{
-       int ret = 0;
-
-       switch (entry_node->type) {
-       case NODE_TYPEDEF:
-               ret = visit_field_class_def(ctx,
-                       entry_node->u.field_class_def.field_class_specifier_list,
-                       &entry_node->u.field_class_def.field_class_declarators);
-               if (ret) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
-                               "Cannot add field class found in variant field class: ret=%d",
-                               ret);
-                       goto end;
-               }
-               break;
-       case NODE_TYPEALIAS:
-               ret = visit_field_class_alias(ctx, entry_node->u.field_class_alias.target,
-                       entry_node->u.field_class_alias.alias);
-               if (ret) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
-                               "Cannot add field class alias found in variant field class: ret=%d",
-                               ret);
-                       goto end;
-               }
-               break;
-       case NODE_STRUCT_OR_VARIANT_DECLARATION:
-               /* Field */
-               ret = visit_variant_decl_field(ctx, variant_decl,
-                       entry_node->u.struct_or_variant_declaration.
-                               field_class_specifier_list,
-                       &entry_node->u.struct_or_variant_declaration.
-                               field_class_declarators);
-               if (ret) {
-                       goto end;
-               }
-               break;
-       default:
-               _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
-                       "Unexpected node type: node-type=%d",
-                       entry_node->type);
-               ret = -EINVAL;
-               goto end;
-       }
-
-end:
-       return ret;
-}
-
-static
-int visit_struct_decl(struct ctf_visitor_generate_ir *ctx, const char *name,
-               struct bt_list_head *decl_list, int has_body,
-               struct bt_list_head *min_align,
-               struct ctf_field_class_struct **struct_decl)
-{
-       int ret = 0;
-
-       BT_ASSERT(struct_decl);
-       *struct_decl = NULL;
-
-       /* For named struct (without body), lookup in declaration scope */
-       if (!has_body) {
-               if (!name) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Bodyless structure field class: missing name.");
-                       ret = -EPERM;
-                       goto error;
-               }
-
-               *struct_decl = ctx_decl_scope_lookup_struct(ctx, ctx->current_scope,
-                       name, -1, true);
-               if (!*struct_decl) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot find structure field class: name=\"struct %s\"",
-                               name);
-                       ret = -EINVAL;
-                       goto error;
-               }
-       } else {
-               struct ctf_node *entry_node;
-               uint64_t min_align_value = 0;
-
-               if (name) {
-                       if (ctx_decl_scope_lookup_struct(ctx,
-                                       ctx->current_scope, name, 1, false)) {
-                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Structure field class already declared in local scope: "
-                                       "name=\"struct %s\"", name);
-                               ret = -EINVAL;
-                               goto error;
-                       }
-               }
-
-               if (!bt_list_empty(min_align)) {
-                       ret = get_unary_unsigned(ctx, min_align,
-                               &min_align_value);
-                       if (ret) {
-                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Unexpected unary expression for structure field class's `align` attribute: "
-                                       "ret=%d", ret);
-                               goto error;
-                       }
-               }
-
-               *struct_decl = ctf_field_class_struct_create();
-               BT_ASSERT(*struct_decl);
-
-               if (min_align_value != 0) {
-                       (*struct_decl)->base.alignment = min_align_value;
-               }
-
-               _TRY_PUSH_SCOPE_OR_GOTO_ERROR();
-
-               bt_list_for_each_entry(entry_node, decl_list, siblings) {
-                       ret = visit_struct_decl_entry(ctx, entry_node,
-                               *struct_decl);
-                       if (ret) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
-                                       "Cannot visit structure field class entry: "
-                                       "ret=%d", ret);
-                               ctx_pop_scope(ctx);
-                               goto error;
-                       }
-               }
-
-               ctx_pop_scope(ctx);
-
-               if (name) {
-                       ret = ctx_decl_scope_register_struct(ctx,
-                               ctx->current_scope, name, *struct_decl);
-                       if (ret) {
-                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot register structure field class in declaration scope: "
-                                       "name=\"struct %s\", ret=%d", name, ret);
-                               goto error;
-                       }
-               }
-       }
-
-       return 0;
-
-error:
-       ctf_field_class_destroy((void *) *struct_decl);
-       *struct_decl = NULL;
-       return ret;
-}
-
-static
-int visit_variant_decl(struct ctf_visitor_generate_ir *ctx, const char *name,
-       const char *tag, struct bt_list_head *decl_list,
-       int has_body, struct ctf_field_class_variant **variant_decl)
-{
-       int ret = 0;
-       struct ctf_field_class_variant *untagged_variant_decl = NULL;
-
-       BT_ASSERT(variant_decl);
-       *variant_decl = NULL;
-
-       /* For named variant (without body), lookup in declaration scope */
-       if (!has_body) {
-               if (!name) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Bodyless variant field class: missing name.");
-                       ret = -EPERM;
-                       goto error;
-               }
-
-               untagged_variant_decl =
-                       ctx_decl_scope_lookup_variant(ctx, ctx->current_scope,
-                               name, -1, true);
-               if (!untagged_variant_decl) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot find variant field class: name=\"variant %s\"",
-                               name);
-                       ret = -EINVAL;
-                       goto error;
-               }
-       } else {
-               struct ctf_node *entry_node;
-
-               if (name) {
-                       if (ctx_decl_scope_lookup_variant(ctx,
-                                       ctx->current_scope, name, 1, false)) {
-                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Variant field class already declared in local scope: "
-                                       "name=\"variant %s\"", name);
-                               ret = -EINVAL;
-                               goto error;
-                       }
-               }
-
-               untagged_variant_decl = ctf_field_class_variant_create();
-               BT_ASSERT(untagged_variant_decl);
-               _TRY_PUSH_SCOPE_OR_GOTO_ERROR();
-
-               bt_list_for_each_entry(entry_node, decl_list, siblings) {
-                       ret = visit_variant_decl_entry(ctx, entry_node,
-                               untagged_variant_decl);
-                       if (ret) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
-                                       "Cannot visit variant field class entry: "
-                                       "ret=%d", ret);
-                               ctx_pop_scope(ctx);
-                               goto error;
-                       }
-               }
-
-               ctx_pop_scope(ctx);
-
-               if (name) {
-                       ret = ctx_decl_scope_register_variant(ctx,
-                               ctx->current_scope, name,
-                               untagged_variant_decl);
-                       if (ret) {
-                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot register variant field class in declaration scope: "
-                                       "name=\"variant %s\", ret=%d", name, ret);
-                               goto error;
-                       }
-               }
-       }
-
-       /*
-        * If tagged, create tagged variant and return; otherwise
-        * return untagged variant.
-        */
-       if (!tag) {
-               *variant_decl = untagged_variant_decl;
-               untagged_variant_decl = NULL;
-       } else {
-               /*
-                * At this point, we have a fresh untagged variant; nobody
-                * else owns it. Set its tag now.
-                */
-               g_string_assign(untagged_variant_decl->tag_ref, tag);
-               *variant_decl = untagged_variant_decl;
-               untagged_variant_decl = NULL;
-       }
-
-       BT_ASSERT(!untagged_variant_decl);
-       BT_ASSERT(*variant_decl);
-       return 0;
-
-error:
-       ctf_field_class_destroy((void *) untagged_variant_decl);
-       untagged_variant_decl = NULL;
-       ctf_field_class_destroy((void *) *variant_decl);
-       *variant_decl = NULL;
-       return ret;
-}
-
-struct uori {
-       bool is_signed;
-       union {
-               uint64_t u;
-               uint64_t i;
-       } value;
-};
-
-static
-int visit_enum_decl_entry(struct ctf_visitor_generate_ir *ctx, struct ctf_node *enumerator,
-               struct ctf_field_class_enum *enum_decl, struct uori *last)
-{
-       int ret = 0;
-       int nr_vals = 0;
-       struct ctf_node *iter;
-       struct uori start = {
-               .is_signed = false,
-               .value.u = 0,
-       };
-       struct uori end = {
-               .is_signed = false,
-               .value.u = 0,
-       };
-       const char *label = enumerator->u.enumerator.id;
-       struct bt_list_head *values = &enumerator->u.enumerator.values;
-
-       bt_list_for_each_entry(iter, values, siblings) {
-               struct uori *target;
-
-               if (iter->type != NODE_UNARY_EXPRESSION) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
-                               "Wrong expression for enumeration field class label: "
-                               "node-type=%d, label=\"%s\"", iter->type,
-                               label);
-                       ret = -EINVAL;
-                       goto error;
-               }
-
-               if (nr_vals == 0) {
-                       target = &start;
-               } else {
-                       target = &end;
-               }
-
-               switch (iter->u.unary_expression.type) {
-               case UNARY_SIGNED_CONSTANT:
-                       target->is_signed = true;
-                       target->value.i =
-                               iter->u.unary_expression.u.signed_constant;
-                       break;
-               case UNARY_UNSIGNED_CONSTANT:
-                       target->is_signed = false;
-                       target->value.u =
-                               iter->u.unary_expression.u.unsigned_constant;
-                       break;
-               default:
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
-                               "Invalid enumeration field class entry: "
-                               "expecting constant signed or unsigned integer: "
-                               "node-type=%d, label=\"%s\"",
-                               iter->u.unary_expression.type, label);
-                       ret = -EINVAL;
-                       goto error;
-               }
-
-               if (nr_vals > 1) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
-                               "Invalid enumeration field class entry: label=\"%s\"",
-                               label);
-                       ret = -EINVAL;
-                       goto error;
-               }
-
-               nr_vals++;
-       }
-
-       if (nr_vals == 0) {
-               start = *last;
-       }
-
-       if (nr_vals <= 1) {
-               end = start;
-       }
-
-       if (end.is_signed) {
-               last->value.i = end.value.i + 1;
-       } else {
-               last->value.u = end.value.u + 1;
-       }
-
-       ctf_field_class_enum_map_range(enum_decl, label,
-               start.value.u, end.value.u);
-       return 0;
-
-error:
-       return ret;
-}
-
-static
-int visit_enum_decl(struct ctf_visitor_generate_ir *ctx, const char *name,
-               struct ctf_node *container_cls,
-               struct bt_list_head *enumerator_list,
-               int has_body, struct ctf_field_class_enum **enum_decl)
-{
-       int ret = 0;
-       GQuark qdummy_id;
-       struct ctf_field_class_int *integer_decl = NULL;
-
-       BT_ASSERT(enum_decl);
-       *enum_decl = NULL;
-
-       /* For named enum (without body), lookup in declaration scope */
-       if (!has_body) {
-               if (!name) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Bodyless enumeration field class: missing name.");
-                       ret = -EPERM;
-                       goto error;
-               }
-
-               *enum_decl = ctx_decl_scope_lookup_enum(ctx, ctx->current_scope,
-                       name, -1, true);
-               if (!*enum_decl) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot find enumeration field class: "
-                               "name=\"enum %s\"", name);
-                       ret = -EINVAL;
-                       goto error;
-               }
-       } else {
-               struct ctf_node *iter;
-               struct uori last_value = {
-                       .is_signed = false,
-                       .value.u = 0,
-               };
-
-               if (name) {
-                       if (ctx_decl_scope_lookup_enum(ctx, ctx->current_scope,
-                                       name, 1, false)) {
-                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Enumeration field class already declared in local scope: "
-                                       "name=\"enum %s\"", name);
-                               ret = -EINVAL;
-                               goto error;
-                       }
-               }
-
-               if (!container_cls) {
-                       integer_decl = (void *) ctx_decl_scope_lookup_alias(ctx,
-                               ctx->current_scope, "int", -1, true);
-                       if (!integer_decl) {
-                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot find implicit `int` field class alias for enumeration field class.");
-                               ret = -EINVAL;
-                               goto error;
-                       }
-               } else {
-                       ret = visit_field_class_declarator(ctx, container_cls,
-                               &qdummy_id, NULL, (void *) &integer_decl,
-                               NULL);
-                       if (ret) {
-                               BT_ASSERT(!integer_decl);
-                               ret = -EINVAL;
-                               goto error;
-                       }
-               }
-
-               BT_ASSERT(integer_decl);
-
-               if (integer_decl->base.base.type != CTF_FIELD_CLASS_TYPE_INT) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Container field class for enumeration field class is not an integer field class: "
-                               "fc-type=%d", integer_decl->base.base.type);
-                       ret = -EINVAL;
-                       goto error;
-               }
-
-               *enum_decl = ctf_field_class_enum_create();
-               BT_ASSERT(*enum_decl);
-               (*enum_decl)->base.base.base.alignment =
-                       integer_decl->base.base.alignment;
-               ctf_field_class_int_copy_content((void *) *enum_decl,
-                               (void *) integer_decl);
-               last_value.is_signed = (*enum_decl)->base.is_signed;
-
-               bt_list_for_each_entry(iter, enumerator_list, siblings) {
-                       ret = visit_enum_decl_entry(ctx, iter, *enum_decl,
-                               &last_value);
-                       if (ret) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
-                                       "Cannot visit enumeration field class entry: "
-                                       "ret=%d", ret);
-                               goto error;
-                       }
-               }
-
-               if (name) {
-                       ret = ctx_decl_scope_register_enum(ctx,
-                               ctx->current_scope, name, *enum_decl);
-                       if (ret) {
-                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot register enumeration field class in declaration scope: "
-                                       "ret=%d", ret);
-                               goto error;
-                       }
-               }
-       }
-
-       goto end;
-
-error:
-       ctf_field_class_destroy((void *) *enum_decl);
-       *enum_decl = NULL;
-
-end:
-       ctf_field_class_destroy((void *) integer_decl);
-       integer_decl = NULL;
-       return ret;
-}
-
-static
-int visit_field_class_specifier(struct ctf_visitor_generate_ir *ctx,
-               struct ctf_node *cls_specifier_list,
-               struct ctf_field_class **decl)
-{
-       int ret = 0;
-       GString *str = NULL;
-
-       *decl = NULL;
-       str = g_string_new("");
-       ret = get_class_specifier_list_name(ctx, cls_specifier_list, str);
-       if (ret) {
-               _BT_COMP_LOGE_APPEND_CAUSE_NODE(cls_specifier_list,
-                       "Cannot get field class specifier list's name: ret=%d", ret);
-               goto error;
-       }
-
-       *decl = ctx_decl_scope_lookup_alias(ctx, ctx->current_scope, str->str,
-               -1, true);
-       if (!*decl) {
-               _BT_COMP_LOGE_APPEND_CAUSE_NODE(cls_specifier_list,
-                       "Cannot find field class alias: name=\"%s\"", str->str);
-               ret = -EINVAL;
-               goto error;
-       }
-
-       goto end;
-
-error:
-       ctf_field_class_destroy(*decl);
-       *decl = NULL;
-
-end:
-       if (str) {
-               g_string_free(str, TRUE);
-       }
-
-       return ret;
-}
-
-static
-int visit_integer_decl(struct ctf_visitor_generate_ir *ctx,
-               struct bt_list_head *expressions,
-               struct ctf_field_class_int **integer_decl)
-{
-       int set = 0;
-       int ret = 0;
-       int signedness = 0;
-       struct ctf_node *expression;
-       uint64_t alignment = 0, size = 0;
-       struct ctf_clock_class *mapped_clock_class = NULL;
-       enum ctf_encoding encoding = CTF_ENCODING_NONE;
-       bt_field_class_integer_preferred_display_base base =
-               BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL;
-       enum ctf_byte_order byte_order = ctx->ctf_tc->default_byte_order;
-
-       *integer_decl = NULL;
-
-       bt_list_for_each_entry(expression, expressions, siblings) {
-               struct ctf_node *left, *right;
-
-               left = _BT_LIST_FIRST_ENTRY(&expression->u.ctf_expression.left,
-                       struct ctf_node, siblings);
-               right = _BT_LIST_FIRST_ENTRY(
-                       &expression->u.ctf_expression.right, struct ctf_node,
-                       siblings);
-
-               if (left->u.unary_expression.type != UNARY_STRING) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(left,
-                               "Unexpected unary expression type: type=%d",
-                               left->u.unary_expression.type);
-                       ret = -EINVAL;
-                       goto error;
-               }
-
-               if (strcmp(left->u.unary_expression.u.string, "signed") == 0) {
-                       if (_IS_SET(&set, _INTEGER_SIGNED_SET)) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "signed",
-                                       "integer field class");
-                               ret = -EPERM;
-                               goto error;
-                       }
-
-                       signedness = get_boolean(ctx, right);
-                       if (signedness < 0) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
-                                       "Invalid boolean value for integer field class's `signed` attribute: "
-                                       "ret=%d", ret);
-                               ret = -EINVAL;
-                               goto error;
-                       }
-
-                       _SET(&set, _INTEGER_SIGNED_SET);
-               } else if (strcmp(left->u.unary_expression.u.string, "byte_order") == 0) {
-                       if (_IS_SET(&set, _INTEGER_BYTE_ORDER_SET)) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "byte_order",
-                                       "integer field class");
-                               ret = -EPERM;
-                               goto error;
-                       }
-
-                       byte_order = get_real_byte_order(ctx, right);
-                       if (byte_order == CTF_BYTE_ORDER_UNKNOWN) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
-                                       "Invalid `byte_order` attribute in integer field class: "
-                                       "ret=%d", ret);
-                               ret = -EINVAL;
-                               goto error;
-                       }
-
-                       _SET(&set, _INTEGER_BYTE_ORDER_SET);
-               } else if (strcmp(left->u.unary_expression.u.string, "size") == 0) {
-                       if (_IS_SET(&set, _INTEGER_SIZE_SET)) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "size",
-                                       "integer field class");
-                               ret = -EPERM;
-                               goto error;
-                       }
-
-                       if (right->u.unary_expression.type !=
-                                       UNARY_UNSIGNED_CONSTANT) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
-                                       "Invalid `size` attribute in integer field class: "
-                                       "expecting unsigned constant integer: "
-                                       "node-type=%d",
-                                       right->u.unary_expression.type);
-                               ret = -EINVAL;
-                               goto error;
-                       }
-
-                       size = right->u.unary_expression.u.unsigned_constant;
-                       if (size == 0) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
-                                       "Invalid `size` attribute in integer field class: "
-                                       "expecting positive constant integer: "
-                                       "size=%" PRIu64, size);
-                               ret = -EINVAL;
-                               goto error;
-                       } else if (size > 64) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
-                                       "Invalid `size` attribute in integer field class: "
-                                       "integer fields over 64 bits are not supported as of this version: "
-                                       "size=%" PRIu64, size);
-                               ret = -EINVAL;
-                               goto error;
-                       }
-
-                       _SET(&set, _INTEGER_SIZE_SET);
-               } else if (strcmp(left->u.unary_expression.u.string, "align") == 0) {
-                       if (_IS_SET(&set, _INTEGER_ALIGN_SET)) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "align",
-                                       "integer field class");
-                               ret = -EPERM;
-                               goto error;
-                       }
-
-                       if (right->u.unary_expression.type !=
-                                       UNARY_UNSIGNED_CONSTANT) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
-                                       "Invalid `align` attribute in integer field class: "
-                                       "expecting unsigned constant integer: "
-                                       "node-type=%d",
-                                       right->u.unary_expression.type);
-                               ret = -EINVAL;
-                               goto error;
-                       }
-
-                       alignment =
-                               right->u.unary_expression.u.unsigned_constant;
-                       if (!is_align_valid(alignment)) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
-                                       "Invalid `align` attribute in integer field class: "
-                                       "expecting power of two: "
-                                       "align=%" PRIu64, alignment);
-                               ret = -EINVAL;
-                               goto error;
-                       }
-
-                       _SET(&set, _INTEGER_ALIGN_SET);
-               } else if (strcmp(left->u.unary_expression.u.string, "base") == 0) {
-                       if (_IS_SET(&set, _INTEGER_BASE_SET)) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "base",
-                                       "integer field class");
-                               ret = -EPERM;
-                               goto error;
-                       }
-
-                       switch (right->u.unary_expression.type) {
-                       case UNARY_UNSIGNED_CONSTANT:
-                       {
-                               uint64_t constant = right->u.unary_expression.
-                                       u.unsigned_constant;
-
-                               switch (constant) {
-                               case 2:
-                                       base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_BINARY;
-                                       break;
-                               case 8:
-                                       base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL;
-                                       break;
-                               case 10:
-                                       base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL;
-                                       break;
-                               case 16:
-                                       base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL;
-                                       break;
-                               default:
-                                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
-                                               "Invalid `base` attribute in integer field class: "
-                                               "base=%" PRIu64,
-                                               right->u.unary_expression.u.unsigned_constant);
-                                       ret = -EINVAL;
-                                       goto error;
-                               }
-                               break;
-                       }
-                       case UNARY_STRING:
-                       {
-                               char *s_right = ctf_ast_concatenate_unary_strings(
-                                       &expression->u.ctf_expression.right);
-                               if (!s_right) {
-                                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
-                                               "Unexpected unary expression for integer field class's `base` attribute.");
-                                       ret = -EINVAL;
-                                       goto error;
-                               }
-
-                               if (strcmp(s_right, "decimal") == 0 ||
-                                               strcmp(s_right, "dec") == 0 ||
-                                               strcmp(s_right, "d") == 0 ||
-                                               strcmp(s_right, "i") == 0 ||
-                                               strcmp(s_right, "u") == 0) {
-                                       base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL;
-                               } else if (strcmp(s_right, "hexadecimal") == 0 ||
-                                               strcmp(s_right, "hex") == 0 ||
-                                               strcmp(s_right, "x") == 0 ||
-                                               strcmp(s_right, "X") == 0 ||
-                                               strcmp(s_right, "p") == 0) {
-                                       base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL;
-                               } else if (strcmp(s_right, "octal") == 0 ||
-                                               strcmp(s_right, "oct") == 0 ||
-                                               strcmp(s_right, "o") == 0) {
-                                       base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL;
-                               } else if (strcmp(s_right, "binary") == 0 ||
-                                               strcmp(s_right, "b") == 0) {
-                                       base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_BINARY;
-                               } else {
-                                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
-                                               "Unexpected unary expression for integer field class's `base` attribute: "
-                                               "base=\"%s\"", s_right);
-                                       g_free(s_right);
-                                       ret = -EINVAL;
-                                       goto error;
-                               }
-
-                               g_free(s_right);
-                               break;
-                       }
-                       default:
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
-                                       "Invalid `base` attribute in integer field class: "
-                                       "expecting unsigned constant integer or unary string.");
-                               ret = -EINVAL;
-                               goto error;
-                       }
-
-                       _SET(&set, _INTEGER_BASE_SET);
-               } else if (strcmp(left->u.unary_expression.u.string, "encoding") == 0) {
-                       char *s_right;
-
-                       if (_IS_SET(&set, _INTEGER_ENCODING_SET)) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "encoding",
-                                       "integer field class");
-                               ret = -EPERM;
-                               goto error;
-                       }
-
-                       if (right->u.unary_expression.type != UNARY_STRING) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
-                                       "Invalid `encoding` attribute in integer field class: "
-                                       "expecting unary string.");
-                               ret = -EINVAL;
-                               goto error;
-                       }
-
-                       s_right = ctf_ast_concatenate_unary_strings(
-                               &expression->u.ctf_expression.right);
-                       if (!s_right) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
-                                       "Unexpected unary expression for integer field class's `encoding` attribute.");
-                               ret = -EINVAL;
-                               goto error;
-                       }
-
-                       if (strcmp(s_right, "UTF8") == 0 ||
-                                       strcmp(s_right, "utf8") == 0 ||
-                                       strcmp(s_right, "utf-8") == 0 ||
-                                       strcmp(s_right, "UTF-8") == 0 ||
-                                       strcmp(s_right, "ASCII") == 0 ||
-                                       strcmp(s_right, "ascii") == 0) {
-                               encoding = CTF_ENCODING_UTF8;
-                       } else if (strcmp(s_right, "none") == 0) {
-                               encoding = CTF_ENCODING_NONE;
-                       } else {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
-                                       "Invalid `encoding` attribute in integer field class: "
-                                       "unknown encoding: encoding=\"%s\"",
-                                       s_right);
-                               g_free(s_right);
-                               ret = -EINVAL;
-                               goto error;
-                       }
-
-                       g_free(s_right);
-                       _SET(&set, _INTEGER_ENCODING_SET);
-               } else if (strcmp(left->u.unary_expression.u.string, "map") == 0) {
-                       const char *clock_name;
-
-                       if (_IS_SET(&set, _INTEGER_MAP_SET)) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "map",
-                                       "integer field class");
-                               ret = -EPERM;
-                               goto error;
-                       }
-
-                       if (right->u.unary_expression.type != UNARY_STRING) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
-                                       "Invalid `map` attribute in integer field class: "
-                                       "expecting unary string.");
-                               ret = -EINVAL;
-                               goto error;
-                       }
-
-                       clock_name =
-                               get_map_clock_name_value(
-                                       &expression->u.ctf_expression.right);
-                       if (!clock_name) {
-                               char *s_right = ctf_ast_concatenate_unary_strings(
-                                       &expression->u.ctf_expression.right);
-
-                               if (!s_right) {
-                                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
-                                               "Unexpected unary expression for integer field class's `map` attribute.");
-                                       ret = -EINVAL;
-                                       goto error;
-                               }
-
-                               _BT_COMP_LOGE_NODE(right,
-                                       "Invalid `map` attribute in integer field class: "
-                                       "cannot find clock class at this point: name=\"%s\"",
-                                       s_right);
-                               _SET(&set, _INTEGER_MAP_SET);
-                               g_free(s_right);
-                               continue;
-                       }
-
-                       mapped_clock_class =
-                               ctf_trace_class_borrow_clock_class_by_name(
-                                       ctx->ctf_tc, clock_name);
-                       if (!mapped_clock_class) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
-                                       "Invalid `map` attribute in integer field class: "
-                                       "cannot find clock class at this point: name=\"%s\"",
-                                       clock_name);
-                               ret = -EINVAL;
-                               goto error;
-                       }
-
-                       _SET(&set, _INTEGER_MAP_SET);
-               } else {
-                       _BT_COMP_LOGW_NODE(left,
-                               "Unknown attribute in integer field class: "
-                               "attr-name=\"%s\"",
-                               left->u.unary_expression.u.string);
-               }
-       }
-
-       if (!_IS_SET(&set, _INTEGER_SIZE_SET)) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Missing `size` attribute in integer field class.");
-               ret = -EPERM;
-               goto error;
-       }
-
-       if (!_IS_SET(&set, _INTEGER_ALIGN_SET)) {
-               if (size % CHAR_BIT) {
-                       /* Bit-packed alignment */
-                       alignment = 1;
-               } else {
-                       /* Byte-packed alignment */
-                       alignment = CHAR_BIT;
-               }
-       }
-
-       *integer_decl = ctf_field_class_int_create();
-       BT_ASSERT(*integer_decl);
-       (*integer_decl)->base.base.alignment = alignment;
-       (*integer_decl)->base.byte_order = byte_order;
-       (*integer_decl)->base.size = size;
-       (*integer_decl)->is_signed = (signedness > 0);
-       (*integer_decl)->disp_base = base;
-       (*integer_decl)->encoding = encoding;
-       (*integer_decl)->mapped_clock_class = mapped_clock_class;
-       return 0;
-
-error:
-       ctf_field_class_destroy((void *) *integer_decl);
-       *integer_decl = NULL;
-       return ret;
-}
-
-static
-int visit_floating_point_number_decl(struct ctf_visitor_generate_ir *ctx,
-               struct bt_list_head *expressions,
-               struct ctf_field_class_float **float_decl)
-{
-       int set = 0;
-       int ret = 0;
-       struct ctf_node *expression;
-       uint64_t alignment = 1, exp_dig = 0, mant_dig = 0;
-       enum ctf_byte_order byte_order = ctx->ctf_tc->default_byte_order;
-
-       *float_decl = NULL;
-
-       bt_list_for_each_entry(expression, expressions, siblings) {
-               struct ctf_node *left, *right;
-
-               left = _BT_LIST_FIRST_ENTRY(&expression->u.ctf_expression.left,
-                       struct ctf_node, siblings);
-               right = _BT_LIST_FIRST_ENTRY(
-                       &expression->u.ctf_expression.right, struct ctf_node,
-                       siblings);
-
-               if (left->u.unary_expression.type != UNARY_STRING) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(left,
-                               "Unexpected unary expression type: type=%d",
-                               left->u.unary_expression.type);
-                       ret = -EINVAL;
-                       goto error;
-               }
-
-               if (strcmp(left->u.unary_expression.u.string, "byte_order") == 0) {
-                       if (_IS_SET(&set, _FLOAT_BYTE_ORDER_SET)) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "byte_order",
-                                       "floating point number field class");
-                               ret = -EPERM;
-                               goto error;
-                       }
-
-                       byte_order = get_real_byte_order(ctx, right);
-                       if (byte_order == CTF_BYTE_ORDER_UNKNOWN) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
-                                       "Invalid `byte_order` attribute in floating point number field class: "
-                                       "ret=%d", ret);
-                               ret = -EINVAL;
-                               goto error;
-                       }
-
-                       _SET(&set, _FLOAT_BYTE_ORDER_SET);
-               } else if (strcmp(left->u.unary_expression.u.string, "exp_dig") == 0) {
-                       if (_IS_SET(&set, _FLOAT_EXP_DIG_SET)) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "exp_dig",
-                                       "floating point number field class");
-                               ret = -EPERM;
-                               goto error;
-                       }
-
-                       if (right->u.unary_expression.type !=
-                                       UNARY_UNSIGNED_CONSTANT) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
-                                       "Invalid `exp_dig` attribute in floating point number field class: "
-                                       "expecting unsigned constant integer: "
-                                       "node-type=%d",
-                                       right->u.unary_expression.type);
-                               ret = -EINVAL;
-                               goto error;
-                       }
-
-                       exp_dig = right->u.unary_expression.u.unsigned_constant;
-                       _SET(&set, _FLOAT_EXP_DIG_SET);
-               } else if (strcmp(left->u.unary_expression.u.string, "mant_dig") == 0) {
-                       if (_IS_SET(&set, _FLOAT_MANT_DIG_SET)) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "mant_dig",
-                                       "floating point number field class");
-                               ret = -EPERM;
-                               goto error;
-                       }
-
-                       if (right->u.unary_expression.type !=
-                                       UNARY_UNSIGNED_CONSTANT) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
-                                       "Invalid `mant_dig` attribute in floating point number field class: "
-                                       "expecting unsigned constant integer: "
-                                       "node-type=%d",
-                                       right->u.unary_expression.type);
-                               ret = -EINVAL;
-                               goto error;
-                       }
-
-                       mant_dig = right->u.unary_expression.u.
-                               unsigned_constant;
-                       _SET(&set, _FLOAT_MANT_DIG_SET);
-               } else if (strcmp(left->u.unary_expression.u.string, "align") == 0) {
-                       if (_IS_SET(&set, _FLOAT_ALIGN_SET)) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "align",
-                                       "floating point number field class");
-                               ret = -EPERM;
-                               goto error;
-                       }
-
-                       if (right->u.unary_expression.type !=
-                                       UNARY_UNSIGNED_CONSTANT) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
-                                       "Invalid `align` attribute in floating point number field class: "
-                                       "expecting unsigned constant integer: "
-                                       "node-type=%d",
-                                       right->u.unary_expression.type);
-                               ret = -EINVAL;
-                               goto error;
-                       }
-
-                       alignment = right->u.unary_expression.u.
-                               unsigned_constant;
-
-                       if (!is_align_valid(alignment)) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
-                                       "Invalid `align` attribute in floating point number field class: "
-                                       "expecting power of two: "
-                                       "align=%" PRIu64, alignment);
-                               ret = -EINVAL;
-                               goto error;
-                       }
-
-                       _SET(&set, _FLOAT_ALIGN_SET);
-               } else {
-                       _BT_COMP_LOGW_NODE(left,
-                               "Unknown attribute in floating point number field class: "
-                               "attr-name=\"%s\"",
-                               left->u.unary_expression.u.string);
-               }
-       }
-
-       if (!_IS_SET(&set, _FLOAT_MANT_DIG_SET)) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Missing `mant_dig` attribute in floating point number field class.");
-               ret = -EPERM;
-               goto error;
-       }
-
-       if (!_IS_SET(&set, _FLOAT_EXP_DIG_SET)) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Missing `exp_dig` attribute in floating point number field class.");
-               ret = -EPERM;
-               goto error;
-       }
-
-       if (mant_dig != 24 && mant_dig != 53) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("`mant_dig` attribute: expecting 24 or 53.");
-               ret = -EPERM;
-               goto error;
-       }
-
-       if (mant_dig == 24 && exp_dig != 8) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("`exp_dig` attribute: expecting 8 because `mant_dig` is 24.");
-               ret = -EPERM;
-               goto error;
-       }
-
-       if (mant_dig == 53 && exp_dig != 11) {
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("`exp_dig` attribute: expecting 11 because `mant_dig` is 53.");
-               ret = -EPERM;
-               goto error;
-       }
-
-       if (!_IS_SET(&set, _INTEGER_ALIGN_SET)) {
-               if ((mant_dig + exp_dig) % CHAR_BIT) {
-                       /* Bit-packed alignment */
-                       alignment = 1;
-               } else {
-                       /* Byte-packed alignment */
-                       alignment = CHAR_BIT;
-               }
-       }
-
-       *float_decl = ctf_field_class_float_create();
-       BT_ASSERT(*float_decl);
-       (*float_decl)->base.base.alignment = alignment;
-       (*float_decl)->base.byte_order = byte_order;
-       (*float_decl)->base.size = mant_dig + exp_dig;
-       return 0;
-
-error:
-       ctf_field_class_destroy((void *) *float_decl);
-       *float_decl = NULL;
-       return ret;
-}
-
-static
-int visit_string_decl(struct ctf_visitor_generate_ir *ctx,
-               struct bt_list_head *expressions,
-               struct ctf_field_class_string **string_decl)
-{
-       int set = 0;
-       int ret = 0;
-       struct ctf_node *expression;
-       enum ctf_encoding encoding = CTF_ENCODING_UTF8;
-
-       *string_decl = NULL;
-
-       bt_list_for_each_entry(expression, expressions, siblings) {
-               struct ctf_node *left, *right;
-
-               left = _BT_LIST_FIRST_ENTRY(&expression->u.ctf_expression.left,
-                       struct ctf_node, siblings);
-               right = _BT_LIST_FIRST_ENTRY(
-                       &expression->u.ctf_expression.right, struct ctf_node,
-                       siblings);
-
-               if (left->u.unary_expression.type != UNARY_STRING) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(left,
-                               "Unexpected unary expression type: type=%d",
-                               left->u.unary_expression.type);
-                       ret = -EINVAL;
-                       goto error;
-               }
-
-               if (strcmp(left->u.unary_expression.u.string, "encoding") == 0) {
-                       char *s_right;
-
-                       if (_IS_SET(&set, _STRING_ENCODING_SET)) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "encoding",
-                                       "string field class");
-                               ret = -EPERM;
-                               goto error;
-                       }
-
-                       if (right->u.unary_expression.type != UNARY_STRING) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
-                                       "Invalid `encoding` attribute in string field class: "
-                                       "expecting unary string.");
-                               ret = -EINVAL;
-                               goto error;
-                       }
-
-                       s_right = ctf_ast_concatenate_unary_strings(
-                               &expression->u.ctf_expression.right);
-                       if (!s_right) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
-                                       "Unexpected unary expression for string field class's `encoding` attribute.");
-                               ret = -EINVAL;
-                               goto error;
-                       }
-
-                       if (strcmp(s_right, "UTF8") == 0 ||
-                                       strcmp(s_right, "utf8") == 0 ||
-                                       strcmp(s_right, "utf-8") == 0 ||
-                                       strcmp(s_right, "UTF-8") == 0 ||
-                                       strcmp(s_right, "ASCII") == 0 ||
-                                       strcmp(s_right, "ascii") == 0) {
-                               encoding = CTF_ENCODING_UTF8;
-                       } else if (strcmp(s_right, "none") == 0) {
-                               encoding = CTF_ENCODING_NONE;
-                       } else {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
-                                       "Invalid `encoding` attribute in string field class: "
-                                       "unknown encoding: encoding=\"%s\"",
-                                       s_right);
-                               g_free(s_right);
-                               ret = -EINVAL;
-                               goto error;
-                       }
-
-                       g_free(s_right);
-                       _SET(&set, _STRING_ENCODING_SET);
-               } else {
-                       _BT_COMP_LOGW_NODE(left,
-                               "Unknown attribute in string field class: "
-                               "attr-name=\"%s\"",
-                               left->u.unary_expression.u.string);
-               }
-       }
-
-       *string_decl = ctf_field_class_string_create();
-       BT_ASSERT(*string_decl);
-       (*string_decl)->encoding = encoding;
-       return 0;
-
-error:
-       ctf_field_class_destroy((void *) *string_decl);
-       *string_decl = NULL;
-       return ret;
-}
-
-static
-int visit_field_class_specifier_list(struct ctf_visitor_generate_ir *ctx,
-               struct ctf_node *ts_list, struct ctf_field_class **decl)
-{
-       int ret = 0;
-       struct ctf_node *first, *node;
-
-       *decl = NULL;
-
-       if (ts_list->type != NODE_TYPE_SPECIFIER_LIST) {
-               _BT_COMP_LOGE_APPEND_CAUSE_NODE(ts_list,
-                       "Unexpected node type: node-type=%d", ts_list->type);
-               ret = -EINVAL;
-               goto error;
-       }
-
-       first = _BT_LIST_FIRST_ENTRY(&ts_list->u.field_class_specifier_list.head,
-               struct ctf_node, siblings);
-       if (first->type != NODE_TYPE_SPECIFIER) {
-               _BT_COMP_LOGE_APPEND_CAUSE_NODE(first,
-                       "Unexpected node type: node-type=%d", first->type);
-               ret = -EINVAL;
-               goto error;
-       }
-
-       node = first->u.field_class_specifier.node;
-
-       switch (first->u.field_class_specifier.type) {
-       case TYPESPEC_INTEGER:
-               ret = visit_integer_decl(ctx, &node->u.integer.expressions,
-                       (void *) decl);
-               if (ret) {
-                       BT_ASSERT(!*decl);
-                       goto error;
-               }
-               break;
-       case TYPESPEC_FLOATING_POINT:
-               ret = visit_floating_point_number_decl(ctx,
-                       &node->u.floating_point.expressions, (void *) decl);
-               if (ret) {
-                       BT_ASSERT(!*decl);
-                       goto error;
-               }
-               break;
-       case TYPESPEC_STRING:
-               ret = visit_string_decl(ctx,
-                       &node->u.string.expressions, (void *) decl);
-               if (ret) {
-                       BT_ASSERT(!*decl);
-                       goto error;
-               }
-               break;
-       case TYPESPEC_STRUCT:
-               ret = visit_struct_decl(ctx, node->u._struct.name,
-                       &node->u._struct.declaration_list,
-                       node->u._struct.has_body,
-                       &node->u._struct.min_align, (void *) decl);
-               if (ret) {
-                       BT_ASSERT(!*decl);
-                       goto error;
-               }
-               break;
-       case TYPESPEC_VARIANT:
-               ret = visit_variant_decl(ctx, node->u.variant.name,
-                       node->u.variant.choice,
-                       &node->u.variant.declaration_list,
-                       node->u.variant.has_body, (void *) decl);
-               if (ret) {
-                       BT_ASSERT(!*decl);
-                       goto error;
-               }
-               break;
-       case TYPESPEC_ENUM:
-               ret = visit_enum_decl(ctx, node->u._enum.enum_id,
-                       node->u._enum.container_field_class,
-                       &node->u._enum.enumerator_list,
-                       node->u._enum.has_body, (void *) decl);
-               if (ret) {
-                       BT_ASSERT(!*decl);
-                       goto error;
-               }
-               break;
-       case TYPESPEC_VOID:
-       case TYPESPEC_CHAR:
-       case TYPESPEC_SHORT:
-       case TYPESPEC_INT:
-       case TYPESPEC_LONG:
-       case TYPESPEC_FLOAT:
-       case TYPESPEC_DOUBLE:
-       case TYPESPEC_SIGNED:
-       case TYPESPEC_UNSIGNED:
-       case TYPESPEC_BOOL:
-       case TYPESPEC_COMPLEX:
-       case TYPESPEC_IMAGINARY:
-       case TYPESPEC_CONST:
-       case TYPESPEC_ID_TYPE:
-               ret = visit_field_class_specifier(ctx, ts_list, decl);
-               if (ret) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(first,
-                               "Cannot visit field class specifier: ret=%d",
-                               ret);
-                       BT_ASSERT(!*decl);
-                       goto error;
-               }
-               break;
-       default:
-               _BT_COMP_LOGE_APPEND_CAUSE_NODE(first,
-                       "Unexpected field class specifier type: node-type=%d",
-                       first->u.field_class_specifier.type);
-               ret = -EINVAL;
-               goto error;
-       }
-
-       BT_ASSERT(*decl);
-       return 0;
-
-error:
-       ctf_field_class_destroy((void *) *decl);
-       *decl = NULL;
-       return ret;
-}
-
-static
-int visit_event_decl_entry(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node,
-               struct ctf_event_class *event_class, uint64_t *stream_id,
-               int *set)
-{
-       int ret = 0;
-       char *left = NULL;
-
-       switch (node->type) {
-       case NODE_TYPEDEF:
-               ret = visit_field_class_def(ctx, node->u.field_class_def.field_class_specifier_list,
-                       &node->u.field_class_def.field_class_declarators);
-               if (ret) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                               "Cannot add field class found in event class.");
-                       goto error;
-               }
-               break;
-       case NODE_TYPEALIAS:
-               ret = visit_field_class_alias(ctx, node->u.field_class_alias.target,
-                       node->u.field_class_alias.alias);
-               if (ret) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                               "Cannot add field class alias found in event class.");
-                       goto error;
-               }
-               break;
-       case NODE_CTF_EXPRESSION:
-       {
-               left = ctf_ast_concatenate_unary_strings(&node->u.ctf_expression.left);
-               if (!left) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Cannot concatenate unary strings.");
-                       ret = -EINVAL;
-                       goto error;
-               }
-
-               if (strcmp(left, "name") == 0) {
-                       /* This is already known at this stage */
-                       if (_IS_SET(set, _EVENT_NAME_SET)) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "name", "event class");
-                               ret = -EPERM;
-                               goto error;
-                       }
-
-                       _SET(set, _EVENT_NAME_SET);
-               } else if (strcmp(left, "id") == 0) {
-                       int64_t id = -1;
-
-                       if (_IS_SET(set, _EVENT_ID_SET)) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "id", "event class");
-                               ret = -EPERM;
-                               goto error;
-                       }
-
-                       ret = get_unary_unsigned(ctx,
-                               &node->u.ctf_expression.right,
-                               (uint64_t *) &id);
-                       /* Only read "id" if get_unary_unsigned() succeeded. */
-                       if (ret || (!ret && id < 0)) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                       "Unexpected unary expression for event class's `id` attribute.");
-                               ret = -EINVAL;
-                               goto error;
-                       }
-
-                       event_class->id = id;
-                       _SET(set, _EVENT_ID_SET);
-               } else if (strcmp(left, "stream_id") == 0) {
-                       if (_IS_SET(set, _EVENT_STREAM_ID_SET)) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "stream_id",
-                                       "event class");
-                               ret = -EPERM;
-                               goto error;
-                       }
-
-                       ret = get_unary_unsigned(ctx,
-                               &node->u.ctf_expression.right, stream_id);
-
-                       /*
-                        * Only read "stream_id" if get_unary_unsigned()
-                        * succeeded.
-                        */
-                       if (ret) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                       "Unexpected unary expression for event class's `stream_id` attribute.");
-                               ret = -EINVAL;
-                               goto error;
-                       }
-
-                       _SET(set, _EVENT_STREAM_ID_SET);
-               } else if (strcmp(left, "context") == 0) {
-                       if (_IS_SET(set, _EVENT_CONTEXT_SET)) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                       "Duplicate `context` entry in event class.");
-                               ret = -EPERM;
-                               goto error;
-                       }
-
-                       ret = visit_field_class_specifier_list(ctx,
-                               _BT_LIST_FIRST_ENTRY(
-                                       &node->u.ctf_expression.right,
-                                       struct ctf_node, siblings),
-                               &event_class->spec_context_fc);
-                       if (ret) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                       "Cannot create event class's context field class.");
-                               goto error;
-                       }
-
-                       BT_ASSERT(event_class->spec_context_fc);
-                       _SET(set, _EVENT_CONTEXT_SET);
-               } else if (strcmp(left, "fields") == 0) {
-                       if (_IS_SET(set, _EVENT_FIELDS_SET)) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                       "Duplicate `fields` entry in event class.");
-                               ret = -EPERM;
-                               goto error;
-                       }
-
-                       ret = visit_field_class_specifier_list(ctx,
-                               _BT_LIST_FIRST_ENTRY(
-                                       &node->u.ctf_expression.right,
-                                       struct ctf_node, siblings),
-                               &event_class->payload_fc);
-                       if (ret) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                       "Cannot create event class's payload field class.");
-                               goto error;
-                       }
-
-                       BT_ASSERT(event_class->payload_fc);
-                       _SET(set, _EVENT_FIELDS_SET);
-               } else if (strcmp(left, "loglevel") == 0) {
-                       uint64_t loglevel_value;
-                       bool is_log_level_known = true;
-                       bt_event_class_log_level log_level = -1;
-
-                       if (_IS_SET(set, _EVENT_LOG_LEVEL_SET)) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "loglevel",
-                                       "event class");
-                               ret = -EPERM;
-                               goto error;
-                       }
-
-                       ret = get_unary_unsigned(ctx,
-                               &node->u.ctf_expression.right, &loglevel_value);
-                       if (ret) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                       "Unexpected unary expression for event class's `loglevel` attribute.");
-                               ret = -EINVAL;
-                               goto error;
-                       }
-
-                       switch (loglevel_value) {
-                       case 0:
-                               log_level = BT_EVENT_CLASS_LOG_LEVEL_EMERGENCY;
-                               break;
-                       case 1:
-                               log_level = BT_EVENT_CLASS_LOG_LEVEL_ALERT;
-                               break;
-                       case 2:
-                               log_level = BT_EVENT_CLASS_LOG_LEVEL_CRITICAL;
-                               break;
-                       case 3:
-                               log_level = BT_EVENT_CLASS_LOG_LEVEL_ERROR;
-                               break;
-                       case 4:
-                               log_level = BT_EVENT_CLASS_LOG_LEVEL_WARNING;
-                               break;
-                       case 5:
-                               log_level = BT_EVENT_CLASS_LOG_LEVEL_NOTICE;
-                               break;
-                       case 6:
-                               log_level = BT_EVENT_CLASS_LOG_LEVEL_INFO;
-                               break;
-                       case 7:
-                               log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM;
-                               break;
-                       case 8:
-                               log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM;
-                               break;
-                       case 9:
-                               log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS;
-                               break;
-                       case 10:
-                               log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE;
-                               break;
-                       case 11:
-                               log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT;
-                               break;
-                       case 12:
-                               log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION;
-                               break;
-                       case 13:
-                               log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_LINE;
-                               break;
-                       case 14:
-                               log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG;
-                               break;
-                       default:
-                               is_log_level_known = false;
-                               _BT_COMP_LOGW_NODE(node, "Not setting event class's log level because its value is unknown: "
-                                       "log-level=%" PRIu64, loglevel_value);
-                       }
-
-                       if (is_log_level_known) {
-                               ctf_event_class_set_log_level(event_class, log_level);
-                       }
-
-                       _SET(set, _EVENT_LOG_LEVEL_SET);
-               } else if (strcmp(left, "model.emf.uri") == 0) {
-                       char *right;
-
-                       if (_IS_SET(set, _EVENT_MODEL_EMF_URI_SET)) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "model.emf.uri",
-                                       "event class");
-                               ret = -EPERM;
-                               goto error;
-                       }
-
-                       right = ctf_ast_concatenate_unary_strings(
-                               &node->u.ctf_expression.right);
-                       if (!right) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                       "Unexpected unary expression for event class's `model.emf.uri` attribute.");
-                               ret = -EINVAL;
-                               goto error;
-                       }
-
-                       if (strlen(right) == 0) {
-                               _BT_COMP_LOGW_NODE(node,
-                                       "Not setting event class's EMF URI because it's empty.");
-                       } else {
-                               g_string_assign(event_class->emf_uri,
-                                       right);
-                       }
-
-                       g_free(right);
-                       _SET(set, _EVENT_MODEL_EMF_URI_SET);
-               } else {
-                       _BT_COMP_LOGW_NODE(node,
-                               "Unknown attribute in event class: "
-                               "attr-name=\"%s\"", left);
-               }
-
-               g_free(left);
-               left = NULL;
-               break;
-       }
-       default:
-               ret = -EPERM;
-               goto error;
-       }
-
-       goto end;
-
-error:
-       g_free(left);
-
-end:
-       return ret;
-}
-
-static
-char *get_event_decl_name(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node)
-{
-       char *left = NULL;
-       char *name = NULL;
-       struct ctf_node *iter;
-       struct bt_list_head *decl_list = &node->u.event.declaration_list;
-
-       bt_list_for_each_entry(iter, decl_list, siblings) {
-               if (iter->type != NODE_CTF_EXPRESSION) {
-                       continue;
-               }
-
-               left = ctf_ast_concatenate_unary_strings(&iter->u.ctf_expression.left);
-               if (!left) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
-                               "Cannot concatenate unary strings.");
-                       goto error;
-               }
-
-               if (strcmp(left, "name") == 0) {
-                       name = ctf_ast_concatenate_unary_strings(
-                               &iter->u.ctf_expression.right);
-                       if (!name) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
-                                       "Unexpected unary expression for event class's `name` attribute.");
-                               goto error;
-                       }
-               }
-
-               g_free(left);
-               left = NULL;
-
-               if (name) {
-                       break;
-               }
-       }
-
-       return name;
-
-error:
-       g_free(left);
-       return NULL;
-}
-
-static
-int visit_event_decl(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node)
-{
-       int ret = 0;
-       int set = 0;
-       struct ctf_node *iter;
-       uint64_t stream_id = 0;
-       char *event_name = NULL;
-       struct ctf_event_class *event_class = NULL;
-       struct ctf_stream_class *stream_class = NULL;
-       struct bt_list_head *decl_list = &node->u.event.declaration_list;
-       bool pop_scope = false;
-
-       if (node->visited) {
-               goto end;
-       }
-
-       node->visited = TRUE;
-       event_name = get_event_decl_name(ctx, node);
-       if (!event_name) {
-               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                       "Missing `name` attribute in event class.");
-               ret = -EPERM;
-               goto error;
-       }
-
-       event_class = ctf_event_class_create();
-       BT_ASSERT(event_class);
-       g_string_assign(event_class->name, event_name);
-       _TRY_PUSH_SCOPE_OR_GOTO_ERROR();
-       pop_scope = true;
-
-       bt_list_for_each_entry(iter, decl_list, siblings) {
-               ret = visit_event_decl_entry(ctx, iter, event_class,
-                       &stream_id, &set);
-               if (ret) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter, "Cannot visit event class's entry: "
-                               "ret=%d", ret);
-                       goto error;
-               }
-       }
-
-       if (!_IS_SET(&set, _EVENT_STREAM_ID_SET)) {
-               /*
-                * Allow missing stream_id if there is only a single
-                * stream class.
-                */
-               switch (ctx->ctf_tc->stream_classes->len) {
-               case 0:
-                       /* Create implicit stream class if there's none */
-                       stream_id = 0;
-                       stream_class = ctf_stream_class_create();
-                       BT_ASSERT(stream_class);
-                       stream_class->id = stream_id;
-                       g_ptr_array_add(ctx->ctf_tc->stream_classes,
-                               stream_class);
-                       break;
-               case 1:
-                       /* Single stream class: get its ID */
-                       stream_class = ctx->ctf_tc->stream_classes->pdata[0];
-                       stream_id = stream_class->id;
-                       break;
-               default:
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                               "Missing `stream_id` attribute in event class.");
-                       ret = -EPERM;
-                       goto error;
-               }
-       }
-
-       /* We have the stream ID now; get the stream class if found */
-       if (!stream_class) {
-               stream_class = ctf_trace_class_borrow_stream_class_by_id(
-                       ctx->ctf_tc, stream_id);
-               if (!stream_class) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                               "Cannot find stream class at this point: "
-                               "id=%" PRId64, stream_id);
-                       ret = -EINVAL;
-                       goto error;
-               }
-       }
-
-       BT_ASSERT(stream_class);
-
-       if (!_IS_SET(&set, _EVENT_ID_SET)) {
-               /* Allow only one event without ID per stream */
-               if (stream_class->event_classes->len != 0) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                               "Missing `id` attribute in event class.");
-                       ret = -EPERM;
-                       goto error;
-               }
-
-               /* Automatic ID */
-               event_class->id = 0;
-       }
-
-       if (ctf_stream_class_borrow_event_class_by_id(stream_class,
-                       event_class->id)) {
-               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                       "Duplicate event class (same ID) in the same stream class: "
-                       "id=%" PRId64, event_class->id);
-               ret = -EEXIST;
-               goto error;
-       }
-
-       ctf_stream_class_append_event_class(stream_class, event_class);
-       event_class = NULL;
-       goto end;
-
-error:
-       ctf_event_class_destroy(event_class);
-       event_class = NULL;
-
-       if (ret >= 0) {
-               ret = -1;
-       }
-
-end:
-       if (pop_scope) {
-               ctx_pop_scope(ctx);
-       }
-
-       g_free(event_name);
-
-       return ret;
-}
-
-static
-int auto_map_field_to_trace_clock_class(struct ctf_visitor_generate_ir *ctx,
-               struct ctf_field_class *fc)
-{
-       struct ctf_clock_class *clock_class_to_map_to = NULL;
-       struct ctf_field_class_int *int_fc = (void *) fc;
-       int ret = 0;
-       uint64_t clock_class_count;
-
-       if (!fc) {
-               goto end;
-       }
-
-       if (fc->type != CTF_FIELD_CLASS_TYPE_INT &&
-                       fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
-               goto end;
-       }
-
-       if (int_fc->mapped_clock_class) {
-               /* Already mapped */
-               goto end;
-       }
-
-       clock_class_count = ctx->ctf_tc->clock_classes->len;
-
-       switch (clock_class_count) {
-       case 0:
-               /*
-                * No clock class exists in the trace at this point. Create an
-                * implicit one at 1 GHz, named `default`, and use this clock
-                * class.
-                */
-               clock_class_to_map_to = ctf_clock_class_create();
-               BT_ASSERT(clock_class_to_map_to);
-               clock_class_to_map_to->frequency = UINT64_C(1000000000);
-               g_string_assign(clock_class_to_map_to->name, "default");
-               BT_ASSERT(ret == 0);
-               g_ptr_array_add(ctx->ctf_tc->clock_classes,
-                       clock_class_to_map_to);
-               break;
-       case 1:
-               /*
-                * Only one clock class exists in the trace at this point: use
-                * this one.
-                */
-               clock_class_to_map_to = ctx->ctf_tc->clock_classes->pdata[0];
-               break;
-       default:
-               /*
-                * Timestamp field not mapped to a clock class and there's more
-                * than one clock class in the trace: this is an error.
-                */
-               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Timestamp field found with no mapped clock class, "
-                       "but there's more than one clock class in the trace at this point.");
-               ret = -1;
-               goto end;
-       }
-
-       BT_ASSERT(clock_class_to_map_to);
-       int_fc->mapped_clock_class = clock_class_to_map_to;
-
-end:
-       return ret;
-}
-
-static
-int auto_map_fields_to_trace_clock_class(struct ctf_visitor_generate_ir *ctx,
-               struct ctf_field_class *root_fc, const char *field_name)
-{
-       int ret = 0;
-       uint64_t i, count;
-       struct ctf_field_class_struct *struct_fc = (void *) root_fc;
-       struct ctf_field_class_variant *var_fc = (void *) root_fc;
-
-       if (!root_fc) {
-               goto end;
-       }
-
-       if (root_fc->type != CTF_FIELD_CLASS_TYPE_STRUCT &&
-                       root_fc->type != CTF_FIELD_CLASS_TYPE_VARIANT) {
-               goto end;
-       }
-
-       if (root_fc->type == CTF_FIELD_CLASS_TYPE_STRUCT) {
-               count = struct_fc->members->len;
-       } else {
-               count = var_fc->options->len;
-       }
-
-       for (i = 0; i < count; i++) {
-               struct ctf_named_field_class *named_fc = NULL;
-
-               if (root_fc->type == CTF_FIELD_CLASS_TYPE_STRUCT) {
-                       named_fc = ctf_field_class_struct_borrow_member_by_index(
-                               struct_fc, i);
-               } else if (root_fc->type == CTF_FIELD_CLASS_TYPE_VARIANT) {
-                       named_fc = ctf_field_class_variant_borrow_option_by_index(
-                               var_fc, i);
-               } else {
-                       bt_common_abort();
-               }
-
-               if (strcmp(named_fc->name->str, field_name) == 0) {
-                       ret = auto_map_field_to_trace_clock_class(ctx,
-                               named_fc->fc);
-                       if (ret) {
-                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot automatically map field to trace's clock class: "
-                                       "field-name=\"%s\"", field_name);
-                               goto end;
-                       }
-               }
-
-               ret = auto_map_fields_to_trace_clock_class(ctx, named_fc->fc,
-                       field_name);
-               if (ret) {
-                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot automatically map structure or variant field class's fields to trace's clock class: "
-                               "field-name=\"%s\", root-field-name=\"%s\"",
-                               field_name, named_fc->name->str);
-                       goto end;
-               }
-       }
-
-end:
-       return ret;
-}
-
-static
-int visit_stream_decl_entry(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node,
-               struct ctf_stream_class *stream_class, int *set)
-{
-       int ret = 0;
-       char *left = NULL;
-
-       switch (node->type) {
-       case NODE_TYPEDEF:
-               ret = visit_field_class_def(ctx, node->u.field_class_def.field_class_specifier_list,
-                       &node->u.field_class_def.field_class_declarators);
-               if (ret) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                               "Cannot add field class found in stream class.");
-                       goto error;
-               }
-               break;
-       case NODE_TYPEALIAS:
-               ret = visit_field_class_alias(ctx, node->u.field_class_alias.target,
-                       node->u.field_class_alias.alias);
-               if (ret) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                               "Cannot add field class alias found in stream class.");
-                       goto error;
-               }
-               break;
-       case NODE_CTF_EXPRESSION:
-       {
-               left = ctf_ast_concatenate_unary_strings(&node->u.ctf_expression.left);
-               if (!left) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Cannot concatenate unary strings.");
-                       ret = -EINVAL;
-                       goto error;
-               }
-
-               if (strcmp(left, "id") == 0) {
-                       int64_t id;
-
-                       if (_IS_SET(set, _STREAM_ID_SET)) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "id",
-                                       "stream declaration");
-                               ret = -EPERM;
-                               goto error;
-                       }
-
-                       ret = get_unary_unsigned(ctx,
-                               &node->u.ctf_expression.right,
-                               (uint64_t *) &id);
-
-                       /* Only read "id" if get_unary_unsigned() succeeded. */
-                       if (ret || (!ret && id < 0)) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                       "Unexpected unary expression for stream class's `id` attribute.");
-                               ret = -EINVAL;
-                               goto error;
-                       }
-
-                       if (ctf_trace_class_borrow_stream_class_by_id(
-                                       ctx->ctf_tc, id)) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                       "Duplicate stream class (same ID): id=%" PRId64,
-                                       id);
-                               ret = -EEXIST;
-                               goto error;
-                       }
-
-                       stream_class->id = id;
-                       _SET(set, _STREAM_ID_SET);
-               } else if (strcmp(left, "event.header") == 0) {
-                       if (_IS_SET(set, _STREAM_EVENT_HEADER_SET)) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                       "Duplicate `event.header` entry in stream class.");
-                               ret = -EPERM;
-                               goto error;
-                       }
-
-                       ret = visit_field_class_specifier_list(ctx,
-                               _BT_LIST_FIRST_ENTRY(
-                                       &node->u.ctf_expression.right,
-                                       struct ctf_node, siblings),
-                               &stream_class->event_header_fc);
-                       if (ret) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                       "Cannot create stream class's event header field class.");
-                               goto error;
-                       }
-
-                       BT_ASSERT(stream_class->event_header_fc);
-                       ret = auto_map_fields_to_trace_clock_class(ctx,
-                               stream_class->event_header_fc, "timestamp");
-                       if (ret) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                       "Cannot automatically map specific event header field class fields named `timestamp` to trace's clock class.");
-                               goto error;
-                       }
-
-                       _SET(set, _STREAM_EVENT_HEADER_SET);
-               } else if (strcmp(left, "event.context") == 0) {
-                       if (_IS_SET(set, _STREAM_EVENT_CONTEXT_SET)) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                       "Duplicate `event.context` entry in stream class.");
-                               ret = -EPERM;
-                               goto error;
-                       }
-
-                       ret = visit_field_class_specifier_list(ctx,
-                               _BT_LIST_FIRST_ENTRY(
-                                       &node->u.ctf_expression.right,
-                                       struct ctf_node, siblings),
-                               &stream_class->event_common_context_fc);
-                       if (ret) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                       "Cannot create stream class's event context field class.");
-                               goto error;
-                       }
-
-                       BT_ASSERT(stream_class->event_common_context_fc);
-                       _SET(set, _STREAM_EVENT_CONTEXT_SET);
-               } else if (strcmp(left, "packet.context") == 0) {
-                       if (_IS_SET(set, _STREAM_PACKET_CONTEXT_SET)) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                       "Duplicate `packet.context` entry in stream class.");
-                               ret = -EPERM;
-                               goto error;
-                       }
-
-                       ret = visit_field_class_specifier_list(ctx,
-                               _BT_LIST_FIRST_ENTRY(
-                                       &node->u.ctf_expression.right,
-                                       struct ctf_node, siblings),
-                               &stream_class->packet_context_fc);
-                       if (ret) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                       "Cannot create stream class's packet context field class.");
-                               goto error;
-                       }
-
-                       BT_ASSERT(stream_class->packet_context_fc);
-                       ret = auto_map_fields_to_trace_clock_class(ctx,
-                               stream_class->packet_context_fc,
-                               "timestamp_begin");
-                       if (ret) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                       "Cannot automatically map specific packet context field class fields named `timestamp_begin` to trace's clock class.");
-                               goto error;
-                       }
-
-                       ret = auto_map_fields_to_trace_clock_class(ctx,
-                               stream_class->packet_context_fc,
-                               "timestamp_end");
-                       if (ret) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                       "Cannot automatically map specific packet context field class fields named `timestamp_end` to trace's clock class.");
-                               goto error;
-                       }
-
-                       _SET(set, _STREAM_PACKET_CONTEXT_SET);
-               } else {
-                       _BT_COMP_LOGW_NODE(node,
-                               "Unknown attribute in stream class: "
-                               "attr-name=\"%s\"", left);
-               }
-
-               g_free(left);
-               left = NULL;
-               break;
-       }
-
-       default:
-               ret = -EPERM;
-               goto error;
-       }
-
-       return 0;
-
-error:
-       g_free(left);
-       return ret;
-}
-
-static
-int visit_stream_decl(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node)
-{
-       int set = 0;
-       int ret = 0;
-       struct ctf_node *iter;
-       struct ctf_stream_class *stream_class = NULL;
-       struct bt_list_head *decl_list = &node->u.stream.declaration_list;
-
-       if (node->visited) {
-               goto end;
-       }
-
-       node->visited = TRUE;
-       stream_class = ctf_stream_class_create();
-       BT_ASSERT(stream_class);
-       _TRY_PUSH_SCOPE_OR_GOTO_ERROR();
-
-       bt_list_for_each_entry(iter, decl_list, siblings) {
-               ret = visit_stream_decl_entry(ctx, iter, stream_class, &set);
-               if (ret) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
-                               "Cannot visit stream class's entry: "
-                               "ret=%d", ret);
-                       ctx_pop_scope(ctx);
-                       goto error;
-               }
-       }
-
-       ctx_pop_scope(ctx);
-
-       if (_IS_SET(&set, _STREAM_ID_SET)) {
-               /* Check that packet header has `stream_id` field */
-               struct ctf_named_field_class *named_fc = NULL;
-
-               if (!ctx->ctf_tc->packet_header_fc) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                               "Stream class has a `id` attribute, "
-                               "but trace has no packet header field class.");
-                       ret = -EINVAL;
-                       goto error;
-               }
-
-               named_fc = ctf_field_class_struct_borrow_member_by_name(
-                       (void *) ctx->ctf_tc->packet_header_fc, "stream_id");
-               if (!named_fc) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                               "Stream class has a `id` attribute, "
-                               "but trace's packet header field class has no `stream_id` field.");
-                       ret = -EINVAL;
-                       goto error;
-               }
-
-               if (named_fc->fc->type != CTF_FIELD_CLASS_TYPE_INT &&
-                               named_fc->fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                               "Stream class has a `id` attribute, "
-                               "but trace's packet header field class's `stream_id` field is not an integer field class.");
-                       ret = -EINVAL;
-                       goto error;
-               }
-       } else {
-               /* Allow only _one_ ID-less stream */
-               if (ctx->ctf_tc->stream_classes->len != 0) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                               "Missing `id` attribute in stream class as there's more than one stream class in the trace.");
-                       ret = -EPERM;
-                       goto error;
-               }
-
-               /* Automatic ID: 0 */
-               stream_class->id = 0;
-       }
-
-       /*
-        * Make sure that this stream class's ID is currently unique in
-        * the trace.
-        */
-       if (ctf_trace_class_borrow_stream_class_by_id(ctx->ctf_tc,
-                       stream_class->id)) {
-               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                       "Duplicate stream class (same ID): id=%" PRId64,
-                       stream_class->id);
-               ret = -EINVAL;
-               goto error;
-       }
-
-       g_ptr_array_add(ctx->ctf_tc->stream_classes, stream_class);
-       stream_class = NULL;
-       goto end;
-
-error:
-       ctf_stream_class_destroy(stream_class);
-       stream_class = NULL;
-
-end:
-       return ret;
-}
-
-static
-int visit_trace_decl_entry(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node,
-               int *set)
-{
-       int ret = 0;
-       char *left = NULL;
-       uint64_t val;
-
-       switch (node->type) {
-       case NODE_TYPEDEF:
-               ret = visit_field_class_def(ctx, node->u.field_class_def.field_class_specifier_list,
-                       &node->u.field_class_def.field_class_declarators);
-               if (ret) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                               "Cannot add field class found in trace (`trace` block).");
-                       goto error;
-               }
-               break;
-       case NODE_TYPEALIAS:
-               ret = visit_field_class_alias(ctx, node->u.field_class_alias.target,
-                       node->u.field_class_alias.alias);
-               if (ret) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                               "Cannot add field class alias found in trace (`trace` block).");
-                       goto error;
-               }
-               break;
-       case NODE_CTF_EXPRESSION:
-       {
-               left = ctf_ast_concatenate_unary_strings(&node->u.ctf_expression.left);
-               if (!left) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Cannot concatenate unary strings.");
-                       ret = -EINVAL;
-                       goto error;
-               }
-
-               if (strcmp(left, "major") == 0) {
-                       if (_IS_SET(set, _TRACE_MAJOR_SET)) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "major", "trace");
-                               ret = -EPERM;
-                               goto error;
-                       }
-
-                       ret = get_unary_unsigned(ctx,
-                               &node->u.ctf_expression.right, &val);
-                       if (ret) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                       "Unexpected unary expression for trace's `major` attribute.");
-                               ret = -EINVAL;
-                               goto error;
-                       }
-
-                       if (val != 1) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                       "Invalid trace's `minor` attribute: expecting 1.");
-                               ret = -EINVAL;
-                               goto error;
-                       }
-
-                       ctx->ctf_tc->major = val;
-                       _SET(set, _TRACE_MAJOR_SET);
-               } else if (strcmp(left, "minor") == 0) {
-                       if (_IS_SET(set, _TRACE_MINOR_SET)) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "minor", "trace");
-                               ret = -EPERM;
-                               goto error;
-                       }
-
-                       ret = get_unary_unsigned(ctx,
-                               &node->u.ctf_expression.right, &val);
-                       if (ret) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                       "Unexpected unary expression for trace's `minor` attribute.");
-                               ret = -EINVAL;
-                               goto error;
-                       }
-
-                       if (val != 8) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                       "Invalid trace's `minor` attribute: expecting 8.");
-                               ret = -EINVAL;
-                               goto error;
-                       }
-
-                       ctx->ctf_tc->minor = val;
-                       _SET(set, _TRACE_MINOR_SET);
-               } else if (strcmp(left, "uuid") == 0) {
-                       if (_IS_SET(set, _TRACE_UUID_SET)) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "uuid", "trace");
-                               ret = -EPERM;
-                               goto error;
-                       }
-
-                       ret = get_unary_uuid(ctx,
-                               &node->u.ctf_expression.right,
-                               ctx->ctf_tc->uuid);
-                       if (ret) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                       "Invalid trace's `uuid` attribute.");
-                               goto error;
-                       }
-
-                       ctx->ctf_tc->is_uuid_set = true;
-                       _SET(set, _TRACE_UUID_SET);
-               } else if (strcmp(left, "byte_order") == 0) {
-                       /* Default byte order is already known at this stage */
-                       if (_IS_SET(set, _TRACE_BYTE_ORDER_SET)) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "byte_order",
-                                       "trace");
-                               ret = -EPERM;
-                               goto error;
-                       }
-
-                       BT_ASSERT(ctx->ctf_tc->default_byte_order != CTF_BYTE_ORDER_UNKNOWN);
-                       _SET(set, _TRACE_BYTE_ORDER_SET);
-               } else if (strcmp(left, "packet.header") == 0) {
-                       if (_IS_SET(set, _TRACE_PACKET_HEADER_SET)) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                       "Duplicate `packet.header` entry in trace.");
-                               ret = -EPERM;
-                               goto error;
-                       }
-
-                       ret = visit_field_class_specifier_list(ctx,
-                               _BT_LIST_FIRST_ENTRY(
-                                       &node->u.ctf_expression.right,
-                                       struct ctf_node, siblings),
-                               &ctx->ctf_tc->packet_header_fc);
-                       if (ret) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                       "Cannot create trace's packet header field class.");
-                               goto error;
-                       }
-
-                       BT_ASSERT(ctx->ctf_tc->packet_header_fc);
-                       _SET(set, _TRACE_PACKET_HEADER_SET);
-               } else {
-                       _BT_COMP_LOGW_NODE(node,
-                               "Unknown attribute in stream class: "
-                               "attr-name=\"%s\"", left);
-               }
-
-               g_free(left);
-               left = NULL;
-               break;
-       }
-       default:
-               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Unknown expression in trace.");
-               ret = -EINVAL;
-               goto error;
-       }
-
-       return 0;
-
-error:
-       g_free(left);
-       return ret;
-}
-
-static
-int visit_trace_decl(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node)
-{
-       int ret = 0;
-       int set = 0;
-       struct ctf_node *iter;
-       struct bt_list_head *decl_list = &node->u.trace.declaration_list;
-
-       if (node->visited) {
-               goto end;
-       }
-
-       node->visited = TRUE;
-
-       if (ctx->is_trace_visited) {
-               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Duplicate trace (`trace` block).");
-               ret = -EEXIST;
-               goto error;
-       }
-
-       _TRY_PUSH_SCOPE_OR_GOTO_ERROR();
-
-       bt_list_for_each_entry(iter, decl_list, siblings) {
-               ret = visit_trace_decl_entry(ctx, iter, &set);
-               if (ret) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter, "Cannot visit trace's entry (`trace` block): "
-                               "ret=%d", ret);
-                       ctx_pop_scope(ctx);
-                       goto error;
-               }
-       }
-
-       ctx_pop_scope(ctx);
-
-       if (!_IS_SET(&set, _TRACE_MAJOR_SET)) {
-               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                       "Missing `major` attribute in trace (`trace` block).");
-               ret = -EPERM;
-               goto error;
-       }
-
-       if (!_IS_SET(&set, _TRACE_MINOR_SET)) {
-               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                       "Missing `minor` attribute in trace (`trace` block).");
-               ret = -EPERM;
-               goto error;
-       }
-
-       if (!_IS_SET(&set, _TRACE_BYTE_ORDER_SET)) {
-               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                       "Missing `byte_order` attribute in trace (`trace` block).");
-               ret = -EPERM;
-               goto error;
-       }
-
-       ctx->is_trace_visited = true;
-
-end:
-       return 0;
-
-error:
-       return ret;
-}
-
-static
-int visit_env(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node)
-{
-       int ret = 0;
-       char *left = NULL;
-       struct ctf_node *entry_node;
-       struct bt_list_head *decl_list = &node->u.env.declaration_list;
-
-       if (node->visited) {
-               goto end;
-       }
-
-       node->visited = TRUE;
-
-       bt_list_for_each_entry(entry_node, decl_list, siblings) {
-               struct bt_list_head *right_head =
-                       &entry_node->u.ctf_expression.right;
-
-               if (entry_node->type != NODE_CTF_EXPRESSION) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
-                               "Wrong expression in environment entry: "
-                               "node-type=%d", entry_node->type);
-                       ret = -EPERM;
-                       goto error;
-               }
-
-               left = ctf_ast_concatenate_unary_strings(
-                       &entry_node->u.ctf_expression.left);
-               if (!left) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
-                               "Cannot get environment entry's name.");
-                       ret = -EINVAL;
-                       goto error;
-               }
-
-               if (is_unary_string(right_head)) {
-                       char *right = ctf_ast_concatenate_unary_strings(right_head);
-
-                       if (!right) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
-                                       "Unexpected unary expression for environment entry's value: "
-                                       "name=\"%s\"", left);
-                               ret = -EINVAL;
-                               goto error;
-                       }
-
-                       if (strcmp(left, "tracer_name") == 0) {
-                               if (strncmp(right, "lttng", 5) == 0) {
-                                       BT_COMP_LOGI("Detected LTTng trace from `%s` environment value: "
-                                               "tracer-name=\"%s\"",
-                                               left, right);
-                                       ctx->is_lttng = true;
-                               }
-                       }
-
-                       ctf_trace_class_append_env_entry(ctx->ctf_tc,
-                               left, CTF_TRACE_CLASS_ENV_ENTRY_TYPE_STR,
-                               right, 0);
-                       g_free(right);
-               } else if (is_unary_unsigned(right_head) ||
-                               is_unary_signed(right_head)) {
-                       int64_t v;
-
-                       if (is_unary_unsigned(right_head)) {
-                               ret = get_unary_unsigned(ctx, right_head,
-                                       (uint64_t *) &v);
-                       } else {
-                               ret = get_unary_signed(right_head, &v);
-                       }
-                       if (ret) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
-                                       "Unexpected unary expression for environment entry's value: "
-                                       "name=\"%s\"", left);
-                               ret = -EINVAL;
-                               goto error;
-                       }
-
-                       ctf_trace_class_append_env_entry(ctx->ctf_tc,
-                               left, CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT,
-                               NULL, v);
-               } else {
-                       _BT_COMP_LOGW_NODE(entry_node,
-                               "Environment entry has unknown type: "
-                               "name=\"%s\"", left);
-               }
-
-               g_free(left);
-               left = NULL;
-       }
-
-end:
-       return 0;
-
-error:
-       g_free(left);
-       return ret;
-}
-
-static
-int set_trace_byte_order(struct ctf_visitor_generate_ir *ctx, struct ctf_node *trace_node)
-{
-       int ret = 0;
-       int set = 0;
-       char *left = NULL;
-       struct ctf_node *node;
-       struct bt_list_head *decl_list = &trace_node->u.trace.declaration_list;
-
-       bt_list_for_each_entry(node, decl_list, siblings) {
-               if (node->type == NODE_CTF_EXPRESSION) {
-                       struct ctf_node *right_node;
-
-                       left = ctf_ast_concatenate_unary_strings(
-                               &node->u.ctf_expression.left);
-                       if (!left) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                       "Cannot concatenate unary strings.");
-                               ret = -EINVAL;
-                               goto error;
-                       }
-
-                       if (strcmp(left, "byte_order") == 0) {
-                               enum ctf_byte_order bo;
-
-                               if (_IS_SET(&set, _TRACE_BYTE_ORDER_SET)) {
-                                       _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "byte_order",
-                                               "trace");
-                                       ret = -EPERM;
-                                       goto error;
-                               }
-
-                               _SET(&set, _TRACE_BYTE_ORDER_SET);
-                               right_node = _BT_LIST_FIRST_ENTRY(
-                                       &node->u.ctf_expression.right,
-                                       struct ctf_node, siblings);
-                               bo = byte_order_from_unary_expr(ctx,
-                                       right_node);
-                               if (bo == CTF_BYTE_ORDER_UNKNOWN) {
-                                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                               "Invalid `byte_order` attribute in trace (`trace` block): "
-                                               "expecting `le`, `be`, or `network`.");
-                                       ret = -EINVAL;
-                                       goto error;
-                               } else if (bo == CTF_BYTE_ORDER_DEFAULT) {
-                                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                               "Invalid `byte_order` attribute in trace (`trace` block): "
-                                               "cannot be set to `native` here.");
-                                       ret = -EPERM;
-                                       goto error;
-                               }
-
-                               ctx->ctf_tc->default_byte_order = bo;
-                       }
-
-                       g_free(left);
-                       left = NULL;
-               }
-       }
-
-       if (!_IS_SET(&set, _TRACE_BYTE_ORDER_SET)) {
-               _BT_COMP_LOGE_APPEND_CAUSE_NODE(trace_node,
-                       "Missing `byte_order` attribute in trace (`trace` block).");
-               ret = -EINVAL;
-               goto error;
-       }
-
-       return 0;
-
-error:
-       g_free(left);
-       return ret;
-}
-
-static
-int visit_clock_decl_entry(struct ctf_visitor_generate_ir *ctx, struct ctf_node *entry_node,
-       struct ctf_clock_class *clock, int *set, int64_t *offset_seconds,
-       uint64_t *offset_cycles)
-{
-       int ret = 0;
-       char *left = NULL;
-
-       if (entry_node->type != NODE_CTF_EXPRESSION) {
-               _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
-                       "Unexpected node type: node-type=%d",
-                       entry_node->type);
-               ret = -EPERM;
-               goto error;
-       }
-
-       left = ctf_ast_concatenate_unary_strings(&entry_node->u.ctf_expression.left);
-       if (!left) {
-               _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node, "Cannot concatenate unary strings.");
-               ret = -EINVAL;
-               goto error;
-       }
-
-       if (strcmp(left, "name") == 0) {
-               char *right;
-
-               if (_IS_SET(set, _CLOCK_NAME_SET)) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "name", "clock class");
-                       ret = -EPERM;
-                       goto error;
-               }
-
-               right = ctf_ast_concatenate_unary_strings(
-                       &entry_node->u.ctf_expression.right);
-               if (!right) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
-                               "Unexpected unary expression for clock class's `name` attribute.");
-                       ret = -EINVAL;
-                       goto error;
-               }
-
-               g_string_assign(clock->name, right);
-               g_free(right);
-               _SET(set, _CLOCK_NAME_SET);
-       } else if (strcmp(left, "uuid") == 0) {
-               bt_uuid_t uuid;
-
-               if (_IS_SET(set, _CLOCK_UUID_SET)) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "uuid", "clock class");
-                       ret = -EPERM;
-                       goto error;
-               }
-
-               ret = get_unary_uuid(ctx, &entry_node->u.ctf_expression.right,
-                       uuid);
-               if (ret) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
-                               "Invalid clock class's `uuid` attribute.");
-                       goto error;
-               }
-
-               clock->has_uuid = true;
-               bt_uuid_copy(clock->uuid, uuid);
-               _SET(set, _CLOCK_UUID_SET);
-       } else if (strcmp(left, "description") == 0) {
-               char *right;
-
-               if (_IS_SET(set, _CLOCK_DESCRIPTION_SET)) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "description",
-                               "clock class");
-                       ret = -EPERM;
-                       goto error;
-               }
-
-               right = ctf_ast_concatenate_unary_strings(
-                       &entry_node->u.ctf_expression.right);
-               if (!right) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
-                               "Unexpected unary expression for clock class's `description` attribute.");
-                       ret = -EINVAL;
-                       goto error;
-               }
-
-               g_string_assign(clock->description, right);
-               g_free(right);
-               _SET(set, _CLOCK_DESCRIPTION_SET);
-       } else if (strcmp(left, "freq") == 0) {
-               uint64_t freq = UINT64_C(-1);
-
-               if (_IS_SET(set, _CLOCK_FREQ_SET)) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "freq", "clock class");
-                       ret = -EPERM;
-                       goto error;
-               }
-
-               ret = get_unary_unsigned(ctx,
-                       &entry_node->u.ctf_expression.right, &freq);
-               if (ret) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
-                               "Unexpected unary expression for clock class's `freq` attribute.");
-                       ret = -EINVAL;
-                       goto error;
-               }
-
-               if (freq == UINT64_C(-1) || freq == 0) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
-                               "Invalid clock class frequency: freq=%" PRIu64,
-                               freq);
-                       ret = -EINVAL;
-                       goto error;
-               }
-
-               clock->frequency = freq;
-               _SET(set, _CLOCK_FREQ_SET);
-       } else if (strcmp(left, "precision") == 0) {
-               uint64_t precision;
-
-               if (_IS_SET(set, _CLOCK_PRECISION_SET)) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "precision",
-                               "clock class");
-                       ret = -EPERM;
-                       goto error;
-               }
-
-               ret = get_unary_unsigned(ctx,
-                       &entry_node->u.ctf_expression.right, &precision);
-               if (ret) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
-                               "Unexpected unary expression for clock class's `precision` attribute.");
-                       ret = -EINVAL;
-                       goto error;
-               }
-
-               clock->precision = precision;
-               _SET(set, _CLOCK_PRECISION_SET);
-       } else if (strcmp(left, "offset_s") == 0) {
-               if (_IS_SET(set, _CLOCK_OFFSET_S_SET)) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "offset_s",
-                               "clock class");
-                       ret = -EPERM;
-                       goto error;
-               }
-
-               ret = get_unary_signed(
-                       &entry_node->u.ctf_expression.right, offset_seconds);
-               if (ret) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
-                               "Unexpected unary expression for clock class's `offset_s` attribute.");
-                       ret = -EINVAL;
-                       goto error;
-               }
-
-               _SET(set, _CLOCK_OFFSET_S_SET);
-       } else if (strcmp(left, "offset") == 0) {
-               if (_IS_SET(set, _CLOCK_OFFSET_SET)) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "offset", "clock class");
-                       ret = -EPERM;
-                       goto error;
-               }
-
-               ret = get_unary_unsigned(ctx,
-                       &entry_node->u.ctf_expression.right, offset_cycles);
-               if (ret) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
-                               "Unexpected unary expression for clock class's `offset` attribute.");
-                       ret = -EINVAL;
-                       goto error;
-               }
-
-               _SET(set, _CLOCK_OFFSET_SET);
-       } else if (strcmp(left, "absolute") == 0) {
-               struct ctf_node *right;
-
-               if (_IS_SET(set, _CLOCK_ABSOLUTE_SET)) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "absolute",
-                               "clock class");
-                       ret = -EPERM;
-                       goto error;
-               }
-
-               right = _BT_LIST_FIRST_ENTRY(
-                       &entry_node->u.ctf_expression.right,
-                       struct ctf_node, siblings);
-               ret = get_boolean(ctx, right);
-               if (ret < 0) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
-                               "Unexpected unary expression for clock class's `absolute` attribute.");
-                       ret = -EINVAL;
-                       goto error;
-               }
-
-               clock->is_absolute = ret;
-               _SET(set, _CLOCK_ABSOLUTE_SET);
-       } else {
-               _BT_COMP_LOGW_NODE(entry_node,
-                       "Unknown attribute in clock class: attr-name=\"%s\"",
-                       left);
-       }
-
-       g_free(left);
-       left = NULL;
-       return 0;
-
-error:
-       g_free(left);
-       return ret;
-}
-
-static inline
-uint64_t cycles_from_ns(uint64_t frequency, uint64_t ns)
-{
-       uint64_t cycles;
-
-       /* 1GHz */
-       if (frequency == UINT64_C(1000000000)) {
-               cycles = ns;
-       } else {
-               cycles = (uint64_t) (((double) ns * (double) frequency) / 1e9);
-       }
-
-       return cycles;
-}
-
-static
-void calibrate_clock_class_offsets(int64_t *offset_seconds,
-               uint64_t *offset_cycles, uint64_t freq)
-{
-       if (*offset_cycles >= freq) {
-               const uint64_t s_in_offset_cycles = *offset_cycles / freq;
-
-               *offset_seconds += (int64_t) s_in_offset_cycles;
-               *offset_cycles -= (s_in_offset_cycles * freq);
-       }
-}
-
-static
-void apply_clock_class_is_absolute(struct ctf_visitor_generate_ir *ctx,
-               struct ctf_clock_class *clock)
-{
-       if (ctx->decoder_config.force_clock_class_origin_unix_epoch) {
-               clock->is_absolute = true;
-       }
-
-       return;
-}
-
-static
-void apply_clock_class_offset(struct ctf_visitor_generate_ir *ctx,
-               struct ctf_clock_class *clock)
-{
-       uint64_t freq;
-       int64_t offset_s_to_apply = ctx->decoder_config.clock_class_offset_s;
-       uint64_t offset_ns_to_apply;
-       int64_t cur_offset_s;
-       uint64_t cur_offset_cycles;
-
-       if (ctx->decoder_config.clock_class_offset_s == 0 &&
-                       ctx->decoder_config.clock_class_offset_ns == 0) {
-               goto end;
-       }
-
-       /* Transfer nanoseconds to seconds as much as possible */
-       if (ctx->decoder_config.clock_class_offset_ns < 0) {
-               const int64_t abs_ns = -ctx->decoder_config.clock_class_offset_ns;
-               const int64_t abs_extra_s = abs_ns / INT64_C(1000000000) + 1;
-               const int64_t extra_s = -abs_extra_s;
-               const int64_t offset_ns = ctx->decoder_config.clock_class_offset_ns -
-                       (extra_s * INT64_C(1000000000));
-
-               BT_ASSERT(offset_ns > 0);
-               offset_ns_to_apply = (uint64_t) offset_ns;
-               offset_s_to_apply += extra_s;
-       } else {
-               const int64_t extra_s = ctx->decoder_config.clock_class_offset_ns /
-                       INT64_C(1000000000);
-               const int64_t offset_ns = ctx->decoder_config.clock_class_offset_ns -
-                       (extra_s * INT64_C(1000000000));
-
-               BT_ASSERT(offset_ns >= 0);
-               offset_ns_to_apply = (uint64_t) offset_ns;
-               offset_s_to_apply += extra_s;
-       }
-
-       freq = clock->frequency;
-       cur_offset_s = clock->offset_seconds;
-       cur_offset_cycles = clock->offset_cycles;
-
-       /* Apply offsets */
-       cur_offset_s += offset_s_to_apply;
-       cur_offset_cycles += cycles_from_ns(freq, offset_ns_to_apply);
-
-       /*
-        * Recalibrate offsets because the part in cycles can be greater
-        * than the frequency at this point.
-        */
-       calibrate_clock_class_offsets(&cur_offset_s, &cur_offset_cycles, freq);
-
-       /* Set final offsets */
-       clock->offset_seconds = cur_offset_s;
-       clock->offset_cycles = cur_offset_cycles;
-
-end:
-       return;
-}
-
-static
-int visit_clock_decl(struct ctf_visitor_generate_ir *ctx, struct ctf_node *clock_node)
-{
-       int ret = 0;
-       int set = 0;
-       struct ctf_clock_class *clock;
-       struct ctf_node *entry_node;
-       struct bt_list_head *decl_list = &clock_node->u.clock.declaration_list;
-       const char *clock_class_name;
-       int64_t offset_seconds = 0;
-       uint64_t offset_cycles = 0;
-       uint64_t freq;
-
-       if (clock_node->visited) {
-               return 0;
-       }
-
-       clock_node->visited = TRUE;
-
-       /* CTF 1.8's default frequency for a clock class is 1 GHz */
-       clock = ctf_clock_class_create();
-       if (!clock) {
-               _BT_COMP_LOGE_APPEND_CAUSE_NODE(clock_node,
-                       "Cannot create default clock class.");
-               ret = -ENOMEM;
-               goto end;
-       }
-
-       bt_list_for_each_entry(entry_node, decl_list, siblings) {
-               ret = visit_clock_decl_entry(ctx, entry_node, clock, &set,
-                       &offset_seconds, &offset_cycles);
-               if (ret) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
-                               "Cannot visit clock class's entry: ret=%d",
-                               ret);
-                       goto end;
-               }
-       }
-
-       if (!_IS_SET(&set, _CLOCK_NAME_SET)) {
-               _BT_COMP_LOGE_APPEND_CAUSE_NODE(clock_node,
-                       "Missing `name` attribute in clock class.");
-               ret = -EPERM;
-               goto end;
-       }
-
-       clock_class_name = clock->name->str;
-       BT_ASSERT(clock_class_name);
-       if (ctx->is_lttng && strcmp(clock_class_name, "monotonic") == 0) {
-               /*
-                * Old versions of LTTng forgot to set its clock class
-                * as absolute, even if it is. This is important because
-                * it's a condition to be able to sort messages
-                * from different sources.
-                */
-               clock->is_absolute = true;
-       }
-
-       /*
-        * Adjust offsets so that the part in cycles is less than the
-        * frequency (move to the part in seconds).
-        */
-       freq = clock->frequency;
-       calibrate_clock_class_offsets(&offset_seconds, &offset_cycles, freq);
-       BT_ASSERT(offset_cycles < clock->frequency);
-       clock->offset_seconds = offset_seconds;
-       clock->offset_cycles = offset_cycles;
-       apply_clock_class_offset(ctx, clock);
-       apply_clock_class_is_absolute(ctx, clock);
-       g_ptr_array_add(ctx->ctf_tc->clock_classes, clock);
-       clock = NULL;
-
-end:
-       if (clock) {
-               ctf_clock_class_destroy(clock);
-       }
-
-       return ret;
-}
-
-static
-int visit_root_decl(struct ctf_visitor_generate_ir *ctx, struct ctf_node *root_decl_node)
-{
-       int ret = 0;
-
-       if (root_decl_node->visited) {
-               goto end;
-       }
-
-       root_decl_node->visited = TRUE;
-
-       switch (root_decl_node->type) {
-       case NODE_TYPEDEF:
-               ret = visit_field_class_def(ctx,
-                       root_decl_node->u.field_class_def.field_class_specifier_list,
-                       &root_decl_node->u.field_class_def.field_class_declarators);
-               if (ret) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(root_decl_node,
-                               "Cannot add field class found in root scope.");
-                       goto end;
-               }
-               break;
-       case NODE_TYPEALIAS:
-               ret = visit_field_class_alias(ctx, root_decl_node->u.field_class_alias.target,
-                       root_decl_node->u.field_class_alias.alias);
-               if (ret) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(root_decl_node,
-                               "Cannot add field class alias found in root scope.");
-                       goto end;
-               }
-               break;
-       case NODE_TYPE_SPECIFIER_LIST:
-       {
-               struct ctf_field_class *decl = NULL;
-
-               /*
-                * Just add the field class specifier to the root
-                * declaration scope. Put local reference.
-                */
-               ret = visit_field_class_specifier_list(ctx, root_decl_node, &decl);
-               if (ret) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(root_decl_node,
-                               "Cannot visit root scope's field class: "
-                               "ret=%d", ret);
-                       BT_ASSERT(!decl);
-                       goto end;
-               }
-
-               ctf_field_class_destroy(decl);
-               decl = NULL;
-               break;
-       }
-       default:
-               _BT_COMP_LOGE_APPEND_CAUSE_NODE(root_decl_node,
-                       "Unexpected node type: node-type=%d",
-                       root_decl_node->type);
-               ret = -EPERM;
-               goto end;
-       }
-
-end:
-       return ret;
-}
-
-BT_HIDDEN
-struct ctf_visitor_generate_ir *ctf_visitor_generate_ir_create(
-               const struct ctf_metadata_decoder_config *decoder_config)
-{
-       struct ctf_visitor_generate_ir *ctx = NULL;
-
-       /* Create visitor's context */
-       ctx = ctx_create(decoder_config);
-       if (!ctx) {
-               BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, decoder_config->log_level,
-                       decoder_config->self_comp,
-                       "Cannot create visitor's context.");
-               goto error;
-       }
-
-       goto end;
-
-error:
-       ctx_destroy(ctx);
-       ctx = NULL;
-
-end:
-       return ctx;
-}
-
-BT_HIDDEN
-void ctf_visitor_generate_ir_destroy(struct ctf_visitor_generate_ir *visitor)
-{
-       ctx_destroy(visitor);
-}
-
-BT_HIDDEN
-bt_trace_class *ctf_visitor_generate_ir_get_ir_trace_class(
-               struct ctf_visitor_generate_ir *ctx)
-{
-       BT_ASSERT_DBG(ctx);
-
-       if (ctx->trace_class) {
-               bt_trace_class_get_ref(ctx->trace_class);
-       }
-
-       return ctx->trace_class;
-}
-
-BT_HIDDEN
-struct ctf_trace_class *ctf_visitor_generate_ir_borrow_ctf_trace_class(
-               struct ctf_visitor_generate_ir *ctx)
-{
-       BT_ASSERT_DBG(ctx);
-       BT_ASSERT_DBG(ctx->ctf_tc);
-       return ctx->ctf_tc;
-}
-
-BT_HIDDEN
-int ctf_visitor_generate_ir_visit_node(struct ctf_visitor_generate_ir *ctx,
-               struct ctf_node *node)
-{
-       int ret = 0;
-
-       BT_COMP_LOGI_STR("Visiting metadata's AST to generate CTF IR objects.");
-
-       switch (node->type) {
-       case NODE_ROOT:
-       {
-               struct ctf_node *iter;
-               bool got_trace_decl = false;
-
-               /*
-                * The first thing we need is the native byte order of
-                * the trace block, because early class aliases can have
-                * a `byte_order` attribute set to `native`. If we don't
-                * have the native byte order yet, and we don't have any
-                * trace block yet, then fail with EINCOMPLETE.
-                */
-               if (ctx->ctf_tc->default_byte_order == CTF_BYTE_ORDER_UNKNOWN) {
-                       bt_list_for_each_entry(iter, &node->u.root.trace, siblings) {
-                               if (got_trace_decl) {
-                                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                               "Duplicate trace (`trace` block).");
-                                       ret = -1;
-                                       goto end;
-                               }
-
-                               ret = set_trace_byte_order(ctx, iter);
-                               if (ret) {
-                                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                                               "Cannot set trace's native byte order: "
-                                               "ret=%d", ret);
-                                       goto end;
-                               }
-
-                               got_trace_decl = true;
-                       }
-
-                       if (!got_trace_decl) {
-                               BT_COMP_LOGD_STR("Incomplete AST: need trace (`trace` block).");
-                               ret = -EINCOMPLETE;
-                               goto end;
-                       }
-               }
-
-               BT_ASSERT(ctx->ctf_tc->default_byte_order == CTF_BYTE_ORDER_LITTLE ||
-                       ctx->ctf_tc->default_byte_order == CTF_BYTE_ORDER_BIG);
-               BT_ASSERT(ctx->current_scope &&
-                               !ctx->current_scope->parent_scope);
-
-               /* Environment */
-               bt_list_for_each_entry(iter, &node->u.root.env, siblings) {
-                       ret = visit_env(ctx, iter);
-                       if (ret) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
-                                       "Cannot visit trace's environment (`env` block) entry: "
-                                       "ret=%d", ret);
-                               goto end;
-                       }
-               }
-
-               BT_ASSERT(ctx->current_scope &&
-                       !ctx->current_scope->parent_scope);
-
-               /*
-                * Visit clock blocks.
-                */
-               bt_list_for_each_entry(iter, &node->u.root.clock, siblings) {
-                       ret = visit_clock_decl(ctx, iter);
-                       if (ret) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
-                                       "Cannot visit clock class: ret=%d",
-                                       ret);
-                               goto end;
-                       }
-               }
-
-               BT_ASSERT(ctx->current_scope &&
-                       !ctx->current_scope->parent_scope);
-
-               /*
-                * Visit root declarations next, as they can be used by any
-                * following entity.
-                */
-               bt_list_for_each_entry(iter, &node->u.root.declaration_list,
-                               siblings) {
-                       ret = visit_root_decl(ctx, iter);
-                       if (ret) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
-                                       "Cannot visit root entry: ret=%d",
-                                       ret);
-                               goto end;
-                       }
-               }
-
-               BT_ASSERT(ctx->current_scope &&
-                       !ctx->current_scope->parent_scope);
-
-               /* Callsite blocks are not supported */
-               bt_list_for_each_entry(iter, &node->u.root.callsite, siblings) {
-                       _BT_COMP_LOGW_NODE(iter,
-                               "\"callsite\" blocks are not supported as of this version.");
-               }
-
-               BT_ASSERT(ctx->current_scope &&
-                       !ctx->current_scope->parent_scope);
-
-               /* Trace */
-               bt_list_for_each_entry(iter, &node->u.root.trace, siblings) {
-                       ret = visit_trace_decl(ctx, iter);
-                       if (ret) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
-                                       "Cannot visit trace (`trace` block): "
-                                       "ret=%d", ret);
-                               goto end;
-                       }
-               }
-
-               BT_ASSERT(ctx->current_scope &&
-                       !ctx->current_scope->parent_scope);
-
-               /* Streams */
-               bt_list_for_each_entry(iter, &node->u.root.stream, siblings) {
-                       ret = visit_stream_decl(ctx, iter);
-                       if (ret) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
-                                       "Cannot visit stream class: ret=%d",
-                                       ret);
-                               goto end;
-                       }
-               }
-
-               BT_ASSERT(ctx->current_scope &&
-                       !ctx->current_scope->parent_scope);
-
-               /* Events */
-               bt_list_for_each_entry(iter, &node->u.root.event, siblings) {
-                       ret = visit_event_decl(ctx, iter);
-                       if (ret) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
-                                       "Cannot visit event class: ret=%d",
-                                       ret);
-                               goto end;
-                       }
-               }
-
-               BT_ASSERT(ctx->current_scope &&
-                       !ctx->current_scope->parent_scope);
-               break;
-       }
-       default:
-               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
-                       "Unexpected node type: node-type=%d",
-                       node->type);
-               ret = -EINVAL;
-               goto end;
-       }
-
-       /* Update default clock classes */
-       ret = ctf_trace_class_update_default_clock_classes(ctx->ctf_tc,
-               &ctx->log_cfg);
-       if (ret) {
-               ret = -EINVAL;
-               goto end;
-       }
-
-       /* Update trace class meanings */
-       ret = ctf_trace_class_update_meanings(ctx->ctf_tc);
-       if (ret) {
-               ret = -EINVAL;
-               goto end;
-       }
-
-       /* Update stream class configuration */
-       ret = ctf_trace_class_update_stream_class_config(ctx->ctf_tc);
-       if (ret) {
-               ret = -EINVAL;
-               goto end;
-       }
-
-       /* Update text arrays and sequences */
-       ret = ctf_trace_class_update_text_array_sequence(ctx->ctf_tc);
-       if (ret) {
-               ret = -EINVAL;
-               goto end;
-       }
-
-       /* Update structure/array/sequence alignments */
-       ret = ctf_trace_class_update_alignments(ctx->ctf_tc);
-       if (ret) {
-               ret = -EINVAL;
-               goto end;
-       }
-
-       /* Resolve sequence lengths and variant tags */
-       ret = ctf_trace_class_resolve_field_classes(ctx->ctf_tc, &ctx->log_cfg);
-       if (ret) {
-               ret = -EINVAL;
-               goto end;
-       }
-
-       if (ctx->trace_class) {
-               /*
-                * Update "in IR" for field classes.
-                *
-                * If we have no IR trace class, then we'll have no way
-                * to create IR fields anyway, so we leave all the
-                * `in_ir` members false.
-                */
-               ret = ctf_trace_class_update_in_ir(ctx->ctf_tc);
-               if (ret) {
-                       ret = -EINVAL;
-                       goto end;
-               }
-       }
-
-       /* Update saved value indexes */
-       ret = ctf_trace_class_update_value_storing_indexes(ctx->ctf_tc);
-       if (ret) {
-               ret = -EINVAL;
-               goto end;
-       }
-
-       /* Validate what we have so far */
-       ret = ctf_trace_class_validate(ctx->ctf_tc, &ctx->log_cfg);
-       if (ret) {
-               ret = -EINVAL;
-               goto end;
-       }
-
-       /*
-        * If there are fields which are not related to the CTF format
-        * itself in the packet header and in event header field
-        * classes, warn about it because they are never translated.
-        */
-       ctf_trace_class_warn_meaningless_header_fields(ctx->ctf_tc,
-               &ctx->log_cfg);
-
-       if (ctx->trace_class) {
-               /* Copy new CTF metadata -> new IR metadata */
-               ret = ctf_trace_class_translate(ctx->log_cfg.self_comp,
-                               ctx->trace_class, ctx->ctf_tc);
-               if (ret) {
-                       ret = -EINVAL;
-                       goto end;
-               }
-       }
-
-end:
-       return ret;
-}
diff --git a/src/plugins/ctf/common/metadata/visitor-generate-ir.cpp b/src/plugins/ctf/common/metadata/visitor-generate-ir.cpp
new file mode 100644 (file)
index 0000000..970b838
--- /dev/null
@@ -0,0 +1,4983 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2010 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Copyright 2015-2018 Philippe Proulx <philippe.proulx@efficios.com>
+ *
+ * Common Trace Format metadata visitor (generates CTF IR objects).
+ */
+
+#define BT_COMP_LOG_SELF_COMP (ctx->log_cfg.self_comp)
+#define BT_COMP_LOG_SELF_COMP_CLASS (ctx->log_cfg.self_comp_class)
+#define BT_LOG_OUTPUT_LEVEL (ctx->log_cfg.log_level)
+#define BT_LOG_TAG "PLUGIN/CTF/META/IR-VISITOR"
+#include "logging/comp-logging.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include "common/assert.h"
+#include <glib.h>
+#include <inttypes.h>
+#include <errno.h>
+#include "common/common.h"
+#include "common/uuid.h"
+#include "compat/endian.h"
+#include <babeltrace2/babeltrace.h>
+
+#include "logging.hpp"
+#include "scanner.hpp"
+#include "ast.hpp"
+#include "decoder.hpp"
+#include "ctf-meta.hpp"
+#include "ctf-meta-visitors.hpp"
+
+/* Bit value (left shift) */
+#define _BV(_val)              (1 << (_val))
+
+/* Bit is set in a set of bits */
+#define _IS_SET(_set, _mask)   (*(_set) & (_mask))
+
+/* Set bit in a set of bits */
+#define _SET(_set, _mask)      (*(_set) |= (_mask))
+
+/* Try to push scope, or go to the `error` label */
+#define _TRY_PUSH_SCOPE_OR_GOTO_ERROR()                                        \
+       do {                                                            \
+               ret = ctx_push_scope(ctx);                              \
+               if (ret) {                                              \
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot push scope.");         \
+                       goto error;                                     \
+               }                                                       \
+       } while (0)
+
+/* Bits for verifying existing attributes in various declarations */
+enum {
+       _CLOCK_NAME_SET =               _BV(0),
+       _CLOCK_UUID_SET =               _BV(1),
+       _CLOCK_FREQ_SET =               _BV(2),
+       _CLOCK_PRECISION_SET =          _BV(3),
+       _CLOCK_OFFSET_S_SET =           _BV(4),
+       _CLOCK_OFFSET_SET =             _BV(5),
+       _CLOCK_ABSOLUTE_SET =           _BV(6),
+       _CLOCK_DESCRIPTION_SET =        _BV(7),
+};
+
+enum {
+       _INTEGER_ALIGN_SET =            _BV(0),
+       _INTEGER_SIZE_SET =             _BV(1),
+       _INTEGER_BASE_SET =             _BV(2),
+       _INTEGER_ENCODING_SET =         _BV(3),
+       _INTEGER_BYTE_ORDER_SET =       _BV(4),
+       _INTEGER_SIGNED_SET =           _BV(5),
+       _INTEGER_MAP_SET =              _BV(6),
+};
+
+enum {
+       _FLOAT_ALIGN_SET =              _BV(0),
+       _FLOAT_MANT_DIG_SET =           _BV(1),
+       _FLOAT_EXP_DIG_SET =            _BV(2),
+       _FLOAT_BYTE_ORDER_SET =         _BV(3),
+};
+
+enum {
+       _STRING_ENCODING_SET =          _BV(0),
+};
+
+enum {
+       _TRACE_MINOR_SET =              _BV(0),
+       _TRACE_MAJOR_SET =              _BV(1),
+       _TRACE_BYTE_ORDER_SET =         _BV(2),
+       _TRACE_UUID_SET =               _BV(3),
+       _TRACE_PACKET_HEADER_SET =      _BV(4),
+};
+
+enum {
+       _STREAM_ID_SET =                _BV(0),
+       _STREAM_PACKET_CONTEXT_SET =    _BV(1),
+       _STREAM_EVENT_HEADER_SET =      _BV(2),
+       _STREAM_EVENT_CONTEXT_SET =     _BV(3),
+};
+
+enum {
+       _EVENT_NAME_SET =               _BV(0),
+       _EVENT_ID_SET =                 _BV(1),
+       _EVENT_MODEL_EMF_URI_SET =      _BV(2),
+       _EVENT_STREAM_ID_SET =          _BV(3),
+       _EVENT_LOG_LEVEL_SET =          _BV(4),
+       _EVENT_CONTEXT_SET =            _BV(5),
+       _EVENT_FIELDS_SET =             _BV(6),
+};
+
+enum loglevel {
+        LOG_LEVEL_EMERG                  = 0,
+        LOG_LEVEL_ALERT                  = 1,
+        LOG_LEVEL_CRIT                   = 2,
+        LOG_LEVEL_ERR                    = 3,
+        LOG_LEVEL_WARNING                = 4,
+        LOG_LEVEL_NOTICE                 = 5,
+        LOG_LEVEL_INFO                   = 6,
+        LOG_LEVEL_DEBUG_SYSTEM           = 7,
+        LOG_LEVEL_DEBUG_PROGRAM          = 8,
+        LOG_LEVEL_DEBUG_PROCESS          = 9,
+        LOG_LEVEL_DEBUG_MODULE           = 10,
+        LOG_LEVEL_DEBUG_UNIT             = 11,
+        LOG_LEVEL_DEBUG_FUNCTION         = 12,
+        LOG_LEVEL_DEBUG_LINE             = 13,
+        LOG_LEVEL_DEBUG                  = 14,
+       _NR_LOGLEVELS                   = 15,
+};
+
+/* Prefixes of class aliases */
+#define _PREFIX_ALIAS                  'a'
+#define _PREFIX_ENUM                   'e'
+#define _PREFIX_STRUCT                 's'
+#define _PREFIX_VARIANT                        'v'
+
+/* First entry in a BT list */
+#define _BT_LIST_FIRST_ENTRY(_ptr, _class, _member)                    \
+       bt_list_entry((_ptr)->next, _class, _member)
+
+#define _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(_node, _attr, _entity)                     \
+       _BT_COMP_LOGE_APPEND_CAUSE_LINENO((_node)->lineno,                              \
+               "Duplicate attribute in %s: attr-name=\"%s\"",          \
+               _entity, _attr)
+
+#define _BT_COMP_LOGE_NODE(_node, _msg, args...)                               \
+       _BT_COMP_LOGE_LINENO((_node)->lineno, _msg, ## args)
+
+#define _BT_COMP_LOGE_APPEND_CAUSE_NODE(_node, _msg, args...)                          \
+       _BT_COMP_LOGE_APPEND_CAUSE_LINENO((_node)->lineno, _msg, ## args)
+
+#define _BT_COMP_LOGW_NODE(_node, _msg, args...)                               \
+       _BT_COMP_LOGW_LINENO((_node)->lineno, _msg, ## args)
+
+#define _BT_COMP_LOGT_NODE(_node, _msg, args...)                               \
+       _BT_COMP_LOGT_LINENO((_node)->lineno, _msg, ## args)
+
+/*
+ * Declaration scope of a visitor context. This represents a TSDL
+ * lexical scope, so that aliases and named structures, variants,
+ * and enumerations may be registered and looked up hierarchically.
+ */
+struct ctx_decl_scope {
+       /*
+        * Alias name to field class.
+        *
+        * GQuark -> struct ctf_field_class * (owned by this)
+        */
+       GHashTable *decl_map;
+
+       /* Parent scope; NULL if this is the root declaration scope */
+       struct ctx_decl_scope *parent_scope;
+};
+
+/*
+ * Visitor context (private).
+ */
+struct ctf_visitor_generate_ir {
+       struct meta_log_config log_cfg;
+
+       /* Trace IR trace class being filled (owned by this) */
+       bt_trace_class *trace_class;
+
+       /* CTF meta trace being filled (owned by this) */
+       struct ctf_trace_class *ctf_tc;
+
+       /* Current declaration scope (top of the stack) (owned by this) */
+       struct ctx_decl_scope *current_scope;
+
+       /* True if trace declaration is visited */
+       bool is_trace_visited;
+
+       /* True if this is an LTTng trace */
+       bool is_lttng;
+
+       /* Config passed by the user */
+       struct ctf_metadata_decoder_config decoder_config;
+};
+
+/*
+ * Visitor (public).
+ */
+struct ctf_visitor_generate_ir;
+
+/**
+ * Creates a new declaration scope.
+ *
+ * @param par_scope    Parent scope (NULL if creating a root scope)
+ * @returns            New declaration scope, or NULL on error
+ */
+static
+struct ctx_decl_scope *ctx_decl_scope_create(struct ctf_visitor_generate_ir *ctx,
+               struct ctx_decl_scope *par_scope)
+{
+       struct ctx_decl_scope *scope;
+
+       scope = g_new(struct ctx_decl_scope, 1);
+       if (!scope) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Failed to allocate one declaration scope.");
+               goto end;
+       }
+
+       scope->decl_map = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+               NULL, (GDestroyNotify) ctf_field_class_destroy);
+       scope->parent_scope = par_scope;
+
+end:
+       return scope;
+}
+
+/**
+ * Destroys a declaration scope.
+ *
+ * This function does not destroy the parent scope.
+ *
+ * @param scope        Scope to destroy
+ */
+static
+void ctx_decl_scope_destroy(struct ctx_decl_scope *scope)
+{
+       if (!scope) {
+               goto end;
+       }
+
+       g_hash_table_destroy(scope->decl_map);
+       g_free(scope);
+
+end:
+       return;
+}
+
+/**
+ * Returns the GQuark of a prefixed alias.
+ *
+ * @param prefix       Prefix character
+ * @param name         Name
+ * @returns            Associated GQuark, or 0 on error
+ */
+static
+GQuark get_prefixed_named_quark(struct ctf_visitor_generate_ir *ctx, char prefix,
+               const char *name)
+{
+       GQuark qname = 0;
+
+       BT_ASSERT(name);
+
+       /* Prefix character + original string + '\0' */
+       char *prname = g_new(char, strlen(name) + 2);
+       if (!prname) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Failed to allocate a string.");
+               goto end;
+       }
+
+       sprintf(prname, "%c%s", prefix, name);
+       qname = g_quark_from_string(prname);
+       g_free(prname);
+
+end:
+       return qname;
+}
+
+/**
+ * Looks up a prefixed class alias within a declaration scope.
+ *
+ * @param scope                Declaration scope
+ * @param prefix       Prefix character
+ * @param name         Alias name
+ * @param levels       Number of levels to dig into (-1 means infinite)
+ * @param copy         True to return a copy
+ * @returns            Declaration (owned by caller if \p copy is true),
+ *                     or NULL if not found
+ */
+static
+struct ctf_field_class *ctx_decl_scope_lookup_prefix_alias(
+               struct ctf_visitor_generate_ir *ctx, struct ctx_decl_scope *scope, char prefix,
+               const char *name, int levels, bool copy)
+{
+       GQuark qname = 0;
+       int cur_levels = 0;
+       struct ctf_field_class *decl = NULL;
+       struct ctx_decl_scope *cur_scope = scope;
+
+       BT_ASSERT(scope);
+       BT_ASSERT(name);
+       qname = get_prefixed_named_quark(ctx, prefix, name);
+       if (!qname) {
+               goto end;
+       }
+
+       if (levels < 0) {
+               levels = INT_MAX;
+       }
+
+       while (cur_scope && cur_levels < levels) {
+               decl = (ctf_field_class *) g_hash_table_lookup(cur_scope->decl_map,
+                       (gconstpointer) GUINT_TO_POINTER(qname));
+               if (decl) {
+                       /* Caller's reference */
+                       if (copy) {
+                               decl = ctf_field_class_copy(decl);
+                               BT_ASSERT(decl);
+                       }
+
+                       goto end;
+               }
+
+               cur_scope = cur_scope->parent_scope;
+               cur_levels++;
+       }
+
+end:
+       return decl;
+}
+
+/**
+ * Looks up a class alias within a declaration scope.
+ *
+ * @param scope                Declaration scope
+ * @param name         Alias name
+ * @param levels       Number of levels to dig into (-1 means infinite)
+ * @param copy         True to return a copy
+ * @returns            Declaration (owned by caller if \p copy is true),
+ *                     or NULL if not found
+ */
+static
+struct ctf_field_class *ctx_decl_scope_lookup_alias(struct ctf_visitor_generate_ir *ctx,
+               struct ctx_decl_scope *scope, const char *name, int levels,
+               bool copy)
+{
+       return ctx_decl_scope_lookup_prefix_alias(ctx, scope, _PREFIX_ALIAS,
+               name, levels, copy);
+}
+
+/**
+ * Looks up an enumeration within a declaration scope.
+ *
+ * @param scope                Declaration scope
+ * @param name         Enumeration name
+ * @param levels       Number of levels to dig into (-1 means infinite)
+ * @param copy         True to return a copy
+ * @returns            Declaration (owned by caller if \p copy is true),
+ *                     or NULL if not found
+ */
+static
+struct ctf_field_class_enum *ctx_decl_scope_lookup_enum(struct ctf_visitor_generate_ir *ctx,
+               struct ctx_decl_scope *scope, const char *name, int levels,
+               bool copy)
+{
+       return ctf_field_class_as_enum(ctx_decl_scope_lookup_prefix_alias(ctx, scope,
+               _PREFIX_ENUM, name, levels, copy));
+}
+
+/**
+ * Looks up a structure within a declaration scope.
+ *
+ * @param scope                Declaration scope
+ * @param name         Structure name
+ * @param levels       Number of levels to dig into (-1 means infinite)
+ * @param copy         True to return a copy
+ * @returns            Declaration (owned by caller if \p copy is true),
+ *                     or NULL if not found
+ */
+static
+struct ctf_field_class_struct *ctx_decl_scope_lookup_struct(struct ctf_visitor_generate_ir *ctx,
+               struct ctx_decl_scope *scope, const char *name, int levels,
+               bool copy)
+{
+       return ctf_field_class_as_struct(ctx_decl_scope_lookup_prefix_alias(ctx, scope,
+               _PREFIX_STRUCT, name, levels, copy));
+}
+
+/**
+ * Looks up a variant within a declaration scope.
+ *
+ * @param scope                Declaration scope
+ * @param name         Variant name
+ * @param levels       Number of levels to dig into (-1 means infinite)
+ * @param copy         True to return a copy
+ * @returns            Declaration (owned by caller if \p copy is true),
+ *                     or NULL if not found
+ */
+static
+struct ctf_field_class_variant *ctx_decl_scope_lookup_variant(struct ctf_visitor_generate_ir *ctx,
+               struct ctx_decl_scope *scope, const char *name, int levels,
+               bool copy)
+{
+       return ctf_field_class_as_variant(ctx_decl_scope_lookup_prefix_alias(ctx, scope,
+               _PREFIX_VARIANT, name, levels, copy));
+}
+
+/**
+ * Registers a prefixed class alias within a declaration scope.
+ *
+ * @param scope                Declaration scope
+ * @param prefix       Prefix character
+ * @param name         Alias name (non-NULL)
+ * @param decl         Field class to register (copied)
+ * @returns            0 if registration went okay, negative value otherwise
+ */
+static
+int ctx_decl_scope_register_prefix_alias(struct ctf_visitor_generate_ir *ctx,
+               struct ctx_decl_scope *scope, char prefix, const char *name,
+               struct ctf_field_class *decl)
+{
+       int ret = 0;
+       GQuark qname = 0;
+
+       BT_ASSERT(scope);
+       BT_ASSERT(name);
+       BT_ASSERT(decl);
+       qname = get_prefixed_named_quark(ctx, prefix, name);
+       if (!qname) {
+               ret = -ENOMEM;
+               goto end;
+       }
+
+       /* Make sure alias does not exist in local scope */
+       if (ctx_decl_scope_lookup_prefix_alias(ctx, scope, prefix, name, 1,
+                       false)) {
+               ret = -EEXIST;
+               goto end;
+       }
+
+       decl = ctf_field_class_copy(decl);
+       BT_ASSERT(decl);
+       g_hash_table_insert(scope->decl_map, GUINT_TO_POINTER(qname), decl);
+
+end:
+       return ret;
+}
+
+/**
+ * Registers a class alias within a declaration scope.
+ *
+ * @param scope        Declaration scope
+ * @param name Alias name (non-NULL)
+ * @param decl Field class to register (copied)
+ * @returns    0 if registration went okay, negative value otherwise
+ */
+static
+int ctx_decl_scope_register_alias(struct ctf_visitor_generate_ir *ctx,
+               struct ctx_decl_scope *scope, const char *name, struct ctf_field_class *decl)
+{
+       return ctx_decl_scope_register_prefix_alias(ctx, scope, _PREFIX_ALIAS,
+               name, decl);
+}
+
+/**
+ * Registers an enumeration declaration within a declaration scope.
+ *
+ * @param scope        Declaration scope
+ * @param name Enumeration name (non-NULL)
+ * @param decl Enumeration field class to register (copied)
+ * @returns    0 if registration went okay, negative value otherwise
+ */
+static
+int ctx_decl_scope_register_enum(struct ctf_visitor_generate_ir *ctx,
+               struct ctx_decl_scope *scope, const char *name,
+               struct ctf_field_class_enum *decl)
+{
+       return ctx_decl_scope_register_prefix_alias(ctx, scope, _PREFIX_ENUM,
+               name, &decl->base.base.base);
+}
+
+/**
+ * Registers a structure declaration within a declaration scope.
+ *
+ * @param scope        Declaration scope
+ * @param name Structure name (non-NULL)
+ * @param decl Structure field class to register (copied)
+ * @returns    0 if registration went okay, negative value otherwise
+ */
+static
+int ctx_decl_scope_register_struct(struct ctf_visitor_generate_ir *ctx,
+               struct ctx_decl_scope *scope, const char *name,
+               struct ctf_field_class_struct *decl)
+{
+       return ctx_decl_scope_register_prefix_alias(ctx, scope, _PREFIX_STRUCT,
+               name, &decl->base);
+}
+
+/**
+ * Registers a variant declaration within a declaration scope.
+ *
+ * @param scope        Declaration scope
+ * @param name Variant name (non-NULL)
+ * @param decl Variant field class to register
+ * @returns    0 if registration went okay, negative value otherwise
+ */
+static
+int ctx_decl_scope_register_variant(struct ctf_visitor_generate_ir *ctx,
+               struct ctx_decl_scope *scope, const char *name,
+               struct ctf_field_class_variant *decl)
+{
+       return ctx_decl_scope_register_prefix_alias(ctx, scope, _PREFIX_VARIANT,
+               name, &decl->base);
+}
+
+/**
+ * Destroys a visitor context.
+ *
+ * @param ctx  Visitor context to destroy
+ */
+static
+void ctx_destroy(struct ctf_visitor_generate_ir *ctx)
+{
+       struct ctx_decl_scope *scope;
+
+       if (!ctx) {
+               goto end;
+       }
+
+       scope = ctx->current_scope;
+
+       /*
+        * Destroy all scopes, from current one to the root scope.
+        */
+       while (scope) {
+               struct ctx_decl_scope *parent_scope = scope->parent_scope;
+
+               ctx_decl_scope_destroy(scope);
+               scope = parent_scope;
+       }
+
+       bt_trace_class_put_ref(ctx->trace_class);
+
+       if (ctx->ctf_tc) {
+               ctf_trace_class_destroy(ctx->ctf_tc);
+       }
+
+       g_free(ctx);
+
+end:
+       return;
+}
+
+/**
+ * Creates a new visitor context.
+ *
+ * @param trace        Associated trace
+ * @returns    New visitor context, or NULL on error
+ */
+static
+struct ctf_visitor_generate_ir *ctx_create(
+               const struct ctf_metadata_decoder_config *decoder_config)
+{
+       struct ctf_visitor_generate_ir *ctx = NULL;
+
+       BT_ASSERT(decoder_config);
+
+       ctx = g_new0(struct ctf_visitor_generate_ir, 1);
+       if (!ctx) {
+               BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, decoder_config->log_level,
+                       decoder_config->self_comp,
+                       "Failed to allocate one visitor context.");
+               goto error;
+       }
+
+       ctx->log_cfg.log_level = decoder_config->log_level;
+       ctx->log_cfg.self_comp = decoder_config->self_comp;
+       ctx->log_cfg.self_comp_class = decoder_config->self_comp_class;
+
+       if (decoder_config->self_comp) {
+               ctx->trace_class = bt_trace_class_create(
+                       decoder_config->self_comp);
+               if (!ctx->trace_class) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot create empty trace class.");
+                       goto error;
+               }
+       }
+
+       ctx->ctf_tc = ctf_trace_class_create();
+       if (!ctx->ctf_tc) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot create CTF trace class.");
+               goto error;
+       }
+
+       /* Root declaration scope */
+       ctx->current_scope = ctx_decl_scope_create(ctx, NULL);
+       if (!ctx->current_scope) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot create declaration scope.");
+               goto error;
+       }
+
+       ctx->decoder_config = *decoder_config;
+       goto end;
+
+error:
+       ctx_destroy(ctx);
+       ctx = NULL;
+
+end:
+       return ctx;
+}
+
+/**
+ * Pushes a new declaration scope on top of a visitor context's
+ * declaration scope stack.
+ *
+ * @param ctx  Visitor context
+ * @returns    0 on success, or a negative value on error
+ */
+static
+int ctx_push_scope(struct ctf_visitor_generate_ir *ctx)
+{
+       int ret = 0;
+       struct ctx_decl_scope *new_scope;
+
+       BT_ASSERT(ctx);
+       new_scope = ctx_decl_scope_create(ctx, ctx->current_scope);
+       if (!new_scope) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot create declaration scope.");
+               ret = -ENOMEM;
+               goto end;
+       }
+
+       ctx->current_scope = new_scope;
+
+end:
+       return ret;
+}
+
+static
+void ctx_pop_scope(struct ctf_visitor_generate_ir *ctx)
+{
+       struct ctx_decl_scope *parent_scope = NULL;
+
+       BT_ASSERT(ctx);
+
+       if (!ctx->current_scope) {
+               goto end;
+       }
+
+       parent_scope = ctx->current_scope->parent_scope;
+       ctx_decl_scope_destroy(ctx->current_scope);
+       ctx->current_scope = parent_scope;
+
+end:
+       return;
+}
+
+static
+int visit_field_class_specifier_list(struct ctf_visitor_generate_ir *ctx,
+               struct ctf_node *ts_list, struct ctf_field_class **decl);
+
+static
+int is_unary_string(struct bt_list_head *head)
+{
+       int ret = TRUE;
+       struct ctf_node *node;
+
+       bt_list_for_each_entry(node, head, siblings) {
+               if (node->type != NODE_UNARY_EXPRESSION) {
+                       ret = FALSE;
+               }
+
+               if (node->u.unary_expression.type != UNARY_STRING) {
+                       ret = FALSE;
+               }
+       }
+
+       return ret;
+}
+
+static
+const char *get_map_clock_name_value(struct bt_list_head *head)
+{
+       int i = 0;
+       struct ctf_node *node;
+       const char *name = NULL;
+
+       bt_list_for_each_entry(node, head, siblings) {
+               char *src_string;
+               int uexpr_type = node->u.unary_expression.type;
+               int uexpr_link = node->u.unary_expression.link;
+               int cond = node->type != NODE_UNARY_EXPRESSION ||
+                       uexpr_type != UNARY_STRING ||
+                       !((uexpr_link != UNARY_LINK_UNKNOWN) ^ (i == 0));
+               if (cond) {
+                       goto error;
+               }
+
+               /* Needs to be chained with . */
+               switch (node->u.unary_expression.link) {
+               case UNARY_DOTLINK:
+                       break;
+               case UNARY_ARROWLINK:
+               case UNARY_DOTDOTDOT:
+                       goto error;
+               default:
+                       break;
+               }
+
+               src_string = node->u.unary_expression.u.string;
+
+               switch (i) {
+               case 0:
+                       if (strcmp("clock", src_string)) {
+                               goto error;
+                       }
+                       break;
+               case 1:
+                       name = src_string;
+                       break;
+               case 2:
+                       if (strcmp("value", src_string)) {
+                               goto error;
+                       }
+                       break;
+               default:
+                       /* Extra identifier, unknown */
+                       goto error;
+               }
+
+               i++;
+       }
+
+       return name;
+
+error:
+       return NULL;
+}
+
+static
+int is_unary_unsigned(struct bt_list_head *head)
+{
+       int ret = TRUE;
+       struct ctf_node *node;
+
+       bt_list_for_each_entry(node, head, siblings) {
+               if (node->type != NODE_UNARY_EXPRESSION) {
+                       ret = FALSE;
+               }
+
+               if (node->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT) {
+                       ret = FALSE;
+               }
+       }
+
+       return ret;
+}
+
+static
+int get_unary_unsigned(struct ctf_visitor_generate_ir *ctx, struct bt_list_head *head,
+               uint64_t *value)
+{
+       int i = 0;
+       int ret = 0;
+       struct ctf_node *node;
+
+       *value = 0;
+
+       if (bt_list_empty(head)) {
+               ret = -1;
+               goto end;
+       }
+
+       bt_list_for_each_entry(node, head, siblings) {
+               int uexpr_type = node->u.unary_expression.type;
+               int uexpr_link = node->u.unary_expression.link;
+               int cond = node->type != NODE_UNARY_EXPRESSION ||
+                       uexpr_type != UNARY_UNSIGNED_CONSTANT ||
+                       uexpr_link != UNARY_LINK_UNKNOWN || i != 0;
+               if (cond) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Invalid constant unsigned integer.");
+                       ret = -EINVAL;
+                       goto end;
+               }
+
+               *value = node->u.unary_expression.u.unsigned_constant;
+               i++;
+       }
+
+end:
+       return ret;
+}
+
+static
+int is_unary_signed(struct bt_list_head *head)
+{
+       int ret = TRUE;
+       struct ctf_node *node;
+
+       bt_list_for_each_entry(node, head, siblings) {
+               if (node->type != NODE_UNARY_EXPRESSION) {
+                       ret = FALSE;
+               }
+
+               if (node->u.unary_expression.type != UNARY_SIGNED_CONSTANT) {
+                       ret = FALSE;
+               }
+       }
+
+       return ret;
+}
+
+static
+int get_unary_signed(struct bt_list_head *head, int64_t *value)
+{
+       int i = 0;
+       int ret = 0;
+       struct ctf_node *node;
+
+       bt_list_for_each_entry(node, head, siblings) {
+               int uexpr_type = node->u.unary_expression.type;
+               int uexpr_link = node->u.unary_expression.link;
+               int cond = node->type != NODE_UNARY_EXPRESSION ||
+                       (uexpr_type != UNARY_UNSIGNED_CONSTANT &&
+                               uexpr_type != UNARY_SIGNED_CONSTANT) ||
+                       uexpr_link != UNARY_LINK_UNKNOWN || i != 0;
+               if (cond) {
+                       ret = -EINVAL;
+                       goto end;
+               }
+
+               switch (uexpr_type) {
+               case UNARY_UNSIGNED_CONSTANT:
+                       *value = (int64_t)
+                               node->u.unary_expression.u.unsigned_constant;
+                       break;
+               case UNARY_SIGNED_CONSTANT:
+                       *value = node->u.unary_expression.u.signed_constant;
+                       break;
+               default:
+                       ret = -EINVAL;
+                       goto end;
+               }
+
+               i++;
+       }
+
+end:
+       return ret;
+}
+
+static
+int get_unary_uuid(struct ctf_visitor_generate_ir *ctx, struct bt_list_head *head,
+               bt_uuid_t uuid)
+{
+       return ctf_ast_get_unary_uuid(head, uuid, ctx->log_cfg.log_level,
+               ctx->log_cfg.self_comp);
+}
+
+static
+int get_boolean(struct ctf_visitor_generate_ir *ctx, struct ctf_node *unary_expr)
+{
+       int ret = 0;
+
+       if (unary_expr->type != NODE_UNARY_EXPRESSION) {
+               _BT_COMP_LOGE_APPEND_CAUSE_NODE(unary_expr,
+                       "Expecting unary expression: node-type=%d",
+                       unary_expr->type);
+               ret = -EINVAL;
+               goto end;
+       }
+
+       switch (unary_expr->u.unary_expression.type) {
+       case UNARY_UNSIGNED_CONSTANT:
+               ret = (unary_expr->u.unary_expression.u.unsigned_constant != 0);
+               break;
+       case UNARY_SIGNED_CONSTANT:
+               ret = (unary_expr->u.unary_expression.u.signed_constant != 0);
+               break;
+       case UNARY_STRING:
+       {
+               const char *str = unary_expr->u.unary_expression.u.string;
+
+               if (strcmp(str, "true") == 0 || strcmp(str, "TRUE") == 0) {
+                       ret = TRUE;
+               } else if (strcmp(str, "false") == 0 || strcmp(str, "FALSE") == 0) {
+                       ret = FALSE;
+               } else {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(unary_expr,
+                               "Unexpected boolean value: value=\"%s\"", str);
+                       ret = -EINVAL;
+                       goto end;
+               }
+               break;
+       }
+       default:
+               _BT_COMP_LOGE_APPEND_CAUSE_NODE(unary_expr,
+                       "Unexpected unary expression type: node-type=%d",
+                       unary_expr->u.unary_expression.type);
+               ret = -EINVAL;
+               goto end;
+       }
+
+end:
+       return ret;
+}
+
+static
+enum ctf_byte_order byte_order_from_unary_expr(struct ctf_visitor_generate_ir *ctx,
+               struct ctf_node *unary_expr)
+{
+       const char *str;
+       enum ctf_byte_order bo = CTF_BYTE_ORDER_UNKNOWN;
+
+       if (unary_expr->u.unary_expression.type != UNARY_STRING) {
+               _BT_COMP_LOGE_APPEND_CAUSE_NODE(unary_expr,
+                       "\"byte_order\" attribute: expecting `be`, `le`, `network`, or `native`.");
+               goto end;
+       }
+
+       str = unary_expr->u.unary_expression.u.string;
+
+       if (strcmp(str, "be") == 0 || strcmp(str, "network") == 0) {
+               bo = CTF_BYTE_ORDER_BIG;
+       } else if (strcmp(str, "le") == 0) {
+               bo = CTF_BYTE_ORDER_LITTLE;
+       } else if (strcmp(str, "native") == 0) {
+               bo = CTF_BYTE_ORDER_DEFAULT;
+       } else {
+               _BT_COMP_LOGE_APPEND_CAUSE_NODE(unary_expr,
+                       "Unexpected \"byte_order\" attribute value: "
+                       "expecting `be`, `le`, `network`, or `native`: value=\"%s\"",
+                       str);
+               goto end;
+       }
+
+end:
+       return bo;
+}
+
+static
+enum ctf_byte_order get_real_byte_order(struct ctf_visitor_generate_ir *ctx,
+               struct ctf_node *uexpr)
+{
+       enum ctf_byte_order bo = byte_order_from_unary_expr(ctx, uexpr);
+
+       if (bo == CTF_BYTE_ORDER_DEFAULT) {
+               bo = ctx->ctf_tc->default_byte_order;
+       }
+
+       return bo;
+}
+
+static
+int is_align_valid(uint64_t align)
+{
+       return (align != 0) && !(align & (align - UINT64_C(1)));
+}
+
+static
+int get_class_specifier_name(struct ctf_visitor_generate_ir *ctx,
+               struct ctf_node *cls_specifier, GString *str)
+{
+       int ret = 0;
+
+       if (cls_specifier->type != NODE_TYPE_SPECIFIER) {
+               _BT_COMP_LOGE_APPEND_CAUSE_NODE(cls_specifier,
+                       "Unexpected node type: node-type=%d",
+                       cls_specifier->type);
+               ret = -EINVAL;
+               goto end;
+       }
+
+       switch (cls_specifier->u.field_class_specifier.type) {
+       case TYPESPEC_VOID:
+               g_string_append(str, "void");
+               break;
+       case TYPESPEC_CHAR:
+               g_string_append(str, "char");
+               break;
+       case TYPESPEC_SHORT:
+               g_string_append(str, "short");
+               break;
+       case TYPESPEC_INT:
+               g_string_append(str, "int");
+               break;
+       case TYPESPEC_LONG:
+               g_string_append(str, "long");
+               break;
+       case TYPESPEC_FLOAT:
+               g_string_append(str, "float");
+               break;
+       case TYPESPEC_DOUBLE:
+               g_string_append(str, "double");
+               break;
+       case TYPESPEC_SIGNED:
+               g_string_append(str, "signed");
+               break;
+       case TYPESPEC_UNSIGNED:
+               g_string_append(str, "unsigned");
+               break;
+       case TYPESPEC_BOOL:
+               g_string_append(str, "bool");
+               break;
+       case TYPESPEC_COMPLEX:
+               g_string_append(str, "_Complex");
+               break;
+       case TYPESPEC_IMAGINARY:
+               g_string_append(str, "_Imaginary");
+               break;
+       case TYPESPEC_CONST:
+               g_string_append(str, "const");
+               break;
+       case TYPESPEC_ID_TYPE:
+               if (cls_specifier->u.field_class_specifier.id_type) {
+                       g_string_append(str,
+                               cls_specifier->u.field_class_specifier.id_type);
+               }
+               break;
+       case TYPESPEC_STRUCT:
+       {
+               struct ctf_node *node = cls_specifier->u.field_class_specifier.node;
+
+               if (!node->u._struct.name) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Unexpected empty structure field class name.");
+                       ret = -EINVAL;
+                       goto end;
+               }
+
+               g_string_append(str, "struct ");
+               g_string_append(str, node->u._struct.name);
+               break;
+       }
+       case TYPESPEC_VARIANT:
+       {
+               struct ctf_node *node = cls_specifier->u.field_class_specifier.node;
+
+               if (!node->u.variant.name) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Unexpected empty variant field class name.");
+                       ret = -EINVAL;
+                       goto end;
+               }
+
+               g_string_append(str, "variant ");
+               g_string_append(str, node->u.variant.name);
+               break;
+       }
+       case TYPESPEC_ENUM:
+       {
+               struct ctf_node *node = cls_specifier->u.field_class_specifier.node;
+
+               if (!node->u._enum.enum_id) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                               "Unexpected empty enumeration field class (`enum`) name.");
+                       ret = -EINVAL;
+                       goto end;
+               }
+
+               g_string_append(str, "enum ");
+               g_string_append(str, node->u._enum.enum_id);
+               break;
+       }
+       case TYPESPEC_FLOATING_POINT:
+       case TYPESPEC_INTEGER:
+       case TYPESPEC_STRING:
+       default:
+               _BT_COMP_LOGE_APPEND_CAUSE_NODE(cls_specifier->u.field_class_specifier.node,
+                       "Unexpected field class specifier type: %d",
+                       cls_specifier->u.field_class_specifier.type);
+               ret = -EINVAL;
+               goto end;
+       }
+
+end:
+       return ret;
+}
+
+static
+int get_class_specifier_list_name(struct ctf_visitor_generate_ir *ctx,
+               struct ctf_node *cls_specifier_list, GString *str)
+{
+       int ret = 0;
+       struct ctf_node *iter;
+       int alias_item_nr = 0;
+       struct bt_list_head *head =
+               &cls_specifier_list->u.field_class_specifier_list.head;
+
+       bt_list_for_each_entry(iter, head, siblings) {
+               if (alias_item_nr != 0) {
+                       g_string_append(str, " ");
+               }
+
+               alias_item_nr++;
+               ret = get_class_specifier_name(ctx, iter, str);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+end:
+       return ret;
+}
+
+static
+GQuark create_class_alias_identifier(struct ctf_visitor_generate_ir *ctx,
+               struct ctf_node *cls_specifier_list,
+               struct ctf_node *node_field_class_declarator)
+{
+       int ret;
+       char *str_c;
+       GString *str;
+       GQuark qalias = 0;
+       struct ctf_node *iter;
+       struct bt_list_head *pointers =
+               &node_field_class_declarator->u.field_class_declarator.pointers;
+
+       str = g_string_new("");
+       ret = get_class_specifier_list_name(ctx, cls_specifier_list, str);
+       if (ret) {
+               g_string_free(str, TRUE);
+               goto end;
+       }
+
+       bt_list_for_each_entry(iter, pointers, siblings) {
+               g_string_append(str, " *");
+
+               if (iter->u.pointer.const_qualifier) {
+                       g_string_append(str, " const");
+               }
+       }
+
+       str_c = g_string_free(str, FALSE);
+       qalias = g_quark_from_string(str_c);
+       g_free(str_c);
+
+end:
+       return qalias;
+}
+
+static
+int visit_field_class_declarator(struct ctf_visitor_generate_ir *ctx,
+               struct ctf_node *cls_specifier_list,
+               GQuark *field_name, struct ctf_node *node_field_class_declarator,
+               struct ctf_field_class **field_decl,
+               struct ctf_field_class *nested_decl)
+{
+       /*
+        * During this whole function, nested_decl is always OURS,
+        * whereas field_decl is an output which we create, but
+        * belongs to the caller (it is moved).
+        */
+       int ret = 0;
+       *field_decl = NULL;
+
+       /* Validate field class declarator node */
+       if (node_field_class_declarator) {
+               if (node_field_class_declarator->u.field_class_declarator.type ==
+                               TYPEDEC_UNKNOWN) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
+                               "Unexpected field class declarator type: type=%d",
+                               node_field_class_declarator->u.field_class_declarator.type);
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               /* TODO: GCC bitfields not supported yet */
+               if (node_field_class_declarator->u.field_class_declarator.bitfield_len !=
+                               NULL) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
+                               "GCC bitfields are not supported as of this version.");
+                       ret = -EPERM;
+                       goto error;
+               }
+       }
+
+       /* Find the right nested declaration if not provided */
+       if (!nested_decl) {
+               struct bt_list_head *pointers =
+                       &node_field_class_declarator->u.field_class_declarator.pointers;
+
+               if (node_field_class_declarator && !bt_list_empty(pointers)) {
+                       GQuark qalias;
+
+                       /*
+                        * If we have a pointer declarator, it HAS to
+                        * be present in the field class aliases (else
+                        * fail).
+                        */
+                       qalias = create_class_alias_identifier(ctx,
+                               cls_specifier_list, node_field_class_declarator);
+                       nested_decl =
+                               ctx_decl_scope_lookup_alias(ctx,
+                                       ctx->current_scope,
+                                       g_quark_to_string(qalias), -1, true);
+                       if (!nested_decl) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
+                                       "Cannot find class alias: name=\"%s\"",
+                                       g_quark_to_string(qalias));
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       if (nested_decl->type == CTF_FIELD_CLASS_TYPE_INT) {
+                               /* Pointer: force integer's base to 16 */
+                               struct ctf_field_class_int *int_fc =
+                                       ctf_field_class_as_int(nested_decl);
+
+                               int_fc->disp_base =
+                                       BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL;
+                       }
+               } else {
+                       ret = visit_field_class_specifier_list(ctx,
+                               cls_specifier_list, &nested_decl);
+                       if (ret) {
+                               BT_ASSERT(!nested_decl);
+                               goto error;
+                       }
+               }
+       }
+
+       BT_ASSERT(nested_decl);
+
+       if (!node_field_class_declarator) {
+               *field_decl = nested_decl;
+               nested_decl = NULL;
+               goto end;
+       }
+
+       if (node_field_class_declarator->u.field_class_declarator.type == TYPEDEC_ID) {
+               if (node_field_class_declarator->u.field_class_declarator.u.id) {
+                       const char *id =
+                               node_field_class_declarator->u.field_class_declarator.u.id;
+
+                       *field_name = g_quark_from_string(id);
+               } else {
+                       *field_name = 0;
+               }
+
+               *field_decl = nested_decl;
+               nested_decl = NULL;
+               goto end;
+       } else {
+               struct ctf_node *first;
+               struct ctf_field_class *decl = NULL;
+               struct ctf_field_class *outer_field_decl = NULL;
+               struct bt_list_head *length =
+                       &node_field_class_declarator->
+                               u.field_class_declarator.u.nested.length;
+
+               /* Create array/sequence, pass nested_decl as child */
+               if (bt_list_empty(length)) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
+                               "Expecting length field reference or value.");
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               first = _BT_LIST_FIRST_ENTRY(length, struct ctf_node, siblings);
+               if (first->type != NODE_UNARY_EXPRESSION) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(first,
+                               "Unexpected node type: node-type=%d",
+                               first->type);
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               switch (first->u.unary_expression.type) {
+               case UNARY_UNSIGNED_CONSTANT:
+               {
+                       struct ctf_field_class_array *array_decl = NULL;
+
+                       array_decl = ctf_field_class_array_create();
+                       BT_ASSERT(array_decl);
+                       array_decl->length =
+                               first->u.unary_expression.u.unsigned_constant;
+                       array_decl->base.elem_fc = nested_decl;
+                       nested_decl = NULL;
+                       decl = &array_decl->base.base;
+                       break;
+               }
+               case UNARY_STRING:
+               {
+                       /* Lookup unsigned integer definition, create seq. */
+                       struct ctf_field_class_sequence *seq_decl = NULL;
+                       char *length_name = ctf_ast_concatenate_unary_strings(length);
+
+                       if (!length_name) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
+                                       "Cannot concatenate unary strings.");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       if (strncmp(length_name, "env.", 4) == 0) {
+                               /* This is, in fact, an array */
+                               const char *env_entry_name = &length_name[4];
+                               struct ctf_trace_class_env_entry *env_entry =
+                                       ctf_trace_class_borrow_env_entry_by_name(
+                                               ctx->ctf_tc, env_entry_name);
+                               struct ctf_field_class_array *array_decl;
+
+                               if (!env_entry) {
+                                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
+                                               "Cannot find environment entry: "
+                                               "name=\"%s\"", env_entry_name);
+                                       ret = -EINVAL;
+                                       goto error;
+                               }
+
+                               if (env_entry->type != CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT) {
+                                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
+                                               "Wrong environment entry type "
+                                               "(expecting integer): "
+                                               "name=\"%s\"", env_entry_name);
+                                       ret = -EINVAL;
+                                       goto error;
+                               }
+
+                               if (env_entry->value.i < 0) {
+                                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
+                                               "Invalid, negative array length: "
+                                               "env-entry-name=\"%s\", "
+                                               "value=%" PRId64,
+                                               env_entry_name,
+                                               env_entry->value.i);
+                                       ret = -EINVAL;
+                                       goto error;
+                               }
+
+                               array_decl = ctf_field_class_array_create();
+                               BT_ASSERT(array_decl);
+                               array_decl->length =
+                                       (uint64_t) env_entry->value.i;
+                               array_decl->base.elem_fc = nested_decl;
+                               nested_decl = NULL;
+                               decl = &array_decl->base.base;
+                       } else {
+                               seq_decl = ctf_field_class_sequence_create();
+                               BT_ASSERT(seq_decl);
+                               seq_decl->base.elem_fc = nested_decl;
+                               nested_decl = NULL;
+                               g_string_assign(seq_decl->length_ref,
+                                       length_name);
+                               decl = &seq_decl->base.base;
+                       }
+
+                       g_free(length_name);
+                       break;
+               }
+               default:
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               BT_ASSERT(!nested_decl);
+               BT_ASSERT(decl);
+               BT_ASSERT(!*field_decl);
+
+               /*
+                * At this point, we found the next nested declaration.
+                * We currently own this (and lost the ownership of
+                * nested_decl in the meantime). Pass this next
+                * nested declaration as the content of the outer
+                * container, MOVING its ownership.
+                */
+               ret = visit_field_class_declarator(ctx, cls_specifier_list,
+                       field_name,
+                       node_field_class_declarator->
+                               u.field_class_declarator.u.nested.field_class_declarator,
+                       &outer_field_decl, decl);
+               decl = NULL;
+               if (ret) {
+                       BT_ASSERT(!outer_field_decl);
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               BT_ASSERT(outer_field_decl);
+               *field_decl = outer_field_decl;
+               outer_field_decl = NULL;
+       }
+
+       BT_ASSERT(*field_decl);
+       goto end;
+
+error:
+       ctf_field_class_destroy(*field_decl);
+       *field_decl = NULL;
+
+       if (ret >= 0) {
+               ret = -1;
+       }
+
+end:
+       ctf_field_class_destroy(nested_decl);
+       nested_decl = NULL;
+       return ret;
+}
+
+static
+int visit_struct_decl_field(struct ctf_visitor_generate_ir *ctx,
+               struct ctf_field_class_struct *struct_decl,
+               struct ctf_node *cls_specifier_list,
+               struct bt_list_head *field_class_declarators)
+{
+       int ret = 0;
+       struct ctf_node *iter;
+       struct ctf_field_class *field_decl = NULL;
+
+       bt_list_for_each_entry(iter, field_class_declarators, siblings) {
+               field_decl = NULL;
+               GQuark qfield_name;
+               const char *field_name;
+
+               ret = visit_field_class_declarator(ctx, cls_specifier_list,
+                       &qfield_name, iter, &field_decl, NULL);
+               if (ret) {
+                       BT_ASSERT(!field_decl);
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(cls_specifier_list,
+                               "Cannot visit field class declarator: ret=%d", ret);
+                       goto error;
+               }
+
+               BT_ASSERT(field_decl);
+               field_name = g_quark_to_string(qfield_name);
+
+               /* Check if field with same name already exists */
+               if (ctf_field_class_struct_borrow_member_by_name(
+                               struct_decl, field_name)) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(cls_specifier_list,
+                               "Duplicate field in structure field class: "
+                               "field-name=\"%s\"", field_name);
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               /* Add field to structure */
+               ctf_field_class_struct_append_member(struct_decl,
+                       field_name, field_decl);
+               field_decl = NULL;
+       }
+
+       return 0;
+
+error:
+       ctf_field_class_destroy(field_decl);
+       field_decl = NULL;
+       return ret;
+}
+
+static
+int visit_variant_decl_field(struct ctf_visitor_generate_ir *ctx,
+               struct ctf_field_class_variant *variant_decl,
+               struct ctf_node *cls_specifier_list,
+               struct bt_list_head *field_class_declarators)
+{
+       int ret = 0;
+       struct ctf_node *iter;
+       struct ctf_field_class *field_decl = NULL;
+
+       bt_list_for_each_entry(iter, field_class_declarators, siblings) {
+               field_decl = NULL;
+               GQuark qfield_name;
+               const char *field_name;
+
+               ret = visit_field_class_declarator(ctx, cls_specifier_list,
+                       &qfield_name, iter, &field_decl, NULL);
+               if (ret) {
+                       BT_ASSERT(!field_decl);
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(cls_specifier_list,
+                               "Cannot visit field class declarator: ret=%d", ret);
+                       goto error;
+               }
+
+               BT_ASSERT(field_decl);
+               field_name = g_quark_to_string(qfield_name);
+
+               /* Check if field with same name already exists */
+               if (ctf_field_class_variant_borrow_option_by_name(
+                               variant_decl, field_name)) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(cls_specifier_list,
+                               "Duplicate field in variant field class: "
+                               "field-name=\"%s\"", field_name);
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               /* Add field to structure */
+               ctf_field_class_variant_append_option(variant_decl,
+                       field_name, field_decl);
+               field_decl = NULL;
+       }
+
+       return 0;
+
+error:
+       ctf_field_class_destroy(field_decl);
+       field_decl = NULL;
+       return ret;
+}
+
+static
+int visit_field_class_def(struct ctf_visitor_generate_ir *ctx,
+               struct ctf_node *cls_specifier_list,
+               struct bt_list_head *field_class_declarators)
+{
+       int ret = 0;
+       GQuark qidentifier;
+       struct ctf_node *iter;
+       struct ctf_field_class *class_decl = NULL;
+
+       bt_list_for_each_entry(iter, field_class_declarators, siblings) {
+               ret = visit_field_class_declarator(ctx, cls_specifier_list,
+                       &qidentifier, iter, &class_decl, NULL);
+               if (ret) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
+                               "Cannot visit field class declarator: ret=%d", ret);
+                       ret = -EINVAL;
+                       goto end;
+               }
+
+               /* Do not allow field class def and alias of untagged variants */
+               if (class_decl->type == CTF_FIELD_CLASS_TYPE_VARIANT) {
+                       struct ctf_field_class_variant *var_fc =
+                               ctf_field_class_as_variant(class_decl);
+
+                       if (var_fc->tag_path.path->len == 0) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
+                                       "Type definition of untagged variant field class is not allowed.");
+                               ret = -EPERM;
+                               goto end;
+                       }
+               }
+
+               ret = ctx_decl_scope_register_alias(ctx, ctx->current_scope,
+                       g_quark_to_string(qidentifier), class_decl);
+               if (ret) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
+                               "Cannot register field class alias: name=\"%s\"",
+                               g_quark_to_string(qidentifier));
+                       goto end;
+               }
+       }
+
+end:
+       ctf_field_class_destroy(class_decl);
+       class_decl = NULL;
+       return ret;
+}
+
+static
+int visit_field_class_alias(struct ctf_visitor_generate_ir *ctx, struct ctf_node *target,
+               struct ctf_node *alias)
+{
+       int ret = 0;
+       GQuark qalias;
+       struct ctf_node *node;
+       GQuark qdummy_field_name;
+       struct ctf_field_class *class_decl = NULL;
+
+       /* Create target field class */
+       if (bt_list_empty(&target->u.field_class_alias_target.field_class_declarators)) {
+               node = NULL;
+       } else {
+               node = _BT_LIST_FIRST_ENTRY(
+                       &target->u.field_class_alias_target.field_class_declarators,
+                       struct ctf_node, siblings);
+       }
+
+       ret = visit_field_class_declarator(ctx,
+               target->u.field_class_alias_target.field_class_specifier_list,
+               &qdummy_field_name, node, &class_decl, NULL);
+       if (ret) {
+               BT_ASSERT(!class_decl);
+               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                       "Cannot visit field class declarator: ret=%d", ret);
+               goto end;
+       }
+
+       /* Do not allow field class def and alias of untagged variants */
+       if (class_decl->type == CTF_FIELD_CLASS_TYPE_VARIANT) {
+               struct ctf_field_class_variant *var_fc =
+                       ctf_field_class_as_variant(class_decl);
+
+               if (var_fc->tag_path.path->len == 0) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(target,
+                               "Type definition of untagged variant field class is not allowed.");
+                       ret = -EPERM;
+                       goto end;
+               }
+       }
+
+       /*
+        * The semantic validator does not check whether the target is
+        * abstract or not (if it has an identifier). Check it here.
+        */
+       if (qdummy_field_name != 0) {
+               _BT_COMP_LOGE_APPEND_CAUSE_NODE(target,
+                       "Expecting empty identifier: id=\"%s\"",
+                       g_quark_to_string(qdummy_field_name));
+               ret = -EINVAL;
+               goto end;
+       }
+
+       /* Create alias identifier */
+       node = _BT_LIST_FIRST_ENTRY(&alias->u.field_class_alias_name.field_class_declarators,
+               struct ctf_node, siblings);
+       qalias = create_class_alias_identifier(ctx,
+               alias->u.field_class_alias_name.field_class_specifier_list, node);
+       ret = ctx_decl_scope_register_alias(ctx, ctx->current_scope,
+               g_quark_to_string(qalias), class_decl);
+       if (ret) {
+               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                       "Cannot register class alias: name=\"%s\"",
+                       g_quark_to_string(qalias));
+               goto end;
+       }
+
+end:
+       ctf_field_class_destroy(class_decl);
+       class_decl = NULL;
+       return ret;
+}
+
+static
+int visit_struct_decl_entry(struct ctf_visitor_generate_ir *ctx, struct ctf_node *entry_node,
+               struct ctf_field_class_struct *struct_decl)
+{
+       int ret = 0;
+
+       switch (entry_node->type) {
+       case NODE_TYPEDEF:
+               ret = visit_field_class_def(ctx,
+                       entry_node->u.field_class_def.field_class_specifier_list,
+                       &entry_node->u.field_class_def.field_class_declarators);
+               if (ret) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+                               "Cannot add field class found in structure field class: ret=%d",
+                               ret);
+                       goto end;
+               }
+               break;
+       case NODE_TYPEALIAS:
+               ret = visit_field_class_alias(ctx, entry_node->u.field_class_alias.target,
+                       entry_node->u.field_class_alias.alias);
+               if (ret) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+                               "Cannot add field class alias found in structure field class: ret=%d",
+                               ret);
+                       goto end;
+               }
+               break;
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+               /* Field */
+               ret = visit_struct_decl_field(ctx, struct_decl,
+                       entry_node->u.struct_or_variant_declaration.
+                               field_class_specifier_list,
+                       &entry_node->u.struct_or_variant_declaration.
+                               field_class_declarators);
+               if (ret) {
+                       goto end;
+               }
+               break;
+       default:
+               _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+                       "Unexpected node type: node-type=%d", entry_node->type);
+               ret = -EINVAL;
+               goto end;
+       }
+
+end:
+       return ret;
+}
+
+static
+int visit_variant_decl_entry(struct ctf_visitor_generate_ir *ctx, struct ctf_node *entry_node,
+               struct ctf_field_class_variant *variant_decl)
+{
+       int ret = 0;
+
+       switch (entry_node->type) {
+       case NODE_TYPEDEF:
+               ret = visit_field_class_def(ctx,
+                       entry_node->u.field_class_def.field_class_specifier_list,
+                       &entry_node->u.field_class_def.field_class_declarators);
+               if (ret) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+                               "Cannot add field class found in variant field class: ret=%d",
+                               ret);
+                       goto end;
+               }
+               break;
+       case NODE_TYPEALIAS:
+               ret = visit_field_class_alias(ctx, entry_node->u.field_class_alias.target,
+                       entry_node->u.field_class_alias.alias);
+               if (ret) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+                               "Cannot add field class alias found in variant field class: ret=%d",
+                               ret);
+                       goto end;
+               }
+               break;
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+               /* Field */
+               ret = visit_variant_decl_field(ctx, variant_decl,
+                       entry_node->u.struct_or_variant_declaration.
+                               field_class_specifier_list,
+                       &entry_node->u.struct_or_variant_declaration.
+                               field_class_declarators);
+               if (ret) {
+                       goto end;
+               }
+               break;
+       default:
+               _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+                       "Unexpected node type: node-type=%d",
+                       entry_node->type);
+               ret = -EINVAL;
+               goto end;
+       }
+
+end:
+       return ret;
+}
+
+static
+int visit_struct_decl(struct ctf_visitor_generate_ir *ctx, const char *name,
+               struct bt_list_head *decl_list, int has_body,
+               struct bt_list_head *min_align,
+               struct ctf_field_class_struct **struct_decl)
+{
+       int ret = 0;
+
+       BT_ASSERT(struct_decl);
+       *struct_decl = NULL;
+
+       /* For named struct (without body), lookup in declaration scope */
+       if (!has_body) {
+               if (!name) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Bodyless structure field class: missing name.");
+                       ret = -EPERM;
+                       goto error;
+               }
+
+               *struct_decl = ctx_decl_scope_lookup_struct(ctx, ctx->current_scope,
+                       name, -1, true);
+               if (!*struct_decl) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot find structure field class: name=\"struct %s\"",
+                               name);
+                       ret = -EINVAL;
+                       goto error;
+               }
+       } else {
+               struct ctf_node *entry_node;
+               uint64_t min_align_value = 0;
+
+               if (name) {
+                       if (ctx_decl_scope_lookup_struct(ctx,
+                                       ctx->current_scope, name, 1, false)) {
+                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Structure field class already declared in local scope: "
+                                       "name=\"struct %s\"", name);
+                               ret = -EINVAL;
+                               goto error;
+                       }
+               }
+
+               if (!bt_list_empty(min_align)) {
+                       ret = get_unary_unsigned(ctx, min_align,
+                               &min_align_value);
+                       if (ret) {
+                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Unexpected unary expression for structure field class's `align` attribute: "
+                                       "ret=%d", ret);
+                               goto error;
+                       }
+               }
+
+               *struct_decl = ctf_field_class_struct_create();
+               BT_ASSERT(*struct_decl);
+
+               if (min_align_value != 0) {
+                       (*struct_decl)->base.alignment = min_align_value;
+               }
+
+               _TRY_PUSH_SCOPE_OR_GOTO_ERROR();
+
+               bt_list_for_each_entry(entry_node, decl_list, siblings) {
+                       ret = visit_struct_decl_entry(ctx, entry_node,
+                               *struct_decl);
+                       if (ret) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+                                       "Cannot visit structure field class entry: "
+                                       "ret=%d", ret);
+                               ctx_pop_scope(ctx);
+                               goto error;
+                       }
+               }
+
+               ctx_pop_scope(ctx);
+
+               if (name) {
+                       ret = ctx_decl_scope_register_struct(ctx,
+                               ctx->current_scope, name, *struct_decl);
+                       if (ret) {
+                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot register structure field class in declaration scope: "
+                                       "name=\"struct %s\", ret=%d", name, ret);
+                               goto error;
+                       }
+               }
+       }
+
+       return 0;
+
+error:
+       ctf_field_class_destroy(&(*struct_decl)->base);
+       *struct_decl = NULL;
+       return ret;
+}
+
+static
+int visit_variant_decl(struct ctf_visitor_generate_ir *ctx, const char *name,
+       const char *tag, struct bt_list_head *decl_list,
+       int has_body, struct ctf_field_class_variant **variant_decl)
+{
+       int ret = 0;
+       struct ctf_field_class_variant *untagged_variant_decl = NULL;
+
+       BT_ASSERT(variant_decl);
+       *variant_decl = NULL;
+
+       /* For named variant (without body), lookup in declaration scope */
+       if (!has_body) {
+               if (!name) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Bodyless variant field class: missing name.");
+                       ret = -EPERM;
+                       goto error;
+               }
+
+               untagged_variant_decl =
+                       ctx_decl_scope_lookup_variant(ctx, ctx->current_scope,
+                               name, -1, true);
+               if (!untagged_variant_decl) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot find variant field class: name=\"variant %s\"",
+                               name);
+                       ret = -EINVAL;
+                       goto error;
+               }
+       } else {
+               struct ctf_node *entry_node;
+
+               if (name) {
+                       if (ctx_decl_scope_lookup_variant(ctx,
+                                       ctx->current_scope, name, 1, false)) {
+                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Variant field class already declared in local scope: "
+                                       "name=\"variant %s\"", name);
+                               ret = -EINVAL;
+                               goto error;
+                       }
+               }
+
+               untagged_variant_decl = ctf_field_class_variant_create();
+               BT_ASSERT(untagged_variant_decl);
+               _TRY_PUSH_SCOPE_OR_GOTO_ERROR();
+
+               bt_list_for_each_entry(entry_node, decl_list, siblings) {
+                       ret = visit_variant_decl_entry(ctx, entry_node,
+                               untagged_variant_decl);
+                       if (ret) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+                                       "Cannot visit variant field class entry: "
+                                       "ret=%d", ret);
+                               ctx_pop_scope(ctx);
+                               goto error;
+                       }
+               }
+
+               ctx_pop_scope(ctx);
+
+               if (name) {
+                       ret = ctx_decl_scope_register_variant(ctx,
+                               ctx->current_scope, name,
+                               untagged_variant_decl);
+                       if (ret) {
+                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot register variant field class in declaration scope: "
+                                       "name=\"variant %s\", ret=%d", name, ret);
+                               goto error;
+                       }
+               }
+       }
+
+       /*
+        * If tagged, create tagged variant and return; otherwise
+        * return untagged variant.
+        */
+       if (!tag) {
+               *variant_decl = untagged_variant_decl;
+               untagged_variant_decl = NULL;
+       } else {
+               /*
+                * At this point, we have a fresh untagged variant; nobody
+                * else owns it. Set its tag now.
+                */
+               g_string_assign(untagged_variant_decl->tag_ref, tag);
+               *variant_decl = untagged_variant_decl;
+               untagged_variant_decl = NULL;
+       }
+
+       BT_ASSERT(!untagged_variant_decl);
+       BT_ASSERT(*variant_decl);
+       return 0;
+
+error:
+       ctf_field_class_destroy(&untagged_variant_decl->base);
+       untagged_variant_decl = NULL;
+       ctf_field_class_destroy(&(*variant_decl)->base);
+       *variant_decl = NULL;
+       return ret;
+}
+
+struct uori {
+       bool is_signed;
+       union {
+               uint64_t u;
+               uint64_t i;
+       } value;
+};
+
+static
+int visit_enum_decl_entry(struct ctf_visitor_generate_ir *ctx, struct ctf_node *enumerator,
+               struct ctf_field_class_enum *enum_decl, struct uori *last)
+{
+       int ret = 0;
+       int nr_vals = 0;
+       struct ctf_node *iter;
+       struct uori start = {
+               .is_signed = false,
+               .value = {
+                       .u = 0,
+               },
+       };
+       struct uori end = {
+               .is_signed = false,
+               .value = {
+                       .u = 0,
+               },
+       };
+       const char *label = enumerator->u.enumerator.id;
+       struct bt_list_head *values = &enumerator->u.enumerator.values;
+
+       bt_list_for_each_entry(iter, values, siblings) {
+               struct uori *target;
+
+               if (iter->type != NODE_UNARY_EXPRESSION) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
+                               "Wrong expression for enumeration field class label: "
+                               "node-type=%d, label=\"%s\"", iter->type,
+                               label);
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               if (nr_vals == 0) {
+                       target = &start;
+               } else {
+                       target = &end;
+               }
+
+               switch (iter->u.unary_expression.type) {
+               case UNARY_SIGNED_CONSTANT:
+                       target->is_signed = true;
+                       target->value.i =
+                               iter->u.unary_expression.u.signed_constant;
+                       break;
+               case UNARY_UNSIGNED_CONSTANT:
+                       target->is_signed = false;
+                       target->value.u =
+                               iter->u.unary_expression.u.unsigned_constant;
+                       break;
+               default:
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
+                               "Invalid enumeration field class entry: "
+                               "expecting constant signed or unsigned integer: "
+                               "node-type=%d, label=\"%s\"",
+                               iter->u.unary_expression.type, label);
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               if (nr_vals > 1) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
+                               "Invalid enumeration field class entry: label=\"%s\"",
+                               label);
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               nr_vals++;
+       }
+
+       if (nr_vals == 0) {
+               start = *last;
+       }
+
+       if (nr_vals <= 1) {
+               end = start;
+       }
+
+       if (end.is_signed) {
+               last->value.i = end.value.i + 1;
+       } else {
+               last->value.u = end.value.u + 1;
+       }
+
+       ctf_field_class_enum_map_range(enum_decl, label,
+               start.value.u, end.value.u);
+       return 0;
+
+error:
+       return ret;
+}
+
+static
+int visit_enum_decl(struct ctf_visitor_generate_ir *ctx, const char *name,
+               struct ctf_node *container_cls,
+               struct bt_list_head *enumerator_list,
+               int has_body, struct ctf_field_class_enum **enum_decl)
+{
+       int ret = 0;
+       GQuark qdummy_id;
+       struct ctf_field_class_int *integer_decl = NULL;
+
+       BT_ASSERT(enum_decl);
+       *enum_decl = NULL;
+
+       /* For named enum (without body), lookup in declaration scope */
+       if (!has_body) {
+               if (!name) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Bodyless enumeration field class: missing name.");
+                       ret = -EPERM;
+                       goto error;
+               }
+
+               *enum_decl = ctx_decl_scope_lookup_enum(ctx, ctx->current_scope,
+                       name, -1, true);
+               if (!*enum_decl) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot find enumeration field class: "
+                               "name=\"enum %s\"", name);
+                       ret = -EINVAL;
+                       goto error;
+               }
+       } else {
+               struct ctf_node *iter;
+               struct uori last_value = {
+                       .is_signed = false,
+                       .value = {
+                               .u = 0,
+                       },
+               };
+
+               if (name) {
+                       if (ctx_decl_scope_lookup_enum(ctx, ctx->current_scope,
+                                       name, 1, false)) {
+                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Enumeration field class already declared in local scope: "
+                                       "name=\"enum %s\"", name);
+                               ret = -EINVAL;
+                               goto error;
+                       }
+               }
+
+               if (!container_cls) {
+                       integer_decl = ctf_field_class_as_int(ctx_decl_scope_lookup_alias(ctx,
+                               ctx->current_scope, "int", -1, true));
+                       if (!integer_decl) {
+                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot find implicit `int` field class alias for enumeration field class.");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+               } else {
+                       ctf_field_class *decl;
+
+                       ret = visit_field_class_declarator(ctx, container_cls,
+                               &qdummy_id, NULL, &decl, NULL);
+                       if (ret) {
+                               BT_ASSERT(!decl);
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       integer_decl = ctf_field_class_as_int(decl);
+               }
+
+               BT_ASSERT(integer_decl);
+
+               if (integer_decl->base.base.type != CTF_FIELD_CLASS_TYPE_INT) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Container field class for enumeration field class is not an integer field class: "
+                               "fc-type=%d", integer_decl->base.base.type);
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               *enum_decl = ctf_field_class_enum_create();
+               BT_ASSERT(*enum_decl);
+               (*enum_decl)->base.base.base.alignment =
+                       integer_decl->base.base.alignment;
+               ctf_field_class_int_copy_content(
+                       &(*enum_decl)->base, integer_decl);
+               last_value.is_signed = (*enum_decl)->base.is_signed;
+
+               bt_list_for_each_entry(iter, enumerator_list, siblings) {
+                       ret = visit_enum_decl_entry(ctx, iter, *enum_decl,
+                               &last_value);
+                       if (ret) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
+                                       "Cannot visit enumeration field class entry: "
+                                       "ret=%d", ret);
+                               goto error;
+                       }
+               }
+
+               if (name) {
+                       ret = ctx_decl_scope_register_enum(ctx,
+                               ctx->current_scope, name, *enum_decl);
+                       if (ret) {
+                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot register enumeration field class in declaration scope: "
+                                       "ret=%d", ret);
+                               goto error;
+                       }
+               }
+       }
+
+       goto end;
+
+error:
+       ctf_field_class_destroy(&(*enum_decl)->base.base.base);
+       *enum_decl = NULL;
+
+end:
+       ctf_field_class_destroy(&integer_decl->base.base);
+       integer_decl = NULL;
+       return ret;
+}
+
+static
+int visit_field_class_specifier(struct ctf_visitor_generate_ir *ctx,
+               struct ctf_node *cls_specifier_list,
+               struct ctf_field_class **decl)
+{
+       int ret = 0;
+       GString *str = NULL;
+
+       *decl = NULL;
+       str = g_string_new("");
+       ret = get_class_specifier_list_name(ctx, cls_specifier_list, str);
+       if (ret) {
+               _BT_COMP_LOGE_APPEND_CAUSE_NODE(cls_specifier_list,
+                       "Cannot get field class specifier list's name: ret=%d", ret);
+               goto error;
+       }
+
+       *decl = ctx_decl_scope_lookup_alias(ctx, ctx->current_scope, str->str,
+               -1, true);
+       if (!*decl) {
+               _BT_COMP_LOGE_APPEND_CAUSE_NODE(cls_specifier_list,
+                       "Cannot find field class alias: name=\"%s\"", str->str);
+               ret = -EINVAL;
+               goto error;
+       }
+
+       goto end;
+
+error:
+       ctf_field_class_destroy(*decl);
+       *decl = NULL;
+
+end:
+       if (str) {
+               g_string_free(str, TRUE);
+       }
+
+       return ret;
+}
+
+static
+int visit_integer_decl(struct ctf_visitor_generate_ir *ctx,
+               struct bt_list_head *expressions,
+               struct ctf_field_class_int **integer_decl)
+{
+       int set = 0;
+       int ret = 0;
+       int signedness = 0;
+       struct ctf_node *expression;
+       uint64_t alignment = 0, size = 0;
+       struct ctf_clock_class *mapped_clock_class = NULL;
+       enum ctf_encoding encoding = CTF_ENCODING_NONE;
+       bt_field_class_integer_preferred_display_base base =
+               BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL;
+       enum ctf_byte_order byte_order = ctx->ctf_tc->default_byte_order;
+
+       *integer_decl = NULL;
+
+       bt_list_for_each_entry(expression, expressions, siblings) {
+               struct ctf_node *left, *right;
+
+               left = _BT_LIST_FIRST_ENTRY(&expression->u.ctf_expression.left,
+                       struct ctf_node, siblings);
+               right = _BT_LIST_FIRST_ENTRY(
+                       &expression->u.ctf_expression.right, struct ctf_node,
+                       siblings);
+
+               if (left->u.unary_expression.type != UNARY_STRING) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(left,
+                               "Unexpected unary expression type: type=%d",
+                               left->u.unary_expression.type);
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               if (strcmp(left->u.unary_expression.u.string, "signed") == 0) {
+                       if (_IS_SET(&set, _INTEGER_SIGNED_SET)) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "signed",
+                                       "integer field class");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       signedness = get_boolean(ctx, right);
+                       if (signedness < 0) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+                                       "Invalid boolean value for integer field class's `signed` attribute: "
+                                       "ret=%d", ret);
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       _SET(&set, _INTEGER_SIGNED_SET);
+               } else if (strcmp(left->u.unary_expression.u.string, "byte_order") == 0) {
+                       if (_IS_SET(&set, _INTEGER_BYTE_ORDER_SET)) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "byte_order",
+                                       "integer field class");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       byte_order = get_real_byte_order(ctx, right);
+                       if (byte_order == CTF_BYTE_ORDER_UNKNOWN) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+                                       "Invalid `byte_order` attribute in integer field class: "
+                                       "ret=%d", ret);
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       _SET(&set, _INTEGER_BYTE_ORDER_SET);
+               } else if (strcmp(left->u.unary_expression.u.string, "size") == 0) {
+                       if (_IS_SET(&set, _INTEGER_SIZE_SET)) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "size",
+                                       "integer field class");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       if (right->u.unary_expression.type !=
+                                       UNARY_UNSIGNED_CONSTANT) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+                                       "Invalid `size` attribute in integer field class: "
+                                       "expecting unsigned constant integer: "
+                                       "node-type=%d",
+                                       right->u.unary_expression.type);
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       size = right->u.unary_expression.u.unsigned_constant;
+                       if (size == 0) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+                                       "Invalid `size` attribute in integer field class: "
+                                       "expecting positive constant integer: "
+                                       "size=%" PRIu64, size);
+                               ret = -EINVAL;
+                               goto error;
+                       } else if (size > 64) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+                                       "Invalid `size` attribute in integer field class: "
+                                       "integer fields over 64 bits are not supported as of this version: "
+                                       "size=%" PRIu64, size);
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       _SET(&set, _INTEGER_SIZE_SET);
+               } else if (strcmp(left->u.unary_expression.u.string, "align") == 0) {
+                       if (_IS_SET(&set, _INTEGER_ALIGN_SET)) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "align",
+                                       "integer field class");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       if (right->u.unary_expression.type !=
+                                       UNARY_UNSIGNED_CONSTANT) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+                                       "Invalid `align` attribute in integer field class: "
+                                       "expecting unsigned constant integer: "
+                                       "node-type=%d",
+                                       right->u.unary_expression.type);
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       alignment =
+                               right->u.unary_expression.u.unsigned_constant;
+                       if (!is_align_valid(alignment)) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+                                       "Invalid `align` attribute in integer field class: "
+                                       "expecting power of two: "
+                                       "align=%" PRIu64, alignment);
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       _SET(&set, _INTEGER_ALIGN_SET);
+               } else if (strcmp(left->u.unary_expression.u.string, "base") == 0) {
+                       if (_IS_SET(&set, _INTEGER_BASE_SET)) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "base",
+                                       "integer field class");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       switch (right->u.unary_expression.type) {
+                       case UNARY_UNSIGNED_CONSTANT:
+                       {
+                               uint64_t constant = right->u.unary_expression.
+                                       u.unsigned_constant;
+
+                               switch (constant) {
+                               case 2:
+                                       base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_BINARY;
+                                       break;
+                               case 8:
+                                       base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL;
+                                       break;
+                               case 10:
+                                       base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL;
+                                       break;
+                               case 16:
+                                       base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL;
+                                       break;
+                               default:
+                                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+                                               "Invalid `base` attribute in integer field class: "
+                                               "base=%" PRIu64,
+                                               right->u.unary_expression.u.unsigned_constant);
+                                       ret = -EINVAL;
+                                       goto error;
+                               }
+                               break;
+                       }
+                       case UNARY_STRING:
+                       {
+                               char *s_right = ctf_ast_concatenate_unary_strings(
+                                       &expression->u.ctf_expression.right);
+                               if (!s_right) {
+                                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+                                               "Unexpected unary expression for integer field class's `base` attribute.");
+                                       ret = -EINVAL;
+                                       goto error;
+                               }
+
+                               if (strcmp(s_right, "decimal") == 0 ||
+                                               strcmp(s_right, "dec") == 0 ||
+                                               strcmp(s_right, "d") == 0 ||
+                                               strcmp(s_right, "i") == 0 ||
+                                               strcmp(s_right, "u") == 0) {
+                                       base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL;
+                               } else if (strcmp(s_right, "hexadecimal") == 0 ||
+                                               strcmp(s_right, "hex") == 0 ||
+                                               strcmp(s_right, "x") == 0 ||
+                                               strcmp(s_right, "X") == 0 ||
+                                               strcmp(s_right, "p") == 0) {
+                                       base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL;
+                               } else if (strcmp(s_right, "octal") == 0 ||
+                                               strcmp(s_right, "oct") == 0 ||
+                                               strcmp(s_right, "o") == 0) {
+                                       base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL;
+                               } else if (strcmp(s_right, "binary") == 0 ||
+                                               strcmp(s_right, "b") == 0) {
+                                       base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_BINARY;
+                               } else {
+                                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+                                               "Unexpected unary expression for integer field class's `base` attribute: "
+                                               "base=\"%s\"", s_right);
+                                       g_free(s_right);
+                                       ret = -EINVAL;
+                                       goto error;
+                               }
+
+                               g_free(s_right);
+                               break;
+                       }
+                       default:
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+                                       "Invalid `base` attribute in integer field class: "
+                                       "expecting unsigned constant integer or unary string.");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       _SET(&set, _INTEGER_BASE_SET);
+               } else if (strcmp(left->u.unary_expression.u.string, "encoding") == 0) {
+                       char *s_right;
+
+                       if (_IS_SET(&set, _INTEGER_ENCODING_SET)) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "encoding",
+                                       "integer field class");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       if (right->u.unary_expression.type != UNARY_STRING) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+                                       "Invalid `encoding` attribute in integer field class: "
+                                       "expecting unary string.");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       s_right = ctf_ast_concatenate_unary_strings(
+                               &expression->u.ctf_expression.right);
+                       if (!s_right) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+                                       "Unexpected unary expression for integer field class's `encoding` attribute.");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       if (strcmp(s_right, "UTF8") == 0 ||
+                                       strcmp(s_right, "utf8") == 0 ||
+                                       strcmp(s_right, "utf-8") == 0 ||
+                                       strcmp(s_right, "UTF-8") == 0 ||
+                                       strcmp(s_right, "ASCII") == 0 ||
+                                       strcmp(s_right, "ascii") == 0) {
+                               encoding = CTF_ENCODING_UTF8;
+                       } else if (strcmp(s_right, "none") == 0) {
+                               encoding = CTF_ENCODING_NONE;
+                       } else {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+                                       "Invalid `encoding` attribute in integer field class: "
+                                       "unknown encoding: encoding=\"%s\"",
+                                       s_right);
+                               g_free(s_right);
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       g_free(s_right);
+                       _SET(&set, _INTEGER_ENCODING_SET);
+               } else if (strcmp(left->u.unary_expression.u.string, "map") == 0) {
+                       const char *clock_name;
+
+                       if (_IS_SET(&set, _INTEGER_MAP_SET)) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "map",
+                                       "integer field class");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       if (right->u.unary_expression.type != UNARY_STRING) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+                                       "Invalid `map` attribute in integer field class: "
+                                       "expecting unary string.");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       clock_name =
+                               get_map_clock_name_value(
+                                       &expression->u.ctf_expression.right);
+                       if (!clock_name) {
+                               char *s_right = ctf_ast_concatenate_unary_strings(
+                                       &expression->u.ctf_expression.right);
+
+                               if (!s_right) {
+                                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+                                               "Unexpected unary expression for integer field class's `map` attribute.");
+                                       ret = -EINVAL;
+                                       goto error;
+                               }
+
+                               _BT_COMP_LOGE_NODE(right,
+                                       "Invalid `map` attribute in integer field class: "
+                                       "cannot find clock class at this point: name=\"%s\"",
+                                       s_right);
+                               _SET(&set, _INTEGER_MAP_SET);
+                               g_free(s_right);
+                               continue;
+                       }
+
+                       mapped_clock_class =
+                               ctf_trace_class_borrow_clock_class_by_name(
+                                       ctx->ctf_tc, clock_name);
+                       if (!mapped_clock_class) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+                                       "Invalid `map` attribute in integer field class: "
+                                       "cannot find clock class at this point: name=\"%s\"",
+                                       clock_name);
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       _SET(&set, _INTEGER_MAP_SET);
+               } else {
+                       _BT_COMP_LOGW_NODE(left,
+                               "Unknown attribute in integer field class: "
+                               "attr-name=\"%s\"",
+                               left->u.unary_expression.u.string);
+               }
+       }
+
+       if (!_IS_SET(&set, _INTEGER_SIZE_SET)) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Missing `size` attribute in integer field class.");
+               ret = -EPERM;
+               goto error;
+       }
+
+       if (!_IS_SET(&set, _INTEGER_ALIGN_SET)) {
+               if (size % CHAR_BIT) {
+                       /* Bit-packed alignment */
+                       alignment = 1;
+               } else {
+                       /* Byte-packed alignment */
+                       alignment = CHAR_BIT;
+               }
+       }
+
+       *integer_decl = ctf_field_class_int_create();
+       BT_ASSERT(*integer_decl);
+       (*integer_decl)->base.base.alignment = alignment;
+       (*integer_decl)->base.byte_order = byte_order;
+       (*integer_decl)->base.size = size;
+       (*integer_decl)->is_signed = (signedness > 0);
+       (*integer_decl)->disp_base = base;
+       (*integer_decl)->encoding = encoding;
+       (*integer_decl)->mapped_clock_class = mapped_clock_class;
+       return 0;
+
+error:
+       ctf_field_class_destroy(&(*integer_decl)->base.base);
+       *integer_decl = NULL;
+       return ret;
+}
+
+static
+int visit_floating_point_number_decl(struct ctf_visitor_generate_ir *ctx,
+               struct bt_list_head *expressions,
+               struct ctf_field_class_float **float_decl)
+{
+       int set = 0;
+       int ret = 0;
+       struct ctf_node *expression;
+       uint64_t alignment = 1, exp_dig = 0, mant_dig = 0;
+       enum ctf_byte_order byte_order = ctx->ctf_tc->default_byte_order;
+
+       *float_decl = NULL;
+
+       bt_list_for_each_entry(expression, expressions, siblings) {
+               struct ctf_node *left, *right;
+
+               left = _BT_LIST_FIRST_ENTRY(&expression->u.ctf_expression.left,
+                       struct ctf_node, siblings);
+               right = _BT_LIST_FIRST_ENTRY(
+                       &expression->u.ctf_expression.right, struct ctf_node,
+                       siblings);
+
+               if (left->u.unary_expression.type != UNARY_STRING) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(left,
+                               "Unexpected unary expression type: type=%d",
+                               left->u.unary_expression.type);
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               if (strcmp(left->u.unary_expression.u.string, "byte_order") == 0) {
+                       if (_IS_SET(&set, _FLOAT_BYTE_ORDER_SET)) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "byte_order",
+                                       "floating point number field class");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       byte_order = get_real_byte_order(ctx, right);
+                       if (byte_order == CTF_BYTE_ORDER_UNKNOWN) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+                                       "Invalid `byte_order` attribute in floating point number field class: "
+                                       "ret=%d", ret);
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       _SET(&set, _FLOAT_BYTE_ORDER_SET);
+               } else if (strcmp(left->u.unary_expression.u.string, "exp_dig") == 0) {
+                       if (_IS_SET(&set, _FLOAT_EXP_DIG_SET)) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "exp_dig",
+                                       "floating point number field class");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       if (right->u.unary_expression.type !=
+                                       UNARY_UNSIGNED_CONSTANT) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+                                       "Invalid `exp_dig` attribute in floating point number field class: "
+                                       "expecting unsigned constant integer: "
+                                       "node-type=%d",
+                                       right->u.unary_expression.type);
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       exp_dig = right->u.unary_expression.u.unsigned_constant;
+                       _SET(&set, _FLOAT_EXP_DIG_SET);
+               } else if (strcmp(left->u.unary_expression.u.string, "mant_dig") == 0) {
+                       if (_IS_SET(&set, _FLOAT_MANT_DIG_SET)) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "mant_dig",
+                                       "floating point number field class");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       if (right->u.unary_expression.type !=
+                                       UNARY_UNSIGNED_CONSTANT) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+                                       "Invalid `mant_dig` attribute in floating point number field class: "
+                                       "expecting unsigned constant integer: "
+                                       "node-type=%d",
+                                       right->u.unary_expression.type);
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       mant_dig = right->u.unary_expression.u.
+                               unsigned_constant;
+                       _SET(&set, _FLOAT_MANT_DIG_SET);
+               } else if (strcmp(left->u.unary_expression.u.string, "align") == 0) {
+                       if (_IS_SET(&set, _FLOAT_ALIGN_SET)) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "align",
+                                       "floating point number field class");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       if (right->u.unary_expression.type !=
+                                       UNARY_UNSIGNED_CONSTANT) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+                                       "Invalid `align` attribute in floating point number field class: "
+                                       "expecting unsigned constant integer: "
+                                       "node-type=%d",
+                                       right->u.unary_expression.type);
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       alignment = right->u.unary_expression.u.
+                               unsigned_constant;
+
+                       if (!is_align_valid(alignment)) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+                                       "Invalid `align` attribute in floating point number field class: "
+                                       "expecting power of two: "
+                                       "align=%" PRIu64, alignment);
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       _SET(&set, _FLOAT_ALIGN_SET);
+               } else {
+                       _BT_COMP_LOGW_NODE(left,
+                               "Unknown attribute in floating point number field class: "
+                               "attr-name=\"%s\"",
+                               left->u.unary_expression.u.string);
+               }
+       }
+
+       if (!_IS_SET(&set, _FLOAT_MANT_DIG_SET)) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Missing `mant_dig` attribute in floating point number field class.");
+               ret = -EPERM;
+               goto error;
+       }
+
+       if (!_IS_SET(&set, _FLOAT_EXP_DIG_SET)) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Missing `exp_dig` attribute in floating point number field class.");
+               ret = -EPERM;
+               goto error;
+       }
+
+       if (mant_dig != 24 && mant_dig != 53) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("`mant_dig` attribute: expecting 24 or 53.");
+               ret = -EPERM;
+               goto error;
+       }
+
+       if (mant_dig == 24 && exp_dig != 8) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("`exp_dig` attribute: expecting 8 because `mant_dig` is 24.");
+               ret = -EPERM;
+               goto error;
+       }
+
+       if (mant_dig == 53 && exp_dig != 11) {
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("`exp_dig` attribute: expecting 11 because `mant_dig` is 53.");
+               ret = -EPERM;
+               goto error;
+       }
+
+       if (!_IS_SET(&set, _INTEGER_ALIGN_SET)) {
+               if ((mant_dig + exp_dig) % CHAR_BIT) {
+                       /* Bit-packed alignment */
+                       alignment = 1;
+               } else {
+                       /* Byte-packed alignment */
+                       alignment = CHAR_BIT;
+               }
+       }
+
+       *float_decl = ctf_field_class_float_create();
+       BT_ASSERT(*float_decl);
+       (*float_decl)->base.base.alignment = alignment;
+       (*float_decl)->base.byte_order = byte_order;
+       (*float_decl)->base.size = mant_dig + exp_dig;
+       return 0;
+
+error:
+       ctf_field_class_destroy(&(*float_decl)->base.base);
+       *float_decl = NULL;
+       return ret;
+}
+
+static
+int visit_string_decl(struct ctf_visitor_generate_ir *ctx,
+               struct bt_list_head *expressions,
+               struct ctf_field_class_string **string_decl)
+{
+       int set = 0;
+       int ret = 0;
+       struct ctf_node *expression;
+       enum ctf_encoding encoding = CTF_ENCODING_UTF8;
+
+       *string_decl = NULL;
+
+       bt_list_for_each_entry(expression, expressions, siblings) {
+               struct ctf_node *left, *right;
+
+               left = _BT_LIST_FIRST_ENTRY(&expression->u.ctf_expression.left,
+                       struct ctf_node, siblings);
+               right = _BT_LIST_FIRST_ENTRY(
+                       &expression->u.ctf_expression.right, struct ctf_node,
+                       siblings);
+
+               if (left->u.unary_expression.type != UNARY_STRING) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(left,
+                               "Unexpected unary expression type: type=%d",
+                               left->u.unary_expression.type);
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               if (strcmp(left->u.unary_expression.u.string, "encoding") == 0) {
+                       char *s_right;
+
+                       if (_IS_SET(&set, _STRING_ENCODING_SET)) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "encoding",
+                                       "string field class");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       if (right->u.unary_expression.type != UNARY_STRING) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+                                       "Invalid `encoding` attribute in string field class: "
+                                       "expecting unary string.");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       s_right = ctf_ast_concatenate_unary_strings(
+                               &expression->u.ctf_expression.right);
+                       if (!s_right) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+                                       "Unexpected unary expression for string field class's `encoding` attribute.");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       if (strcmp(s_right, "UTF8") == 0 ||
+                                       strcmp(s_right, "utf8") == 0 ||
+                                       strcmp(s_right, "utf-8") == 0 ||
+                                       strcmp(s_right, "UTF-8") == 0 ||
+                                       strcmp(s_right, "ASCII") == 0 ||
+                                       strcmp(s_right, "ascii") == 0) {
+                               encoding = CTF_ENCODING_UTF8;
+                       } else if (strcmp(s_right, "none") == 0) {
+                               encoding = CTF_ENCODING_NONE;
+                       } else {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+                                       "Invalid `encoding` attribute in string field class: "
+                                       "unknown encoding: encoding=\"%s\"",
+                                       s_right);
+                               g_free(s_right);
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       g_free(s_right);
+                       _SET(&set, _STRING_ENCODING_SET);
+               } else {
+                       _BT_COMP_LOGW_NODE(left,
+                               "Unknown attribute in string field class: "
+                               "attr-name=\"%s\"",
+                               left->u.unary_expression.u.string);
+               }
+       }
+
+       *string_decl = ctf_field_class_string_create();
+       BT_ASSERT(*string_decl);
+       (*string_decl)->encoding = encoding;
+       return 0;
+
+error:
+       ctf_field_class_destroy(&(*string_decl)->base);
+       *string_decl = NULL;
+       return ret;
+}
+
+static
+int visit_field_class_specifier_list(struct ctf_visitor_generate_ir *ctx,
+               struct ctf_node *ts_list, struct ctf_field_class **decl)
+{
+       int ret = 0;
+       struct ctf_node *first, *node;
+
+       *decl = NULL;
+
+       if (ts_list->type != NODE_TYPE_SPECIFIER_LIST) {
+               _BT_COMP_LOGE_APPEND_CAUSE_NODE(ts_list,
+                       "Unexpected node type: node-type=%d", ts_list->type);
+               ret = -EINVAL;
+               goto error;
+       }
+
+       first = _BT_LIST_FIRST_ENTRY(&ts_list->u.field_class_specifier_list.head,
+               struct ctf_node, siblings);
+       if (first->type != NODE_TYPE_SPECIFIER) {
+               _BT_COMP_LOGE_APPEND_CAUSE_NODE(first,
+                       "Unexpected node type: node-type=%d", first->type);
+               ret = -EINVAL;
+               goto error;
+       }
+
+       node = first->u.field_class_specifier.node;
+
+       switch (first->u.field_class_specifier.type) {
+       case TYPESPEC_INTEGER: {
+               ctf_field_class_int *int_decl;
+
+               ret = visit_integer_decl(ctx, &node->u.integer.expressions,
+                       &int_decl);
+               if (ret) {
+                       BT_ASSERT(!int_decl);
+                       goto error;
+               }
+
+               *decl = &int_decl->base.base;
+               break;
+       }
+       case TYPESPEC_FLOATING_POINT: {
+               ctf_field_class_float *float_decl;
+
+               ret = visit_floating_point_number_decl(ctx,
+                       &node->u.floating_point.expressions, &float_decl);
+               if (ret) {
+                       BT_ASSERT(!float_decl);
+                       goto error;
+               }
+
+               *decl = &float_decl->base.base;
+               break;
+       }
+       case TYPESPEC_STRING: {
+               ctf_field_class_string *string_decl;
+
+               ret = visit_string_decl(ctx,
+                       &node->u.string.expressions, &string_decl);
+               if (ret) {
+                       BT_ASSERT(!string_decl);
+                       goto error;
+               }
+
+               *decl = &string_decl->base;
+               break;
+       }
+       case TYPESPEC_STRUCT: {
+               ctf_field_class_struct *struct_decl;
+
+               ret = visit_struct_decl(ctx, node->u._struct.name,
+                       &node->u._struct.declaration_list,
+                       node->u._struct.has_body,
+                       &node->u._struct.min_align, &struct_decl);
+               if (ret) {
+                       BT_ASSERT(!struct_decl);
+                       goto error;
+               }
+
+               *decl = &struct_decl->base;
+               break;
+       }
+       case TYPESPEC_VARIANT: {
+               ctf_field_class_variant *variant_decl;
+
+               ret = visit_variant_decl(ctx, node->u.variant.name,
+                       node->u.variant.choice,
+                       &node->u.variant.declaration_list,
+                       node->u.variant.has_body, &variant_decl);
+               if (ret) {
+                       BT_ASSERT(!variant_decl);
+                       goto error;
+               }
+
+               *decl = &variant_decl->base;
+               break;
+       }
+       case TYPESPEC_ENUM: {
+               ctf_field_class_enum *enum_decl;
+
+               ret = visit_enum_decl(ctx, node->u._enum.enum_id,
+                       node->u._enum.container_field_class,
+                       &node->u._enum.enumerator_list,
+                       node->u._enum.has_body, &enum_decl);
+               if (ret) {
+                       BT_ASSERT(!enum_decl);
+                       goto error;
+               }
+
+               *decl = &enum_decl->base.base.base;
+               break;
+       }
+       case TYPESPEC_VOID:
+       case TYPESPEC_CHAR:
+       case TYPESPEC_SHORT:
+       case TYPESPEC_INT:
+       case TYPESPEC_LONG:
+       case TYPESPEC_FLOAT:
+       case TYPESPEC_DOUBLE:
+       case TYPESPEC_SIGNED:
+       case TYPESPEC_UNSIGNED:
+       case TYPESPEC_BOOL:
+       case TYPESPEC_COMPLEX:
+       case TYPESPEC_IMAGINARY:
+       case TYPESPEC_CONST:
+       case TYPESPEC_ID_TYPE:
+               ret = visit_field_class_specifier(ctx, ts_list, decl);
+               if (ret) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(first,
+                               "Cannot visit field class specifier: ret=%d",
+                               ret);
+                       BT_ASSERT(!*decl);
+                       goto error;
+               }
+               break;
+       default:
+               _BT_COMP_LOGE_APPEND_CAUSE_NODE(first,
+                       "Unexpected field class specifier type: node-type=%d",
+                       first->u.field_class_specifier.type);
+               ret = -EINVAL;
+               goto error;
+       }
+
+       BT_ASSERT(*decl);
+       return 0;
+
+error:
+       ctf_field_class_destroy(*decl);
+       *decl = NULL;
+       return ret;
+}
+
+static
+int visit_event_decl_entry(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node,
+               struct ctf_event_class *event_class, uint64_t *stream_id,
+               int *set)
+{
+       int ret = 0;
+       char *left = NULL;
+
+       switch (node->type) {
+       case NODE_TYPEDEF:
+               ret = visit_field_class_def(ctx, node->u.field_class_def.field_class_specifier_list,
+                       &node->u.field_class_def.field_class_declarators);
+               if (ret) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                               "Cannot add field class found in event class.");
+                       goto error;
+               }
+               break;
+       case NODE_TYPEALIAS:
+               ret = visit_field_class_alias(ctx, node->u.field_class_alias.target,
+                       node->u.field_class_alias.alias);
+               if (ret) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                               "Cannot add field class alias found in event class.");
+                       goto error;
+               }
+               break;
+       case NODE_CTF_EXPRESSION:
+       {
+               left = ctf_ast_concatenate_unary_strings(&node->u.ctf_expression.left);
+               if (!left) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Cannot concatenate unary strings.");
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               if (strcmp(left, "name") == 0) {
+                       /* This is already known at this stage */
+                       if (_IS_SET(set, _EVENT_NAME_SET)) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "name", "event class");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       _SET(set, _EVENT_NAME_SET);
+               } else if (strcmp(left, "id") == 0) {
+                       int64_t id = -1;
+
+                       if (_IS_SET(set, _EVENT_ID_SET)) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "id", "event class");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       ret = get_unary_unsigned(ctx,
+                               &node->u.ctf_expression.right,
+                               (uint64_t *) &id);
+                       /* Only read "id" if get_unary_unsigned() succeeded. */
+                       if (ret || (!ret && id < 0)) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                                       "Unexpected unary expression for event class's `id` attribute.");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       event_class->id = id;
+                       _SET(set, _EVENT_ID_SET);
+               } else if (strcmp(left, "stream_id") == 0) {
+                       if (_IS_SET(set, _EVENT_STREAM_ID_SET)) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "stream_id",
+                                       "event class");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       ret = get_unary_unsigned(ctx,
+                               &node->u.ctf_expression.right, stream_id);
+
+                       /*
+                        * Only read "stream_id" if get_unary_unsigned()
+                        * succeeded.
+                        */
+                       if (ret) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                                       "Unexpected unary expression for event class's `stream_id` attribute.");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       _SET(set, _EVENT_STREAM_ID_SET);
+               } else if (strcmp(left, "context") == 0) {
+                       if (_IS_SET(set, _EVENT_CONTEXT_SET)) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                                       "Duplicate `context` entry in event class.");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       ret = visit_field_class_specifier_list(ctx,
+                               _BT_LIST_FIRST_ENTRY(
+                                       &node->u.ctf_expression.right,
+                                       struct ctf_node, siblings),
+                               &event_class->spec_context_fc);
+                       if (ret) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                                       "Cannot create event class's context field class.");
+                               goto error;
+                       }
+
+                       BT_ASSERT(event_class->spec_context_fc);
+                       _SET(set, _EVENT_CONTEXT_SET);
+               } else if (strcmp(left, "fields") == 0) {
+                       if (_IS_SET(set, _EVENT_FIELDS_SET)) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                                       "Duplicate `fields` entry in event class.");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       ret = visit_field_class_specifier_list(ctx,
+                               _BT_LIST_FIRST_ENTRY(
+                                       &node->u.ctf_expression.right,
+                                       struct ctf_node, siblings),
+                               &event_class->payload_fc);
+                       if (ret) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                                       "Cannot create event class's payload field class.");
+                               goto error;
+                       }
+
+                       BT_ASSERT(event_class->payload_fc);
+                       _SET(set, _EVENT_FIELDS_SET);
+               } else if (strcmp(left, "loglevel") == 0) {
+                       uint64_t loglevel_value;
+                       bool is_log_level_known = true;
+                       bt_event_class_log_level log_level;
+
+                       if (_IS_SET(set, _EVENT_LOG_LEVEL_SET)) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "loglevel",
+                                       "event class");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       ret = get_unary_unsigned(ctx,
+                               &node->u.ctf_expression.right, &loglevel_value);
+                       if (ret) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                                       "Unexpected unary expression for event class's `loglevel` attribute.");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       switch (loglevel_value) {
+                       case 0:
+                               log_level = BT_EVENT_CLASS_LOG_LEVEL_EMERGENCY;
+                               break;
+                       case 1:
+                               log_level = BT_EVENT_CLASS_LOG_LEVEL_ALERT;
+                               break;
+                       case 2:
+                               log_level = BT_EVENT_CLASS_LOG_LEVEL_CRITICAL;
+                               break;
+                       case 3:
+                               log_level = BT_EVENT_CLASS_LOG_LEVEL_ERROR;
+                               break;
+                       case 4:
+                               log_level = BT_EVENT_CLASS_LOG_LEVEL_WARNING;
+                               break;
+                       case 5:
+                               log_level = BT_EVENT_CLASS_LOG_LEVEL_NOTICE;
+                               break;
+                       case 6:
+                               log_level = BT_EVENT_CLASS_LOG_LEVEL_INFO;
+                               break;
+                       case 7:
+                               log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM;
+                               break;
+                       case 8:
+                               log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM;
+                               break;
+                       case 9:
+                               log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS;
+                               break;
+                       case 10:
+                               log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE;
+                               break;
+                       case 11:
+                               log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT;
+                               break;
+                       case 12:
+                               log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION;
+                               break;
+                       case 13:
+                               log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_LINE;
+                               break;
+                       case 14:
+                               log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG;
+                               break;
+                       default:
+                               is_log_level_known = false;
+                               _BT_COMP_LOGW_NODE(node, "Not setting event class's log level because its value is unknown: "
+                                       "log-level=%" PRIu64, loglevel_value);
+                       }
+
+                       if (is_log_level_known) {
+                               ctf_event_class_set_log_level(event_class, log_level);
+                       }
+
+                       _SET(set, _EVENT_LOG_LEVEL_SET);
+               } else if (strcmp(left, "model.emf.uri") == 0) {
+                       char *right;
+
+                       if (_IS_SET(set, _EVENT_MODEL_EMF_URI_SET)) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "model.emf.uri",
+                                       "event class");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       right = ctf_ast_concatenate_unary_strings(
+                               &node->u.ctf_expression.right);
+                       if (!right) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                                       "Unexpected unary expression for event class's `model.emf.uri` attribute.");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       if (strlen(right) == 0) {
+                               _BT_COMP_LOGW_NODE(node,
+                                       "Not setting event class's EMF URI because it's empty.");
+                       } else {
+                               g_string_assign(event_class->emf_uri,
+                                       right);
+                       }
+
+                       g_free(right);
+                       _SET(set, _EVENT_MODEL_EMF_URI_SET);
+               } else {
+                       _BT_COMP_LOGW_NODE(node,
+                               "Unknown attribute in event class: "
+                               "attr-name=\"%s\"", left);
+               }
+
+               g_free(left);
+               left = NULL;
+               break;
+       }
+       default:
+               ret = -EPERM;
+               goto error;
+       }
+
+       goto end;
+
+error:
+       g_free(left);
+
+end:
+       return ret;
+}
+
+static
+char *get_event_decl_name(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node)
+{
+       char *left = NULL;
+       char *name = NULL;
+       struct ctf_node *iter;
+       struct bt_list_head *decl_list = &node->u.event.declaration_list;
+
+       bt_list_for_each_entry(iter, decl_list, siblings) {
+               if (iter->type != NODE_CTF_EXPRESSION) {
+                       continue;
+               }
+
+               left = ctf_ast_concatenate_unary_strings(&iter->u.ctf_expression.left);
+               if (!left) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
+                               "Cannot concatenate unary strings.");
+                       goto error;
+               }
+
+               if (strcmp(left, "name") == 0) {
+                       name = ctf_ast_concatenate_unary_strings(
+                               &iter->u.ctf_expression.right);
+                       if (!name) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
+                                       "Unexpected unary expression for event class's `name` attribute.");
+                               goto error;
+                       }
+               }
+
+               g_free(left);
+               left = NULL;
+
+               if (name) {
+                       break;
+               }
+       }
+
+       return name;
+
+error:
+       g_free(left);
+       return NULL;
+}
+
+static
+int visit_event_decl(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node)
+{
+       int ret = 0;
+       int set = 0;
+       struct ctf_node *iter;
+       uint64_t stream_id = 0;
+       char *event_name = NULL;
+       struct ctf_event_class *event_class = NULL;
+       struct ctf_stream_class *stream_class = NULL;
+       struct bt_list_head *decl_list = &node->u.event.declaration_list;
+       bool pop_scope = false;
+
+       if (node->visited) {
+               goto end;
+       }
+
+       node->visited = TRUE;
+       event_name = get_event_decl_name(ctx, node);
+       if (!event_name) {
+               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                       "Missing `name` attribute in event class.");
+               ret = -EPERM;
+               goto error;
+       }
+
+       event_class = ctf_event_class_create();
+       BT_ASSERT(event_class);
+       g_string_assign(event_class->name, event_name);
+       _TRY_PUSH_SCOPE_OR_GOTO_ERROR();
+       pop_scope = true;
+
+       bt_list_for_each_entry(iter, decl_list, siblings) {
+               ret = visit_event_decl_entry(ctx, iter, event_class,
+                       &stream_id, &set);
+               if (ret) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter, "Cannot visit event class's entry: "
+                               "ret=%d", ret);
+                       goto error;
+               }
+       }
+
+       if (!_IS_SET(&set, _EVENT_STREAM_ID_SET)) {
+               /*
+                * Allow missing stream_id if there is only a single
+                * stream class.
+                */
+               switch (ctx->ctf_tc->stream_classes->len) {
+               case 0:
+                       /* Create implicit stream class if there's none */
+                       stream_id = 0;
+                       stream_class = ctf_stream_class_create();
+                       BT_ASSERT(stream_class);
+                       stream_class->id = stream_id;
+                       g_ptr_array_add(ctx->ctf_tc->stream_classes,
+                               stream_class);
+                       break;
+               case 1:
+                       /* Single stream class: get its ID */
+                       stream_class = (ctf_stream_class *) ctx->ctf_tc->stream_classes->pdata[0];
+                       stream_id = stream_class->id;
+                       break;
+               default:
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                               "Missing `stream_id` attribute in event class.");
+                       ret = -EPERM;
+                       goto error;
+               }
+       }
+
+       /* We have the stream ID now; get the stream class if found */
+       if (!stream_class) {
+               stream_class = ctf_trace_class_borrow_stream_class_by_id(
+                       ctx->ctf_tc, stream_id);
+               if (!stream_class) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                               "Cannot find stream class at this point: "
+                               "id=%" PRId64, stream_id);
+                       ret = -EINVAL;
+                       goto error;
+               }
+       }
+
+       BT_ASSERT(stream_class);
+
+       if (!_IS_SET(&set, _EVENT_ID_SET)) {
+               /* Allow only one event without ID per stream */
+               if (stream_class->event_classes->len != 0) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                               "Missing `id` attribute in event class.");
+                       ret = -EPERM;
+                       goto error;
+               }
+
+               /* Automatic ID */
+               event_class->id = 0;
+       }
+
+       if (ctf_stream_class_borrow_event_class_by_id(stream_class,
+                       event_class->id)) {
+               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                       "Duplicate event class (same ID) in the same stream class: "
+                       "id=%" PRId64, event_class->id);
+               ret = -EEXIST;
+               goto error;
+       }
+
+       ctf_stream_class_append_event_class(stream_class, event_class);
+       event_class = NULL;
+       goto end;
+
+error:
+       ctf_event_class_destroy(event_class);
+       event_class = NULL;
+
+       if (ret >= 0) {
+               ret = -1;
+       }
+
+end:
+       if (pop_scope) {
+               ctx_pop_scope(ctx);
+       }
+
+       g_free(event_name);
+
+       return ret;
+}
+
+static
+int auto_map_field_to_trace_clock_class(struct ctf_visitor_generate_ir *ctx,
+               struct ctf_field_class *fc)
+{
+       struct ctf_clock_class *clock_class_to_map_to = NULL;
+       struct ctf_field_class_int *int_fc = ctf_field_class_as_int(fc);
+       int ret = 0;
+       uint64_t clock_class_count;
+
+       if (!fc) {
+               goto end;
+       }
+
+       if (fc->type != CTF_FIELD_CLASS_TYPE_INT &&
+                       fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
+               goto end;
+       }
+
+       if (int_fc->mapped_clock_class) {
+               /* Already mapped */
+               goto end;
+       }
+
+       clock_class_count = ctx->ctf_tc->clock_classes->len;
+
+       switch (clock_class_count) {
+       case 0:
+               /*
+                * No clock class exists in the trace at this point. Create an
+                * implicit one at 1 GHz, named `default`, and use this clock
+                * class.
+                */
+               clock_class_to_map_to = ctf_clock_class_create();
+               BT_ASSERT(clock_class_to_map_to);
+               clock_class_to_map_to->frequency = UINT64_C(1000000000);
+               g_string_assign(clock_class_to_map_to->name, "default");
+               BT_ASSERT(ret == 0);
+               g_ptr_array_add(ctx->ctf_tc->clock_classes,
+                       clock_class_to_map_to);
+               break;
+       case 1:
+               /*
+                * Only one clock class exists in the trace at this point: use
+                * this one.
+                */
+               clock_class_to_map_to = (ctf_clock_class *) ctx->ctf_tc->clock_classes->pdata[0];
+               break;
+       default:
+               /*
+                * Timestamp field not mapped to a clock class and there's more
+                * than one clock class in the trace: this is an error.
+                */
+               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Timestamp field found with no mapped clock class, "
+                       "but there's more than one clock class in the trace at this point.");
+               ret = -1;
+               goto end;
+       }
+
+       BT_ASSERT(clock_class_to_map_to);
+       int_fc->mapped_clock_class = clock_class_to_map_to;
+
+end:
+       return ret;
+}
+
+static
+int auto_map_fields_to_trace_clock_class(struct ctf_visitor_generate_ir *ctx,
+               struct ctf_field_class *root_fc, const char *field_name)
+{
+       int ret = 0;
+       uint64_t i, count;
+       struct ctf_field_class_struct *struct_fc = (ctf_field_class_struct *) root_fc;
+       struct ctf_field_class_variant *var_fc = (ctf_field_class_variant *) root_fc;
+
+       if (!root_fc) {
+               goto end;
+       }
+
+       if (root_fc->type != CTF_FIELD_CLASS_TYPE_STRUCT &&
+                       root_fc->type != CTF_FIELD_CLASS_TYPE_VARIANT) {
+               goto end;
+       }
+
+       if (root_fc->type == CTF_FIELD_CLASS_TYPE_STRUCT) {
+               count = struct_fc->members->len;
+       } else {
+               count = var_fc->options->len;
+       }
+
+       for (i = 0; i < count; i++) {
+               struct ctf_named_field_class *named_fc = NULL;
+
+               if (root_fc->type == CTF_FIELD_CLASS_TYPE_STRUCT) {
+                       named_fc = ctf_field_class_struct_borrow_member_by_index(
+                               struct_fc, i);
+               } else if (root_fc->type == CTF_FIELD_CLASS_TYPE_VARIANT) {
+                       named_fc = ctf_field_class_variant_borrow_option_by_index(
+                               var_fc, i);
+               } else {
+                       bt_common_abort();
+               }
+
+               if (strcmp(named_fc->name->str, field_name) == 0) {
+                       ret = auto_map_field_to_trace_clock_class(ctx,
+                               named_fc->fc);
+                       if (ret) {
+                               _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot automatically map field to trace's clock class: "
+                                       "field-name=\"%s\"", field_name);
+                               goto end;
+                       }
+               }
+
+               ret = auto_map_fields_to_trace_clock_class(ctx, named_fc->fc,
+                       field_name);
+               if (ret) {
+                       _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot automatically map structure or variant field class's fields to trace's clock class: "
+                               "field-name=\"%s\", root-field-name=\"%s\"",
+                               field_name, named_fc->name->str);
+                       goto end;
+               }
+       }
+
+end:
+       return ret;
+}
+
+static
+int visit_stream_decl_entry(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node,
+               struct ctf_stream_class *stream_class, int *set)
+{
+       int ret = 0;
+       char *left = NULL;
+
+       switch (node->type) {
+       case NODE_TYPEDEF:
+               ret = visit_field_class_def(ctx, node->u.field_class_def.field_class_specifier_list,
+                       &node->u.field_class_def.field_class_declarators);
+               if (ret) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                               "Cannot add field class found in stream class.");
+                       goto error;
+               }
+               break;
+       case NODE_TYPEALIAS:
+               ret = visit_field_class_alias(ctx, node->u.field_class_alias.target,
+                       node->u.field_class_alias.alias);
+               if (ret) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                               "Cannot add field class alias found in stream class.");
+                       goto error;
+               }
+               break;
+       case NODE_CTF_EXPRESSION:
+       {
+               left = ctf_ast_concatenate_unary_strings(&node->u.ctf_expression.left);
+               if (!left) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Cannot concatenate unary strings.");
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               if (strcmp(left, "id") == 0) {
+                       int64_t id;
+
+                       if (_IS_SET(set, _STREAM_ID_SET)) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "id",
+                                       "stream declaration");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       ret = get_unary_unsigned(ctx,
+                               &node->u.ctf_expression.right,
+                               (uint64_t *) &id);
+
+                       /* Only read "id" if get_unary_unsigned() succeeded. */
+                       if (ret || (!ret && id < 0)) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                                       "Unexpected unary expression for stream class's `id` attribute.");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       if (ctf_trace_class_borrow_stream_class_by_id(
+                                       ctx->ctf_tc, id)) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                                       "Duplicate stream class (same ID): id=%" PRId64,
+                                       id);
+                               ret = -EEXIST;
+                               goto error;
+                       }
+
+                       stream_class->id = id;
+                       _SET(set, _STREAM_ID_SET);
+               } else if (strcmp(left, "event.header") == 0) {
+                       if (_IS_SET(set, _STREAM_EVENT_HEADER_SET)) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                                       "Duplicate `event.header` entry in stream class.");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       ret = visit_field_class_specifier_list(ctx,
+                               _BT_LIST_FIRST_ENTRY(
+                                       &node->u.ctf_expression.right,
+                                       struct ctf_node, siblings),
+                               &stream_class->event_header_fc);
+                       if (ret) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                                       "Cannot create stream class's event header field class.");
+                               goto error;
+                       }
+
+                       BT_ASSERT(stream_class->event_header_fc);
+                       ret = auto_map_fields_to_trace_clock_class(ctx,
+                               stream_class->event_header_fc, "timestamp");
+                       if (ret) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                                       "Cannot automatically map specific event header field class fields named `timestamp` to trace's clock class.");
+                               goto error;
+                       }
+
+                       _SET(set, _STREAM_EVENT_HEADER_SET);
+               } else if (strcmp(left, "event.context") == 0) {
+                       if (_IS_SET(set, _STREAM_EVENT_CONTEXT_SET)) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                                       "Duplicate `event.context` entry in stream class.");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       ret = visit_field_class_specifier_list(ctx,
+                               _BT_LIST_FIRST_ENTRY(
+                                       &node->u.ctf_expression.right,
+                                       struct ctf_node, siblings),
+                               &stream_class->event_common_context_fc);
+                       if (ret) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                                       "Cannot create stream class's event context field class.");
+                               goto error;
+                       }
+
+                       BT_ASSERT(stream_class->event_common_context_fc);
+                       _SET(set, _STREAM_EVENT_CONTEXT_SET);
+               } else if (strcmp(left, "packet.context") == 0) {
+                       if (_IS_SET(set, _STREAM_PACKET_CONTEXT_SET)) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                                       "Duplicate `packet.context` entry in stream class.");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       ret = visit_field_class_specifier_list(ctx,
+                               _BT_LIST_FIRST_ENTRY(
+                                       &node->u.ctf_expression.right,
+                                       struct ctf_node, siblings),
+                               &stream_class->packet_context_fc);
+                       if (ret) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                                       "Cannot create stream class's packet context field class.");
+                               goto error;
+                       }
+
+                       BT_ASSERT(stream_class->packet_context_fc);
+                       ret = auto_map_fields_to_trace_clock_class(ctx,
+                               stream_class->packet_context_fc,
+                               "timestamp_begin");
+                       if (ret) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                                       "Cannot automatically map specific packet context field class fields named `timestamp_begin` to trace's clock class.");
+                               goto error;
+                       }
+
+                       ret = auto_map_fields_to_trace_clock_class(ctx,
+                               stream_class->packet_context_fc,
+                               "timestamp_end");
+                       if (ret) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                                       "Cannot automatically map specific packet context field class fields named `timestamp_end` to trace's clock class.");
+                               goto error;
+                       }
+
+                       _SET(set, _STREAM_PACKET_CONTEXT_SET);
+               } else {
+                       _BT_COMP_LOGW_NODE(node,
+                               "Unknown attribute in stream class: "
+                               "attr-name=\"%s\"", left);
+               }
+
+               g_free(left);
+               left = NULL;
+               break;
+       }
+
+       default:
+               ret = -EPERM;
+               goto error;
+       }
+
+       return 0;
+
+error:
+       g_free(left);
+       return ret;
+}
+
+static
+int visit_stream_decl(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node)
+{
+       int set = 0;
+       int ret = 0;
+       struct ctf_node *iter;
+       struct ctf_stream_class *stream_class = NULL;
+       struct bt_list_head *decl_list = &node->u.stream.declaration_list;
+
+       if (node->visited) {
+               goto end;
+       }
+
+       node->visited = TRUE;
+       stream_class = ctf_stream_class_create();
+       BT_ASSERT(stream_class);
+       _TRY_PUSH_SCOPE_OR_GOTO_ERROR();
+
+       bt_list_for_each_entry(iter, decl_list, siblings) {
+               ret = visit_stream_decl_entry(ctx, iter, stream_class, &set);
+               if (ret) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
+                               "Cannot visit stream class's entry: "
+                               "ret=%d", ret);
+                       ctx_pop_scope(ctx);
+                       goto error;
+               }
+       }
+
+       ctx_pop_scope(ctx);
+
+       if (_IS_SET(&set, _STREAM_ID_SET)) {
+               /* Check that packet header has `stream_id` field */
+               struct ctf_named_field_class *named_fc = NULL;
+
+               if (!ctx->ctf_tc->packet_header_fc) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                               "Stream class has a `id` attribute, "
+                               "but trace has no packet header field class.");
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               named_fc = ctf_field_class_struct_borrow_member_by_name(
+                       ctf_field_class_as_struct(ctx->ctf_tc->packet_header_fc), "stream_id");
+               if (!named_fc) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                               "Stream class has a `id` attribute, "
+                               "but trace's packet header field class has no `stream_id` field.");
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               if (named_fc->fc->type != CTF_FIELD_CLASS_TYPE_INT &&
+                               named_fc->fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                               "Stream class has a `id` attribute, "
+                               "but trace's packet header field class's `stream_id` field is not an integer field class.");
+                       ret = -EINVAL;
+                       goto error;
+               }
+       } else {
+               /* Allow only _one_ ID-less stream */
+               if (ctx->ctf_tc->stream_classes->len != 0) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                               "Missing `id` attribute in stream class as there's more than one stream class in the trace.");
+                       ret = -EPERM;
+                       goto error;
+               }
+
+               /* Automatic ID: 0 */
+               stream_class->id = 0;
+       }
+
+       /*
+        * Make sure that this stream class's ID is currently unique in
+        * the trace.
+        */
+       if (ctf_trace_class_borrow_stream_class_by_id(ctx->ctf_tc,
+                       stream_class->id)) {
+               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                       "Duplicate stream class (same ID): id=%" PRId64,
+                       stream_class->id);
+               ret = -EINVAL;
+               goto error;
+       }
+
+       g_ptr_array_add(ctx->ctf_tc->stream_classes, stream_class);
+       stream_class = NULL;
+       goto end;
+
+error:
+       ctf_stream_class_destroy(stream_class);
+       stream_class = NULL;
+
+end:
+       return ret;
+}
+
+static
+int visit_trace_decl_entry(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node,
+               int *set)
+{
+       int ret = 0;
+       char *left = NULL;
+       uint64_t val;
+
+       switch (node->type) {
+       case NODE_TYPEDEF:
+               ret = visit_field_class_def(ctx, node->u.field_class_def.field_class_specifier_list,
+                       &node->u.field_class_def.field_class_declarators);
+               if (ret) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                               "Cannot add field class found in trace (`trace` block).");
+                       goto error;
+               }
+               break;
+       case NODE_TYPEALIAS:
+               ret = visit_field_class_alias(ctx, node->u.field_class_alias.target,
+                       node->u.field_class_alias.alias);
+               if (ret) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                               "Cannot add field class alias found in trace (`trace` block).");
+                       goto error;
+               }
+               break;
+       case NODE_CTF_EXPRESSION:
+       {
+               left = ctf_ast_concatenate_unary_strings(&node->u.ctf_expression.left);
+               if (!left) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Cannot concatenate unary strings.");
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               if (strcmp(left, "major") == 0) {
+                       if (_IS_SET(set, _TRACE_MAJOR_SET)) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "major", "trace");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       ret = get_unary_unsigned(ctx,
+                               &node->u.ctf_expression.right, &val);
+                       if (ret) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                                       "Unexpected unary expression for trace's `major` attribute.");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       if (val != 1) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                                       "Invalid trace's `minor` attribute: expecting 1.");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       ctx->ctf_tc->major = val;
+                       _SET(set, _TRACE_MAJOR_SET);
+               } else if (strcmp(left, "minor") == 0) {
+                       if (_IS_SET(set, _TRACE_MINOR_SET)) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "minor", "trace");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       ret = get_unary_unsigned(ctx,
+                               &node->u.ctf_expression.right, &val);
+                       if (ret) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                                       "Unexpected unary expression for trace's `minor` attribute.");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       if (val != 8) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                                       "Invalid trace's `minor` attribute: expecting 8.");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       ctx->ctf_tc->minor = val;
+                       _SET(set, _TRACE_MINOR_SET);
+               } else if (strcmp(left, "uuid") == 0) {
+                       if (_IS_SET(set, _TRACE_UUID_SET)) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "uuid", "trace");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       ret = get_unary_uuid(ctx,
+                               &node->u.ctf_expression.right,
+                               ctx->ctf_tc->uuid);
+                       if (ret) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                                       "Invalid trace's `uuid` attribute.");
+                               goto error;
+                       }
+
+                       ctx->ctf_tc->is_uuid_set = true;
+                       _SET(set, _TRACE_UUID_SET);
+               } else if (strcmp(left, "byte_order") == 0) {
+                       /* Default byte order is already known at this stage */
+                       if (_IS_SET(set, _TRACE_BYTE_ORDER_SET)) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "byte_order",
+                                       "trace");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       BT_ASSERT(ctx->ctf_tc->default_byte_order != CTF_BYTE_ORDER_UNKNOWN);
+                       _SET(set, _TRACE_BYTE_ORDER_SET);
+               } else if (strcmp(left, "packet.header") == 0) {
+                       if (_IS_SET(set, _TRACE_PACKET_HEADER_SET)) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                                       "Duplicate `packet.header` entry in trace.");
+                               ret = -EPERM;
+                               goto error;
+                       }
+
+                       ret = visit_field_class_specifier_list(ctx,
+                               _BT_LIST_FIRST_ENTRY(
+                                       &node->u.ctf_expression.right,
+                                       struct ctf_node, siblings),
+                               &ctx->ctf_tc->packet_header_fc);
+                       if (ret) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                                       "Cannot create trace's packet header field class.");
+                               goto error;
+                       }
+
+                       BT_ASSERT(ctx->ctf_tc->packet_header_fc);
+                       _SET(set, _TRACE_PACKET_HEADER_SET);
+               } else {
+                       _BT_COMP_LOGW_NODE(node,
+                               "Unknown attribute in stream class: "
+                               "attr-name=\"%s\"", left);
+               }
+
+               g_free(left);
+               left = NULL;
+               break;
+       }
+       default:
+               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Unknown expression in trace.");
+               ret = -EINVAL;
+               goto error;
+       }
+
+       return 0;
+
+error:
+       g_free(left);
+       return ret;
+}
+
+static
+int visit_trace_decl(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node)
+{
+       int ret = 0;
+       int set = 0;
+       struct ctf_node *iter;
+       struct bt_list_head *decl_list = &node->u.trace.declaration_list;
+
+       if (node->visited) {
+               goto end;
+       }
+
+       node->visited = TRUE;
+
+       if (ctx->is_trace_visited) {
+               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Duplicate trace (`trace` block).");
+               ret = -EEXIST;
+               goto error;
+       }
+
+       _TRY_PUSH_SCOPE_OR_GOTO_ERROR();
+
+       bt_list_for_each_entry(iter, decl_list, siblings) {
+               ret = visit_trace_decl_entry(ctx, iter, &set);
+               if (ret) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter, "Cannot visit trace's entry (`trace` block): "
+                               "ret=%d", ret);
+                       ctx_pop_scope(ctx);
+                       goto error;
+               }
+       }
+
+       ctx_pop_scope(ctx);
+
+       if (!_IS_SET(&set, _TRACE_MAJOR_SET)) {
+               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                       "Missing `major` attribute in trace (`trace` block).");
+               ret = -EPERM;
+               goto error;
+       }
+
+       if (!_IS_SET(&set, _TRACE_MINOR_SET)) {
+               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                       "Missing `minor` attribute in trace (`trace` block).");
+               ret = -EPERM;
+               goto error;
+       }
+
+       if (!_IS_SET(&set, _TRACE_BYTE_ORDER_SET)) {
+               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                       "Missing `byte_order` attribute in trace (`trace` block).");
+               ret = -EPERM;
+               goto error;
+       }
+
+       ctx->is_trace_visited = true;
+
+end:
+       return 0;
+
+error:
+       return ret;
+}
+
+static
+int visit_env(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node)
+{
+       int ret = 0;
+       char *left = NULL;
+       struct ctf_node *entry_node;
+       struct bt_list_head *decl_list = &node->u.env.declaration_list;
+
+       if (node->visited) {
+               goto end;
+       }
+
+       node->visited = TRUE;
+
+       bt_list_for_each_entry(entry_node, decl_list, siblings) {
+               struct bt_list_head *right_head =
+                       &entry_node->u.ctf_expression.right;
+
+               if (entry_node->type != NODE_CTF_EXPRESSION) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+                               "Wrong expression in environment entry: "
+                               "node-type=%d", entry_node->type);
+                       ret = -EPERM;
+                       goto error;
+               }
+
+               left = ctf_ast_concatenate_unary_strings(
+                       &entry_node->u.ctf_expression.left);
+               if (!left) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+                               "Cannot get environment entry's name.");
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               if (is_unary_string(right_head)) {
+                       char *right = ctf_ast_concatenate_unary_strings(right_head);
+
+                       if (!right) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+                                       "Unexpected unary expression for environment entry's value: "
+                                       "name=\"%s\"", left);
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       if (strcmp(left, "tracer_name") == 0) {
+                               if (strncmp(right, "lttng", 5) == 0) {
+                                       BT_COMP_LOGI("Detected LTTng trace from `%s` environment value: "
+                                               "tracer-name=\"%s\"",
+                                               left, right);
+                                       ctx->is_lttng = true;
+                               }
+                       }
+
+                       ctf_trace_class_append_env_entry(ctx->ctf_tc,
+                               left, CTF_TRACE_CLASS_ENV_ENTRY_TYPE_STR,
+                               right, 0);
+                       g_free(right);
+               } else if (is_unary_unsigned(right_head) ||
+                               is_unary_signed(right_head)) {
+                       int64_t v;
+
+                       if (is_unary_unsigned(right_head)) {
+                               ret = get_unary_unsigned(ctx, right_head,
+                                       (uint64_t *) &v);
+                       } else {
+                               ret = get_unary_signed(right_head, &v);
+                       }
+                       if (ret) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+                                       "Unexpected unary expression for environment entry's value: "
+                                       "name=\"%s\"", left);
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       ctf_trace_class_append_env_entry(ctx->ctf_tc,
+                               left, CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT,
+                               NULL, v);
+               } else {
+                       _BT_COMP_LOGW_NODE(entry_node,
+                               "Environment entry has unknown type: "
+                               "name=\"%s\"", left);
+               }
+
+               g_free(left);
+               left = NULL;
+       }
+
+end:
+       return 0;
+
+error:
+       g_free(left);
+       return ret;
+}
+
+static
+int set_trace_byte_order(struct ctf_visitor_generate_ir *ctx, struct ctf_node *trace_node)
+{
+       int ret = 0;
+       int set = 0;
+       char *left = NULL;
+       struct ctf_node *node;
+       struct bt_list_head *decl_list = &trace_node->u.trace.declaration_list;
+
+       bt_list_for_each_entry(node, decl_list, siblings) {
+               if (node->type == NODE_CTF_EXPRESSION) {
+                       struct ctf_node *right_node;
+
+                       left = ctf_ast_concatenate_unary_strings(
+                               &node->u.ctf_expression.left);
+                       if (!left) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                                       "Cannot concatenate unary strings.");
+                               ret = -EINVAL;
+                               goto error;
+                       }
+
+                       if (strcmp(left, "byte_order") == 0) {
+                               enum ctf_byte_order bo;
+
+                               if (_IS_SET(&set, _TRACE_BYTE_ORDER_SET)) {
+                                       _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "byte_order",
+                                               "trace");
+                                       ret = -EPERM;
+                                       goto error;
+                               }
+
+                               _SET(&set, _TRACE_BYTE_ORDER_SET);
+                               right_node = _BT_LIST_FIRST_ENTRY(
+                                       &node->u.ctf_expression.right,
+                                       struct ctf_node, siblings);
+                               bo = byte_order_from_unary_expr(ctx,
+                                       right_node);
+                               if (bo == CTF_BYTE_ORDER_UNKNOWN) {
+                                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                                               "Invalid `byte_order` attribute in trace (`trace` block): "
+                                               "expecting `le`, `be`, or `network`.");
+                                       ret = -EINVAL;
+                                       goto error;
+                               } else if (bo == CTF_BYTE_ORDER_DEFAULT) {
+                                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                                               "Invalid `byte_order` attribute in trace (`trace` block): "
+                                               "cannot be set to `native` here.");
+                                       ret = -EPERM;
+                                       goto error;
+                               }
+
+                               ctx->ctf_tc->default_byte_order = bo;
+                       }
+
+                       g_free(left);
+                       left = NULL;
+               }
+       }
+
+       if (!_IS_SET(&set, _TRACE_BYTE_ORDER_SET)) {
+               _BT_COMP_LOGE_APPEND_CAUSE_NODE(trace_node,
+                       "Missing `byte_order` attribute in trace (`trace` block).");
+               ret = -EINVAL;
+               goto error;
+       }
+
+       return 0;
+
+error:
+       g_free(left);
+       return ret;
+}
+
+static
+int visit_clock_decl_entry(struct ctf_visitor_generate_ir *ctx, struct ctf_node *entry_node,
+       struct ctf_clock_class *clock, int *set, int64_t *offset_seconds,
+       uint64_t *offset_cycles)
+{
+       int ret = 0;
+       char *left = NULL;
+
+       if (entry_node->type != NODE_CTF_EXPRESSION) {
+               _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+                       "Unexpected node type: node-type=%d",
+                       entry_node->type);
+               ret = -EPERM;
+               goto error;
+       }
+
+       left = ctf_ast_concatenate_unary_strings(&entry_node->u.ctf_expression.left);
+       if (!left) {
+               _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node, "Cannot concatenate unary strings.");
+               ret = -EINVAL;
+               goto error;
+       }
+
+       if (strcmp(left, "name") == 0) {
+               char *right;
+
+               if (_IS_SET(set, _CLOCK_NAME_SET)) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "name", "clock class");
+                       ret = -EPERM;
+                       goto error;
+               }
+
+               right = ctf_ast_concatenate_unary_strings(
+                       &entry_node->u.ctf_expression.right);
+               if (!right) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+                               "Unexpected unary expression for clock class's `name` attribute.");
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               g_string_assign(clock->name, right);
+               g_free(right);
+               _SET(set, _CLOCK_NAME_SET);
+       } else if (strcmp(left, "uuid") == 0) {
+               bt_uuid_t uuid;
+
+               if (_IS_SET(set, _CLOCK_UUID_SET)) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "uuid", "clock class");
+                       ret = -EPERM;
+                       goto error;
+               }
+
+               ret = get_unary_uuid(ctx, &entry_node->u.ctf_expression.right,
+                       uuid);
+               if (ret) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+                               "Invalid clock class's `uuid` attribute.");
+                       goto error;
+               }
+
+               clock->has_uuid = true;
+               bt_uuid_copy(clock->uuid, uuid);
+               _SET(set, _CLOCK_UUID_SET);
+       } else if (strcmp(left, "description") == 0) {
+               char *right;
+
+               if (_IS_SET(set, _CLOCK_DESCRIPTION_SET)) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "description",
+                               "clock class");
+                       ret = -EPERM;
+                       goto error;
+               }
+
+               right = ctf_ast_concatenate_unary_strings(
+                       &entry_node->u.ctf_expression.right);
+               if (!right) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+                               "Unexpected unary expression for clock class's `description` attribute.");
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               g_string_assign(clock->description, right);
+               g_free(right);
+               _SET(set, _CLOCK_DESCRIPTION_SET);
+       } else if (strcmp(left, "freq") == 0) {
+               uint64_t freq = UINT64_C(-1);
+
+               if (_IS_SET(set, _CLOCK_FREQ_SET)) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "freq", "clock class");
+                       ret = -EPERM;
+                       goto error;
+               }
+
+               ret = get_unary_unsigned(ctx,
+                       &entry_node->u.ctf_expression.right, &freq);
+               if (ret) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+                               "Unexpected unary expression for clock class's `freq` attribute.");
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               if (freq == UINT64_C(-1) || freq == 0) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+                               "Invalid clock class frequency: freq=%" PRIu64,
+                               freq);
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               clock->frequency = freq;
+               _SET(set, _CLOCK_FREQ_SET);
+       } else if (strcmp(left, "precision") == 0) {
+               uint64_t precision;
+
+               if (_IS_SET(set, _CLOCK_PRECISION_SET)) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "precision",
+                               "clock class");
+                       ret = -EPERM;
+                       goto error;
+               }
+
+               ret = get_unary_unsigned(ctx,
+                       &entry_node->u.ctf_expression.right, &precision);
+               if (ret) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+                               "Unexpected unary expression for clock class's `precision` attribute.");
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               clock->precision = precision;
+               _SET(set, _CLOCK_PRECISION_SET);
+       } else if (strcmp(left, "offset_s") == 0) {
+               if (_IS_SET(set, _CLOCK_OFFSET_S_SET)) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "offset_s",
+                               "clock class");
+                       ret = -EPERM;
+                       goto error;
+               }
+
+               ret = get_unary_signed(
+                       &entry_node->u.ctf_expression.right, offset_seconds);
+               if (ret) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+                               "Unexpected unary expression for clock class's `offset_s` attribute.");
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               _SET(set, _CLOCK_OFFSET_S_SET);
+       } else if (strcmp(left, "offset") == 0) {
+               if (_IS_SET(set, _CLOCK_OFFSET_SET)) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "offset", "clock class");
+                       ret = -EPERM;
+                       goto error;
+               }
+
+               ret = get_unary_unsigned(ctx,
+                       &entry_node->u.ctf_expression.right, offset_cycles);
+               if (ret) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+                               "Unexpected unary expression for clock class's `offset` attribute.");
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               _SET(set, _CLOCK_OFFSET_SET);
+       } else if (strcmp(left, "absolute") == 0) {
+               struct ctf_node *right;
+
+               if (_IS_SET(set, _CLOCK_ABSOLUTE_SET)) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "absolute",
+                               "clock class");
+                       ret = -EPERM;
+                       goto error;
+               }
+
+               right = _BT_LIST_FIRST_ENTRY(
+                       &entry_node->u.ctf_expression.right,
+                       struct ctf_node, siblings);
+               ret = get_boolean(ctx, right);
+               if (ret < 0) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+                               "Unexpected unary expression for clock class's `absolute` attribute.");
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               clock->is_absolute = ret;
+               _SET(set, _CLOCK_ABSOLUTE_SET);
+       } else {
+               _BT_COMP_LOGW_NODE(entry_node,
+                       "Unknown attribute in clock class: attr-name=\"%s\"",
+                       left);
+       }
+
+       g_free(left);
+       left = NULL;
+       return 0;
+
+error:
+       g_free(left);
+       return ret;
+}
+
+static inline
+uint64_t cycles_from_ns(uint64_t frequency, uint64_t ns)
+{
+       uint64_t cycles;
+
+       /* 1GHz */
+       if (frequency == UINT64_C(1000000000)) {
+               cycles = ns;
+       } else {
+               cycles = (uint64_t) (((double) ns * (double) frequency) / 1e9);
+       }
+
+       return cycles;
+}
+
+static
+void calibrate_clock_class_offsets(int64_t *offset_seconds,
+               uint64_t *offset_cycles, uint64_t freq)
+{
+       if (*offset_cycles >= freq) {
+               const uint64_t s_in_offset_cycles = *offset_cycles / freq;
+
+               *offset_seconds += (int64_t) s_in_offset_cycles;
+               *offset_cycles -= (s_in_offset_cycles * freq);
+       }
+}
+
+static
+void apply_clock_class_is_absolute(struct ctf_visitor_generate_ir *ctx,
+               struct ctf_clock_class *clock)
+{
+       if (ctx->decoder_config.force_clock_class_origin_unix_epoch) {
+               clock->is_absolute = true;
+       }
+
+       return;
+}
+
+static
+void apply_clock_class_offset(struct ctf_visitor_generate_ir *ctx,
+               struct ctf_clock_class *clock)
+{
+       uint64_t freq;
+       int64_t offset_s_to_apply = ctx->decoder_config.clock_class_offset_s;
+       uint64_t offset_ns_to_apply;
+       int64_t cur_offset_s;
+       uint64_t cur_offset_cycles;
+
+       if (ctx->decoder_config.clock_class_offset_s == 0 &&
+                       ctx->decoder_config.clock_class_offset_ns == 0) {
+               goto end;
+       }
+
+       /* Transfer nanoseconds to seconds as much as possible */
+       if (ctx->decoder_config.clock_class_offset_ns < 0) {
+               const int64_t abs_ns = -ctx->decoder_config.clock_class_offset_ns;
+               const int64_t abs_extra_s = abs_ns / INT64_C(1000000000) + 1;
+               const int64_t extra_s = -abs_extra_s;
+               const int64_t offset_ns = ctx->decoder_config.clock_class_offset_ns -
+                       (extra_s * INT64_C(1000000000));
+
+               BT_ASSERT(offset_ns > 0);
+               offset_ns_to_apply = (uint64_t) offset_ns;
+               offset_s_to_apply += extra_s;
+       } else {
+               const int64_t extra_s = ctx->decoder_config.clock_class_offset_ns /
+                       INT64_C(1000000000);
+               const int64_t offset_ns = ctx->decoder_config.clock_class_offset_ns -
+                       (extra_s * INT64_C(1000000000));
+
+               BT_ASSERT(offset_ns >= 0);
+               offset_ns_to_apply = (uint64_t) offset_ns;
+               offset_s_to_apply += extra_s;
+       }
+
+       freq = clock->frequency;
+       cur_offset_s = clock->offset_seconds;
+       cur_offset_cycles = clock->offset_cycles;
+
+       /* Apply offsets */
+       cur_offset_s += offset_s_to_apply;
+       cur_offset_cycles += cycles_from_ns(freq, offset_ns_to_apply);
+
+       /*
+        * Recalibrate offsets because the part in cycles can be greater
+        * than the frequency at this point.
+        */
+       calibrate_clock_class_offsets(&cur_offset_s, &cur_offset_cycles, freq);
+
+       /* Set final offsets */
+       clock->offset_seconds = cur_offset_s;
+       clock->offset_cycles = cur_offset_cycles;
+
+end:
+       return;
+}
+
+static
+int visit_clock_decl(struct ctf_visitor_generate_ir *ctx, struct ctf_node *clock_node)
+{
+       int ret = 0;
+       int set = 0;
+       struct ctf_clock_class *clock;
+       struct ctf_node *entry_node;
+       struct bt_list_head *decl_list = &clock_node->u.clock.declaration_list;
+       const char *clock_class_name;
+       int64_t offset_seconds = 0;
+       uint64_t offset_cycles = 0;
+       uint64_t freq;
+
+       if (clock_node->visited) {
+               return 0;
+       }
+
+       clock_node->visited = TRUE;
+
+       /* CTF 1.8's default frequency for a clock class is 1 GHz */
+       clock = ctf_clock_class_create();
+       if (!clock) {
+               _BT_COMP_LOGE_APPEND_CAUSE_NODE(clock_node,
+                       "Cannot create default clock class.");
+               ret = -ENOMEM;
+               goto end;
+       }
+
+       bt_list_for_each_entry(entry_node, decl_list, siblings) {
+               ret = visit_clock_decl_entry(ctx, entry_node, clock, &set,
+                       &offset_seconds, &offset_cycles);
+               if (ret) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+                               "Cannot visit clock class's entry: ret=%d",
+                               ret);
+                       goto end;
+               }
+       }
+
+       if (!_IS_SET(&set, _CLOCK_NAME_SET)) {
+               _BT_COMP_LOGE_APPEND_CAUSE_NODE(clock_node,
+                       "Missing `name` attribute in clock class.");
+               ret = -EPERM;
+               goto end;
+       }
+
+       clock_class_name = clock->name->str;
+       BT_ASSERT(clock_class_name);
+       if (ctx->is_lttng && strcmp(clock_class_name, "monotonic") == 0) {
+               /*
+                * Old versions of LTTng forgot to set its clock class
+                * as absolute, even if it is. This is important because
+                * it's a condition to be able to sort messages
+                * from different sources.
+                */
+               clock->is_absolute = true;
+       }
+
+       /*
+        * Adjust offsets so that the part in cycles is less than the
+        * frequency (move to the part in seconds).
+        */
+       freq = clock->frequency;
+       calibrate_clock_class_offsets(&offset_seconds, &offset_cycles, freq);
+       BT_ASSERT(offset_cycles < clock->frequency);
+       clock->offset_seconds = offset_seconds;
+       clock->offset_cycles = offset_cycles;
+       apply_clock_class_offset(ctx, clock);
+       apply_clock_class_is_absolute(ctx, clock);
+       g_ptr_array_add(ctx->ctf_tc->clock_classes, clock);
+       clock = NULL;
+
+end:
+       if (clock) {
+               ctf_clock_class_destroy(clock);
+       }
+
+       return ret;
+}
+
+static
+int visit_root_decl(struct ctf_visitor_generate_ir *ctx, struct ctf_node *root_decl_node)
+{
+       int ret = 0;
+
+       if (root_decl_node->visited) {
+               goto end;
+       }
+
+       root_decl_node->visited = TRUE;
+
+       switch (root_decl_node->type) {
+       case NODE_TYPEDEF:
+               ret = visit_field_class_def(ctx,
+                       root_decl_node->u.field_class_def.field_class_specifier_list,
+                       &root_decl_node->u.field_class_def.field_class_declarators);
+               if (ret) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(root_decl_node,
+                               "Cannot add field class found in root scope.");
+                       goto end;
+               }
+               break;
+       case NODE_TYPEALIAS:
+               ret = visit_field_class_alias(ctx, root_decl_node->u.field_class_alias.target,
+                       root_decl_node->u.field_class_alias.alias);
+               if (ret) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(root_decl_node,
+                               "Cannot add field class alias found in root scope.");
+                       goto end;
+               }
+               break;
+       case NODE_TYPE_SPECIFIER_LIST:
+       {
+               struct ctf_field_class *decl = NULL;
+
+               /*
+                * Just add the field class specifier to the root
+                * declaration scope. Put local reference.
+                */
+               ret = visit_field_class_specifier_list(ctx, root_decl_node, &decl);
+               if (ret) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(root_decl_node,
+                               "Cannot visit root scope's field class: "
+                               "ret=%d", ret);
+                       BT_ASSERT(!decl);
+                       goto end;
+               }
+
+               ctf_field_class_destroy(decl);
+               decl = NULL;
+               break;
+       }
+       default:
+               _BT_COMP_LOGE_APPEND_CAUSE_NODE(root_decl_node,
+                       "Unexpected node type: node-type=%d",
+                       root_decl_node->type);
+               ret = -EPERM;
+               goto end;
+       }
+
+end:
+       return ret;
+}
+
+BT_HIDDEN
+struct ctf_visitor_generate_ir *ctf_visitor_generate_ir_create(
+               const struct ctf_metadata_decoder_config *decoder_config)
+{
+       struct ctf_visitor_generate_ir *ctx = NULL;
+
+       /* Create visitor's context */
+       ctx = ctx_create(decoder_config);
+       if (!ctx) {
+               BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, decoder_config->log_level,
+                       decoder_config->self_comp,
+                       "Cannot create visitor's context.");
+               goto error;
+       }
+
+       goto end;
+
+error:
+       ctx_destroy(ctx);
+       ctx = NULL;
+
+end:
+       return ctx;
+}
+
+BT_HIDDEN
+void ctf_visitor_generate_ir_destroy(struct ctf_visitor_generate_ir *visitor)
+{
+       ctx_destroy(visitor);
+}
+
+BT_HIDDEN
+bt_trace_class *ctf_visitor_generate_ir_get_ir_trace_class(
+               struct ctf_visitor_generate_ir *ctx)
+{
+       BT_ASSERT_DBG(ctx);
+
+       if (ctx->trace_class) {
+               bt_trace_class_get_ref(ctx->trace_class);
+       }
+
+       return ctx->trace_class;
+}
+
+BT_HIDDEN
+struct ctf_trace_class *ctf_visitor_generate_ir_borrow_ctf_trace_class(
+               struct ctf_visitor_generate_ir *ctx)
+{
+       BT_ASSERT_DBG(ctx);
+       BT_ASSERT_DBG(ctx->ctf_tc);
+       return ctx->ctf_tc;
+}
+
+BT_HIDDEN
+int ctf_visitor_generate_ir_visit_node(struct ctf_visitor_generate_ir *ctx,
+               struct ctf_node *node)
+{
+       int ret = 0;
+
+       BT_COMP_LOGI_STR("Visiting metadata's AST to generate CTF IR objects.");
+
+       switch (node->type) {
+       case NODE_ROOT:
+       {
+               struct ctf_node *iter;
+               bool got_trace_decl = false;
+
+               /*
+                * The first thing we need is the native byte order of
+                * the trace block, because early class aliases can have
+                * a `byte_order` attribute set to `native`. If we don't
+                * have the native byte order yet, and we don't have any
+                * trace block yet, then fail with EINCOMPLETE.
+                */
+               if (ctx->ctf_tc->default_byte_order == CTF_BYTE_ORDER_UNKNOWN) {
+                       bt_list_for_each_entry(iter, &node->u.root.trace, siblings) {
+                               if (got_trace_decl) {
+                                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                                               "Duplicate trace (`trace` block).");
+                                       ret = -1;
+                                       goto end;
+                               }
+
+                               ret = set_trace_byte_order(ctx, iter);
+                               if (ret) {
+                                       _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                                               "Cannot set trace's native byte order: "
+                                               "ret=%d", ret);
+                                       goto end;
+                               }
+
+                               got_trace_decl = true;
+                       }
+
+                       if (!got_trace_decl) {
+                               BT_COMP_LOGD_STR("Incomplete AST: need trace (`trace` block).");
+                               ret = -EINCOMPLETE;
+                               goto end;
+                       }
+               }
+
+               BT_ASSERT(ctx->ctf_tc->default_byte_order == CTF_BYTE_ORDER_LITTLE ||
+                       ctx->ctf_tc->default_byte_order == CTF_BYTE_ORDER_BIG);
+               BT_ASSERT(ctx->current_scope &&
+                               !ctx->current_scope->parent_scope);
+
+               /* Environment */
+               bt_list_for_each_entry(iter, &node->u.root.env, siblings) {
+                       ret = visit_env(ctx, iter);
+                       if (ret) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
+                                       "Cannot visit trace's environment (`env` block) entry: "
+                                       "ret=%d", ret);
+                               goto end;
+                       }
+               }
+
+               BT_ASSERT(ctx->current_scope &&
+                       !ctx->current_scope->parent_scope);
+
+               /*
+                * Visit clock blocks.
+                */
+               bt_list_for_each_entry(iter, &node->u.root.clock, siblings) {
+                       ret = visit_clock_decl(ctx, iter);
+                       if (ret) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
+                                       "Cannot visit clock class: ret=%d",
+                                       ret);
+                               goto end;
+                       }
+               }
+
+               BT_ASSERT(ctx->current_scope &&
+                       !ctx->current_scope->parent_scope);
+
+               /*
+                * Visit root declarations next, as they can be used by any
+                * following entity.
+                */
+               bt_list_for_each_entry(iter, &node->u.root.declaration_list,
+                               siblings) {
+                       ret = visit_root_decl(ctx, iter);
+                       if (ret) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
+                                       "Cannot visit root entry: ret=%d",
+                                       ret);
+                               goto end;
+                       }
+               }
+
+               BT_ASSERT(ctx->current_scope &&
+                       !ctx->current_scope->parent_scope);
+
+               /* Callsite blocks are not supported */
+               bt_list_for_each_entry(iter, &node->u.root.callsite, siblings) {
+                       _BT_COMP_LOGW_NODE(iter,
+                               "\"callsite\" blocks are not supported as of this version.");
+               }
+
+               BT_ASSERT(ctx->current_scope &&
+                       !ctx->current_scope->parent_scope);
+
+               /* Trace */
+               bt_list_for_each_entry(iter, &node->u.root.trace, siblings) {
+                       ret = visit_trace_decl(ctx, iter);
+                       if (ret) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
+                                       "Cannot visit trace (`trace` block): "
+                                       "ret=%d", ret);
+                               goto end;
+                       }
+               }
+
+               BT_ASSERT(ctx->current_scope &&
+                       !ctx->current_scope->parent_scope);
+
+               /* Streams */
+               bt_list_for_each_entry(iter, &node->u.root.stream, siblings) {
+                       ret = visit_stream_decl(ctx, iter);
+                       if (ret) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
+                                       "Cannot visit stream class: ret=%d",
+                                       ret);
+                               goto end;
+                       }
+               }
+
+               BT_ASSERT(ctx->current_scope &&
+                       !ctx->current_scope->parent_scope);
+
+               /* Events */
+               bt_list_for_each_entry(iter, &node->u.root.event, siblings) {
+                       ret = visit_event_decl(ctx, iter);
+                       if (ret) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
+                                       "Cannot visit event class: ret=%d",
+                                       ret);
+                               goto end;
+                       }
+               }
+
+               BT_ASSERT(ctx->current_scope &&
+                       !ctx->current_scope->parent_scope);
+               break;
+       }
+       default:
+               _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+                       "Unexpected node type: node-type=%d",
+                       node->type);
+               ret = -EINVAL;
+               goto end;
+       }
+
+       /* Update default clock classes */
+       ret = ctf_trace_class_update_default_clock_classes(ctx->ctf_tc,
+               &ctx->log_cfg);
+       if (ret) {
+               ret = -EINVAL;
+               goto end;
+       }
+
+       /* Update trace class meanings */
+       ret = ctf_trace_class_update_meanings(ctx->ctf_tc);
+       if (ret) {
+               ret = -EINVAL;
+               goto end;
+       }
+
+       /* Update stream class configuration */
+       ret = ctf_trace_class_update_stream_class_config(ctx->ctf_tc);
+       if (ret) {
+               ret = -EINVAL;
+               goto end;
+       }
+
+       /* Update text arrays and sequences */
+       ret = ctf_trace_class_update_text_array_sequence(ctx->ctf_tc);
+       if (ret) {
+               ret = -EINVAL;
+               goto end;
+       }
+
+       /* Update structure/array/sequence alignments */
+       ret = ctf_trace_class_update_alignments(ctx->ctf_tc);
+       if (ret) {
+               ret = -EINVAL;
+               goto end;
+       }
+
+       /* Resolve sequence lengths and variant tags */
+       ret = ctf_trace_class_resolve_field_classes(ctx->ctf_tc, &ctx->log_cfg);
+       if (ret) {
+               ret = -EINVAL;
+               goto end;
+       }
+
+       if (ctx->trace_class) {
+               /*
+                * Update "in IR" for field classes.
+                *
+                * If we have no IR trace class, then we'll have no way
+                * to create IR fields anyway, so we leave all the
+                * `in_ir` members false.
+                */
+               ret = ctf_trace_class_update_in_ir(ctx->ctf_tc);
+               if (ret) {
+                       ret = -EINVAL;
+                       goto end;
+               }
+       }
+
+       /* Update saved value indexes */
+       ret = ctf_trace_class_update_value_storing_indexes(ctx->ctf_tc);
+       if (ret) {
+               ret = -EINVAL;
+               goto end;
+       }
+
+       /* Validate what we have so far */
+       ret = ctf_trace_class_validate(ctx->ctf_tc, &ctx->log_cfg);
+       if (ret) {
+               ret = -EINVAL;
+               goto end;
+       }
+
+       /*
+        * If there are fields which are not related to the CTF format
+        * itself in the packet header and in event header field
+        * classes, warn about it because they are never translated.
+        */
+       ctf_trace_class_warn_meaningless_header_fields(ctx->ctf_tc,
+               &ctx->log_cfg);
+
+       if (ctx->trace_class) {
+               /* Copy new CTF metadata -> new IR metadata */
+               ret = ctf_trace_class_translate(ctx->log_cfg.self_comp,
+                               ctx->trace_class, ctx->ctf_tc);
+               if (ret) {
+                       ret = -EINVAL;
+                       goto end;
+               }
+       }
+
+end:
+       return ret;
+}
diff --git a/src/plugins/ctf/common/metadata/visitor-parent-links.c b/src/plugins/ctf/common/metadata/visitor-parent-links.c
deleted file mode 100644 (file)
index 5afcb0e..0000000
+++ /dev/null
@@ -1,498 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2010 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * Common Trace Format Metadata Parent Link Creator.
- */
-
-#define BT_COMP_LOG_SELF_COMP (log_cfg->self_comp)
-#define BT_LOG_OUTPUT_LEVEL (log_cfg->log_level)
-#define BT_LOG_TAG "PLUGIN/CTF/META/PARENT-LINKS-VISITOR"
-#include "logging/comp-logging.h"
-
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-#include "common/assert.h"
-#include <glib.h>
-#include <inttypes.h>
-#include <errno.h>
-#include "common/macros.h"
-#include "common/list.h"
-#include "scanner.h"
-#include "ast.h"
-#include "logging.h"
-
-static
-int ctf_visitor_unary_expression(int depth, struct ctf_node *node,
-               struct meta_log_config *log_cfg)
-{
-       int ret = 0;
-
-       switch (node->u.unary_expression.link) {
-       case UNARY_LINK_UNKNOWN:
-       case UNARY_DOTLINK:
-       case UNARY_ARROWLINK:
-       case UNARY_DOTDOTDOT:
-               break;
-       default:
-               _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-                       "Unknown expression link type: type=%d\n",
-                       node->u.unary_expression.link);
-               return -EINVAL;
-       }
-
-       switch (node->u.unary_expression.type) {
-       case UNARY_STRING:
-       case UNARY_SIGNED_CONSTANT:
-       case UNARY_UNSIGNED_CONSTANT:
-               break;
-       case UNARY_SBRAC:
-               node->u.unary_expression.u.sbrac_exp->parent = node;
-               ret = ctf_visitor_unary_expression(depth + 1,
-                       node->u.unary_expression.u.sbrac_exp,
-                       log_cfg);
-               if (ret)
-                       return ret;
-               break;
-
-       case UNARY_UNKNOWN:
-       default:
-               _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-                       "Unknown expression link type: type=%d\n",
-                       node->u.unary_expression.link);
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static
-int ctf_visitor_type_specifier(int depth, struct ctf_node *node,
-               struct meta_log_config *log_cfg)
-{
-       int ret;
-
-       switch (node->u.field_class_specifier.type) {
-       case TYPESPEC_VOID:
-       case TYPESPEC_CHAR:
-       case TYPESPEC_SHORT:
-       case TYPESPEC_INT:
-       case TYPESPEC_LONG:
-       case TYPESPEC_FLOAT:
-       case TYPESPEC_DOUBLE:
-       case TYPESPEC_SIGNED:
-       case TYPESPEC_UNSIGNED:
-       case TYPESPEC_BOOL:
-       case TYPESPEC_COMPLEX:
-       case TYPESPEC_IMAGINARY:
-       case TYPESPEC_CONST:
-       case TYPESPEC_ID_TYPE:
-               break;
-       case TYPESPEC_FLOATING_POINT:
-       case TYPESPEC_INTEGER:
-       case TYPESPEC_STRING:
-       case TYPESPEC_STRUCT:
-       case TYPESPEC_VARIANT:
-       case TYPESPEC_ENUM:
-               node->u.field_class_specifier.node->parent = node;
-               ret = ctf_visitor_parent_links(depth + 1,
-                       node->u.field_class_specifier.node,
-                       log_cfg);
-               if (ret)
-                       return ret;
-               break;
-
-       case TYPESPEC_UNKNOWN:
-       default:
-               _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-                       "Unknown type specifier: type=%d\n",
-                       node->u.field_class_specifier.type);
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static
-int ctf_visitor_field_class_declarator(int depth, struct ctf_node *node,
-               struct meta_log_config *log_cfg)
-{
-       int ret = 0;
-       struct ctf_node *iter;
-
-       depth++;
-
-       bt_list_for_each_entry(iter, &node->u.field_class_declarator.pointers,
-                               siblings) {
-               iter->parent = node;
-               ret = ctf_visitor_parent_links(depth + 1, iter, log_cfg);
-               if (ret)
-                       return ret;
-       }
-
-       switch (node->u.field_class_declarator.type) {
-       case TYPEDEC_ID:
-               break;
-       case TYPEDEC_NESTED:
-               if (node->u.field_class_declarator.u.nested.field_class_declarator) {
-                       node->u.field_class_declarator.u.nested.field_class_declarator->parent = node;
-                       ret = ctf_visitor_parent_links(depth + 1,
-                               node->u.field_class_declarator.u.nested.field_class_declarator,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               if (!node->u.field_class_declarator.u.nested.abstract_array) {
-                       bt_list_for_each_entry(iter, &node->u.field_class_declarator.u.nested.length,
-                                               siblings) {
-                               iter->parent = node;
-                               ret = ctf_visitor_parent_links(depth + 1, iter,
-                                       log_cfg);
-                               if (ret)
-                                       return ret;
-                       }
-               }
-               if (node->u.field_class_declarator.bitfield_len) {
-                       node->u.field_class_declarator.bitfield_len = node;
-                       ret = ctf_visitor_parent_links(depth + 1,
-                               node->u.field_class_declarator.bitfield_len,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               break;
-       case TYPEDEC_UNKNOWN:
-       default:
-               _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-                       "Unknown type declarator: type=%d\n",
-                       node->u.field_class_declarator.type);
-               return -EINVAL;
-       }
-       depth--;
-       return 0;
-}
-
-int ctf_visitor_parent_links(int depth, struct ctf_node *node,
-               struct meta_log_config *log_cfg)
-{
-       int ret = 0;
-       struct ctf_node *iter;
-
-       if (node->visited)
-               return 0;
-
-       switch (node->type) {
-       case NODE_ROOT:
-               bt_list_for_each_entry(iter, &node->u.root.declaration_list, siblings) {
-                       iter->parent = node;
-                       ret = ctf_visitor_parent_links(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               bt_list_for_each_entry(iter, &node->u.root.trace, siblings) {
-                       iter->parent = node;
-                       ret = ctf_visitor_parent_links(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               bt_list_for_each_entry(iter, &node->u.root.stream, siblings) {
-                       iter->parent = node;
-                       ret = ctf_visitor_parent_links(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               bt_list_for_each_entry(iter, &node->u.root.event, siblings) {
-                       iter->parent = node;
-                       ret = ctf_visitor_parent_links(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               bt_list_for_each_entry(iter, &node->u.root.clock, siblings) {
-                       iter->parent = node;
-                       ret = ctf_visitor_parent_links(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               bt_list_for_each_entry(iter, &node->u.root.callsite, siblings) {
-                       iter->parent = node;
-                       ret = ctf_visitor_parent_links(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               break;
-
-       case NODE_EVENT:
-               bt_list_for_each_entry(iter, &node->u.event.declaration_list, siblings) {
-                       iter->parent = node;
-                       ret = ctf_visitor_parent_links(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               break;
-       case NODE_STREAM:
-               bt_list_for_each_entry(iter, &node->u.stream.declaration_list, siblings) {
-                       iter->parent = node;
-                       ret = ctf_visitor_parent_links(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               break;
-       case NODE_ENV:
-               bt_list_for_each_entry(iter, &node->u.env.declaration_list, siblings) {
-                       iter->parent = node;
-                       ret = ctf_visitor_parent_links(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               break;
-       case NODE_TRACE:
-               bt_list_for_each_entry(iter, &node->u.trace.declaration_list, siblings) {
-                       iter->parent = node;
-                       ret = ctf_visitor_parent_links(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               break;
-       case NODE_CLOCK:
-               bt_list_for_each_entry(iter, &node->u.clock.declaration_list, siblings) {
-                       iter->parent = node;
-                       ret = ctf_visitor_parent_links(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               break;
-       case NODE_CALLSITE:
-               bt_list_for_each_entry(iter, &node->u.callsite.declaration_list, siblings) {
-                       iter->parent = node;
-                       ret = ctf_visitor_parent_links(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               break;
-
-       case NODE_CTF_EXPRESSION:
-               depth++;
-               bt_list_for_each_entry(iter, &node->u.ctf_expression.left, siblings) {
-                       iter->parent = node;
-                       ret = ctf_visitor_parent_links(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               bt_list_for_each_entry(iter, &node->u.ctf_expression.right, siblings) {
-                       iter->parent = node;
-                       ret = ctf_visitor_parent_links(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               depth--;
-               break;
-       case NODE_UNARY_EXPRESSION:
-               return ctf_visitor_unary_expression(depth, node, log_cfg);
-
-       case NODE_TYPEDEF:
-               depth++;
-               node->u.field_class_def.field_class_specifier_list->parent = node;
-               ret = ctf_visitor_parent_links(depth + 1,
-                       node->u.field_class_def.field_class_specifier_list,
-                       log_cfg);
-               if (ret)
-                       return ret;
-               bt_list_for_each_entry(iter, &node->u.field_class_def.field_class_declarators, siblings) {
-                       iter->parent = node;
-                       ret = ctf_visitor_parent_links(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               depth--;
-               break;
-       case NODE_TYPEALIAS_TARGET:
-               depth++;
-               node->u.field_class_alias_target.field_class_specifier_list->parent = node;
-               ret = ctf_visitor_parent_links(depth + 1,
-                       node->u.field_class_alias_target.field_class_specifier_list,
-                       log_cfg);
-               if (ret)
-                       return ret;
-               bt_list_for_each_entry(iter, &node->u.field_class_alias_target.field_class_declarators, siblings) {
-                       iter->parent = node;
-                       ret = ctf_visitor_parent_links(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               depth--;
-               break;
-       case NODE_TYPEALIAS_ALIAS:
-               depth++;
-               node->u.field_class_alias_name.field_class_specifier_list->parent = node;
-               ret = ctf_visitor_parent_links(depth + 1,
-                       node->u.field_class_alias_name.field_class_specifier_list,
-                       log_cfg);
-               if (ret)
-                       return ret;
-               bt_list_for_each_entry(iter, &node->u.field_class_alias_name.field_class_declarators, siblings) {
-                       iter->parent = node;
-                       ret = ctf_visitor_parent_links(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               depth--;
-               break;
-       case NODE_TYPEALIAS:
-               node->u.field_class_alias.target->parent = node;
-               ret = ctf_visitor_parent_links(depth + 1,
-                       node->u.field_class_alias.target, log_cfg);
-               if (ret)
-                       return ret;
-               node->u.field_class_alias.alias->parent = node;
-               ret = ctf_visitor_parent_links(depth + 1,
-                       node->u.field_class_alias.alias, log_cfg);
-               if (ret)
-                       return ret;
-               break;
-
-       case NODE_TYPE_SPECIFIER_LIST:
-               bt_list_for_each_entry(iter, &node->u.field_class_specifier_list.head, siblings) {
-                       iter->parent = node;
-                       ret = ctf_visitor_parent_links(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               break;
-
-       case NODE_TYPE_SPECIFIER:
-               ret = ctf_visitor_type_specifier(depth, node, log_cfg);
-               if (ret)
-                       return ret;
-               break;
-       case NODE_POINTER:
-               break;
-       case NODE_TYPE_DECLARATOR:
-               ret = ctf_visitor_field_class_declarator(depth, node,
-                       log_cfg);
-               if (ret)
-                       return ret;
-               break;
-
-       case NODE_FLOATING_POINT:
-               bt_list_for_each_entry(iter, &node->u.floating_point.expressions, siblings) {
-                       iter->parent = node;
-                       ret = ctf_visitor_parent_links(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               break;
-       case NODE_INTEGER:
-               bt_list_for_each_entry(iter, &node->u.integer.expressions, siblings) {
-                       iter->parent = node;
-                       ret = ctf_visitor_parent_links(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               break;
-       case NODE_STRING:
-               bt_list_for_each_entry(iter, &node->u.string.expressions, siblings) {
-                       iter->parent = node;
-                       ret = ctf_visitor_parent_links(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               break;
-       case NODE_ENUMERATOR:
-               bt_list_for_each_entry(iter, &node->u.enumerator.values, siblings) {
-                       iter->parent = node;
-                       ret = ctf_visitor_parent_links(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               break;
-       case NODE_ENUM:
-               depth++;
-               if (node->u._enum.container_field_class) {
-                       ret = ctf_visitor_parent_links(depth + 1,
-                               node->u._enum.container_field_class, log_cfg);
-                       if (ret)
-                               return ret;
-               }
-
-               bt_list_for_each_entry(iter, &node->u._enum.enumerator_list, siblings) {
-                       iter->parent = node;
-                       ret = ctf_visitor_parent_links(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               depth--;
-               break;
-       case NODE_STRUCT_OR_VARIANT_DECLARATION:
-               node->u.struct_or_variant_declaration.field_class_specifier_list->parent = node;
-               ret = ctf_visitor_parent_links(depth + 1,
-                       node->u.struct_or_variant_declaration.field_class_specifier_list,
-                       log_cfg);
-               if (ret)
-                       return ret;
-               bt_list_for_each_entry(iter, &node->u.struct_or_variant_declaration.field_class_declarators, siblings) {
-                       iter->parent = node;
-                       ret = ctf_visitor_parent_links(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               break;
-       case NODE_VARIANT:
-               bt_list_for_each_entry(iter, &node->u.variant.declaration_list, siblings) {
-                       iter->parent = node;
-                       ret = ctf_visitor_parent_links(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               break;
-       case NODE_STRUCT:
-               bt_list_for_each_entry(iter, &node->u._struct.declaration_list, siblings) {
-                       iter->parent = node;
-                       ret = ctf_visitor_parent_links(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               bt_list_for_each_entry(iter, &node->u._struct.min_align,
-                                       siblings) {
-                       iter->parent = node;
-                       ret = ctf_visitor_parent_links(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               break;
-
-       case NODE_UNKNOWN:
-       default:
-               _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-                       "Unknown node type: type=%d\n", node->type);
-               return -EINVAL;
-       }
-       return ret;
-}
diff --git a/src/plugins/ctf/common/metadata/visitor-parent-links.cpp b/src/plugins/ctf/common/metadata/visitor-parent-links.cpp
new file mode 100644 (file)
index 0000000..f34df9f
--- /dev/null
@@ -0,0 +1,498 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2010 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * Common Trace Format Metadata Parent Link Creator.
+ */
+
+#define BT_COMP_LOG_SELF_COMP (log_cfg->self_comp)
+#define BT_LOG_OUTPUT_LEVEL (log_cfg->log_level)
+#define BT_LOG_TAG "PLUGIN/CTF/META/PARENT-LINKS-VISITOR"
+#include "logging/comp-logging.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include "common/assert.h"
+#include <glib.h>
+#include <inttypes.h>
+#include <errno.h>
+#include "common/macros.h"
+#include "common/list.h"
+#include "scanner.hpp"
+#include "ast.hpp"
+#include "logging.hpp"
+
+static
+int ctf_visitor_unary_expression(int depth, struct ctf_node *node,
+               struct meta_log_config *log_cfg)
+{
+       int ret = 0;
+
+       switch (node->u.unary_expression.link) {
+       case UNARY_LINK_UNKNOWN:
+       case UNARY_DOTLINK:
+       case UNARY_ARROWLINK:
+       case UNARY_DOTDOTDOT:
+               break;
+       default:
+               _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
+                       "Unknown expression link type: type=%d\n",
+                       node->u.unary_expression.link);
+               return -EINVAL;
+       }
+
+       switch (node->u.unary_expression.type) {
+       case UNARY_STRING:
+       case UNARY_SIGNED_CONSTANT:
+       case UNARY_UNSIGNED_CONSTANT:
+               break;
+       case UNARY_SBRAC:
+               node->u.unary_expression.u.sbrac_exp->parent = node;
+               ret = ctf_visitor_unary_expression(depth + 1,
+                       node->u.unary_expression.u.sbrac_exp,
+                       log_cfg);
+               if (ret)
+                       return ret;
+               break;
+
+       case UNARY_UNKNOWN:
+       default:
+               _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
+                       "Unknown expression link type: type=%d\n",
+                       node->u.unary_expression.link);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static
+int ctf_visitor_type_specifier(int depth, struct ctf_node *node,
+               struct meta_log_config *log_cfg)
+{
+       int ret;
+
+       switch (node->u.field_class_specifier.type) {
+       case TYPESPEC_VOID:
+       case TYPESPEC_CHAR:
+       case TYPESPEC_SHORT:
+       case TYPESPEC_INT:
+       case TYPESPEC_LONG:
+       case TYPESPEC_FLOAT:
+       case TYPESPEC_DOUBLE:
+       case TYPESPEC_SIGNED:
+       case TYPESPEC_UNSIGNED:
+       case TYPESPEC_BOOL:
+       case TYPESPEC_COMPLEX:
+       case TYPESPEC_IMAGINARY:
+       case TYPESPEC_CONST:
+       case TYPESPEC_ID_TYPE:
+               break;
+       case TYPESPEC_FLOATING_POINT:
+       case TYPESPEC_INTEGER:
+       case TYPESPEC_STRING:
+       case TYPESPEC_STRUCT:
+       case TYPESPEC_VARIANT:
+       case TYPESPEC_ENUM:
+               node->u.field_class_specifier.node->parent = node;
+               ret = ctf_visitor_parent_links(depth + 1,
+                       node->u.field_class_specifier.node,
+                       log_cfg);
+               if (ret)
+                       return ret;
+               break;
+
+       case TYPESPEC_UNKNOWN:
+       default:
+               _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
+                       "Unknown type specifier: type=%d\n",
+                       node->u.field_class_specifier.type);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static
+int ctf_visitor_field_class_declarator(int depth, struct ctf_node *node,
+               struct meta_log_config *log_cfg)
+{
+       int ret = 0;
+       struct ctf_node *iter;
+
+       depth++;
+
+       bt_list_for_each_entry(iter, &node->u.field_class_declarator.pointers,
+                               siblings) {
+               iter->parent = node;
+               ret = ctf_visitor_parent_links(depth + 1, iter, log_cfg);
+               if (ret)
+                       return ret;
+       }
+
+       switch (node->u.field_class_declarator.type) {
+       case TYPEDEC_ID:
+               break;
+       case TYPEDEC_NESTED:
+               if (node->u.field_class_declarator.u.nested.field_class_declarator) {
+                       node->u.field_class_declarator.u.nested.field_class_declarator->parent = node;
+                       ret = ctf_visitor_parent_links(depth + 1,
+                               node->u.field_class_declarator.u.nested.field_class_declarator,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               if (!node->u.field_class_declarator.u.nested.abstract_array) {
+                       bt_list_for_each_entry(iter, &node->u.field_class_declarator.u.nested.length,
+                                               siblings) {
+                               iter->parent = node;
+                               ret = ctf_visitor_parent_links(depth + 1, iter,
+                                       log_cfg);
+                               if (ret)
+                                       return ret;
+                       }
+               }
+               if (node->u.field_class_declarator.bitfield_len) {
+                       node->u.field_class_declarator.bitfield_len = node;
+                       ret = ctf_visitor_parent_links(depth + 1,
+                               node->u.field_class_declarator.bitfield_len,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case TYPEDEC_UNKNOWN:
+       default:
+               _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
+                       "Unknown type declarator: type=%d\n",
+                       node->u.field_class_declarator.type);
+               return -EINVAL;
+       }
+       depth--;
+       return 0;
+}
+
+int ctf_visitor_parent_links(int depth, struct ctf_node *node,
+               struct meta_log_config *log_cfg)
+{
+       int ret = 0;
+       struct ctf_node *iter;
+
+       if (node->visited)
+               return 0;
+
+       switch (node->type) {
+       case NODE_ROOT:
+               bt_list_for_each_entry(iter, &node->u.root.declaration_list, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               bt_list_for_each_entry(iter, &node->u.root.trace, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               bt_list_for_each_entry(iter, &node->u.root.stream, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               bt_list_for_each_entry(iter, &node->u.root.event, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               bt_list_for_each_entry(iter, &node->u.root.clock, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               bt_list_for_each_entry(iter, &node->u.root.callsite, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               break;
+
+       case NODE_EVENT:
+               bt_list_for_each_entry(iter, &node->u.event.declaration_list, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_STREAM:
+               bt_list_for_each_entry(iter, &node->u.stream.declaration_list, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_ENV:
+               bt_list_for_each_entry(iter, &node->u.env.declaration_list, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_TRACE:
+               bt_list_for_each_entry(iter, &node->u.trace.declaration_list, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_CLOCK:
+               bt_list_for_each_entry(iter, &node->u.clock.declaration_list, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_CALLSITE:
+               bt_list_for_each_entry(iter, &node->u.callsite.declaration_list, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               break;
+
+       case NODE_CTF_EXPRESSION:
+               depth++;
+               bt_list_for_each_entry(iter, &node->u.ctf_expression.left, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               bt_list_for_each_entry(iter, &node->u.ctf_expression.right, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               depth--;
+               break;
+       case NODE_UNARY_EXPRESSION:
+               return ctf_visitor_unary_expression(depth, node, log_cfg);
+
+       case NODE_TYPEDEF:
+               depth++;
+               node->u.field_class_def.field_class_specifier_list->parent = node;
+               ret = ctf_visitor_parent_links(depth + 1,
+                       node->u.field_class_def.field_class_specifier_list,
+                       log_cfg);
+               if (ret)
+                       return ret;
+               bt_list_for_each_entry(iter, &node->u.field_class_def.field_class_declarators, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               depth--;
+               break;
+       case NODE_TYPEALIAS_TARGET:
+               depth++;
+               node->u.field_class_alias_target.field_class_specifier_list->parent = node;
+               ret = ctf_visitor_parent_links(depth + 1,
+                       node->u.field_class_alias_target.field_class_specifier_list,
+                       log_cfg);
+               if (ret)
+                       return ret;
+               bt_list_for_each_entry(iter, &node->u.field_class_alias_target.field_class_declarators, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               depth--;
+               break;
+       case NODE_TYPEALIAS_ALIAS:
+               depth++;
+               node->u.field_class_alias_name.field_class_specifier_list->parent = node;
+               ret = ctf_visitor_parent_links(depth + 1,
+                       node->u.field_class_alias_name.field_class_specifier_list,
+                       log_cfg);
+               if (ret)
+                       return ret;
+               bt_list_for_each_entry(iter, &node->u.field_class_alias_name.field_class_declarators, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               depth--;
+               break;
+       case NODE_TYPEALIAS:
+               node->u.field_class_alias.target->parent = node;
+               ret = ctf_visitor_parent_links(depth + 1,
+                       node->u.field_class_alias.target, log_cfg);
+               if (ret)
+                       return ret;
+               node->u.field_class_alias.alias->parent = node;
+               ret = ctf_visitor_parent_links(depth + 1,
+                       node->u.field_class_alias.alias, log_cfg);
+               if (ret)
+                       return ret;
+               break;
+
+       case NODE_TYPE_SPECIFIER_LIST:
+               bt_list_for_each_entry(iter, &node->u.field_class_specifier_list.head, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               break;
+
+       case NODE_TYPE_SPECIFIER:
+               ret = ctf_visitor_type_specifier(depth, node, log_cfg);
+               if (ret)
+                       return ret;
+               break;
+       case NODE_POINTER:
+               break;
+       case NODE_TYPE_DECLARATOR:
+               ret = ctf_visitor_field_class_declarator(depth, node,
+                       log_cfg);
+               if (ret)
+                       return ret;
+               break;
+
+       case NODE_FLOATING_POINT:
+               bt_list_for_each_entry(iter, &node->u.floating_point.expressions, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_INTEGER:
+               bt_list_for_each_entry(iter, &node->u.integer.expressions, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_STRING:
+               bt_list_for_each_entry(iter, &node->u.string.expressions, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_ENUMERATOR:
+               bt_list_for_each_entry(iter, &node->u.enumerator.values, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_ENUM:
+               depth++;
+               if (node->u._enum.container_field_class) {
+                       ret = ctf_visitor_parent_links(depth + 1,
+                               node->u._enum.container_field_class, log_cfg);
+                       if (ret)
+                               return ret;
+               }
+
+               bt_list_for_each_entry(iter, &node->u._enum.enumerator_list, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               depth--;
+               break;
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+               node->u.struct_or_variant_declaration.field_class_specifier_list->parent = node;
+               ret = ctf_visitor_parent_links(depth + 1,
+                       node->u.struct_or_variant_declaration.field_class_specifier_list,
+                       log_cfg);
+               if (ret)
+                       return ret;
+               bt_list_for_each_entry(iter, &node->u.struct_or_variant_declaration.field_class_declarators, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_VARIANT:
+               bt_list_for_each_entry(iter, &node->u.variant.declaration_list, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_STRUCT:
+               bt_list_for_each_entry(iter, &node->u._struct.declaration_list, siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               bt_list_for_each_entry(iter, &node->u._struct.min_align,
+                                       siblings) {
+                       iter->parent = node;
+                       ret = ctf_visitor_parent_links(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               break;
+
+       case NODE_UNKNOWN:
+       default:
+               _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
+                       "Unknown node type: type=%d\n", node->type);
+               return -EINVAL;
+       }
+       return ret;
+}
diff --git a/src/plugins/ctf/common/metadata/visitor-semantic-validator.c b/src/plugins/ctf/common/metadata/visitor-semantic-validator.c
deleted file mode 100644 (file)
index 4ac2a06..0000000
+++ /dev/null
@@ -1,1039 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2010 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * Common Trace Format Metadata Semantic Validator.
- */
-
-#define BT_COMP_LOG_SELF_COMP (log_cfg->self_comp)
-#define BT_LOG_OUTPUT_LEVEL (log_cfg->log_level)
-#define BT_LOG_TAG "PLUGIN/CTF/META/SEMANTIC-VALIDATOR-VISITOR"
-#include "logging/comp-logging.h"
-
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-#include "common/assert.h"
-#include <glib.h>
-#include <inttypes.h>
-#include <errno.h>
-#include "common/list.h"
-#include "scanner.h"
-#include "ast.h"
-#include "logging.h"
-
-#define _bt_list_first_entry(ptr, type, member)        \
-       bt_list_entry((ptr)->next, type, member)
-
-static
-int _ctf_visitor_semantic_check(int depth, struct ctf_node *node,
-               struct meta_log_config *log_cfg);
-
-static
-int ctf_visitor_unary_expression(int depth, struct ctf_node *node,
-               struct meta_log_config *log_cfg)
-{
-       struct ctf_node *iter;
-       int is_ctf_exp = 0, is_ctf_exp_left = 0;
-
-       switch (node->parent->type) {
-       case NODE_CTF_EXPRESSION:
-               is_ctf_exp = 1;
-               bt_list_for_each_entry(iter, &node->parent->u.ctf_expression.left,
-                                       siblings) {
-                       if (iter == node) {
-                               is_ctf_exp_left = 1;
-                               /*
-                                * We are a left child of a ctf expression.
-                                * We are only allowed to be a string.
-                                */
-                               if (node->u.unary_expression.type != UNARY_STRING) {
-                                       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-                                               "Left child of a CTF expression is only allowed to be a string.");
-                                       goto errperm;
-                               }
-                               break;
-                       }
-               }
-               /* Right child of a ctf expression can be any type of unary exp. */
-               break;                  /* OK */
-       case NODE_TYPE_DECLARATOR:
-               /*
-                * We are the length of a type declarator.
-                */
-               switch (node->u.unary_expression.type) {
-               case UNARY_UNSIGNED_CONSTANT:
-               case UNARY_STRING:
-                       break;
-               default:
-                       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-                               "Children of field class declarator and `enum` can only be unsigned numeric constants or references to fields (e.g., `a.b.c`).");
-                       goto errperm;
-               }
-               break;                  /* OK */
-
-       case NODE_STRUCT:
-               /*
-                * We are the size of a struct align attribute.
-                */
-               switch (node->u.unary_expression.type) {
-               case UNARY_UNSIGNED_CONSTANT:
-                       break;
-               default:
-                       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-                               "Structure alignment attribute can only be an unsigned numeric constant.");
-                       goto errperm;
-               }
-               break;
-
-       case NODE_ENUMERATOR:
-               /* The enumerator's parent has validated its validity already. */
-               break;                  /* OK */
-
-       case NODE_UNARY_EXPRESSION:
-               /*
-                * We disallow nested unary expressions and "sbrac" unary
-                * expressions.
-                */
-               _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-                       "Nested unary expressions not allowed (`()` and `[]`).");
-               goto errperm;
-
-       case NODE_ROOT:
-       case NODE_EVENT:
-       case NODE_STREAM:
-       case NODE_ENV:
-       case NODE_TRACE:
-       case NODE_CLOCK:
-       case NODE_CALLSITE:
-       case NODE_TYPEDEF:
-       case NODE_TYPEALIAS_TARGET:
-       case NODE_TYPEALIAS_ALIAS:
-       case NODE_TYPEALIAS:
-       case NODE_TYPE_SPECIFIER:
-       case NODE_POINTER:
-       case NODE_FLOATING_POINT:
-       case NODE_INTEGER:
-       case NODE_STRING:
-       case NODE_ENUM:
-       case NODE_STRUCT_OR_VARIANT_DECLARATION:
-       case NODE_VARIANT:
-       default:
-               goto errinval;
-       }
-
-       switch (node->u.unary_expression.link) {
-       case UNARY_LINK_UNKNOWN:
-               /* We don't allow empty link except on the first node of the list */
-               if (is_ctf_exp && _bt_list_first_entry(is_ctf_exp_left ?
-                                         &node->parent->u.ctf_expression.left :
-                                         &node->parent->u.ctf_expression.right,
-                                         struct ctf_node,
-                                         siblings) != node) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-                               "Empty link is not allowed except on first node of unary expression (need to separate nodes with `.` or `->`).");
-                       goto errperm;
-               }
-               break;                  /* OK */
-       case UNARY_DOTLINK:
-       case UNARY_ARROWLINK:
-               /* We only allow -> and . links between children of ctf_expression. */
-               if (node->parent->type != NODE_CTF_EXPRESSION) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-                               "Links `.` and `->` are only allowed as children of CTF expression.");
-                       goto errperm;
-               }
-               /*
-                * Only strings can be separated linked by . or ->.
-                * This includes "", '' and non-quoted identifiers.
-                */
-               if (node->u.unary_expression.type != UNARY_STRING) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-                               "Links `.` and `->` are only allowed to separate strings and identifiers.");
-                       goto errperm;
-               }
-               /* We don't allow link on the first node of the list */
-               if (is_ctf_exp && _bt_list_first_entry(is_ctf_exp_left ?
-                                         &node->parent->u.ctf_expression.left :
-                                         &node->parent->u.ctf_expression.right,
-                                         struct ctf_node,
-                                         siblings) == node) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-                               "Links `.` and `->` are not allowed before first node of the unary expression list.");
-                       goto errperm;
-               }
-               break;
-       case UNARY_DOTDOTDOT:
-               /* We only allow ... link between children of enumerator. */
-               if (node->parent->type != NODE_ENUMERATOR) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-                               "Link `...` is only allowed within enumerator.");
-                       goto errperm;
-               }
-               /* We don't allow link on the first node of the list */
-               if (_bt_list_first_entry(&node->parent->u.enumerator.values,
-                                         struct ctf_node,
-                                         siblings) == node) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-                               "Link `...` is not allowed on the first node of the unary expression list.");
-                       goto errperm;
-               }
-               break;
-       default:
-               _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-                       "Unknown expression link type: type=%d",
-                       node->u.unary_expression.link);
-               return -EINVAL;
-       }
-       return 0;
-
-errinval:
-       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-               "Incoherent parent node's type: node-type=%s, parent-node-type=%s",
-               node_type(node), node_type(node->parent));
-       return -EINVAL;         /* Incoherent structure */
-
-errperm:
-       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-               "Semantic error: node-type=%s, parent-node-type=%s",
-               node_type(node), node_type(node->parent));
-       return -EPERM;          /* Structure not allowed */
-}
-
-static
-int ctf_visitor_field_class_specifier_list(int depth, struct ctf_node *node,
-               struct meta_log_config *log_cfg)
-{
-       switch (node->parent->type) {
-       case NODE_CTF_EXPRESSION:
-       case NODE_TYPE_DECLARATOR:
-       case NODE_TYPEDEF:
-       case NODE_TYPEALIAS_TARGET:
-       case NODE_TYPEALIAS_ALIAS:
-       case NODE_ENUM:
-       case NODE_STRUCT_OR_VARIANT_DECLARATION:
-       case NODE_ROOT:
-               break;                  /* OK */
-
-       case NODE_EVENT:
-       case NODE_STREAM:
-       case NODE_ENV:
-       case NODE_TRACE:
-       case NODE_CLOCK:
-       case NODE_CALLSITE:
-       case NODE_UNARY_EXPRESSION:
-       case NODE_TYPEALIAS:
-       case NODE_TYPE_SPECIFIER:
-       case NODE_TYPE_SPECIFIER_LIST:
-       case NODE_POINTER:
-       case NODE_FLOATING_POINT:
-       case NODE_INTEGER:
-       case NODE_STRING:
-       case NODE_ENUMERATOR:
-       case NODE_VARIANT:
-       case NODE_STRUCT:
-       default:
-               goto errinval;
-       }
-       return 0;
-errinval:
-       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-               "Incoherent parent node's type: node-type=%s, parent-node-type=%s",
-               node_type(node), node_type(node->parent));
-       return -EINVAL;         /* Incoherent structure */
-}
-
-static
-int ctf_visitor_field_class_specifier(int depth, struct ctf_node *node,
-               struct meta_log_config *log_cfg)
-{
-       switch (node->parent->type) {
-       case NODE_TYPE_SPECIFIER_LIST:
-               break;                  /* OK */
-
-       case NODE_CTF_EXPRESSION:
-       case NODE_TYPE_DECLARATOR:
-       case NODE_TYPEDEF:
-       case NODE_TYPEALIAS_TARGET:
-       case NODE_TYPEALIAS_ALIAS:
-       case NODE_ENUM:
-       case NODE_STRUCT_OR_VARIANT_DECLARATION:
-       case NODE_ROOT:
-       case NODE_EVENT:
-       case NODE_STREAM:
-       case NODE_ENV:
-       case NODE_TRACE:
-       case NODE_CLOCK:
-       case NODE_CALLSITE:
-       case NODE_UNARY_EXPRESSION:
-       case NODE_TYPEALIAS:
-       case NODE_TYPE_SPECIFIER:
-       case NODE_POINTER:
-       case NODE_FLOATING_POINT:
-       case NODE_INTEGER:
-       case NODE_STRING:
-       case NODE_ENUMERATOR:
-       case NODE_VARIANT:
-       case NODE_STRUCT:
-       default:
-               goto errinval;
-       }
-       return 0;
-errinval:
-       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-               "Incoherent parent node's type: node-type=%s, parent-node-type=%s",
-               node_type(node), node_type(node->parent));
-       return -EINVAL;         /* Incoherent structure */
-}
-
-static
-int ctf_visitor_field_class_declarator(int depth, struct ctf_node *node,
-               struct meta_log_config *log_cfg)
-{
-       int ret = 0;
-       struct ctf_node *iter;
-
-       depth++;
-
-       switch (node->parent->type) {
-       case NODE_TYPE_DECLARATOR:
-               /*
-                * A nested field class declarator is not allowed to
-                * contain pointers.
-                */
-               if (!bt_list_empty(&node->u.field_class_declarator.pointers))
-                       goto errperm;
-               break;                  /* OK */
-       case NODE_TYPEALIAS_TARGET:
-               break;                  /* OK */
-       case NODE_TYPEALIAS_ALIAS:
-               /*
-                * Only accept alias name containing:
-                * - identifier
-                * - identifier *   (any number of pointers)
-                * NOT accepting alias names containing [] (would otherwise
-                * cause semantic clash for later declarations of
-                * arrays/sequences of elements, where elements could be
-                * arrays/sequences themselves (if allowed in field class alias).
-                * NOT accepting alias with identifier. The declarator should
-                * be either empty or contain pointer(s).
-                */
-               if (node->u.field_class_declarator.type == TYPEDEC_NESTED)
-                       goto errperm;
-               bt_list_for_each_entry(iter, &node->parent->u.field_class_alias_name.field_class_specifier_list->u.field_class_specifier_list.head,
-                                       siblings) {
-                       switch (iter->u.field_class_specifier.type) {
-                       case TYPESPEC_FLOATING_POINT:
-                       case TYPESPEC_INTEGER:
-                       case TYPESPEC_STRING:
-                       case TYPESPEC_STRUCT:
-                       case TYPESPEC_VARIANT:
-                       case TYPESPEC_ENUM:
-                               if (bt_list_empty(&node->u.field_class_declarator.pointers))
-                                       goto errperm;
-                               break;
-                       default:
-                               break;
-                       }
-               }
-               if (node->u.field_class_declarator.type == TYPEDEC_ID &&
-                   node->u.field_class_declarator.u.id)
-                       goto errperm;
-               break;                  /* OK */
-       case NODE_TYPEDEF:
-       case NODE_STRUCT_OR_VARIANT_DECLARATION:
-               break;                  /* OK */
-
-       case NODE_ROOT:
-       case NODE_EVENT:
-       case NODE_STREAM:
-       case NODE_ENV:
-       case NODE_TRACE:
-       case NODE_CLOCK:
-       case NODE_CALLSITE:
-       case NODE_CTF_EXPRESSION:
-       case NODE_UNARY_EXPRESSION:
-       case NODE_TYPEALIAS:
-       case NODE_TYPE_SPECIFIER:
-       case NODE_POINTER:
-       case NODE_FLOATING_POINT:
-       case NODE_INTEGER:
-       case NODE_STRING:
-       case NODE_ENUMERATOR:
-       case NODE_ENUM:
-       case NODE_VARIANT:
-       case NODE_STRUCT:
-       default:
-               goto errinval;
-       }
-
-       bt_list_for_each_entry(iter, &node->u.field_class_declarator.pointers,
-                               siblings) {
-               ret = _ctf_visitor_semantic_check(depth + 1, iter, log_cfg);
-               if (ret)
-                       return ret;
-       }
-
-       switch (node->u.field_class_declarator.type) {
-       case TYPEDEC_ID:
-               break;
-       case TYPEDEC_NESTED:
-       {
-               if (node->u.field_class_declarator.u.nested.field_class_declarator) {
-                       ret = _ctf_visitor_semantic_check(depth + 1,
-                               node->u.field_class_declarator.u.nested.field_class_declarator,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               if (!node->u.field_class_declarator.u.nested.abstract_array) {
-                       bt_list_for_each_entry(iter, &node->u.field_class_declarator.u.nested.length,
-                                               siblings) {
-                               if (iter->type != NODE_UNARY_EXPRESSION) {
-                                       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-                                               "Expecting unary expression as length: node-type=%s",
-                                               node_type(iter));
-                                       return -EINVAL;
-                               }
-                               ret = _ctf_visitor_semantic_check(depth + 1,
-                                       iter, log_cfg);
-                               if (ret)
-                                       return ret;
-                       }
-               } else {
-                       if (node->parent->type == NODE_TYPEALIAS_TARGET) {
-                               _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-                                       "Abstract array declarator not permitted as target of field class alias.");
-                               return -EINVAL;
-                       }
-               }
-               if (node->u.field_class_declarator.bitfield_len) {
-                       ret = _ctf_visitor_semantic_check(depth + 1,
-                               node->u.field_class_declarator.bitfield_len,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               break;
-       }
-       case TYPEDEC_UNKNOWN:
-       default:
-               _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-                       "Unknown field class declarator: type=%d",
-                       node->u.field_class_declarator.type);
-               return -EINVAL;
-       }
-       depth--;
-       return 0;
-
-errinval:
-       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-               "Incoherent parent node's type: node-type=%s, parent-node-type=%s",
-               node_type(node), node_type(node->parent));
-       return -EINVAL;         /* Incoherent structure */
-
-errperm:
-       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-               "Semantic error: node-type=%s, parent-node-type=%s",
-               node_type(node), node_type(node->parent));
-       return -EPERM;          /* Structure not allowed */
-}
-
-static
-int _ctf_visitor_semantic_check(int depth, struct ctf_node *node,
-               struct meta_log_config *log_cfg)
-{
-       int ret = 0;
-       struct ctf_node *iter;
-
-       if (node->visited)
-               return 0;
-
-       switch (node->type) {
-       case NODE_ROOT:
-               bt_list_for_each_entry(iter, &node->u.root.declaration_list, siblings) {
-                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               bt_list_for_each_entry(iter, &node->u.root.trace, siblings) {
-                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               bt_list_for_each_entry(iter, &node->u.root.stream, siblings) {
-                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               bt_list_for_each_entry(iter, &node->u.root.event, siblings) {
-                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               break;
-
-       case NODE_EVENT:
-               switch (node->parent->type) {
-               case NODE_ROOT:
-                       break;                  /* OK */
-               default:
-                       goto errinval;
-               }
-
-               bt_list_for_each_entry(iter, &node->u.event.declaration_list, siblings) {
-                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               break;
-       case NODE_STREAM:
-               switch (node->parent->type) {
-               case NODE_ROOT:
-                       break;                  /* OK */
-               default:
-                       goto errinval;
-               }
-
-               bt_list_for_each_entry(iter, &node->u.stream.declaration_list, siblings) {
-                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               break;
-       case NODE_ENV:
-               switch (node->parent->type) {
-               case NODE_ROOT:
-                       break;                  /* OK */
-               default:
-                       goto errinval;
-               }
-
-               bt_list_for_each_entry(iter, &node->u.env.declaration_list, siblings) {
-                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               break;
-       case NODE_TRACE:
-               switch (node->parent->type) {
-               case NODE_ROOT:
-                       break;                  /* OK */
-               default:
-                       goto errinval;
-               }
-
-               bt_list_for_each_entry(iter, &node->u.trace.declaration_list, siblings) {
-                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               break;
-       case NODE_CLOCK:
-               switch (node->parent->type) {
-               case NODE_ROOT:
-                       break;                  /* OK */
-               default:
-                       goto errinval;
-               }
-
-               bt_list_for_each_entry(iter, &node->u.clock.declaration_list, siblings) {
-                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               break;
-       case NODE_CALLSITE:
-               switch (node->parent->type) {
-               case NODE_ROOT:
-                       break;                  /* OK */
-               default:
-                       goto errinval;
-               }
-
-               bt_list_for_each_entry(iter, &node->u.callsite.declaration_list, siblings) {
-                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               break;
-
-       case NODE_CTF_EXPRESSION:
-               switch (node->parent->type) {
-               case NODE_ROOT:
-               case NODE_EVENT:
-               case NODE_STREAM:
-               case NODE_ENV:
-               case NODE_TRACE:
-               case NODE_CLOCK:
-               case NODE_CALLSITE:
-               case NODE_FLOATING_POINT:
-               case NODE_INTEGER:
-               case NODE_STRING:
-                       break;                  /* OK */
-
-               case NODE_CTF_EXPRESSION:
-               case NODE_UNARY_EXPRESSION:
-               case NODE_TYPEDEF:
-               case NODE_TYPEALIAS_TARGET:
-               case NODE_TYPEALIAS_ALIAS:
-               case NODE_STRUCT_OR_VARIANT_DECLARATION:
-               case NODE_TYPEALIAS:
-               case NODE_TYPE_SPECIFIER:
-               case NODE_TYPE_SPECIFIER_LIST:
-               case NODE_POINTER:
-               case NODE_TYPE_DECLARATOR:
-               case NODE_ENUMERATOR:
-               case NODE_ENUM:
-               case NODE_VARIANT:
-               case NODE_STRUCT:
-               default:
-                       goto errinval;
-               }
-
-               depth++;
-               bt_list_for_each_entry(iter, &node->u.ctf_expression.left, siblings) {
-                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               bt_list_for_each_entry(iter, &node->u.ctf_expression.right, siblings) {
-                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               depth--;
-               break;
-       case NODE_UNARY_EXPRESSION:
-               return ctf_visitor_unary_expression(depth, node, log_cfg);
-
-       case NODE_TYPEDEF:
-               switch (node->parent->type) {
-               case NODE_ROOT:
-               case NODE_EVENT:
-               case NODE_STREAM:
-               case NODE_TRACE:
-               case NODE_VARIANT:
-               case NODE_STRUCT:
-                       break;                  /* OK */
-
-               case NODE_CTF_EXPRESSION:
-               case NODE_UNARY_EXPRESSION:
-               case NODE_TYPEDEF:
-               case NODE_TYPEALIAS_TARGET:
-               case NODE_TYPEALIAS_ALIAS:
-               case NODE_TYPEALIAS:
-               case NODE_STRUCT_OR_VARIANT_DECLARATION:
-               case NODE_TYPE_SPECIFIER:
-               case NODE_TYPE_SPECIFIER_LIST:
-               case NODE_POINTER:
-               case NODE_TYPE_DECLARATOR:
-               case NODE_FLOATING_POINT:
-               case NODE_INTEGER:
-               case NODE_STRING:
-               case NODE_ENUMERATOR:
-               case NODE_ENUM:
-               case NODE_CLOCK:
-               case NODE_CALLSITE:
-               case NODE_ENV:
-               default:
-                       goto errinval;
-               }
-
-               depth++;
-               ret = _ctf_visitor_semantic_check(depth + 1,
-                       node->u.field_class_def.field_class_specifier_list,
-                       log_cfg);
-               if (ret)
-                       return ret;
-               bt_list_for_each_entry(iter, &node->u.field_class_def.field_class_declarators, siblings) {
-                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               depth--;
-               break;
-       case NODE_TYPEALIAS_TARGET:
-       {
-               int nr_declarators;
-
-               switch (node->parent->type) {
-               case NODE_TYPEALIAS:
-                       break;                  /* OK */
-               default:
-                       goto errinval;
-               }
-
-               depth++;
-               ret = _ctf_visitor_semantic_check(depth + 1,
-                       node->u.field_class_alias_target.field_class_specifier_list,
-                       log_cfg);
-               if (ret)
-                       return ret;
-               nr_declarators = 0;
-               bt_list_for_each_entry(iter, &node->u.field_class_alias_target.field_class_declarators, siblings) {
-                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-                       nr_declarators++;
-               }
-               if (nr_declarators > 1) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-                               "Too many declarators in field class alias's name (maximum is 1): count=%d",
-                               nr_declarators);
-                       return -EINVAL;
-               }
-               depth--;
-               break;
-       }
-       case NODE_TYPEALIAS_ALIAS:
-       {
-               int nr_declarators;
-
-               switch (node->parent->type) {
-               case NODE_TYPEALIAS:
-                       break;                  /* OK */
-               default:
-                       goto errinval;
-               }
-
-               depth++;
-               ret = _ctf_visitor_semantic_check(depth + 1,
-                       node->u.field_class_alias_name.field_class_specifier_list,
-                       log_cfg);
-               if (ret)
-                       return ret;
-               nr_declarators = 0;
-               bt_list_for_each_entry(iter, &node->u.field_class_alias_name.field_class_declarators, siblings) {
-                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-                       nr_declarators++;
-               }
-               if (nr_declarators > 1) {
-                       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-                               "Too many declarators in field class alias's name (maximum is 1): count=%d",
-                               nr_declarators);
-                       return -EINVAL;
-               }
-               depth--;
-               break;
-       }
-       case NODE_TYPEALIAS:
-               switch (node->parent->type) {
-               case NODE_ROOT:
-               case NODE_EVENT:
-               case NODE_STREAM:
-               case NODE_TRACE:
-               case NODE_VARIANT:
-               case NODE_STRUCT:
-                       break;                  /* OK */
-
-               case NODE_CTF_EXPRESSION:
-               case NODE_UNARY_EXPRESSION:
-               case NODE_TYPEDEF:
-               case NODE_TYPEALIAS_TARGET:
-               case NODE_TYPEALIAS_ALIAS:
-               case NODE_TYPEALIAS:
-               case NODE_STRUCT_OR_VARIANT_DECLARATION:
-               case NODE_TYPE_SPECIFIER:
-               case NODE_TYPE_SPECIFIER_LIST:
-               case NODE_POINTER:
-               case NODE_TYPE_DECLARATOR:
-               case NODE_FLOATING_POINT:
-               case NODE_INTEGER:
-               case NODE_STRING:
-               case NODE_ENUMERATOR:
-               case NODE_ENUM:
-               case NODE_CLOCK:
-               case NODE_CALLSITE:
-               case NODE_ENV:
-               default:
-                       goto errinval;
-               }
-
-               ret = _ctf_visitor_semantic_check(depth + 1,
-                       node->u.field_class_alias.target, log_cfg);
-               if (ret)
-                       return ret;
-               ret = _ctf_visitor_semantic_check(depth + 1,
-                       node->u.field_class_alias.alias, log_cfg);
-               if (ret)
-                       return ret;
-               break;
-
-       case NODE_TYPE_SPECIFIER_LIST:
-               ret = ctf_visitor_field_class_specifier_list(depth, node,
-                       log_cfg);
-               if (ret)
-                       return ret;
-               break;
-       case NODE_TYPE_SPECIFIER:
-               ret = ctf_visitor_field_class_specifier(depth, node,
-                       log_cfg);
-               if (ret)
-                       return ret;
-               break;
-       case NODE_POINTER:
-               switch (node->parent->type) {
-               case NODE_TYPE_DECLARATOR:
-                       break;                  /* OK */
-               default:
-                       goto errinval;
-               }
-               break;
-       case NODE_TYPE_DECLARATOR:
-               ret = ctf_visitor_field_class_declarator(depth, node,
-                       log_cfg);
-               if (ret)
-                       return ret;
-               break;
-
-       case NODE_FLOATING_POINT:
-               switch (node->parent->type) {
-               case NODE_TYPE_SPECIFIER:
-                       break;                  /* OK */
-               default:
-                       goto errinval;
-
-               case NODE_UNARY_EXPRESSION:
-                       goto errperm;
-               }
-               bt_list_for_each_entry(iter, &node->u.floating_point.expressions, siblings) {
-                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               break;
-       case NODE_INTEGER:
-               switch (node->parent->type) {
-               case NODE_TYPE_SPECIFIER:
-                       break;                  /* OK */
-               default:
-                       goto errinval;
-
-               }
-
-               bt_list_for_each_entry(iter, &node->u.integer.expressions, siblings) {
-                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               break;
-       case NODE_STRING:
-               switch (node->parent->type) {
-               case NODE_TYPE_SPECIFIER:
-                       break;                  /* OK */
-               default:
-                       goto errinval;
-
-               case NODE_UNARY_EXPRESSION:
-                       goto errperm;
-               }
-
-               bt_list_for_each_entry(iter, &node->u.string.expressions, siblings) {
-                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               break;
-       case NODE_ENUMERATOR:
-               switch (node->parent->type) {
-               case NODE_ENUM:
-                       break;
-               default:
-                       goto errinval;
-               }
-               /*
-                * Enumerators are only allows to contain:
-                *    numeric unary expression
-                * or num. unary exp. ... num. unary exp
-                */
-               {
-                       int count = 0;
-
-                       bt_list_for_each_entry(iter, &node->u.enumerator.values,
-                                               siblings) {
-                               switch (count++) {
-                               case 0: if (iter->type != NODE_UNARY_EXPRESSION
-                                           || (iter->u.unary_expression.type != UNARY_SIGNED_CONSTANT
-                                               && iter->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT)
-                                           || iter->u.unary_expression.link != UNARY_LINK_UNKNOWN) {
-                                               _BT_COMP_LOGE_APPEND_CAUSE_LINENO(iter->lineno,
-                                                       "First unary expression of enumerator is unexpected.");
-                                               goto errperm;
-                                       }
-                                       break;
-                               case 1: if (iter->type != NODE_UNARY_EXPRESSION
-                                           || (iter->u.unary_expression.type != UNARY_SIGNED_CONSTANT
-                                               && iter->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT)
-                                           || iter->u.unary_expression.link != UNARY_DOTDOTDOT) {
-                                               _BT_COMP_LOGE_APPEND_CAUSE_LINENO(iter->lineno,
-                                                       "Second unary expression of enumerator is unexpected.");
-                                               goto errperm;
-                                       }
-                                       break;
-                               default:
-                                       goto errperm;
-                               }
-                       }
-               }
-
-               bt_list_for_each_entry(iter, &node->u.enumerator.values, siblings) {
-                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               break;
-       case NODE_ENUM:
-               switch (node->parent->type) {
-               case NODE_TYPE_SPECIFIER:
-                       break;                  /* OK */
-               default:
-                       goto errinval;
-
-               case NODE_UNARY_EXPRESSION:
-                       goto errperm;
-               }
-
-               depth++;
-               ret = _ctf_visitor_semantic_check(depth + 1,
-                       node->u._enum.container_field_class, log_cfg);
-               if (ret)
-                       return ret;
-
-               bt_list_for_each_entry(iter, &node->u._enum.enumerator_list, siblings) {
-                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               depth--;
-               break;
-       case NODE_STRUCT_OR_VARIANT_DECLARATION:
-               switch (node->parent->type) {
-               case NODE_STRUCT:
-               case NODE_VARIANT:
-                       break;
-               default:
-                       goto errinval;
-               }
-               ret = _ctf_visitor_semantic_check(depth + 1,
-                       node->u.struct_or_variant_declaration.field_class_specifier_list,
-                       log_cfg);
-               if (ret)
-                       return ret;
-               bt_list_for_each_entry(iter, &node->u.struct_or_variant_declaration.field_class_declarators, siblings) {
-                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               break;
-       case NODE_VARIANT:
-               switch (node->parent->type) {
-               case NODE_TYPE_SPECIFIER:
-                       break;                  /* OK */
-               default:
-                       goto errinval;
-
-               case NODE_UNARY_EXPRESSION:
-                       goto errperm;
-               }
-               bt_list_for_each_entry(iter, &node->u.variant.declaration_list, siblings) {
-                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               break;
-
-       case NODE_STRUCT:
-               switch (node->parent->type) {
-               case NODE_TYPE_SPECIFIER:
-                       break;                  /* OK */
-               default:
-                       goto errinval;
-
-               case NODE_UNARY_EXPRESSION:
-                       goto errperm;
-               }
-               bt_list_for_each_entry(iter, &node->u._struct.declaration_list, siblings) {
-                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
-                               log_cfg);
-                       if (ret)
-                               return ret;
-               }
-               break;
-
-       case NODE_UNKNOWN:
-       default:
-               _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-                       "Unknown node type: type=%d", node->type);
-               return -EINVAL;
-       }
-       return ret;
-
-errinval:
-       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-               "Incoherent parent node's type: node-type=%s, parent-node-type=%s",
-               node_type(node), node_type(node->parent));
-       return -EINVAL;         /* Incoherent structure */
-
-errperm:
-       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-               "Semantic error: node-type=%s, parent-node-type=%s",
-               node_type(node), node_type(node->parent));
-       return -EPERM;          /* Structure not allowed */
-}
-
-int ctf_visitor_semantic_check(int depth, struct ctf_node *node,
-               struct meta_log_config *log_cfg)
-{
-       int ret = 0;
-
-       /*
-        * First make sure we create the parent links for all children. Let's
-        * take the safe route and recreate them at each validation, just in
-        * case the structure has changed.
-        */
-       ret = ctf_visitor_parent_links(depth, node, log_cfg);
-       if (ret) {
-               _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-                       "Cannot create parent links in metadata's AST: "
-                       "ret=%d", ret);
-               goto end;
-       }
-
-       ret = _ctf_visitor_semantic_check(depth, node, log_cfg);
-       if (ret) {
-               _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
-                       "Cannot check metadata's AST semantics: "
-                       "ret=%d", ret);
-               goto end;
-       }
-
-end:
-       return ret;
-}
diff --git a/src/plugins/ctf/common/metadata/visitor-semantic-validator.cpp b/src/plugins/ctf/common/metadata/visitor-semantic-validator.cpp
new file mode 100644 (file)
index 0000000..14025eb
--- /dev/null
@@ -0,0 +1,1039 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2010 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * Common Trace Format Metadata Semantic Validator.
+ */
+
+#define BT_COMP_LOG_SELF_COMP (log_cfg->self_comp)
+#define BT_LOG_OUTPUT_LEVEL (log_cfg->log_level)
+#define BT_LOG_TAG "PLUGIN/CTF/META/SEMANTIC-VALIDATOR-VISITOR"
+#include "logging/comp-logging.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include "common/assert.h"
+#include <glib.h>
+#include <inttypes.h>
+#include <errno.h>
+#include "common/list.h"
+#include "scanner.hpp"
+#include "ast.hpp"
+#include "logging.hpp"
+
+#define _bt_list_first_entry(ptr, type, member)        \
+       bt_list_entry((ptr)->next, type, member)
+
+static
+int _ctf_visitor_semantic_check(int depth, struct ctf_node *node,
+               struct meta_log_config *log_cfg);
+
+static
+int ctf_visitor_unary_expression(int depth, struct ctf_node *node,
+               struct meta_log_config *log_cfg)
+{
+       struct ctf_node *iter;
+       int is_ctf_exp = 0, is_ctf_exp_left = 0;
+
+       switch (node->parent->type) {
+       case NODE_CTF_EXPRESSION:
+               is_ctf_exp = 1;
+               bt_list_for_each_entry(iter, &node->parent->u.ctf_expression.left,
+                                       siblings) {
+                       if (iter == node) {
+                               is_ctf_exp_left = 1;
+                               /*
+                                * We are a left child of a ctf expression.
+                                * We are only allowed to be a string.
+                                */
+                               if (node->u.unary_expression.type != UNARY_STRING) {
+                                       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
+                                               "Left child of a CTF expression is only allowed to be a string.");
+                                       goto errperm;
+                               }
+                               break;
+                       }
+               }
+               /* Right child of a ctf expression can be any type of unary exp. */
+               break;                  /* OK */
+       case NODE_TYPE_DECLARATOR:
+               /*
+                * We are the length of a type declarator.
+                */
+               switch (node->u.unary_expression.type) {
+               case UNARY_UNSIGNED_CONSTANT:
+               case UNARY_STRING:
+                       break;
+               default:
+                       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
+                               "Children of field class declarator and `enum` can only be unsigned numeric constants or references to fields (e.g., `a.b.c`).");
+                       goto errperm;
+               }
+               break;                  /* OK */
+
+       case NODE_STRUCT:
+               /*
+                * We are the size of a struct align attribute.
+                */
+               switch (node->u.unary_expression.type) {
+               case UNARY_UNSIGNED_CONSTANT:
+                       break;
+               default:
+                       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
+                               "Structure alignment attribute can only be an unsigned numeric constant.");
+                       goto errperm;
+               }
+               break;
+
+       case NODE_ENUMERATOR:
+               /* The enumerator's parent has validated its validity already. */
+               break;                  /* OK */
+
+       case NODE_UNARY_EXPRESSION:
+               /*
+                * We disallow nested unary expressions and "sbrac" unary
+                * expressions.
+                */
+               _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
+                       "Nested unary expressions not allowed (`()` and `[]`).");
+               goto errperm;
+
+       case NODE_ROOT:
+       case NODE_EVENT:
+       case NODE_STREAM:
+       case NODE_ENV:
+       case NODE_TRACE:
+       case NODE_CLOCK:
+       case NODE_CALLSITE:
+       case NODE_TYPEDEF:
+       case NODE_TYPEALIAS_TARGET:
+       case NODE_TYPEALIAS_ALIAS:
+       case NODE_TYPEALIAS:
+       case NODE_TYPE_SPECIFIER:
+       case NODE_POINTER:
+       case NODE_FLOATING_POINT:
+       case NODE_INTEGER:
+       case NODE_STRING:
+       case NODE_ENUM:
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+       case NODE_VARIANT:
+       default:
+               goto errinval;
+       }
+
+       switch (node->u.unary_expression.link) {
+       case UNARY_LINK_UNKNOWN:
+               /* We don't allow empty link except on the first node of the list */
+               if (is_ctf_exp && _bt_list_first_entry(is_ctf_exp_left ?
+                                         &node->parent->u.ctf_expression.left :
+                                         &node->parent->u.ctf_expression.right,
+                                         struct ctf_node,
+                                         siblings) != node) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
+                               "Empty link is not allowed except on first node of unary expression (need to separate nodes with `.` or `->`).");
+                       goto errperm;
+               }
+               break;                  /* OK */
+       case UNARY_DOTLINK:
+       case UNARY_ARROWLINK:
+               /* We only allow -> and . links between children of ctf_expression. */
+               if (node->parent->type != NODE_CTF_EXPRESSION) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
+                               "Links `.` and `->` are only allowed as children of CTF expression.");
+                       goto errperm;
+               }
+               /*
+                * Only strings can be separated linked by . or ->.
+                * This includes "", '' and non-quoted identifiers.
+                */
+               if (node->u.unary_expression.type != UNARY_STRING) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
+                               "Links `.` and `->` are only allowed to separate strings and identifiers.");
+                       goto errperm;
+               }
+               /* We don't allow link on the first node of the list */
+               if (is_ctf_exp && _bt_list_first_entry(is_ctf_exp_left ?
+                                         &node->parent->u.ctf_expression.left :
+                                         &node->parent->u.ctf_expression.right,
+                                         struct ctf_node,
+                                         siblings) == node) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
+                               "Links `.` and `->` are not allowed before first node of the unary expression list.");
+                       goto errperm;
+               }
+               break;
+       case UNARY_DOTDOTDOT:
+               /* We only allow ... link between children of enumerator. */
+               if (node->parent->type != NODE_ENUMERATOR) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
+                               "Link `...` is only allowed within enumerator.");
+                       goto errperm;
+               }
+               /* We don't allow link on the first node of the list */
+               if (_bt_list_first_entry(&node->parent->u.enumerator.values,
+                                         struct ctf_node,
+                                         siblings) == node) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
+                               "Link `...` is not allowed on the first node of the unary expression list.");
+                       goto errperm;
+               }
+               break;
+       default:
+               _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
+                       "Unknown expression link type: type=%d",
+                       node->u.unary_expression.link);
+               return -EINVAL;
+       }
+       return 0;
+
+errinval:
+       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
+               "Incoherent parent node's type: node-type=%s, parent-node-type=%s",
+               node_type(node), node_type(node->parent));
+       return -EINVAL;         /* Incoherent structure */
+
+errperm:
+       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
+               "Semantic error: node-type=%s, parent-node-type=%s",
+               node_type(node), node_type(node->parent));
+       return -EPERM;          /* Structure not allowed */
+}
+
+static
+int ctf_visitor_field_class_specifier_list(int depth, struct ctf_node *node,
+               struct meta_log_config *log_cfg)
+{
+       switch (node->parent->type) {
+       case NODE_CTF_EXPRESSION:
+       case NODE_TYPE_DECLARATOR:
+       case NODE_TYPEDEF:
+       case NODE_TYPEALIAS_TARGET:
+       case NODE_TYPEALIAS_ALIAS:
+       case NODE_ENUM:
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+       case NODE_ROOT:
+               break;                  /* OK */
+
+       case NODE_EVENT:
+       case NODE_STREAM:
+       case NODE_ENV:
+       case NODE_TRACE:
+       case NODE_CLOCK:
+       case NODE_CALLSITE:
+       case NODE_UNARY_EXPRESSION:
+       case NODE_TYPEALIAS:
+       case NODE_TYPE_SPECIFIER:
+       case NODE_TYPE_SPECIFIER_LIST:
+       case NODE_POINTER:
+       case NODE_FLOATING_POINT:
+       case NODE_INTEGER:
+       case NODE_STRING:
+       case NODE_ENUMERATOR:
+       case NODE_VARIANT:
+       case NODE_STRUCT:
+       default:
+               goto errinval;
+       }
+       return 0;
+errinval:
+       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
+               "Incoherent parent node's type: node-type=%s, parent-node-type=%s",
+               node_type(node), node_type(node->parent));
+       return -EINVAL;         /* Incoherent structure */
+}
+
+static
+int ctf_visitor_field_class_specifier(int depth, struct ctf_node *node,
+               struct meta_log_config *log_cfg)
+{
+       switch (node->parent->type) {
+       case NODE_TYPE_SPECIFIER_LIST:
+               break;                  /* OK */
+
+       case NODE_CTF_EXPRESSION:
+       case NODE_TYPE_DECLARATOR:
+       case NODE_TYPEDEF:
+       case NODE_TYPEALIAS_TARGET:
+       case NODE_TYPEALIAS_ALIAS:
+       case NODE_ENUM:
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+       case NODE_ROOT:
+       case NODE_EVENT:
+       case NODE_STREAM:
+       case NODE_ENV:
+       case NODE_TRACE:
+       case NODE_CLOCK:
+       case NODE_CALLSITE:
+       case NODE_UNARY_EXPRESSION:
+       case NODE_TYPEALIAS:
+       case NODE_TYPE_SPECIFIER:
+       case NODE_POINTER:
+       case NODE_FLOATING_POINT:
+       case NODE_INTEGER:
+       case NODE_STRING:
+       case NODE_ENUMERATOR:
+       case NODE_VARIANT:
+       case NODE_STRUCT:
+       default:
+               goto errinval;
+       }
+       return 0;
+errinval:
+       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
+               "Incoherent parent node's type: node-type=%s, parent-node-type=%s",
+               node_type(node), node_type(node->parent));
+       return -EINVAL;         /* Incoherent structure */
+}
+
+static
+int ctf_visitor_field_class_declarator(int depth, struct ctf_node *node,
+               struct meta_log_config *log_cfg)
+{
+       int ret = 0;
+       struct ctf_node *iter;
+
+       depth++;
+
+       switch (node->parent->type) {
+       case NODE_TYPE_DECLARATOR:
+               /*
+                * A nested field class declarator is not allowed to
+                * contain pointers.
+                */
+               if (!bt_list_empty(&node->u.field_class_declarator.pointers))
+                       goto errperm;
+               break;                  /* OK */
+       case NODE_TYPEALIAS_TARGET:
+               break;                  /* OK */
+       case NODE_TYPEALIAS_ALIAS:
+               /*
+                * Only accept alias name containing:
+                * - identifier
+                * - identifier *   (any number of pointers)
+                * NOT accepting alias names containing [] (would otherwise
+                * cause semantic clash for later declarations of
+                * arrays/sequences of elements, where elements could be
+                * arrays/sequences themselves (if allowed in field class alias).
+                * NOT accepting alias with identifier. The declarator should
+                * be either empty or contain pointer(s).
+                */
+               if (node->u.field_class_declarator.type == TYPEDEC_NESTED)
+                       goto errperm;
+               bt_list_for_each_entry(iter, &node->parent->u.field_class_alias_name.field_class_specifier_list->u.field_class_specifier_list.head,
+                                       siblings) {
+                       switch (iter->u.field_class_specifier.type) {
+                       case TYPESPEC_FLOATING_POINT:
+                       case TYPESPEC_INTEGER:
+                       case TYPESPEC_STRING:
+                       case TYPESPEC_STRUCT:
+                       case TYPESPEC_VARIANT:
+                       case TYPESPEC_ENUM:
+                               if (bt_list_empty(&node->u.field_class_declarator.pointers))
+                                       goto errperm;
+                               break;
+                       default:
+                               break;
+                       }
+               }
+               if (node->u.field_class_declarator.type == TYPEDEC_ID &&
+                   node->u.field_class_declarator.u.id)
+                       goto errperm;
+               break;                  /* OK */
+       case NODE_TYPEDEF:
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+               break;                  /* OK */
+
+       case NODE_ROOT:
+       case NODE_EVENT:
+       case NODE_STREAM:
+       case NODE_ENV:
+       case NODE_TRACE:
+       case NODE_CLOCK:
+       case NODE_CALLSITE:
+       case NODE_CTF_EXPRESSION:
+       case NODE_UNARY_EXPRESSION:
+       case NODE_TYPEALIAS:
+       case NODE_TYPE_SPECIFIER:
+       case NODE_POINTER:
+       case NODE_FLOATING_POINT:
+       case NODE_INTEGER:
+       case NODE_STRING:
+       case NODE_ENUMERATOR:
+       case NODE_ENUM:
+       case NODE_VARIANT:
+       case NODE_STRUCT:
+       default:
+               goto errinval;
+       }
+
+       bt_list_for_each_entry(iter, &node->u.field_class_declarator.pointers,
+                               siblings) {
+               ret = _ctf_visitor_semantic_check(depth + 1, iter, log_cfg);
+               if (ret)
+                       return ret;
+       }
+
+       switch (node->u.field_class_declarator.type) {
+       case TYPEDEC_ID:
+               break;
+       case TYPEDEC_NESTED:
+       {
+               if (node->u.field_class_declarator.u.nested.field_class_declarator) {
+                       ret = _ctf_visitor_semantic_check(depth + 1,
+                               node->u.field_class_declarator.u.nested.field_class_declarator,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               if (!node->u.field_class_declarator.u.nested.abstract_array) {
+                       bt_list_for_each_entry(iter, &node->u.field_class_declarator.u.nested.length,
+                                               siblings) {
+                               if (iter->type != NODE_UNARY_EXPRESSION) {
+                                       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
+                                               "Expecting unary expression as length: node-type=%s",
+                                               node_type(iter));
+                                       return -EINVAL;
+                               }
+                               ret = _ctf_visitor_semantic_check(depth + 1,
+                                       iter, log_cfg);
+                               if (ret)
+                                       return ret;
+                       }
+               } else {
+                       if (node->parent->type == NODE_TYPEALIAS_TARGET) {
+                               _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
+                                       "Abstract array declarator not permitted as target of field class alias.");
+                               return -EINVAL;
+                       }
+               }
+               if (node->u.field_class_declarator.bitfield_len) {
+                       ret = _ctf_visitor_semantic_check(depth + 1,
+                               node->u.field_class_declarator.bitfield_len,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       }
+       case TYPEDEC_UNKNOWN:
+       default:
+               _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
+                       "Unknown field class declarator: type=%d",
+                       node->u.field_class_declarator.type);
+               return -EINVAL;
+       }
+       depth--;
+       return 0;
+
+errinval:
+       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
+               "Incoherent parent node's type: node-type=%s, parent-node-type=%s",
+               node_type(node), node_type(node->parent));
+       return -EINVAL;         /* Incoherent structure */
+
+errperm:
+       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
+               "Semantic error: node-type=%s, parent-node-type=%s",
+               node_type(node), node_type(node->parent));
+       return -EPERM;          /* Structure not allowed */
+}
+
+static
+int _ctf_visitor_semantic_check(int depth, struct ctf_node *node,
+               struct meta_log_config *log_cfg)
+{
+       int ret = 0;
+       struct ctf_node *iter;
+
+       if (node->visited)
+               return 0;
+
+       switch (node->type) {
+       case NODE_ROOT:
+               bt_list_for_each_entry(iter, &node->u.root.declaration_list, siblings) {
+                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               bt_list_for_each_entry(iter, &node->u.root.trace, siblings) {
+                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               bt_list_for_each_entry(iter, &node->u.root.stream, siblings) {
+                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               bt_list_for_each_entry(iter, &node->u.root.event, siblings) {
+                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               break;
+
+       case NODE_EVENT:
+               switch (node->parent->type) {
+               case NODE_ROOT:
+                       break;                  /* OK */
+               default:
+                       goto errinval;
+               }
+
+               bt_list_for_each_entry(iter, &node->u.event.declaration_list, siblings) {
+                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_STREAM:
+               switch (node->parent->type) {
+               case NODE_ROOT:
+                       break;                  /* OK */
+               default:
+                       goto errinval;
+               }
+
+               bt_list_for_each_entry(iter, &node->u.stream.declaration_list, siblings) {
+                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_ENV:
+               switch (node->parent->type) {
+               case NODE_ROOT:
+                       break;                  /* OK */
+               default:
+                       goto errinval;
+               }
+
+               bt_list_for_each_entry(iter, &node->u.env.declaration_list, siblings) {
+                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_TRACE:
+               switch (node->parent->type) {
+               case NODE_ROOT:
+                       break;                  /* OK */
+               default:
+                       goto errinval;
+               }
+
+               bt_list_for_each_entry(iter, &node->u.trace.declaration_list, siblings) {
+                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_CLOCK:
+               switch (node->parent->type) {
+               case NODE_ROOT:
+                       break;                  /* OK */
+               default:
+                       goto errinval;
+               }
+
+               bt_list_for_each_entry(iter, &node->u.clock.declaration_list, siblings) {
+                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_CALLSITE:
+               switch (node->parent->type) {
+               case NODE_ROOT:
+                       break;                  /* OK */
+               default:
+                       goto errinval;
+               }
+
+               bt_list_for_each_entry(iter, &node->u.callsite.declaration_list, siblings) {
+                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               break;
+
+       case NODE_CTF_EXPRESSION:
+               switch (node->parent->type) {
+               case NODE_ROOT:
+               case NODE_EVENT:
+               case NODE_STREAM:
+               case NODE_ENV:
+               case NODE_TRACE:
+               case NODE_CLOCK:
+               case NODE_CALLSITE:
+               case NODE_FLOATING_POINT:
+               case NODE_INTEGER:
+               case NODE_STRING:
+                       break;                  /* OK */
+
+               case NODE_CTF_EXPRESSION:
+               case NODE_UNARY_EXPRESSION:
+               case NODE_TYPEDEF:
+               case NODE_TYPEALIAS_TARGET:
+               case NODE_TYPEALIAS_ALIAS:
+               case NODE_STRUCT_OR_VARIANT_DECLARATION:
+               case NODE_TYPEALIAS:
+               case NODE_TYPE_SPECIFIER:
+               case NODE_TYPE_SPECIFIER_LIST:
+               case NODE_POINTER:
+               case NODE_TYPE_DECLARATOR:
+               case NODE_ENUMERATOR:
+               case NODE_ENUM:
+               case NODE_VARIANT:
+               case NODE_STRUCT:
+               default:
+                       goto errinval;
+               }
+
+               depth++;
+               bt_list_for_each_entry(iter, &node->u.ctf_expression.left, siblings) {
+                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               bt_list_for_each_entry(iter, &node->u.ctf_expression.right, siblings) {
+                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               depth--;
+               break;
+       case NODE_UNARY_EXPRESSION:
+               return ctf_visitor_unary_expression(depth, node, log_cfg);
+
+       case NODE_TYPEDEF:
+               switch (node->parent->type) {
+               case NODE_ROOT:
+               case NODE_EVENT:
+               case NODE_STREAM:
+               case NODE_TRACE:
+               case NODE_VARIANT:
+               case NODE_STRUCT:
+                       break;                  /* OK */
+
+               case NODE_CTF_EXPRESSION:
+               case NODE_UNARY_EXPRESSION:
+               case NODE_TYPEDEF:
+               case NODE_TYPEALIAS_TARGET:
+               case NODE_TYPEALIAS_ALIAS:
+               case NODE_TYPEALIAS:
+               case NODE_STRUCT_OR_VARIANT_DECLARATION:
+               case NODE_TYPE_SPECIFIER:
+               case NODE_TYPE_SPECIFIER_LIST:
+               case NODE_POINTER:
+               case NODE_TYPE_DECLARATOR:
+               case NODE_FLOATING_POINT:
+               case NODE_INTEGER:
+               case NODE_STRING:
+               case NODE_ENUMERATOR:
+               case NODE_ENUM:
+               case NODE_CLOCK:
+               case NODE_CALLSITE:
+               case NODE_ENV:
+               default:
+                       goto errinval;
+               }
+
+               depth++;
+               ret = _ctf_visitor_semantic_check(depth + 1,
+                       node->u.field_class_def.field_class_specifier_list,
+                       log_cfg);
+               if (ret)
+                       return ret;
+               bt_list_for_each_entry(iter, &node->u.field_class_def.field_class_declarators, siblings) {
+                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               depth--;
+               break;
+       case NODE_TYPEALIAS_TARGET:
+       {
+               int nr_declarators;
+
+               switch (node->parent->type) {
+               case NODE_TYPEALIAS:
+                       break;                  /* OK */
+               default:
+                       goto errinval;
+               }
+
+               depth++;
+               ret = _ctf_visitor_semantic_check(depth + 1,
+                       node->u.field_class_alias_target.field_class_specifier_list,
+                       log_cfg);
+               if (ret)
+                       return ret;
+               nr_declarators = 0;
+               bt_list_for_each_entry(iter, &node->u.field_class_alias_target.field_class_declarators, siblings) {
+                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+                       nr_declarators++;
+               }
+               if (nr_declarators > 1) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
+                               "Too many declarators in field class alias's name (maximum is 1): count=%d",
+                               nr_declarators);
+                       return -EINVAL;
+               }
+               depth--;
+               break;
+       }
+       case NODE_TYPEALIAS_ALIAS:
+       {
+               int nr_declarators;
+
+               switch (node->parent->type) {
+               case NODE_TYPEALIAS:
+                       break;                  /* OK */
+               default:
+                       goto errinval;
+               }
+
+               depth++;
+               ret = _ctf_visitor_semantic_check(depth + 1,
+                       node->u.field_class_alias_name.field_class_specifier_list,
+                       log_cfg);
+               if (ret)
+                       return ret;
+               nr_declarators = 0;
+               bt_list_for_each_entry(iter, &node->u.field_class_alias_name.field_class_declarators, siblings) {
+                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+                       nr_declarators++;
+               }
+               if (nr_declarators > 1) {
+                       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
+                               "Too many declarators in field class alias's name (maximum is 1): count=%d",
+                               nr_declarators);
+                       return -EINVAL;
+               }
+               depth--;
+               break;
+       }
+       case NODE_TYPEALIAS:
+               switch (node->parent->type) {
+               case NODE_ROOT:
+               case NODE_EVENT:
+               case NODE_STREAM:
+               case NODE_TRACE:
+               case NODE_VARIANT:
+               case NODE_STRUCT:
+                       break;                  /* OK */
+
+               case NODE_CTF_EXPRESSION:
+               case NODE_UNARY_EXPRESSION:
+               case NODE_TYPEDEF:
+               case NODE_TYPEALIAS_TARGET:
+               case NODE_TYPEALIAS_ALIAS:
+               case NODE_TYPEALIAS:
+               case NODE_STRUCT_OR_VARIANT_DECLARATION:
+               case NODE_TYPE_SPECIFIER:
+               case NODE_TYPE_SPECIFIER_LIST:
+               case NODE_POINTER:
+               case NODE_TYPE_DECLARATOR:
+               case NODE_FLOATING_POINT:
+               case NODE_INTEGER:
+               case NODE_STRING:
+               case NODE_ENUMERATOR:
+               case NODE_ENUM:
+               case NODE_CLOCK:
+               case NODE_CALLSITE:
+               case NODE_ENV:
+               default:
+                       goto errinval;
+               }
+
+               ret = _ctf_visitor_semantic_check(depth + 1,
+                       node->u.field_class_alias.target, log_cfg);
+               if (ret)
+                       return ret;
+               ret = _ctf_visitor_semantic_check(depth + 1,
+                       node->u.field_class_alias.alias, log_cfg);
+               if (ret)
+                       return ret;
+               break;
+
+       case NODE_TYPE_SPECIFIER_LIST:
+               ret = ctf_visitor_field_class_specifier_list(depth, node,
+                       log_cfg);
+               if (ret)
+                       return ret;
+               break;
+       case NODE_TYPE_SPECIFIER:
+               ret = ctf_visitor_field_class_specifier(depth, node,
+                       log_cfg);
+               if (ret)
+                       return ret;
+               break;
+       case NODE_POINTER:
+               switch (node->parent->type) {
+               case NODE_TYPE_DECLARATOR:
+                       break;                  /* OK */
+               default:
+                       goto errinval;
+               }
+               break;
+       case NODE_TYPE_DECLARATOR:
+               ret = ctf_visitor_field_class_declarator(depth, node,
+                       log_cfg);
+               if (ret)
+                       return ret;
+               break;
+
+       case NODE_FLOATING_POINT:
+               switch (node->parent->type) {
+               case NODE_TYPE_SPECIFIER:
+                       break;                  /* OK */
+               default:
+                       goto errinval;
+
+               case NODE_UNARY_EXPRESSION:
+                       goto errperm;
+               }
+               bt_list_for_each_entry(iter, &node->u.floating_point.expressions, siblings) {
+                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_INTEGER:
+               switch (node->parent->type) {
+               case NODE_TYPE_SPECIFIER:
+                       break;                  /* OK */
+               default:
+                       goto errinval;
+
+               }
+
+               bt_list_for_each_entry(iter, &node->u.integer.expressions, siblings) {
+                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_STRING:
+               switch (node->parent->type) {
+               case NODE_TYPE_SPECIFIER:
+                       break;                  /* OK */
+               default:
+                       goto errinval;
+
+               case NODE_UNARY_EXPRESSION:
+                       goto errperm;
+               }
+
+               bt_list_for_each_entry(iter, &node->u.string.expressions, siblings) {
+                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_ENUMERATOR:
+               switch (node->parent->type) {
+               case NODE_ENUM:
+                       break;
+               default:
+                       goto errinval;
+               }
+               /*
+                * Enumerators are only allows to contain:
+                *    numeric unary expression
+                * or num. unary exp. ... num. unary exp
+                */
+               {
+                       int count = 0;
+
+                       bt_list_for_each_entry(iter, &node->u.enumerator.values,
+                                               siblings) {
+                               switch (count++) {
+                               case 0: if (iter->type != NODE_UNARY_EXPRESSION
+                                           || (iter->u.unary_expression.type != UNARY_SIGNED_CONSTANT
+                                               && iter->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT)
+                                           || iter->u.unary_expression.link != UNARY_LINK_UNKNOWN) {
+                                               _BT_COMP_LOGE_APPEND_CAUSE_LINENO(iter->lineno,
+                                                       "First unary expression of enumerator is unexpected.");
+                                               goto errperm;
+                                       }
+                                       break;
+                               case 1: if (iter->type != NODE_UNARY_EXPRESSION
+                                           || (iter->u.unary_expression.type != UNARY_SIGNED_CONSTANT
+                                               && iter->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT)
+                                           || iter->u.unary_expression.link != UNARY_DOTDOTDOT) {
+                                               _BT_COMP_LOGE_APPEND_CAUSE_LINENO(iter->lineno,
+                                                       "Second unary expression of enumerator is unexpected.");
+                                               goto errperm;
+                                       }
+                                       break;
+                               default:
+                                       goto errperm;
+                               }
+                       }
+               }
+
+               bt_list_for_each_entry(iter, &node->u.enumerator.values, siblings) {
+                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_ENUM:
+               switch (node->parent->type) {
+               case NODE_TYPE_SPECIFIER:
+                       break;                  /* OK */
+               default:
+                       goto errinval;
+
+               case NODE_UNARY_EXPRESSION:
+                       goto errperm;
+               }
+
+               depth++;
+               ret = _ctf_visitor_semantic_check(depth + 1,
+                       node->u._enum.container_field_class, log_cfg);
+               if (ret)
+                       return ret;
+
+               bt_list_for_each_entry(iter, &node->u._enum.enumerator_list, siblings) {
+                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               depth--;
+               break;
+       case NODE_STRUCT_OR_VARIANT_DECLARATION:
+               switch (node->parent->type) {
+               case NODE_STRUCT:
+               case NODE_VARIANT:
+                       break;
+               default:
+                       goto errinval;
+               }
+               ret = _ctf_visitor_semantic_check(depth + 1,
+                       node->u.struct_or_variant_declaration.field_class_specifier_list,
+                       log_cfg);
+               if (ret)
+                       return ret;
+               bt_list_for_each_entry(iter, &node->u.struct_or_variant_declaration.field_class_declarators, siblings) {
+                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       case NODE_VARIANT:
+               switch (node->parent->type) {
+               case NODE_TYPE_SPECIFIER:
+                       break;                  /* OK */
+               default:
+                       goto errinval;
+
+               case NODE_UNARY_EXPRESSION:
+                       goto errperm;
+               }
+               bt_list_for_each_entry(iter, &node->u.variant.declaration_list, siblings) {
+                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               break;
+
+       case NODE_STRUCT:
+               switch (node->parent->type) {
+               case NODE_TYPE_SPECIFIER:
+                       break;                  /* OK */
+               default:
+                       goto errinval;
+
+               case NODE_UNARY_EXPRESSION:
+                       goto errperm;
+               }
+               bt_list_for_each_entry(iter, &node->u._struct.declaration_list, siblings) {
+                       ret = _ctf_visitor_semantic_check(depth + 1, iter,
+                               log_cfg);
+                       if (ret)
+                               return ret;
+               }
+               break;
+
+       case NODE_UNKNOWN:
+       default:
+               _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
+                       "Unknown node type: type=%d", node->type);
+               return -EINVAL;
+       }
+       return ret;
+
+errinval:
+       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
+               "Incoherent parent node's type: node-type=%s, parent-node-type=%s",
+               node_type(node), node_type(node->parent));
+       return -EINVAL;         /* Incoherent structure */
+
+errperm:
+       _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
+               "Semantic error: node-type=%s, parent-node-type=%s",
+               node_type(node), node_type(node->parent));
+       return -EPERM;          /* Structure not allowed */
+}
+
+int ctf_visitor_semantic_check(int depth, struct ctf_node *node,
+               struct meta_log_config *log_cfg)
+{
+       int ret = 0;
+
+       /*
+        * First make sure we create the parent links for all children. Let's
+        * take the safe route and recreate them at each validation, just in
+        * case the structure has changed.
+        */
+       ret = ctf_visitor_parent_links(depth, node, log_cfg);
+       if (ret) {
+               _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
+                       "Cannot create parent links in metadata's AST: "
+                       "ret=%d", ret);
+               goto end;
+       }
+
+       ret = _ctf_visitor_semantic_check(depth, node, log_cfg);
+       if (ret) {
+               _BT_COMP_LOGE_APPEND_CAUSE_LINENO(node->lineno,
+                       "Cannot check metadata's AST semantics: "
+                       "ret=%d", ret);
+               goto end;
+       }
+
+end:
+       return ret;
+}
index 3682a74714405b37f0621557ed0410dd02ae4b04..d963df77a70bbf9ba346015c624b658bd243f221 100644 (file)
@@ -3,5 +3,5 @@
 noinst_LTLIBRARIES = libctf-msg-iter.la
 
 libctf_msg_iter_la_SOURCES = \
-       msg-iter.c \
-       msg-iter.h
+       msg-iter.cpp \
+       msg-iter.hpp
diff --git a/src/plugins/ctf/common/msg-iter/msg-iter.c b/src/plugins/ctf/common/msg-iter/msg-iter.c
deleted file mode 100644 (file)
index b56cf0c..0000000
+++ /dev/null
@@ -1,3191 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright (c) 2015-2018 EfficiOS Inc. and Linux Foundation
- * Copyright (c) 2015-2018 Philippe Proulx <pproulx@efficios.com>
- *
- * Babeltrace - CTF message iterator
- */
-
-#define BT_COMP_LOG_SELF_COMP (msg_it->self_comp)
-#define BT_LOG_OUTPUT_LEVEL (msg_it->log_level)
-#define BT_LOG_TAG "PLUGIN/CTF/MSG-ITER"
-#include "logging/comp-logging.h"
-
-#include <stdint.h>
-#include <inttypes.h>
-#include <stdio.h>
-#include <stddef.h>
-#include <stdbool.h>
-#include "common/assert.h"
-#include <string.h>
-#include <babeltrace2/babeltrace.h>
-#include "common/common.h"
-#include <glib.h>
-#include <stdlib.h>
-
-#include "msg-iter.h"
-#include "../bfcr/bfcr.h"
-
-struct ctf_msg_iter;
-
-/* A visit stack entry */
-struct stack_entry {
-       /*
-        * Current base field, one of:
-        *
-        *   * string
-        *   * structure
-        *   * array
-        *   * sequence
-        *   * variant
-        *
-        * Field is borrowed.
-        */
-       bt_field *base;
-
-       /* Index of next field to set */
-       size_t index;
-};
-
-struct ctf_msg_iter;
-
-/* Visit stack */
-struct stack {
-       struct ctf_msg_iter *msg_it;
-
-       /* Entries (struct stack_entry) */
-       GArray *entries;
-
-       /* Number of active entries */
-       size_t size;
-};
-
-/* State */
-enum state {
-       STATE_INIT,
-       STATE_SWITCH_PACKET,
-       STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN,
-       STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE,
-       STATE_AFTER_TRACE_PACKET_HEADER,
-       STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN,
-       STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE,
-       STATE_AFTER_STREAM_PACKET_CONTEXT,
-       STATE_EMIT_MSG_STREAM_BEGINNING,
-       STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS,
-       STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS,
-       STATE_EMIT_MSG_DISCARDED_EVENTS,
-       STATE_EMIT_MSG_DISCARDED_PACKETS,
-       STATE_EMIT_MSG_PACKET_BEGINNING,
-       STATE_DSCOPE_EVENT_HEADER_BEGIN,
-       STATE_DSCOPE_EVENT_HEADER_CONTINUE,
-       STATE_AFTER_EVENT_HEADER,
-       STATE_DSCOPE_EVENT_COMMON_CONTEXT_BEGIN,
-       STATE_DSCOPE_EVENT_COMMON_CONTEXT_CONTINUE,
-       STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN,
-       STATE_DSCOPE_EVENT_SPEC_CONTEXT_CONTINUE,
-       STATE_DSCOPE_EVENT_PAYLOAD_BEGIN,
-       STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE,
-       STATE_EMIT_MSG_EVENT,
-       STATE_EMIT_QUEUED_MSG_EVENT,
-       STATE_SKIP_PACKET_PADDING,
-       STATE_EMIT_MSG_PACKET_END_MULTI,
-       STATE_EMIT_MSG_PACKET_END_SINGLE,
-       STATE_EMIT_QUEUED_MSG_PACKET_END,
-       STATE_CHECK_EMIT_MSG_STREAM_END,
-       STATE_EMIT_MSG_STREAM_END,
-       STATE_DONE,
-};
-
-struct end_of_packet_snapshots {
-       uint64_t discarded_events;
-       uint64_t packets;
-       uint64_t beginning_clock;
-       uint64_t end_clock;
-};
-
-/* CTF message iterator */
-struct ctf_msg_iter {
-       /* Visit stack */
-       struct stack *stack;
-
-       /* Current message iterator to create messages (weak) */
-       bt_self_message_iterator *self_msg_iter;
-
-       /*
-        * True if library objects are unavailable during the decoding and
-        * should not be created/used.
-        */
-       bool dry_run;
-
-       /*
-        * Current dynamic scope field pointer.
-        *
-        * This is set by read_dscope_begin_state() and contains the
-        * value of one of the pointers in `dscopes` below.
-        */
-       bt_field *cur_dscope_field;
-
-       /*
-        * True if we're done filling a string field from a text
-        * array/sequence payload.
-        */
-       bool done_filling_string;
-
-       /* Trace and classes */
-       /* True to set IR fields */
-       bool set_ir_fields;
-
-       struct {
-               struct ctf_trace_class *tc;
-               struct ctf_stream_class *sc;
-               struct ctf_event_class *ec;
-       } meta;
-
-       /* Current packet (NULL if not created yet) */
-       bt_packet *packet;
-
-       /* Current stream (NULL if not set yet) */
-       bt_stream *stream;
-
-       /* Current event (NULL if not created yet) */
-       bt_event *event;
-
-       /* Current event message (NULL if not created yet) */
-       bt_message *event_msg;
-
-       /*
-        * True if we need to emit a packet beginning message before we emit
-        * the next event message or the packet end message.
-        */
-       bool emit_delayed_packet_beginning_msg;
-
-       /*
-        * True if this is the first packet we are reading, and therefore if we
-        * should emit a stream beginning message.
-        */
-       bool emit_stream_beginning_message;
-
-       /*
-        * True if we need to emit a stream end message at the end of the
-        * current stream. A live stream may never receive any data and thus
-        * never send a stream beginning message which removes the need to emit
-        * a stream end message.
-        */
-       bool emit_stream_end_message;
-
-       /* Database of current dynamic scopes */
-       struct {
-               bt_field *stream_packet_context;
-               bt_field *event_common_context;
-               bt_field *event_spec_context;
-               bt_field *event_payload;
-       } dscopes;
-
-       /* Current state */
-       enum state state;
-
-       /* Current medium buffer data */
-       struct {
-               /* Last address provided by medium */
-               const uint8_t *addr;
-
-               /* Buffer size provided by medium (bytes) */
-               size_t sz;
-
-               /* Offset within whole packet of addr (bits) */
-               size_t packet_offset;
-
-               /* Current position from addr (bits) */
-               size_t at;
-
-               /* Position of the last event header from addr (bits) */
-               size_t last_eh_at;
-       } buf;
-
-       /* Binary type reader */
-       struct bt_bfcr *bfcr;
-
-       /* Current medium data */
-       struct {
-               struct ctf_msg_iter_medium_ops medops;
-               size_t max_request_sz;
-               void *data;
-       } medium;
-
-       /* Current packet size (bits) (-1 if unknown) */
-       int64_t cur_exp_packet_total_size;
-
-       /* Current content size (bits) (-1 if unknown) */
-       int64_t cur_exp_packet_content_size;
-
-       /* Current stream class ID */
-       int64_t cur_stream_class_id;
-
-       /* Current event class ID */
-       int64_t cur_event_class_id;
-
-       /* Current data stream ID */
-       int64_t cur_data_stream_id;
-
-       /*
-        * Offset, in the underlying media, of the current packet's
-        * start (-1 if unknown).
-        */
-       off_t cur_packet_offset;
-
-       /* Default clock's current value */
-       uint64_t default_clock_snapshot;
-
-       /* End of current packet snapshots */
-       struct end_of_packet_snapshots snapshots;
-
-       /* End of previous packet snapshots */
-       struct end_of_packet_snapshots prev_packet_snapshots;
-
-       /* Stored values (for sequence lengths, variant tags) */
-       GArray *stored_values;
-
-       /* Iterator's current log level */
-       bt_logging_level log_level;
-
-       /* Iterator's owning self component, or `NULL` if none (query) */
-       bt_self_component *self_comp;
-};
-
-static inline
-const char *state_string(enum state state)
-{
-       switch (state) {
-       case STATE_INIT:
-               return "INIT";
-       case STATE_SWITCH_PACKET:
-               return "SWITCH_PACKET";
-       case STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN:
-               return "DSCOPE_TRACE_PACKET_HEADER_BEGIN";
-       case STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE:
-               return "DSCOPE_TRACE_PACKET_HEADER_CONTINUE";
-       case STATE_AFTER_TRACE_PACKET_HEADER:
-               return "AFTER_TRACE_PACKET_HEADER";
-       case STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN:
-               return "DSCOPE_STREAM_PACKET_CONTEXT_BEGIN";
-       case STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE:
-               return "DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE";
-       case STATE_AFTER_STREAM_PACKET_CONTEXT:
-               return "AFTER_STREAM_PACKET_CONTEXT";
-       case STATE_EMIT_MSG_STREAM_BEGINNING:
-               return "EMIT_MSG_STREAM_BEGINNING";
-       case STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS:
-               return "CHECK_EMIT_MSG_DISCARDED_EVENTS";
-       case STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS:
-               return "CHECK_EMIT_MSG_DISCARDED_PACKETS";
-       case STATE_EMIT_MSG_PACKET_BEGINNING:
-               return "EMIT_MSG_PACKET_BEGINNING";
-       case STATE_EMIT_MSG_DISCARDED_EVENTS:
-               return "EMIT_MSG_DISCARDED_EVENTS";
-       case STATE_EMIT_MSG_DISCARDED_PACKETS:
-               return "EMIT_MSG_DISCARDED_PACKETS";
-       case STATE_DSCOPE_EVENT_HEADER_BEGIN:
-               return "DSCOPE_EVENT_HEADER_BEGIN";
-       case STATE_DSCOPE_EVENT_HEADER_CONTINUE:
-               return "DSCOPE_EVENT_HEADER_CONTINUE";
-       case STATE_AFTER_EVENT_HEADER:
-               return "AFTER_EVENT_HEADER";
-       case STATE_DSCOPE_EVENT_COMMON_CONTEXT_BEGIN:
-               return "DSCOPE_EVENT_COMMON_CONTEXT_BEGIN";
-       case STATE_DSCOPE_EVENT_COMMON_CONTEXT_CONTINUE:
-               return "DSCOPE_EVENT_COMMON_CONTEXT_CONTINUE";
-       case STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN:
-               return "DSCOPE_EVENT_SPEC_CONTEXT_BEGIN";
-       case STATE_DSCOPE_EVENT_SPEC_CONTEXT_CONTINUE:
-               return "DSCOPE_EVENT_SPEC_CONTEXT_CONTINUE";
-       case STATE_DSCOPE_EVENT_PAYLOAD_BEGIN:
-               return "DSCOPE_EVENT_PAYLOAD_BEGIN";
-       case STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE:
-               return "DSCOPE_EVENT_PAYLOAD_CONTINUE";
-       case STATE_EMIT_MSG_EVENT:
-               return "EMIT_MSG_EVENT";
-       case STATE_EMIT_QUEUED_MSG_EVENT:
-               return "EMIT_QUEUED_MSG_EVENT";
-       case STATE_SKIP_PACKET_PADDING:
-               return "SKIP_PACKET_PADDING";
-       case STATE_EMIT_MSG_PACKET_END_MULTI:
-               return "EMIT_MSG_PACKET_END_MULTI";
-       case STATE_EMIT_MSG_PACKET_END_SINGLE:
-               return "EMIT_MSG_PACKET_END_SINGLE";
-       case STATE_EMIT_QUEUED_MSG_PACKET_END:
-               return "EMIT_QUEUED_MSG_PACKET_END";
-       case STATE_CHECK_EMIT_MSG_STREAM_END:
-               return "CHECK_EMIT_MSG_STREAM_END";
-       case STATE_EMIT_MSG_STREAM_END:
-               return "EMIT_MSG_STREAM_END";
-       case STATE_DONE:
-               return "DONE";
-       }
-
-       bt_common_abort();
-}
-
-static
-struct stack *stack_new(struct ctf_msg_iter *msg_it)
-{
-       bt_self_component *self_comp = msg_it->self_comp;
-       struct stack *stack = NULL;
-
-       stack = g_new0(struct stack, 1);
-       if (!stack) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Failed to allocate one stack.");
-               goto error;
-       }
-
-       stack->msg_it = msg_it;
-       stack->entries = g_array_new(FALSE, TRUE, sizeof(struct stack_entry));
-       if (!stack->entries) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Failed to allocate a GArray.");
-               goto error;
-       }
-
-       BT_COMP_LOGD("Created stack: msg-it-addr=%p, stack-addr=%p", msg_it, stack);
-       goto end;
-
-error:
-       g_free(stack);
-       stack = NULL;
-
-end:
-       return stack;
-}
-
-static
-void stack_destroy(struct stack *stack)
-{
-       struct ctf_msg_iter *msg_it;
-
-       BT_ASSERT_DBG(stack);
-       msg_it = stack->msg_it;
-       BT_COMP_LOGD("Destroying stack: addr=%p", stack);
-
-       if (stack->entries) {
-               g_array_free(stack->entries, TRUE);
-       }
-
-       g_free(stack);
-}
-
-static
-void stack_push(struct stack *stack, bt_field *base)
-{
-       struct stack_entry *entry;
-       struct ctf_msg_iter *msg_it;
-
-       BT_ASSERT_DBG(stack);
-       msg_it = stack->msg_it;
-       BT_ASSERT_DBG(base);
-       BT_COMP_LOGT("Pushing base field on stack: stack-addr=%p, "
-               "stack-size-before=%zu, stack-size-after=%zu",
-               stack, stack->size, stack->size + 1);
-
-       if (stack->entries->len == stack->size) {
-               g_array_set_size(stack->entries, stack->size + 1);
-       }
-
-       entry = &g_array_index(stack->entries, struct stack_entry, stack->size);
-       entry->base = base;
-       entry->index = 0;
-       stack->size++;
-}
-
-static inline
-unsigned int stack_size(struct stack *stack)
-{
-       BT_ASSERT_DBG(stack);
-       return stack->size;
-}
-
-static
-void stack_pop(struct stack *stack)
-{
-       struct ctf_msg_iter *msg_it;
-
-       BT_ASSERT_DBG(stack);
-       BT_ASSERT_DBG(stack_size(stack));
-       msg_it = stack->msg_it;
-       BT_COMP_LOGT("Popping from stack: "
-               "stack-addr=%p, stack-size-before=%zu, stack-size-after=%zu",
-               stack, stack->size, stack->size - 1);
-       stack->size--;
-}
-
-static inline
-struct stack_entry *stack_top(struct stack *stack)
-{
-       BT_ASSERT_DBG(stack);
-       BT_ASSERT_DBG(stack_size(stack));
-       return &g_array_index(stack->entries, struct stack_entry,
-               stack->size - 1);
-}
-
-static inline
-bool stack_empty(struct stack *stack)
-{
-       return stack_size(stack) == 0;
-}
-
-static
-void stack_clear(struct stack *stack)
-{
-       BT_ASSERT_DBG(stack);
-       stack->size = 0;
-}
-
-static inline
-enum ctf_msg_iter_status msg_iter_status_from_m_status(
-               enum ctf_msg_iter_medium_status m_status)
-{
-       /* They are the same */
-       return (int) m_status;
-}
-
-static inline
-size_t buf_size_bits(struct ctf_msg_iter *msg_it)
-{
-       return msg_it->buf.sz * 8;
-}
-
-static inline
-size_t buf_available_bits(struct ctf_msg_iter *msg_it)
-{
-       return buf_size_bits(msg_it) - msg_it->buf.at;
-}
-
-static inline
-size_t packet_at(struct ctf_msg_iter *msg_it)
-{
-       return msg_it->buf.packet_offset + msg_it->buf.at;
-}
-
-static inline
-void buf_consume_bits(struct ctf_msg_iter *msg_it, size_t incr)
-{
-       BT_COMP_LOGT("Advancing cursor: msg-it-addr=%p, cur-before=%zu, cur-after=%zu",
-               msg_it, msg_it->buf.at, msg_it->buf.at + incr);
-       msg_it->buf.at += incr;
-}
-
-static
-enum ctf_msg_iter_status request_medium_bytes(
-               struct ctf_msg_iter *msg_it)
-{
-       bt_self_component *self_comp = msg_it->self_comp;
-       uint8_t *buffer_addr = NULL;
-       size_t buffer_sz = 0;
-       enum ctf_msg_iter_medium_status m_status;
-
-       BT_COMP_LOGD("Calling user function (request bytes): msg-it-addr=%p, "
-               "request-size=%zu", msg_it, msg_it->medium.max_request_sz);
-       m_status = msg_it->medium.medops.request_bytes(
-               msg_it->medium.max_request_sz, &buffer_addr,
-               &buffer_sz, msg_it->medium.data);
-       BT_COMP_LOGD("User function returned: status=%s, buf-addr=%p, buf-size=%zu",
-               ctf_msg_iter_medium_status_string(m_status),
-               buffer_addr, buffer_sz);
-       if (m_status == CTF_MSG_ITER_MEDIUM_STATUS_OK) {
-               BT_ASSERT(buffer_sz != 0);
-
-               /* New packet offset is old one + old size (in bits) */
-               msg_it->buf.packet_offset += buf_size_bits(msg_it);
-
-               /* Restart at the beginning of the new medium buffer */
-               msg_it->buf.at = 0;
-               msg_it->buf.last_eh_at = SIZE_MAX;
-
-               /* New medium buffer size */
-               msg_it->buf.sz = buffer_sz;
-
-               /* New medium buffer address */
-               msg_it->buf.addr = buffer_addr;
-
-               BT_COMP_LOGD("User function returned new bytes: "
-                       "packet-offset=%zu, cur=%zu, size=%zu, addr=%p",
-                       msg_it->buf.packet_offset, msg_it->buf.at,
-                       msg_it->buf.sz, msg_it->buf.addr);
-               BT_COMP_LOGT_MEM(buffer_addr, buffer_sz, "Returned bytes at %p:",
-                       buffer_addr);
-       } else if (m_status == CTF_MSG_ITER_MEDIUM_STATUS_EOF) {
-               /*
-                * User returned end of stream: validate that we're not
-                * in the middle of a packet header, packet context, or
-                * event.
-                */
-               if (msg_it->cur_exp_packet_total_size >= 0) {
-                       if (packet_at(msg_it) ==
-                                       msg_it->cur_exp_packet_total_size) {
-                               goto end;
-                       }
-               } else {
-                       if (packet_at(msg_it) == 0) {
-                               goto end;
-                       }
-
-                       if (msg_it->buf.last_eh_at != SIZE_MAX &&
-                                       msg_it->buf.at == msg_it->buf.last_eh_at) {
-                               goto end;
-                       }
-               }
-
-               /* All other states are invalid */
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "User function returned %s, but message iterator is in an unexpected state: "
-                       "state=%s, cur-packet-size=%" PRId64 ", cur=%zu, "
-                       "packet-cur=%zu, last-eh-at=%zu",
-                       ctf_msg_iter_medium_status_string(m_status),
-                       state_string(msg_it->state),
-                       msg_it->cur_exp_packet_total_size,
-                       msg_it->buf.at, packet_at(msg_it),
-                       msg_it->buf.last_eh_at);
-               m_status = CTF_MSG_ITER_MEDIUM_STATUS_ERROR;
-       } else if (m_status < 0) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp, "User function failed: "
-                       "status=%s", ctf_msg_iter_medium_status_string(m_status));
-       }
-
-end:
-       return msg_iter_status_from_m_status(m_status);
-}
-
-static inline
-enum ctf_msg_iter_status buf_ensure_available_bits(
-               struct ctf_msg_iter *msg_it)
-{
-       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-
-       if (G_UNLIKELY(buf_available_bits(msg_it) == 0)) {
-               /*
-                * This _cannot_ return CTF_MSG_ITER_STATUS_OK
-                * _and_ no bits.
-                */
-               status = request_medium_bytes(msg_it);
-       }
-
-       return status;
-}
-
-static
-enum ctf_msg_iter_status read_dscope_begin_state(
-               struct ctf_msg_iter *msg_it,
-               struct ctf_field_class *dscope_fc,
-               enum state done_state, enum state continue_state,
-               bt_field *dscope_field)
-{
-       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-       bt_self_component *self_comp = msg_it->self_comp;
-       enum bt_bfcr_status bfcr_status;
-       size_t consumed_bits;
-
-       msg_it->cur_dscope_field = dscope_field;
-       BT_COMP_LOGT("Starting BFCR: msg-it-addr=%p, bfcr-addr=%p, fc-addr=%p",
-               msg_it, msg_it->bfcr, dscope_fc);
-       consumed_bits = bt_bfcr_start(msg_it->bfcr, dscope_fc,
-               msg_it->buf.addr, msg_it->buf.at, packet_at(msg_it),
-               msg_it->buf.sz, &bfcr_status);
-       BT_COMP_LOGT("BFCR consumed bits: size=%zu", consumed_bits);
-
-       switch (bfcr_status) {
-       case BT_BFCR_STATUS_OK:
-               /* Field class was read completely */
-               BT_COMP_LOGT_STR("Field was completely decoded.");
-               msg_it->state = done_state;
-               break;
-       case BT_BFCR_STATUS_EOF:
-               BT_COMP_LOGT_STR("BFCR needs more data to decode field completely.");
-               msg_it->state = continue_state;
-               break;
-       default:
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "BFCR failed to start: msg-it-addr=%p, bfcr-addr=%p, "
-                       "status=%s", msg_it, msg_it->bfcr,
-                       bt_bfcr_status_string(bfcr_status));
-               status = CTF_MSG_ITER_STATUS_ERROR;
-               goto end;
-       }
-
-       /* Consume bits now since we know we're not in an error state */
-       buf_consume_bits(msg_it, consumed_bits);
-
-end:
-       return status;
-}
-
-static
-enum ctf_msg_iter_status read_dscope_continue_state(
-               struct ctf_msg_iter *msg_it, enum state done_state)
-{
-       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-       bt_self_component *self_comp = msg_it->self_comp;
-       enum bt_bfcr_status bfcr_status;
-       size_t consumed_bits;
-
-       BT_COMP_LOGT("Continuing BFCR: msg-it-addr=%p, bfcr-addr=%p",
-               msg_it, msg_it->bfcr);
-
-       status = buf_ensure_available_bits(msg_it);
-       if (status != CTF_MSG_ITER_STATUS_OK) {
-               if (status < 0) {
-                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                               "Cannot ensure that buffer has at least one byte: "
-                               "msg-addr=%p, status=%s",
-                               msg_it, ctf_msg_iter_status_string(status));
-               } else {
-                       BT_COMP_LOGT("Cannot ensure that buffer has at least one byte: "
-                               "msg-addr=%p, status=%s",
-                               msg_it, ctf_msg_iter_status_string(status));
-               }
-
-               goto end;
-       }
-
-       consumed_bits = bt_bfcr_continue(msg_it->bfcr, msg_it->buf.addr,
-               msg_it->buf.sz, &bfcr_status);
-       BT_COMP_LOGT("BFCR consumed bits: size=%zu", consumed_bits);
-
-       switch (bfcr_status) {
-       case BT_BFCR_STATUS_OK:
-               /* Type was read completely. */
-               BT_COMP_LOGT_STR("Field was completely decoded.");
-               msg_it->state = done_state;
-               break;
-       case BT_BFCR_STATUS_EOF:
-               /* Stay in this continue state. */
-               BT_COMP_LOGT_STR("BFCR needs more data to decode field completely.");
-               break;
-       default:
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "BFCR failed to continue: msg-it-addr=%p, bfcr-addr=%p, "
-                       "status=%s", msg_it, msg_it->bfcr,
-                       bt_bfcr_status_string(bfcr_status));
-               status = CTF_MSG_ITER_STATUS_ERROR;
-               goto end;
-       }
-
-       /* Consume bits now since we know we're not in an error state. */
-       buf_consume_bits(msg_it, consumed_bits);
-end:
-       return status;
-}
-
-static
-void release_event_dscopes(struct ctf_msg_iter *msg_it)
-{
-       msg_it->dscopes.event_common_context = NULL;
-       msg_it->dscopes.event_spec_context = NULL;
-       msg_it->dscopes.event_payload = NULL;
-}
-
-static
-void release_all_dscopes(struct ctf_msg_iter *msg_it)
-{
-       msg_it->dscopes.stream_packet_context = NULL;
-
-       release_event_dscopes(msg_it);
-}
-
-static
-enum ctf_msg_iter_status switch_packet_state(struct ctf_msg_iter *msg_it)
-{
-       enum ctf_msg_iter_status status;
-       bt_self_component *self_comp = msg_it->self_comp;
-
-       /*
-        * We don't put the stream class here because we need to make
-        * sure that all the packets processed by the same message
-        * iterator refer to the same stream class (the first one).
-        */
-       BT_ASSERT(msg_it);
-
-       if (msg_it->cur_exp_packet_total_size != -1) {
-               msg_it->cur_packet_offset += msg_it->cur_exp_packet_total_size;
-       }
-
-       BT_COMP_LOGD("Switching packet: msg-it-addr=%p, cur=%zu, "
-               "packet-offset=%" PRId64, msg_it, msg_it->buf.at,
-               msg_it->cur_packet_offset);
-       stack_clear(msg_it->stack);
-       msg_it->meta.ec = NULL;
-       BT_PACKET_PUT_REF_AND_RESET(msg_it->packet);
-       BT_MESSAGE_PUT_REF_AND_RESET(msg_it->event_msg);
-       release_all_dscopes(msg_it);
-       msg_it->cur_dscope_field = NULL;
-
-       if (msg_it->medium.medops.switch_packet) {
-               enum ctf_msg_iter_medium_status medium_status;
-
-               medium_status = msg_it->medium.medops.switch_packet(msg_it->medium.data);
-               if (medium_status == CTF_MSG_ITER_MEDIUM_STATUS_EOF) {
-                       /* No more packets. */
-                       msg_it->state = STATE_CHECK_EMIT_MSG_STREAM_END;
-                       status = CTF_MSG_ITER_STATUS_OK;
-                       goto end;
-               } else if (medium_status != CTF_MSG_ITER_MEDIUM_STATUS_OK) {
-                       status = (int) medium_status;
-                       goto end;
-               }
-
-               /*
-                * After the packet switch, the medium might want to give us a
-                * different buffer for the new packet.
-                */
-               status = request_medium_bytes(msg_it);
-               if (status != CTF_MSG_ITER_STATUS_OK) {
-                       goto end;
-               }
-       }
-
-       /*
-        * Adjust current buffer so that addr points to the beginning of the new
-        * packet.
-        */
-       if (msg_it->buf.addr) {
-               size_t consumed_bytes = (size_t) (msg_it->buf.at / CHAR_BIT);
-
-               /* Packets are assumed to start on a byte frontier. */
-               if (msg_it->buf.at % CHAR_BIT) {
-                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                               "Cannot switch packet: current position is not a multiple of 8: "
-                               "msg-it-addr=%p, cur=%zu", msg_it, msg_it->buf.at);
-                       status = CTF_MSG_ITER_STATUS_ERROR;
-                       goto end;
-               }
-
-               msg_it->buf.addr += consumed_bytes;
-               msg_it->buf.sz -= consumed_bytes;
-               msg_it->buf.at = 0;
-               msg_it->buf.packet_offset = 0;
-               BT_COMP_LOGD("Adjusted buffer: addr=%p, size=%zu",
-                       msg_it->buf.addr, msg_it->buf.sz);
-       }
-
-       msg_it->cur_exp_packet_content_size = -1;
-       msg_it->cur_exp_packet_total_size = -1;
-       msg_it->cur_stream_class_id = -1;
-       msg_it->cur_event_class_id = -1;
-       msg_it->cur_data_stream_id = -1;
-       msg_it->prev_packet_snapshots = msg_it->snapshots;
-       msg_it->snapshots.discarded_events = UINT64_C(-1);
-       msg_it->snapshots.packets = UINT64_C(-1);
-       msg_it->snapshots.beginning_clock = UINT64_C(-1);
-       msg_it->snapshots.end_clock = UINT64_C(-1);
-       msg_it->state = STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN;
-
-       status = CTF_MSG_ITER_STATUS_OK;
-end:
-       return status;
-}
-
-static
-enum ctf_msg_iter_status read_packet_header_begin_state(
-               struct ctf_msg_iter *msg_it)
-{
-       struct ctf_field_class *packet_header_fc = NULL;
-       bt_self_component *self_comp = msg_it->self_comp;
-       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-
-       /*
-        * Make sure at least one bit is available for this packet. An
-        * empty packet is impossible. If we reach the end of the medium
-        * at this point, then it's considered the end of the stream.
-        */
-       status = buf_ensure_available_bits(msg_it);
-       switch (status) {
-       case CTF_MSG_ITER_STATUS_OK:
-               break;
-       case CTF_MSG_ITER_STATUS_EOF:
-               status = CTF_MSG_ITER_STATUS_OK;
-               msg_it->state = STATE_CHECK_EMIT_MSG_STREAM_END;
-               goto end;
-       default:
-               goto end;
-       }
-
-       /* Packet header class is common to the whole trace class. */
-       packet_header_fc = msg_it->meta.tc->packet_header_fc;
-       if (!packet_header_fc) {
-               msg_it->state = STATE_AFTER_TRACE_PACKET_HEADER;
-               goto end;
-       }
-
-       msg_it->cur_stream_class_id = -1;
-       msg_it->cur_event_class_id = -1;
-       msg_it->cur_data_stream_id = -1;
-       BT_COMP_LOGD("Decoding packet header field: "
-               "msg-it-addr=%p, trace-class-addr=%p, fc-addr=%p",
-               msg_it, msg_it->meta.tc, packet_header_fc);
-       status = read_dscope_begin_state(msg_it, packet_header_fc,
-               STATE_AFTER_TRACE_PACKET_HEADER,
-               STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE, NULL);
-       if (status < 0) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Cannot decode packet header field: "
-                       "msg-it-addr=%p, trace-class-addr=%p, "
-                       "fc-addr=%p",
-                       msg_it, msg_it->meta.tc, packet_header_fc);
-       }
-
-end:
-       return status;
-}
-
-static
-enum ctf_msg_iter_status read_packet_header_continue_state(
-               struct ctf_msg_iter *msg_it)
-{
-       return read_dscope_continue_state(msg_it,
-               STATE_AFTER_TRACE_PACKET_HEADER);
-}
-
-static inline
-enum ctf_msg_iter_status set_current_stream_class(struct ctf_msg_iter *msg_it)
-{
-       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-       bt_self_component *self_comp = msg_it->self_comp;
-       struct ctf_stream_class *new_stream_class = NULL;
-
-       if (msg_it->cur_stream_class_id == -1) {
-               /*
-                * No current stream class ID field, therefore only one
-                * stream class.
-                */
-               if (msg_it->meta.tc->stream_classes->len != 1) {
-                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                               "Need exactly one stream class since there's "
-                               "no stream class ID field: "
-                               "msg-it-addr=%p", msg_it);
-                       status = CTF_MSG_ITER_STATUS_ERROR;
-                       goto end;
-               }
-
-               new_stream_class = msg_it->meta.tc->stream_classes->pdata[0];
-               msg_it->cur_stream_class_id = new_stream_class->id;
-       }
-
-       new_stream_class = ctf_trace_class_borrow_stream_class_by_id(
-               msg_it->meta.tc, msg_it->cur_stream_class_id);
-       if (!new_stream_class) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "No stream class with ID of stream class ID to use in trace class: "
-                       "msg-it-addr=%p, stream-class-id=%" PRIu64 ", "
-                       "trace-class-addr=%p",
-                       msg_it, msg_it->cur_stream_class_id, msg_it->meta.tc);
-               status = CTF_MSG_ITER_STATUS_ERROR;
-               goto end;
-       }
-
-       if (msg_it->meta.sc) {
-               if (new_stream_class != msg_it->meta.sc) {
-                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                               "Two packets refer to two different stream classes within the same packet sequence: "
-                               "msg-it-addr=%p, prev-stream-class-addr=%p, "
-                               "prev-stream-class-id=%" PRId64 ", "
-                               "next-stream-class-addr=%p, "
-                               "next-stream-class-id=%" PRId64 ", "
-                               "trace-addr=%p",
-                               msg_it, msg_it->meta.sc,
-                               msg_it->meta.sc->id,
-                               new_stream_class,
-                               new_stream_class->id,
-                               msg_it->meta.tc);
-                       status = CTF_MSG_ITER_STATUS_ERROR;
-                       goto end;
-               }
-       } else {
-               msg_it->meta.sc = new_stream_class;
-       }
-
-       BT_COMP_LOGD("Set current stream class: "
-               "msg-it-addr=%p, stream-class-addr=%p, "
-               "stream-class-id=%" PRId64,
-               msg_it, msg_it->meta.sc, msg_it->meta.sc->id);
-
-end:
-       return status;
-}
-
-static inline
-enum ctf_msg_iter_status set_current_stream(struct ctf_msg_iter *msg_it)
-{
-       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-       bt_self_component *self_comp = msg_it->self_comp;
-       bt_stream *stream = NULL;
-
-       BT_COMP_LOGD("Calling user function (get stream): msg-it-addr=%p, "
-               "stream-class-addr=%p, stream-class-id=%" PRId64,
-               msg_it, msg_it->meta.sc,
-               msg_it->meta.sc->id);
-       stream = msg_it->medium.medops.borrow_stream(
-               msg_it->meta.sc->ir_sc, msg_it->cur_data_stream_id,
-               msg_it->medium.data);
-       bt_stream_get_ref(stream);
-       BT_COMP_LOGD("User function returned: stream-addr=%p", stream);
-       if (!stream) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "User function failed to return a stream object for the given stream class.");
-               status = CTF_MSG_ITER_STATUS_ERROR;
-               goto end;
-       }
-
-       if (msg_it->stream && stream != msg_it->stream) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "User function returned a different stream than the previous one for the same sequence of packets.");
-               status = CTF_MSG_ITER_STATUS_ERROR;
-               goto end;
-       }
-
-       BT_STREAM_MOVE_REF(msg_it->stream, stream);
-
-end:
-       bt_stream_put_ref(stream);
-       return status;
-}
-
-static inline
-enum ctf_msg_iter_status set_current_packet(struct ctf_msg_iter *msg_it)
-{
-       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-       bt_self_component *self_comp = msg_it->self_comp;
-       bt_packet *packet = NULL;
-
-       BT_COMP_LOGD("Creating packet from stream: "
-               "msg-it-addr=%p, stream-addr=%p, "
-               "stream-class-addr=%p, "
-               "stream-class-id=%" PRId64,
-               msg_it, msg_it->stream, msg_it->meta.sc,
-               msg_it->meta.sc->id);
-
-       /* Create packet */
-       BT_ASSERT(msg_it->stream);
-       packet = bt_packet_create(msg_it->stream);
-       if (!packet) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Cannot create packet from stream: "
-                       "msg-it-addr=%p, stream-addr=%p, "
-                       "stream-class-addr=%p, "
-                       "stream-class-id=%" PRId64,
-                       msg_it, msg_it->stream, msg_it->meta.sc,
-                       msg_it->meta.sc->id);
-               goto error;
-       }
-
-       goto end;
-
-error:
-       BT_PACKET_PUT_REF_AND_RESET(packet);
-       status = CTF_MSG_ITER_STATUS_ERROR;
-
-end:
-       BT_PACKET_MOVE_REF(msg_it->packet, packet);
-       return status;
-}
-
-static
-enum ctf_msg_iter_status after_packet_header_state(
-               struct ctf_msg_iter *msg_it)
-{
-       enum ctf_msg_iter_status status;
-
-       status = set_current_stream_class(msg_it);
-       if (status != CTF_MSG_ITER_STATUS_OK) {
-               goto end;
-       }
-
-       if (!msg_it->dry_run) {
-               status = set_current_stream(msg_it);
-               if (status != CTF_MSG_ITER_STATUS_OK) {
-                       goto end;
-               }
-
-               status = set_current_packet(msg_it);
-               if (status != CTF_MSG_ITER_STATUS_OK) {
-                       goto end;
-               }
-       }
-
-       msg_it->state = STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN;
-
-       status = CTF_MSG_ITER_STATUS_OK;
-
-end:
-       return status;
-}
-
-static
-enum ctf_msg_iter_status read_packet_context_begin_state(
-               struct ctf_msg_iter *msg_it)
-{
-       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-       bt_self_component *self_comp = msg_it->self_comp;
-       struct ctf_field_class *packet_context_fc;
-
-       BT_ASSERT(msg_it->meta.sc);
-       packet_context_fc = msg_it->meta.sc->packet_context_fc;
-       if (!packet_context_fc) {
-               BT_COMP_LOGD("No packet packet context field class in stream class: continuing: "
-                       "msg-it-addr=%p, stream-class-addr=%p, "
-                       "stream-class-id=%" PRId64,
-                       msg_it, msg_it->meta.sc,
-                       msg_it->meta.sc->id);
-               msg_it->state = STATE_AFTER_STREAM_PACKET_CONTEXT;
-               goto end;
-       }
-
-       if (packet_context_fc->in_ir && !msg_it->dry_run) {
-               BT_ASSERT(!msg_it->dscopes.stream_packet_context);
-               BT_ASSERT(msg_it->packet);
-               msg_it->dscopes.stream_packet_context =
-                       bt_packet_borrow_context_field(msg_it->packet);
-               BT_ASSERT(msg_it->dscopes.stream_packet_context);
-       }
-
-       BT_COMP_LOGD("Decoding packet context field: "
-               "msg-it-addr=%p, stream-class-addr=%p, "
-               "stream-class-id=%" PRId64 ", fc-addr=%p",
-               msg_it, msg_it->meta.sc,
-               msg_it->meta.sc->id, packet_context_fc);
-       status = read_dscope_begin_state(msg_it, packet_context_fc,
-               STATE_AFTER_STREAM_PACKET_CONTEXT,
-               STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE,
-               msg_it->dscopes.stream_packet_context);
-       if (status < 0) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Cannot decode packet context field: "
-                       "msg-it-addr=%p, stream-class-addr=%p, "
-                       "stream-class-id=%" PRId64 ", fc-addr=%p",
-                       msg_it, msg_it->meta.sc,
-                       msg_it->meta.sc->id,
-                       packet_context_fc);
-       }
-
-end:
-       return status;
-}
-
-static
-enum ctf_msg_iter_status read_packet_context_continue_state(
-               struct ctf_msg_iter *msg_it)
-{
-       return read_dscope_continue_state(msg_it,
-                       STATE_AFTER_STREAM_PACKET_CONTEXT);
-}
-
-static
-enum ctf_msg_iter_status set_current_packet_content_sizes(
-               struct ctf_msg_iter *msg_it)
-{
-       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-       bt_self_component *self_comp = msg_it->self_comp;
-
-       if (msg_it->cur_exp_packet_total_size == -1) {
-               if (msg_it->cur_exp_packet_content_size != -1) {
-                       msg_it->cur_exp_packet_total_size =
-                               msg_it->cur_exp_packet_content_size;
-               }
-       } else {
-               if (msg_it->cur_exp_packet_content_size == -1) {
-                       msg_it->cur_exp_packet_content_size =
-                               msg_it->cur_exp_packet_total_size;
-               }
-       }
-
-       BT_ASSERT((msg_it->cur_exp_packet_total_size >= 0 &&
-               msg_it->cur_exp_packet_content_size >= 0) ||
-               (msg_it->cur_exp_packet_total_size < 0 &&
-               msg_it->cur_exp_packet_content_size < 0));
-
-       if (msg_it->cur_exp_packet_content_size >
-                       msg_it->cur_exp_packet_total_size) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Invalid packet or content size: "
-                       "content size is greater than packet size: "
-                       "msg-it-addr=%p, packet-context-field-addr=%p, "
-                       "packet-size=%" PRId64 ", content-size=%" PRId64,
-                       msg_it, msg_it->dscopes.stream_packet_context,
-                       msg_it->cur_exp_packet_total_size,
-                       msg_it->cur_exp_packet_content_size);
-               status = CTF_MSG_ITER_STATUS_ERROR;
-               goto end;
-       }
-
-       BT_COMP_LOGD("Set current packet and content sizes: "
-               "msg-it-addr=%p, packet-size=%" PRIu64 ", content-size=%" PRIu64,
-               msg_it, msg_it->cur_exp_packet_total_size,
-               msg_it->cur_exp_packet_content_size);
-
-end:
-       return status;
-}
-
-static
-enum ctf_msg_iter_status after_packet_context_state(struct ctf_msg_iter *msg_it)
-{
-       enum ctf_msg_iter_status status;
-
-       status = set_current_packet_content_sizes(msg_it);
-       if (status != CTF_MSG_ITER_STATUS_OK) {
-               goto end;
-       }
-
-       if (msg_it->emit_stream_beginning_message) {
-               msg_it->state = STATE_EMIT_MSG_STREAM_BEGINNING;
-       } else {
-               msg_it->state = STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS;
-       }
-
-end:
-       return status;
-}
-
-static
-enum ctf_msg_iter_status read_event_header_begin_state(struct ctf_msg_iter *msg_it)
-{
-       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-       bt_self_component *self_comp = msg_it->self_comp;
-       struct ctf_field_class *event_header_fc = NULL;
-
-       /* Reset the position of the last event header */
-       msg_it->buf.last_eh_at = msg_it->buf.at;
-       msg_it->cur_event_class_id = -1;
-
-       /* Check if we have some content left */
-       if (msg_it->cur_exp_packet_content_size >= 0) {
-               if (G_UNLIKELY(packet_at(msg_it) ==
-                               msg_it->cur_exp_packet_content_size)) {
-                       /* No more events! */
-                       BT_COMP_LOGD("Reached end of packet: msg-it-addr=%p, "
-                               "cur=%zu", msg_it, packet_at(msg_it));
-                       msg_it->state = STATE_EMIT_MSG_PACKET_END_MULTI;
-                       goto end;
-               } else if (G_UNLIKELY(packet_at(msg_it) >
-                               msg_it->cur_exp_packet_content_size)) {
-                       /* That's not supposed to happen */
-                       BT_COMP_LOGD("Before decoding event header field: cursor is passed the packet's content: "
-                               "msg-it-addr=%p, content-size=%" PRId64 ", "
-                               "cur=%zu", msg_it,
-                               msg_it->cur_exp_packet_content_size,
-                               packet_at(msg_it));
-                       status = CTF_MSG_ITER_STATUS_ERROR;
-                       goto end;
-               }
-       } else {
-               /*
-                * "Infinite" content: we're done when the medium has
-                * nothing else for us.
-                */
-               status = buf_ensure_available_bits(msg_it);
-               switch (status) {
-               case CTF_MSG_ITER_STATUS_OK:
-                       break;
-               case CTF_MSG_ITER_STATUS_EOF:
-                       status = CTF_MSG_ITER_STATUS_OK;
-                       msg_it->state = STATE_EMIT_MSG_PACKET_END_SINGLE;
-                       goto end;
-               default:
-                       goto end;
-               }
-       }
-
-       release_event_dscopes(msg_it);
-       BT_ASSERT(msg_it->meta.sc);
-       event_header_fc = msg_it->meta.sc->event_header_fc;
-       if (!event_header_fc) {
-               msg_it->state = STATE_AFTER_EVENT_HEADER;
-               goto end;
-       }
-
-       BT_COMP_LOGD("Decoding event header field: "
-               "msg-it-addr=%p, stream-class-addr=%p, "
-               "stream-class-id=%" PRId64 ", "
-               "fc-addr=%p",
-               msg_it, msg_it->meta.sc,
-               msg_it->meta.sc->id,
-               event_header_fc);
-       status = read_dscope_begin_state(msg_it, event_header_fc,
-               STATE_AFTER_EVENT_HEADER,
-               STATE_DSCOPE_EVENT_HEADER_CONTINUE, NULL);
-       if (status < 0) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Cannot decode event header field: "
-                       "msg-it-addr=%p, stream-class-addr=%p, "
-                       "stream-class-id=%" PRId64 ", fc-addr=%p",
-                       msg_it, msg_it->meta.sc,
-                       msg_it->meta.sc->id,
-                       event_header_fc);
-       }
-
-end:
-       return status;
-}
-
-static
-enum ctf_msg_iter_status read_event_header_continue_state(
-               struct ctf_msg_iter *msg_it)
-{
-       return read_dscope_continue_state(msg_it,
-               STATE_AFTER_EVENT_HEADER);
-}
-
-static inline
-enum ctf_msg_iter_status set_current_event_class(struct ctf_msg_iter *msg_it)
-{
-       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-       bt_self_component *self_comp = msg_it->self_comp;
-
-       struct ctf_event_class *new_event_class = NULL;
-
-       if (msg_it->cur_event_class_id == -1) {
-               /*
-                * No current event class ID field, therefore only one
-                * event class.
-                */
-               if (msg_it->meta.sc->event_classes->len != 1) {
-                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                               "Need exactly one event class since there's no event class ID field: "
-                               "msg-it-addr=%p", msg_it);
-                       status = CTF_MSG_ITER_STATUS_ERROR;
-                       goto end;
-               }
-
-               new_event_class = msg_it->meta.sc->event_classes->pdata[0];
-               msg_it->cur_event_class_id = new_event_class->id;
-       }
-
-       new_event_class = ctf_stream_class_borrow_event_class_by_id(
-               msg_it->meta.sc, msg_it->cur_event_class_id);
-       if (!new_event_class) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "No event class with ID of event class ID to use in stream class: "
-                       "msg-it-addr=%p, stream-class-id=%" PRIu64 ", "
-                       "event-class-id=%" PRIu64 ", "
-                       "trace-class-addr=%p",
-                       msg_it, msg_it->meta.sc->id, msg_it->cur_event_class_id,
-                       msg_it->meta.tc);
-               status = CTF_MSG_ITER_STATUS_ERROR;
-               goto end;
-       }
-
-       msg_it->meta.ec = new_event_class;
-       BT_COMP_LOGD("Set current event class: "
-               "msg-it-addr=%p, event-class-addr=%p, "
-               "event-class-id=%" PRId64 ", "
-               "event-class-name=\"%s\"",
-               msg_it, msg_it->meta.ec, msg_it->meta.ec->id,
-               msg_it->meta.ec->name->str);
-
-end:
-       return status;
-}
-
-static inline
-enum ctf_msg_iter_status set_current_event_message(
-               struct ctf_msg_iter *msg_it)
-{
-       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-       bt_self_component *self_comp = msg_it->self_comp;
-       bt_message *msg = NULL;
-
-       BT_ASSERT_DBG(msg_it->meta.ec);
-       BT_ASSERT_DBG(msg_it->packet);
-       BT_COMP_LOGD("Creating event message from event class and packet: "
-               "msg-it-addr=%p, ec-addr=%p, ec-name=\"%s\", packet-addr=%p",
-               msg_it, msg_it->meta.ec,
-               msg_it->meta.ec->name->str,
-               msg_it->packet);
-       BT_ASSERT_DBG(msg_it->self_msg_iter);
-       BT_ASSERT_DBG(msg_it->meta.sc);
-
-       if (bt_stream_class_borrow_default_clock_class(msg_it->meta.sc->ir_sc)) {
-               msg = bt_message_event_create_with_packet_and_default_clock_snapshot(
-                       msg_it->self_msg_iter, msg_it->meta.ec->ir_ec,
-                       msg_it->packet, msg_it->default_clock_snapshot);
-       } else {
-               msg = bt_message_event_create_with_packet(msg_it->self_msg_iter,
-                       msg_it->meta.ec->ir_ec, msg_it->packet);
-       }
-
-       if (!msg) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Cannot create event message: "
-                       "msg-it-addr=%p, ec-addr=%p, ec-name=\"%s\", "
-                       "packet-addr=%p",
-                       msg_it, msg_it->meta.ec,
-                       msg_it->meta.ec->name->str,
-                       msg_it->packet);
-               goto error;
-       }
-
-       goto end;
-
-error:
-       BT_MESSAGE_PUT_REF_AND_RESET(msg);
-       status = CTF_MSG_ITER_STATUS_ERROR;
-
-end:
-       BT_MESSAGE_MOVE_REF(msg_it->event_msg, msg);
-       return status;
-}
-
-static
-enum ctf_msg_iter_status after_event_header_state(
-               struct ctf_msg_iter *msg_it)
-{
-       enum ctf_msg_iter_status status;
-
-       status = set_current_event_class(msg_it);
-       if (status != CTF_MSG_ITER_STATUS_OK) {
-               goto end;
-       }
-
-       if (G_UNLIKELY(msg_it->dry_run)) {
-               goto next_state;
-       }
-
-       status = set_current_event_message(msg_it);
-       if (status != CTF_MSG_ITER_STATUS_OK) {
-               goto end;
-       }
-
-       msg_it->event = bt_message_event_borrow_event(
-               msg_it->event_msg);
-       BT_ASSERT_DBG(msg_it->event);
-
-next_state:
-       msg_it->state = STATE_DSCOPE_EVENT_COMMON_CONTEXT_BEGIN;
-
-end:
-       return status;
-}
-
-static
-enum ctf_msg_iter_status read_event_common_context_begin_state(
-               struct ctf_msg_iter *msg_it)
-{
-       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-       bt_self_component *self_comp = msg_it->self_comp;
-       struct ctf_field_class *event_common_context_fc;
-
-       event_common_context_fc = msg_it->meta.sc->event_common_context_fc;
-       if (!event_common_context_fc) {
-               msg_it->state = STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN;
-               goto end;
-       }
-
-       if (event_common_context_fc->in_ir && !msg_it->dry_run) {
-               BT_ASSERT_DBG(!msg_it->dscopes.event_common_context);
-               msg_it->dscopes.event_common_context =
-                       bt_event_borrow_common_context_field(
-                               msg_it->event);
-               BT_ASSERT_DBG(msg_it->dscopes.event_common_context);
-       }
-
-       BT_COMP_LOGT("Decoding event common context field: "
-               "msg-it-addr=%p, stream-class-addr=%p, "
-               "stream-class-id=%" PRId64 ", "
-               "fc-addr=%p",
-               msg_it, msg_it->meta.sc,
-               msg_it->meta.sc->id,
-               event_common_context_fc);
-       status = read_dscope_begin_state(msg_it, event_common_context_fc,
-               STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN,
-               STATE_DSCOPE_EVENT_COMMON_CONTEXT_CONTINUE,
-               msg_it->dscopes.event_common_context);
-       if (status < 0) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Cannot decode event common context field: "
-                       "msg-it-addr=%p, stream-class-addr=%p, "
-                       "stream-class-id=%" PRId64 ", fc-addr=%p",
-                       msg_it, msg_it->meta.sc,
-                       msg_it->meta.sc->id,
-                       event_common_context_fc);
-       }
-
-end:
-       return status;
-}
-
-static
-enum ctf_msg_iter_status read_event_common_context_continue_state(
-               struct ctf_msg_iter *msg_it)
-{
-       return read_dscope_continue_state(msg_it,
-               STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN);
-}
-
-static
-enum ctf_msg_iter_status read_event_spec_context_begin_state(
-               struct ctf_msg_iter *msg_it)
-{
-       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-       bt_self_component *self_comp = msg_it->self_comp;
-       struct ctf_field_class *event_spec_context_fc;
-
-       event_spec_context_fc = msg_it->meta.ec->spec_context_fc;
-       if (!event_spec_context_fc) {
-               msg_it->state = STATE_DSCOPE_EVENT_PAYLOAD_BEGIN;
-               goto end;
-       }
-
-       if (event_spec_context_fc->in_ir && !msg_it->dry_run) {
-               BT_ASSERT_DBG(!msg_it->dscopes.event_spec_context);
-               msg_it->dscopes.event_spec_context =
-                       bt_event_borrow_specific_context_field(
-                               msg_it->event);
-               BT_ASSERT_DBG(msg_it->dscopes.event_spec_context);
-       }
-
-       BT_COMP_LOGT("Decoding event specific context field: "
-               "msg-it-addr=%p, event-class-addr=%p, "
-               "event-class-name=\"%s\", event-class-id=%" PRId64 ", "
-               "fc-addr=%p",
-               msg_it, msg_it->meta.ec,
-               msg_it->meta.ec->name->str,
-               msg_it->meta.ec->id,
-               event_spec_context_fc);
-       status = read_dscope_begin_state(msg_it, event_spec_context_fc,
-               STATE_DSCOPE_EVENT_PAYLOAD_BEGIN,
-               STATE_DSCOPE_EVENT_SPEC_CONTEXT_CONTINUE,
-               msg_it->dscopes.event_spec_context);
-       if (status < 0) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Cannot decode event specific context field: "
-                       "msg-it-addr=%p, event-class-addr=%p, "
-                       "event-class-name=\"%s\", "
-                       "event-class-id=%" PRId64 ", fc-addr=%p",
-                       msg_it, msg_it->meta.ec,
-                       msg_it->meta.ec->name->str,
-                       msg_it->meta.ec->id,
-                       event_spec_context_fc);
-       }
-
-end:
-       return status;
-}
-
-static
-enum ctf_msg_iter_status read_event_spec_context_continue_state(
-               struct ctf_msg_iter *msg_it)
-{
-       return read_dscope_continue_state(msg_it,
-               STATE_DSCOPE_EVENT_PAYLOAD_BEGIN);
-}
-
-static
-enum ctf_msg_iter_status read_event_payload_begin_state(
-               struct ctf_msg_iter *msg_it)
-{
-       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-       bt_self_component *self_comp = msg_it->self_comp;
-       struct ctf_field_class *event_payload_fc;
-
-       event_payload_fc = msg_it->meta.ec->payload_fc;
-       if (!event_payload_fc) {
-               msg_it->state = STATE_EMIT_MSG_EVENT;
-               goto end;
-       }
-
-       if (event_payload_fc->in_ir && !msg_it->dry_run) {
-               BT_ASSERT_DBG(!msg_it->dscopes.event_payload);
-               msg_it->dscopes.event_payload =
-                       bt_event_borrow_payload_field(
-                               msg_it->event);
-               BT_ASSERT_DBG(msg_it->dscopes.event_payload);
-       }
-
-       BT_COMP_LOGT("Decoding event payload field: "
-               "msg-it-addr=%p, event-class-addr=%p, "
-               "event-class-name=\"%s\", event-class-id=%" PRId64 ", "
-               "fc-addr=%p",
-               msg_it, msg_it->meta.ec,
-               msg_it->meta.ec->name->str,
-               msg_it->meta.ec->id,
-               event_payload_fc);
-       status = read_dscope_begin_state(msg_it, event_payload_fc,
-               STATE_EMIT_MSG_EVENT,
-               STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE,
-               msg_it->dscopes.event_payload);
-       if (status < 0) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Cannot decode event payload field: "
-                       "msg-it-addr=%p, event-class-addr=%p, "
-                       "event-class-name=\"%s\", "
-                       "event-class-id=%" PRId64 ", fc-addr=%p",
-                       msg_it, msg_it->meta.ec,
-                       msg_it->meta.ec->name->str,
-                       msg_it->meta.ec->id,
-                       event_payload_fc);
-       }
-
-end:
-       return status;
-}
-
-static
-enum ctf_msg_iter_status read_event_payload_continue_state(
-               struct ctf_msg_iter *msg_it)
-{
-       return read_dscope_continue_state(msg_it, STATE_EMIT_MSG_EVENT);
-}
-
-static
-enum ctf_msg_iter_status skip_packet_padding_state(struct ctf_msg_iter *msg_it)
-{
-       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-       size_t bits_to_skip;
-       const enum state next_state = STATE_SWITCH_PACKET;
-
-       BT_ASSERT(msg_it->cur_exp_packet_total_size > 0);
-       bits_to_skip = msg_it->cur_exp_packet_total_size - packet_at(msg_it);
-       if (bits_to_skip == 0) {
-               msg_it->state = next_state;
-               goto end;
-       } else {
-               size_t bits_to_consume;
-
-               BT_COMP_LOGD("Trying to skip %zu bits of padding: msg-it-addr=%p, size=%zu",
-                       bits_to_skip, msg_it, bits_to_skip);
-               status = buf_ensure_available_bits(msg_it);
-               if (status != CTF_MSG_ITER_STATUS_OK) {
-                       goto end;
-               }
-
-               bits_to_consume = MIN(buf_available_bits(msg_it), bits_to_skip);
-               BT_COMP_LOGD("Skipping %zu bits of padding: msg-it-addr=%p, size=%zu",
-                       bits_to_consume, msg_it, bits_to_consume);
-               buf_consume_bits(msg_it, bits_to_consume);
-               bits_to_skip = msg_it->cur_exp_packet_total_size -
-                       packet_at(msg_it);
-               if (bits_to_skip == 0) {
-                       msg_it->state = next_state;
-                       goto end;
-               }
-       }
-
-end:
-       return status;
-}
-
-static
-enum ctf_msg_iter_status check_emit_msg_discarded_events(
-               struct ctf_msg_iter *msg_it)
-{
-       msg_it->state = STATE_EMIT_MSG_DISCARDED_EVENTS;
-
-       if (!msg_it->meta.sc->has_discarded_events) {
-               msg_it->state = STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS;
-               goto end;
-       }
-
-       if (msg_it->prev_packet_snapshots.discarded_events == UINT64_C(-1)) {
-               if (msg_it->snapshots.discarded_events == 0 ||
-                               msg_it->snapshots.discarded_events == UINT64_C(-1)) {
-                       /*
-                        * Stream's first packet with no discarded
-                        * events or no information about discarded
-                        * events: do not emit.
-                        */
-                       msg_it->state = STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS;
-               }
-       } else {
-               /*
-                * If the previous packet has a value for this counter,
-                * then this counter is defined for the whole stream.
-                */
-               BT_ASSERT(msg_it->snapshots.discarded_events != UINT64_C(-1));
-
-               if (msg_it->snapshots.discarded_events -
-                               msg_it->prev_packet_snapshots.discarded_events == 0) {
-                       /*
-                        * No discarded events since previous packet: do
-                        * not emit.
-                        */
-                       msg_it->state = STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS;
-               }
-       }
-
-end:
-       return CTF_MSG_ITER_STATUS_OK;
-}
-
-static
-enum ctf_msg_iter_status check_emit_msg_discarded_packets(
-               struct ctf_msg_iter *msg_it)
-{
-       msg_it->state = STATE_EMIT_MSG_DISCARDED_PACKETS;
-
-       if (!msg_it->meta.sc->has_discarded_packets) {
-               msg_it->state = STATE_EMIT_MSG_PACKET_BEGINNING;
-               goto end;
-       }
-
-       if (msg_it->prev_packet_snapshots.packets == UINT64_C(-1)) {
-               /*
-                * Stream's first packet or no information about
-                * discarded packets: do not emit. In other words, if
-                * this is the first packet and its sequence number is
-                * not 0, do not consider that packets were previously
-                * lost: we might be reading a partial stream (LTTng
-                * snapshot for example).
-                */
-               msg_it->state = STATE_EMIT_MSG_PACKET_BEGINNING;
-       } else {
-               /*
-                * If the previous packet has a value for this counter,
-                * then this counter is defined for the whole stream.
-                */
-               BT_ASSERT(msg_it->snapshots.packets != UINT64_C(-1));
-
-               if (msg_it->snapshots.packets -
-                               msg_it->prev_packet_snapshots.packets <= 1) {
-                       /*
-                        * No discarded packets since previous packet:
-                        * do not emit.
-                        */
-                       msg_it->state = STATE_EMIT_MSG_PACKET_BEGINNING;
-               }
-       }
-
-end:
-       return CTF_MSG_ITER_STATUS_OK;
-}
-
-static inline
-enum state check_emit_msg_stream_end(struct ctf_msg_iter *msg_it)
-{
-       enum state next_state;
-
-       if (msg_it->emit_stream_end_message) {
-               next_state = STATE_EMIT_MSG_STREAM_END;
-       } else {
-               next_state = STATE_DONE;
-       }
-
-       return next_state;
-}
-
-static inline
-enum ctf_msg_iter_status handle_state(struct ctf_msg_iter *msg_it)
-{
-       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-       const enum state state = msg_it->state;
-
-       BT_COMP_LOGT("Handling state: msg-it-addr=%p, state=%s",
-               msg_it, state_string(state));
-
-       // TODO: optimalize!
-       switch (state) {
-       case STATE_INIT:
-               msg_it->state = STATE_SWITCH_PACKET;
-               break;
-       case STATE_SWITCH_PACKET:
-               status = switch_packet_state(msg_it);
-               break;
-       case STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN:
-               status = read_packet_header_begin_state(msg_it);
-               break;
-       case STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE:
-               status = read_packet_header_continue_state(msg_it);
-               break;
-       case STATE_AFTER_TRACE_PACKET_HEADER:
-               status = after_packet_header_state(msg_it);
-               break;
-       case STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN:
-               status = read_packet_context_begin_state(msg_it);
-               break;
-       case STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE:
-               status = read_packet_context_continue_state(msg_it);
-               break;
-       case STATE_AFTER_STREAM_PACKET_CONTEXT:
-               status = after_packet_context_state(msg_it);
-               break;
-       case STATE_EMIT_MSG_STREAM_BEGINNING:
-               msg_it->state = STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS;
-               break;
-       case STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS:
-               status = check_emit_msg_discarded_events(msg_it);
-               break;
-       case STATE_EMIT_MSG_DISCARDED_EVENTS:
-               msg_it->state = STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS;
-               break;
-       case STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS:
-               status = check_emit_msg_discarded_packets(msg_it);
-               break;
-       case STATE_EMIT_MSG_DISCARDED_PACKETS:
-               msg_it->state = STATE_EMIT_MSG_PACKET_BEGINNING;
-               break;
-       case STATE_EMIT_MSG_PACKET_BEGINNING:
-               msg_it->state = STATE_DSCOPE_EVENT_HEADER_BEGIN;
-               break;
-       case STATE_DSCOPE_EVENT_HEADER_BEGIN:
-               status = read_event_header_begin_state(msg_it);
-               break;
-       case STATE_DSCOPE_EVENT_HEADER_CONTINUE:
-               status = read_event_header_continue_state(msg_it);
-               break;
-       case STATE_AFTER_EVENT_HEADER:
-               status = after_event_header_state(msg_it);
-               break;
-       case STATE_DSCOPE_EVENT_COMMON_CONTEXT_BEGIN:
-               status = read_event_common_context_begin_state(msg_it);
-               break;
-       case STATE_DSCOPE_EVENT_COMMON_CONTEXT_CONTINUE:
-               status = read_event_common_context_continue_state(msg_it);
-               break;
-       case STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN:
-               status = read_event_spec_context_begin_state(msg_it);
-               break;
-       case STATE_DSCOPE_EVENT_SPEC_CONTEXT_CONTINUE:
-               status = read_event_spec_context_continue_state(msg_it);
-               break;
-       case STATE_DSCOPE_EVENT_PAYLOAD_BEGIN:
-               status = read_event_payload_begin_state(msg_it);
-               break;
-       case STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE:
-               status = read_event_payload_continue_state(msg_it);
-               break;
-       case STATE_EMIT_MSG_EVENT:
-               msg_it->state = STATE_DSCOPE_EVENT_HEADER_BEGIN;
-               break;
-       case STATE_EMIT_QUEUED_MSG_EVENT:
-               msg_it->state = STATE_EMIT_MSG_EVENT;
-               break;
-       case STATE_SKIP_PACKET_PADDING:
-               status = skip_packet_padding_state(msg_it);
-               break;
-       case STATE_EMIT_MSG_PACKET_END_MULTI:
-               msg_it->state = STATE_SKIP_PACKET_PADDING;
-               break;
-       case STATE_EMIT_MSG_PACKET_END_SINGLE:
-               msg_it->state = STATE_EMIT_MSG_STREAM_END;
-               break;
-       case STATE_EMIT_QUEUED_MSG_PACKET_END:
-               msg_it->state = STATE_EMIT_MSG_PACKET_END_SINGLE;
-               break;
-       case STATE_CHECK_EMIT_MSG_STREAM_END:
-               msg_it->state = check_emit_msg_stream_end(msg_it);
-               break;
-       case STATE_EMIT_MSG_STREAM_END:
-               msg_it->state = STATE_DONE;
-               break;
-       case STATE_DONE:
-               break;
-       default:
-               BT_COMP_LOGF("Unknown CTF plugin message iterator state: "
-                       "msg-it-addr=%p, state=%d", msg_it, msg_it->state);
-               bt_common_abort();
-       }
-
-       BT_COMP_LOGT("Handled state: msg-it-addr=%p, status=%s, "
-               "prev-state=%s, cur-state=%s",
-               msg_it, ctf_msg_iter_status_string(status),
-               state_string(state), state_string(msg_it->state));
-       return status;
-}
-
-BT_HIDDEN
-void ctf_msg_iter_reset_for_next_stream_file(struct ctf_msg_iter *msg_it)
-{
-       BT_ASSERT(msg_it);
-       BT_COMP_LOGD("Resetting message iterator: addr=%p", msg_it);
-       stack_clear(msg_it->stack);
-       msg_it->meta.sc = NULL;
-       msg_it->meta.ec = NULL;
-       BT_PACKET_PUT_REF_AND_RESET(msg_it->packet);
-       BT_STREAM_PUT_REF_AND_RESET(msg_it->stream);
-       BT_MESSAGE_PUT_REF_AND_RESET(msg_it->event_msg);
-       release_all_dscopes(msg_it);
-       msg_it->cur_dscope_field = NULL;
-
-       msg_it->buf.addr = NULL;
-       msg_it->buf.sz = 0;
-       msg_it->buf.at = 0;
-       msg_it->buf.last_eh_at = SIZE_MAX;
-       msg_it->buf.packet_offset = 0;
-       msg_it->state = STATE_INIT;
-       msg_it->cur_exp_packet_content_size = -1;
-       msg_it->cur_exp_packet_total_size = -1;
-       msg_it->cur_packet_offset = -1;
-       msg_it->cur_event_class_id = -1;
-       msg_it->snapshots.beginning_clock = UINT64_C(-1);
-       msg_it->snapshots.end_clock = UINT64_C(-1);
-}
-
-/**
- * Resets the internal state of a CTF message iterator.
- */
-BT_HIDDEN
-void ctf_msg_iter_reset(struct ctf_msg_iter *msg_it)
-{
-       ctf_msg_iter_reset_for_next_stream_file(msg_it);
-       msg_it->cur_stream_class_id = -1;
-       msg_it->cur_data_stream_id = -1;
-       msg_it->snapshots.discarded_events = UINT64_C(-1);
-       msg_it->snapshots.packets = UINT64_C(-1);
-       msg_it->prev_packet_snapshots.discarded_events = UINT64_C(-1);
-       msg_it->prev_packet_snapshots.packets = UINT64_C(-1);
-       msg_it->prev_packet_snapshots.beginning_clock = UINT64_C(-1);
-       msg_it->prev_packet_snapshots.end_clock = UINT64_C(-1);
-       msg_it->emit_stream_beginning_message = true;
-       msg_it->emit_stream_end_message = false;
-}
-
-static
-bt_field *borrow_next_field(struct ctf_msg_iter *msg_it)
-{
-       bt_field *next_field = NULL;
-       bt_field *base_field;
-       const bt_field_class *base_fc;
-       bt_field_class_type base_fc_type;
-       size_t index;
-
-       BT_ASSERT_DBG(!stack_empty(msg_it->stack));
-       index = stack_top(msg_it->stack)->index;
-       base_field = stack_top(msg_it->stack)->base;
-       BT_ASSERT_DBG(base_field);
-       base_fc = bt_field_borrow_class_const(base_field);
-       BT_ASSERT_DBG(base_fc);
-       base_fc_type = bt_field_class_get_type(base_fc);
-
-       if (base_fc_type == BT_FIELD_CLASS_TYPE_STRUCTURE) {
-               BT_ASSERT_DBG(index <
-                       bt_field_class_structure_get_member_count(
-                               bt_field_borrow_class_const(
-                                               base_field)));
-               next_field =
-                       bt_field_structure_borrow_member_field_by_index(
-                               base_field, index);
-       } else if (bt_field_class_type_is(base_fc_type,
-                       BT_FIELD_CLASS_TYPE_ARRAY)) {
-               BT_ASSERT_DBG(index < bt_field_array_get_length(base_field));
-               next_field = bt_field_array_borrow_element_field_by_index(
-                       base_field, index);
-       } else if (bt_field_class_type_is(base_fc_type,
-                       BT_FIELD_CLASS_TYPE_VARIANT)) {
-               BT_ASSERT_DBG(index == 0);
-               next_field = bt_field_variant_borrow_selected_option_field(
-                       base_field);
-       } else {
-               bt_common_abort();
-       }
-
-       BT_ASSERT_DBG(next_field);
-       return next_field;
-}
-
-static
-void update_default_clock(struct ctf_msg_iter *msg_it, uint64_t new_val,
-               uint64_t new_val_size)
-{
-       uint64_t new_val_mask;
-       uint64_t cur_value_masked;
-
-       BT_ASSERT_DBG(new_val_size > 0);
-
-       /*
-        * Special case for a 64-bit new value, which is the limit
-        * of a clock value as of this version: overwrite the
-        * current value directly.
-        */
-       if (new_val_size == 64) {
-               msg_it->default_clock_snapshot = new_val;
-               goto end;
-       }
-
-       new_val_mask = (1ULL << new_val_size) - 1;
-       cur_value_masked = msg_it->default_clock_snapshot & new_val_mask;
-
-       if (new_val < cur_value_masked) {
-               /*
-                * It looks like a wrap happened on the number of bits
-                * of the requested new value. Assume that the clock
-                * value wrapped only one time.
-                */
-               msg_it->default_clock_snapshot += new_val_mask + 1;
-       }
-
-       /* Clear the low bits of the current clock value. */
-       msg_it->default_clock_snapshot &= ~new_val_mask;
-
-       /* Set the low bits of the current clock value. */
-       msg_it->default_clock_snapshot |= new_val;
-
-end:
-       BT_COMP_LOGT("Updated default clock's value from integer field's value: "
-               "value=%" PRIu64, msg_it->default_clock_snapshot);
-}
-
-static
-enum bt_bfcr_status bfcr_unsigned_int_cb(uint64_t value,
-               struct ctf_field_class *fc, void *data)
-{
-       struct ctf_msg_iter *msg_it = data;
-       bt_self_component *self_comp = msg_it->self_comp;
-       enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
-
-       bt_field *field = NULL;
-       struct ctf_field_class_int *int_fc = (void *) fc;
-
-       BT_COMP_LOGT("Unsigned integer function called from BFCR: "
-               "msg-it-addr=%p, bfcr-addr=%p, fc-addr=%p, "
-               "fc-type=%d, fc-in-ir=%d, value=%" PRIu64,
-               msg_it, msg_it->bfcr, fc, fc->type, fc->in_ir, value);
-
-       if (G_LIKELY(int_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE)) {
-               goto update_def_clock;
-       }
-
-       switch (int_fc->meaning) {
-       case CTF_FIELD_CLASS_MEANING_EVENT_CLASS_ID:
-               msg_it->cur_event_class_id = value;
-               break;
-       case CTF_FIELD_CLASS_MEANING_DATA_STREAM_ID:
-               msg_it->cur_data_stream_id = value;
-               break;
-       case CTF_FIELD_CLASS_MEANING_PACKET_BEGINNING_TIME:
-               msg_it->snapshots.beginning_clock = value;
-               break;
-       case CTF_FIELD_CLASS_MEANING_PACKET_END_TIME:
-               msg_it->snapshots.end_clock = value;
-               break;
-       case CTF_FIELD_CLASS_MEANING_STREAM_CLASS_ID:
-               msg_it->cur_stream_class_id = value;
-               break;
-       case CTF_FIELD_CLASS_MEANING_MAGIC:
-               if (value != 0xc1fc1fc1) {
-                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                               "Invalid CTF magic number: msg-it-addr=%p, "
-                               "magic=%" PRIx64, msg_it, value);
-                       status = BT_BFCR_STATUS_ERROR;
-                       goto end;
-               }
-
-               break;
-       case CTF_FIELD_CLASS_MEANING_PACKET_COUNTER_SNAPSHOT:
-               msg_it->snapshots.packets = value;
-               break;
-       case CTF_FIELD_CLASS_MEANING_DISC_EV_REC_COUNTER_SNAPSHOT:
-               msg_it->snapshots.discarded_events = value;
-               break;
-       case CTF_FIELD_CLASS_MEANING_EXP_PACKET_TOTAL_SIZE:
-               msg_it->cur_exp_packet_total_size = value;
-               break;
-       case CTF_FIELD_CLASS_MEANING_EXP_PACKET_CONTENT_SIZE:
-               msg_it->cur_exp_packet_content_size = value;
-               break;
-       default:
-               bt_common_abort();
-       }
-
-update_def_clock:
-       if (G_UNLIKELY(int_fc->mapped_clock_class)) {
-               update_default_clock(msg_it, value, int_fc->base.size);
-       }
-
-       if (G_UNLIKELY(int_fc->storing_index >= 0)) {
-               g_array_index(msg_it->stored_values, uint64_t,
-                       (uint64_t) int_fc->storing_index) = value;
-       }
-
-       if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
-               goto end;
-       }
-
-       field = borrow_next_field(msg_it);
-       BT_ASSERT_DBG(field);
-       BT_ASSERT_DBG(bt_field_borrow_class_const(field) == fc->ir_fc);
-       BT_ASSERT_DBG(bt_field_class_type_is(bt_field_get_class_type(field),
-               BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER));
-       bt_field_integer_unsigned_set_value(field, value);
-       stack_top(msg_it->stack)->index++;
-
-end:
-       return status;
-}
-
-static
-enum bt_bfcr_status bfcr_unsigned_int_char_cb(uint64_t value,
-               struct ctf_field_class *fc, void *data)
-{
-       int ret;
-       struct ctf_msg_iter *msg_it = data;
-       bt_self_component *self_comp = msg_it->self_comp;
-       enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
-       bt_field *string_field = NULL;
-       struct ctf_field_class_int *int_fc = (void *) fc;
-       char str[2] = {'\0', '\0'};
-
-       BT_COMP_LOGT("Unsigned integer character function called from BFCR: "
-               "msg-it-addr=%p, bfcr-addr=%p, fc-addr=%p, "
-               "fc-type=%d, fc-in-ir=%d, value=%" PRIu64,
-               msg_it, msg_it->bfcr, fc, fc->type, fc->in_ir, value);
-       BT_ASSERT_DBG(int_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE);
-       BT_ASSERT_DBG(!int_fc->mapped_clock_class);
-       BT_ASSERT_DBG(int_fc->storing_index < 0);
-
-       if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
-               goto end;
-       }
-
-       if (msg_it->done_filling_string) {
-               goto end;
-       }
-
-       if (value == 0) {
-               msg_it->done_filling_string = true;
-               goto end;
-       }
-
-       string_field = stack_top(msg_it->stack)->base;
-       BT_ASSERT_DBG(bt_field_get_class_type(string_field) ==
-               BT_FIELD_CLASS_TYPE_STRING);
-
-       /* Append character */
-       str[0] = (char) value;
-       ret = bt_field_string_append_with_length(string_field, str, 1);
-       if (ret) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Cannot append character to string field's value: "
-                       "msg-it-addr=%p, field-addr=%p, ret=%d",
-                       msg_it, string_field, ret);
-               status = BT_BFCR_STATUS_ERROR;
-               goto end;
-       }
-
-end:
-       return status;
-}
-
-static
-enum bt_bfcr_status bfcr_signed_int_cb(int64_t value,
-               struct ctf_field_class *fc, void *data)
-{
-       enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
-       bt_field *field = NULL;
-       struct ctf_msg_iter *msg_it = data;
-       struct ctf_field_class_int *int_fc = (void *) fc;
-
-       BT_COMP_LOGT("Signed integer function called from BFCR: "
-               "msg-it-addr=%p, bfcr-addr=%p, fc-addr=%p, "
-               "fc-type=%d, fc-in-ir=%d, value=%" PRId64,
-               msg_it, msg_it->bfcr, fc, fc->type, fc->in_ir, value);
-       BT_ASSERT_DBG(int_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE);
-
-       if (G_UNLIKELY(int_fc->storing_index >= 0)) {
-               g_array_index(msg_it->stored_values, uint64_t,
-                       (uint64_t) int_fc->storing_index) = (uint64_t) value;
-       }
-
-       if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
-               goto end;
-       }
-
-       field = borrow_next_field(msg_it);
-       BT_ASSERT_DBG(field);
-       BT_ASSERT_DBG(bt_field_borrow_class_const(field) == fc->ir_fc);
-       BT_ASSERT_DBG(bt_field_class_type_is(bt_field_get_class_type(field),
-               BT_FIELD_CLASS_TYPE_SIGNED_INTEGER));
-       bt_field_integer_signed_set_value(field, value);
-       stack_top(msg_it->stack)->index++;
-
-end:
-       return status;
-}
-
-static
-enum bt_bfcr_status bfcr_floating_point_cb(double value,
-               struct ctf_field_class *fc, void *data)
-{
-       enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
-       bt_field *field = NULL;
-       struct ctf_msg_iter *msg_it = data;
-       bt_field_class_type type;
-
-       BT_COMP_LOGT("Floating point number function called from BFCR: "
-               "msg-it-addr=%p, bfcr-addr=%p, fc-addr=%p, "
-               "fc-type=%d, fc-in-ir=%d, value=%f",
-               msg_it, msg_it->bfcr, fc, fc->type, fc->in_ir, value);
-
-       if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
-               goto end;
-       }
-
-       field = borrow_next_field(msg_it);
-       type = bt_field_get_class_type(field);
-       BT_ASSERT_DBG(field);
-       BT_ASSERT_DBG(bt_field_borrow_class_const(field) == fc->ir_fc);
-       BT_ASSERT_DBG(bt_field_class_type_is(type, BT_FIELD_CLASS_TYPE_REAL));
-
-       if (type == BT_FIELD_CLASS_TYPE_SINGLE_PRECISION_REAL) {
-               bt_field_real_single_precision_set_value(field, (float) value);
-       } else {
-               bt_field_real_double_precision_set_value(field, value);
-       }
-       stack_top(msg_it->stack)->index++;
-
-end:
-       return status;
-}
-
-static
-enum bt_bfcr_status bfcr_string_begin_cb(
-               struct ctf_field_class *fc, void *data)
-{
-       bt_field *field = NULL;
-       struct ctf_msg_iter *msg_it = data;
-
-       BT_COMP_LOGT("String (beginning) function called from BFCR: "
-               "msg-it-addr=%p, bfcr-addr=%p, fc-addr=%p, "
-               "fc-type=%d, fc-in-ir=%d",
-               msg_it, msg_it->bfcr, fc, fc->type, fc->in_ir);
-
-       if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
-               goto end;
-       }
-
-       field = borrow_next_field(msg_it);
-       BT_ASSERT_DBG(field);
-       BT_ASSERT_DBG(bt_field_borrow_class_const(field) == fc->ir_fc);
-       BT_ASSERT_DBG(bt_field_get_class_type(field) ==
-                 BT_FIELD_CLASS_TYPE_STRING);
-       bt_field_string_clear(field);
-
-       /*
-        * Push on stack. Not a compound class per se, but we know that
-        * only bfcr_string_cb() may be called between this call and a
-        * subsequent call to bfcr_string_end_cb().
-        */
-       stack_push(msg_it->stack, field);
-
-end:
-       return BT_BFCR_STATUS_OK;
-}
-
-static
-enum bt_bfcr_status bfcr_string_cb(const char *value,
-               size_t len, struct ctf_field_class *fc, void *data)
-{
-       enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
-       bt_field *field = NULL;
-       struct ctf_msg_iter *msg_it = data;
-       bt_self_component *self_comp = msg_it->self_comp;
-       int ret;
-
-       BT_COMP_LOGT("String (substring) function called from BFCR: "
-               "msg-it-addr=%p, bfcr-addr=%p, fc-addr=%p, "
-               "fc-type=%d, fc-in-ir=%d, string-length=%zu",
-               msg_it, msg_it->bfcr, fc, fc->type, fc->in_ir,
-               len);
-
-       if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
-               goto end;
-       }
-
-       field = stack_top(msg_it->stack)->base;
-       BT_ASSERT_DBG(field);
-
-       /* Append current substring */
-       ret = bt_field_string_append_with_length(field, value, len);
-       if (ret) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Cannot append substring to string field's value: "
-                       "msg-it-addr=%p, field-addr=%p, string-length=%zu, "
-                       "ret=%d", msg_it, field, len, ret);
-               status = BT_BFCR_STATUS_ERROR;
-               goto end;
-       }
-
-end:
-       return status;
-}
-
-static
-enum bt_bfcr_status bfcr_string_end_cb(
-               struct ctf_field_class *fc, void *data)
-{
-       struct ctf_msg_iter *msg_it = data;
-
-       BT_COMP_LOGT("String (end) function called from BFCR: "
-               "msg-it-addr=%p, bfcr-addr=%p, fc-addr=%p, "
-               "fc-type=%d, fc-in-ir=%d",
-               msg_it, msg_it->bfcr, fc, fc->type, fc->in_ir);
-
-       if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
-               goto end;
-       }
-
-       /* Pop string field */
-       stack_pop(msg_it->stack);
-
-       /* Go to next field */
-       stack_top(msg_it->stack)->index++;
-
-end:
-       return BT_BFCR_STATUS_OK;
-}
-
-static
-enum bt_bfcr_status bfcr_compound_begin_cb(
-               struct ctf_field_class *fc, void *data)
-{
-       struct ctf_msg_iter *msg_it = data;
-       bt_field *field;
-
-       BT_COMP_LOGT("Compound (beginning) function called from BFCR: "
-               "msg-it-addr=%p, bfcr-addr=%p, fc-addr=%p, "
-               "fc-type=%d, fc-in-ir=%d",
-               msg_it, msg_it->bfcr, fc, fc->type, fc->in_ir);
-
-       if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
-               goto end;
-       }
-
-       /* Borrow field */
-       if (stack_empty(msg_it->stack)) {
-               /* Root: already set by read_dscope_begin_state() */
-               field = msg_it->cur_dscope_field;
-       } else {
-               field = borrow_next_field(msg_it);
-               BT_ASSERT_DBG(field);
-       }
-
-       /* Push field */
-       BT_ASSERT_DBG(field);
-       BT_ASSERT_DBG(bt_field_borrow_class_const(field) == fc->ir_fc);
-       stack_push(msg_it->stack, field);
-
-       /*
-        * Change BFCR "unsigned int" callback if it's a text
-        * array/sequence.
-        */
-       if (fc->type == CTF_FIELD_CLASS_TYPE_ARRAY ||
-                       fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE) {
-               struct ctf_field_class_array_base *array_fc = (void *) fc;
-
-               if (array_fc->is_text) {
-                       BT_ASSERT_DBG(bt_field_get_class_type(field) ==
-                                 BT_FIELD_CLASS_TYPE_STRING);
-                       msg_it->done_filling_string = false;
-                       bt_field_string_clear(field);
-                       bt_bfcr_set_unsigned_int_cb(msg_it->bfcr,
-                               bfcr_unsigned_int_char_cb);
-               }
-       }
-
-end:
-       return BT_BFCR_STATUS_OK;
-}
-
-static
-enum bt_bfcr_status bfcr_compound_end_cb(
-               struct ctf_field_class *fc, void *data)
-{
-       struct ctf_msg_iter *msg_it = data;
-
-       BT_COMP_LOGT("Compound (end) function called from BFCR: "
-               "msg-it-addr=%p, bfcr-addr=%p, fc-addr=%p, "
-               "fc-type=%d, fc-in-ir=%d",
-               msg_it, msg_it->bfcr, fc, fc->type, fc->in_ir);
-
-       if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
-               goto end;
-       }
-
-       BT_ASSERT_DBG(!stack_empty(msg_it->stack));
-       BT_ASSERT_DBG(bt_field_borrow_class_const(stack_top(msg_it->stack)->base) ==
-               fc->ir_fc);
-
-       /*
-        * Reset BFCR "unsigned int" callback if it's a text
-        * array/sequence.
-        */
-       if (fc->type == CTF_FIELD_CLASS_TYPE_ARRAY ||
-                       fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE) {
-               struct ctf_field_class_array_base *array_fc = (void *) fc;
-
-               if (array_fc->is_text) {
-                       BT_ASSERT_DBG(bt_field_get_class_type(
-                               stack_top(msg_it->stack)->base) ==
-                               BT_FIELD_CLASS_TYPE_STRING);
-                       bt_bfcr_set_unsigned_int_cb(msg_it->bfcr,
-                               bfcr_unsigned_int_cb);
-               }
-       }
-
-       /* Pop stack */
-       stack_pop(msg_it->stack);
-
-       /* If the stack is not empty, increment the base's index */
-       if (!stack_empty(msg_it->stack)) {
-               stack_top(msg_it->stack)->index++;
-       }
-
-end:
-       return BT_BFCR_STATUS_OK;
-}
-
-static
-int64_t bfcr_get_sequence_length_cb(struct ctf_field_class *fc, void *data)
-{
-       bt_field *seq_field;
-       struct ctf_msg_iter *msg_it = data;
-       bt_self_component *self_comp = msg_it->self_comp;
-       struct ctf_field_class_sequence *seq_fc = (void *) fc;
-       int64_t length;
-       int ret;
-
-       length = (uint64_t) g_array_index(msg_it->stored_values, uint64_t,
-               seq_fc->stored_length_index);
-
-       if (G_UNLIKELY(msg_it->dry_run)){
-               goto end;
-       }
-
-       seq_field = stack_top(msg_it->stack)->base;
-       BT_ASSERT_DBG(seq_field);
-
-       /*
-        * bfcr_get_sequence_length_cb() also gets called back for a
-        * text sequence, but the destination field is a string field.
-        * Only set the field's sequence length if the destination field
-        * is a sequence field.
-        */
-       if (!seq_fc->base.is_text) {
-               BT_ASSERT_DBG(bt_field_class_type_is(
-                       bt_field_get_class_type(seq_field),
-                               BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY));
-               ret = bt_field_array_dynamic_set_length(seq_field,
-                       (uint64_t) length);
-               if (ret) {
-                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                               "Cannot set dynamic array field's length field: "
-                               "msg-it-addr=%p, field-addr=%p, "
-                               "length=%" PRIu64, msg_it, seq_field, length);
-                       length = -1;
-               }
-       }
-
-end:
-       return length;
-}
-
-static
-struct ctf_field_class *bfcr_borrow_variant_selected_field_class_cb(
-               struct ctf_field_class *fc, void *data)
-{
-       int ret;
-       uint64_t i;
-       int64_t option_index = -1;
-       struct ctf_msg_iter *msg_it = data;
-       struct ctf_field_class_variant *var_fc = (void *) fc;
-       struct ctf_named_field_class *selected_option = NULL;
-       bt_self_component *self_comp = msg_it->self_comp;
-       struct ctf_field_class *ret_fc = NULL;
-       union {
-               uint64_t u;
-               int64_t i;
-       } tag;
-
-       /* Get variant's tag */
-       tag.u = g_array_index(msg_it->stored_values, uint64_t,
-               var_fc->stored_tag_index);
-
-       /*
-        * Check each range to find the selected option's index.
-        */
-       if (var_fc->tag_fc->base.is_signed) {
-               for (i = 0; i < var_fc->ranges->len; i++) {
-                       struct ctf_field_class_variant_range *range =
-                               ctf_field_class_variant_borrow_range_by_index(
-                                       var_fc, i);
-
-                       if (tag.i >= range->range.lower.i &&
-                                       tag.i <= range->range.upper.i) {
-                               option_index = (int64_t) range->option_index;
-                               break;
-                       }
-               }
-       } else {
-               for (i = 0; i < var_fc->ranges->len; i++) {
-                       struct ctf_field_class_variant_range *range =
-                               ctf_field_class_variant_borrow_range_by_index(
-                                       var_fc, i);
-
-                       if (tag.u >= range->range.lower.u &&
-                                       tag.u <= range->range.upper.u) {
-                               option_index = (int64_t) range->option_index;
-                               break;
-                       }
-               }
-       }
-
-       if (option_index < 0) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Cannot find variant field class's option: "
-                       "msg-it-addr=%p, var-fc-addr=%p, u-tag=%" PRIu64 ", "
-                       "i-tag=%" PRId64, msg_it, var_fc, tag.u, tag.i);
-               ret_fc = NULL;
-               goto end;
-       }
-
-       selected_option = ctf_field_class_variant_borrow_option_by_index(
-               var_fc, (uint64_t) option_index);
-
-       if (selected_option->fc->in_ir && !msg_it->dry_run) {
-               bt_field *var_field = stack_top(msg_it->stack)->base;
-
-               ret = bt_field_variant_select_option_by_index(
-                       var_field, option_index);
-               if (ret) {
-                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                               "Cannot select variant field's option field: "
-                               "msg-it-addr=%p, var-field-addr=%p, "
-                               "opt-index=%" PRId64, msg_it, var_field,
-                               option_index);
-                       ret_fc = NULL;
-                       goto end;
-               }
-       }
-
-       ret_fc = selected_option->fc;
-
-end:
-       return ret_fc;
-}
-
-static
-bt_message *create_msg_stream_beginning(struct ctf_msg_iter *msg_it)
-{
-       bt_self_component *self_comp = msg_it->self_comp;
-       bt_message *msg;
-
-       BT_ASSERT(msg_it->stream);
-       BT_ASSERT(msg_it->self_msg_iter);
-       msg = bt_message_stream_beginning_create(msg_it->self_msg_iter,
-               msg_it->stream);
-       if (!msg) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Cannot create stream beginning message: "
-                       "msg-it-addr=%p, stream-addr=%p",
-                       msg_it, msg_it->stream);
-       }
-
-       return msg;
-}
-
-static
-bt_message *create_msg_stream_end(struct ctf_msg_iter *msg_it)
-{
-       bt_self_component *self_comp = msg_it->self_comp;
-       bt_message *msg;
-
-       if (!msg_it->stream) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Cannot create stream end message because stream is NULL: "
-                       "msg-it-addr=%p", msg_it);
-               msg = NULL;
-               goto end;
-       }
-
-       BT_ASSERT(msg_it->self_msg_iter);
-       msg = bt_message_stream_end_create(msg_it->self_msg_iter,
-               msg_it->stream);
-       if (!msg) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Cannot create stream end message: "
-                       "msg-it-addr=%p, stream-addr=%p",
-                       msg_it, msg_it->stream);
-       }
-
-end:
-       return msg;
-}
-
-static
-bt_message *create_msg_packet_beginning(struct ctf_msg_iter *msg_it,
-               bool use_default_cs)
-{
-       bt_self_component *self_comp = msg_it->self_comp;
-       bt_message *msg;
-       const bt_stream_class *sc = msg_it->meta.sc->ir_sc;
-
-       BT_ASSERT(msg_it->packet);
-       BT_ASSERT(sc);
-       BT_ASSERT(msg_it->self_msg_iter);
-
-       if (msg_it->meta.sc->packets_have_ts_begin) {
-               BT_ASSERT(msg_it->snapshots.beginning_clock != UINT64_C(-1));
-               uint64_t raw_cs_value;
-
-               /*
-                * Either use the decoded packet `timestamp_begin` field or the
-                * current stream's default clock_snapshot.
-                */
-               if (use_default_cs) {
-                       raw_cs_value = msg_it->default_clock_snapshot;
-               } else {
-                       raw_cs_value = msg_it->snapshots.beginning_clock;
-               }
-
-               msg = bt_message_packet_beginning_create_with_default_clock_snapshot(
-                       msg_it->self_msg_iter, msg_it->packet,
-                       raw_cs_value);
-       } else {
-               msg = bt_message_packet_beginning_create(msg_it->self_msg_iter,
-                       msg_it->packet);
-       }
-
-       if (!msg) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Cannot create packet beginning message: "
-                       "msg-it-addr=%p, packet-addr=%p",
-                       msg_it, msg_it->packet);
-               goto end;
-       }
-
-end:
-       return msg;
-}
-
-static
-bt_message *emit_delayed_packet_beg_msg(struct ctf_msg_iter *msg_it)
-{
-       bool packet_beg_ts_need_fix_up;
-
-       msg_it->emit_delayed_packet_beginning_msg = false;
-
-       /*
-        * Only fix the packet's timestamp_begin if it's larger than the first
-        * event of the packet. If there was no event in the packet, the
-        * `default_clock_snapshot` field will be either equal or greater than
-        * `snapshots.beginning_clock` so there is not fix needed.
-        */
-       packet_beg_ts_need_fix_up =
-               msg_it->default_clock_snapshot < msg_it->snapshots.beginning_clock;
-
-       /* create_msg_packet_beginning() logs errors */
-       return create_msg_packet_beginning(msg_it, packet_beg_ts_need_fix_up);
-}
-
-
-static
-bt_message *create_msg_packet_end(struct ctf_msg_iter *msg_it)
-{
-       bt_message *msg;
-       bool update_default_cs = true;
-       bt_self_component *self_comp = msg_it->self_comp;
-
-       if (!msg_it->packet) {
-               msg = NULL;
-               goto end;
-       }
-
-       /*
-        * Check if we need to emit the delayed packet
-        * beginning message instead of the packet end message.
-        */
-       if (G_UNLIKELY(msg_it->emit_delayed_packet_beginning_msg)) {
-               msg = emit_delayed_packet_beg_msg(msg_it);
-               /* Don't forget to emit the packet end message. */
-               msg_it->state = STATE_EMIT_QUEUED_MSG_PACKET_END;
-               goto end;
-       }
-
-       /* Check if may be affected by lttng-crash timestamp_end quirk. */
-       if (G_UNLIKELY(msg_it->meta.tc->quirks.lttng_crash)) {
-               /*
-                * Check if the `timestamp_begin` field is non-zero but
-                * `timestamp_end` is zero. It means the trace is affected by
-                * the lttng-crash packet `timestamp_end` quirk and must be
-                * fixed up by omitting to update the default clock snapshot to
-                * the `timestamp_end` as is typically done.
-                */
-               if (msg_it->snapshots.beginning_clock != 0 &&
-                               msg_it->snapshots.end_clock == 0) {
-                       update_default_cs = false;
-               }
-       }
-
-       /*
-        * Check if may be affected by lttng event-after-packet `timestamp_end`
-        * quirk.
-        */
-       if (msg_it->meta.tc->quirks.lttng_event_after_packet) {
-               /*
-                * Check if `timestamp_end` is smaller then the current
-                * default_clock_snapshot (which is set to the last event
-                * decoded). It means the trace is affected by the lttng
-                * `event-after-packet` packet `timestamp_end` quirk and must
-                * be fixed up by omitting to update the default clock snapshot
-                * to the `timestamp_end` as is typically done.
-                */
-               if (msg_it->snapshots.end_clock < msg_it->default_clock_snapshot) {
-                       update_default_cs = false;
-               }
-       }
-
-       /* Update default clock from packet's end time. */
-       if (msg_it->snapshots.end_clock != UINT64_C(-1) && update_default_cs) {
-               msg_it->default_clock_snapshot = msg_it->snapshots.end_clock;
-       }
-
-       BT_ASSERT(msg_it->self_msg_iter);
-
-       if (msg_it->meta.sc->packets_have_ts_end) {
-               BT_ASSERT(msg_it->snapshots.end_clock != UINT64_C(-1));
-               msg = bt_message_packet_end_create_with_default_clock_snapshot(
-                       msg_it->self_msg_iter, msg_it->packet,
-                       msg_it->default_clock_snapshot);
-       } else {
-               msg = bt_message_packet_end_create(msg_it->self_msg_iter,
-                       msg_it->packet);
-       }
-
-       if (!msg) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Cannot create packet end message: "
-                       "msg-it-addr=%p, packet-addr=%p",
-                       msg_it, msg_it->packet);
-               goto end;
-
-       }
-
-       BT_PACKET_PUT_REF_AND_RESET(msg_it->packet);
-
-end:
-       return msg;
-}
-
-static
-bt_message *create_msg_discarded_events(struct ctf_msg_iter *msg_it)
-{
-       bt_message *msg;
-       bt_self_component *self_comp = msg_it->self_comp;
-       uint64_t beginning_raw_value = UINT64_C(-1);
-       uint64_t end_raw_value = UINT64_C(-1);
-
-       BT_ASSERT(msg_it->self_msg_iter);
-       BT_ASSERT(msg_it->stream);
-       BT_ASSERT(msg_it->meta.sc->has_discarded_events);
-
-       if (msg_it->meta.sc->discarded_events_have_default_cs) {
-               if (msg_it->prev_packet_snapshots.discarded_events == UINT64_C(-1)) {
-                       /*
-                        * We discarded events, but before (and possibly
-                        * including) the current packet: use this packet's time
-                        * range, and do not have a specific count.
-                        */
-                       beginning_raw_value = msg_it->snapshots.beginning_clock;
-                       end_raw_value = msg_it->snapshots.end_clock;
-               } else {
-                       beginning_raw_value = msg_it->prev_packet_snapshots.end_clock;
-                       end_raw_value = msg_it->snapshots.end_clock;
-               }
-
-               BT_ASSERT(beginning_raw_value != UINT64_C(-1));
-               BT_ASSERT(end_raw_value != UINT64_C(-1));
-               msg = bt_message_discarded_events_create_with_default_clock_snapshots(
-                       msg_it->self_msg_iter, msg_it->stream, beginning_raw_value,
-                       end_raw_value);
-       } else {
-               msg = bt_message_discarded_events_create(msg_it->self_msg_iter,
-                       msg_it->stream);
-       }
-
-       if (!msg) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Cannot create discarded events message: "
-                       "msg-it-addr=%p, stream-addr=%p",
-                       msg_it, msg_it->stream);
-               goto end;
-       }
-
-       if (msg_it->prev_packet_snapshots.discarded_events != UINT64_C(-1)) {
-               bt_message_discarded_events_set_count(msg,
-                       msg_it->snapshots.discarded_events -
-                       msg_it->prev_packet_snapshots.discarded_events);
-       }
-
-end:
-       return msg;
-}
-
-static
-bt_message *create_msg_discarded_packets(struct ctf_msg_iter *msg_it)
-{
-       bt_message *msg;
-       bt_self_component *self_comp = msg_it->self_comp;
-
-       BT_ASSERT(msg_it->self_msg_iter);
-       BT_ASSERT(msg_it->stream);
-       BT_ASSERT(msg_it->meta.sc->has_discarded_packets);
-       BT_ASSERT(msg_it->prev_packet_snapshots.packets !=
-               UINT64_C(-1));
-
-       if (msg_it->meta.sc->discarded_packets_have_default_cs) {
-               BT_ASSERT(msg_it->prev_packet_snapshots.end_clock != UINT64_C(-1));
-               BT_ASSERT(msg_it->snapshots.beginning_clock != UINT64_C(-1));
-               msg = bt_message_discarded_packets_create_with_default_clock_snapshots(
-                       msg_it->self_msg_iter, msg_it->stream,
-                       msg_it->prev_packet_snapshots.end_clock,
-                       msg_it->snapshots.beginning_clock);
-       } else {
-               msg = bt_message_discarded_packets_create(msg_it->self_msg_iter,
-                       msg_it->stream);
-       }
-
-       if (!msg) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Cannot create discarded packets message: "
-                       "msg-it-addr=%p, stream-addr=%p",
-                       msg_it, msg_it->stream);
-               goto end;
-       }
-
-       bt_message_discarded_packets_set_count(msg,
-               msg_it->snapshots.packets -
-                       msg_it->prev_packet_snapshots.packets - 1);
-
-end:
-       return msg;
-}
-
-BT_HIDDEN
-struct ctf_msg_iter *ctf_msg_iter_create(
-               struct ctf_trace_class *tc,
-               size_t max_request_sz,
-               struct ctf_msg_iter_medium_ops medops, void *data,
-               bt_logging_level log_level,
-               bt_self_component *self_comp,
-               bt_self_message_iterator *self_msg_iter)
-{
-       struct ctf_msg_iter *msg_it = NULL;
-       struct bt_bfcr_cbs cbs = {
-               .classes = {
-                       .signed_int = bfcr_signed_int_cb,
-                       .unsigned_int = bfcr_unsigned_int_cb,
-                       .floating_point = bfcr_floating_point_cb,
-                       .string_begin = bfcr_string_begin_cb,
-                       .string = bfcr_string_cb,
-                       .string_end = bfcr_string_end_cb,
-                       .compound_begin = bfcr_compound_begin_cb,
-                       .compound_end = bfcr_compound_end_cb,
-               },
-               .query = {
-                       .get_sequence_length = bfcr_get_sequence_length_cb,
-                       .borrow_variant_selected_field_class = bfcr_borrow_variant_selected_field_class_cb,
-               },
-       };
-
-       BT_ASSERT(tc);
-       BT_ASSERT(medops.request_bytes);
-       BT_ASSERT(medops.borrow_stream);
-       BT_ASSERT(max_request_sz > 0);
-
-       BT_COMP_LOG_CUR_LVL(BT_LOG_DEBUG, log_level, self_comp,
-               "Creating CTF plugin message iterator: "
-               "trace-addr=%p, max-request-size=%zu, "
-               "data=%p, log-level=%s", tc, max_request_sz, data,
-               bt_common_logging_level_string(log_level));
-       msg_it = g_new0(struct ctf_msg_iter, 1);
-       if (!msg_it) {
-               BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, log_level, self_comp,
-                       "Failed to allocate one CTF plugin message iterator.");
-               goto end;
-       }
-       msg_it->self_comp = self_comp;
-       msg_it->self_msg_iter = self_msg_iter;
-       msg_it->log_level = log_level;
-       msg_it->meta.tc = tc;
-       msg_it->medium.medops = medops;
-       msg_it->medium.max_request_sz = max_request_sz;
-       msg_it->medium.data = data;
-       msg_it->stack = stack_new(msg_it);
-       msg_it->stored_values = g_array_new(FALSE, TRUE, sizeof(uint64_t));
-       g_array_set_size(msg_it->stored_values, tc->stored_value_count);
-
-       if (!msg_it->stack) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Failed to create field stack.");
-               goto error;
-       }
-
-       msg_it->bfcr = bt_bfcr_create(cbs, msg_it, log_level, NULL);
-       if (!msg_it->bfcr) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Failed to create binary class reader (BFCR).");
-               goto error;
-       }
-
-       ctf_msg_iter_reset(msg_it);
-       BT_COMP_LOGD("Created CTF plugin message iterator: "
-               "trace-addr=%p, max-request-size=%zu, "
-               "data=%p, msg-it-addr=%p, log-level=%s",
-               tc, max_request_sz, data, msg_it,
-               bt_common_logging_level_string(log_level));
-       msg_it->cur_packet_offset = 0;
-
-end:
-       return msg_it;
-
-error:
-       ctf_msg_iter_destroy(msg_it);
-       msg_it = NULL;
-       goto end;
-}
-
-void ctf_msg_iter_destroy(struct ctf_msg_iter *msg_it)
-{
-       BT_PACKET_PUT_REF_AND_RESET(msg_it->packet);
-       BT_STREAM_PUT_REF_AND_RESET(msg_it->stream);
-       release_all_dscopes(msg_it);
-
-       BT_COMP_LOGD("Destroying CTF plugin message iterator: addr=%p", msg_it);
-
-       if (msg_it->stack) {
-               BT_COMP_LOGD_STR("Destroying field stack.");
-               stack_destroy(msg_it->stack);
-       }
-
-       if (msg_it->bfcr) {
-               BT_COMP_LOGD("Destroying BFCR: bfcr-addr=%p", msg_it->bfcr);
-               bt_bfcr_destroy(msg_it->bfcr);
-       }
-
-       if (msg_it->stored_values) {
-               g_array_free(msg_it->stored_values, TRUE);
-       }
-
-       g_free(msg_it);
-}
-
-enum ctf_msg_iter_status ctf_msg_iter_get_next_message(
-               struct ctf_msg_iter *msg_it,
-               const bt_message **message)
-{
-       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-       bt_self_component *self_comp = msg_it->self_comp;
-
-       BT_ASSERT_DBG(msg_it);
-       BT_ASSERT_DBG(message);
-       BT_COMP_LOGD("Getting next message: msg-it-addr=%p", msg_it);
-
-       while (true) {
-               status = handle_state(msg_it);
-               if (G_UNLIKELY(status == CTF_MSG_ITER_STATUS_AGAIN)) {
-                       BT_COMP_LOGD_STR("Medium returned CTF_MSG_ITER_STATUS_AGAIN.");
-                       goto end;
-               } else if (G_UNLIKELY(status != CTF_MSG_ITER_STATUS_OK)) {
-                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                               "Cannot handle state: msg-it-addr=%p, state=%s",
-                               msg_it, state_string(msg_it->state));
-                       goto end;
-               }
-
-               switch (msg_it->state) {
-               case STATE_EMIT_MSG_EVENT:
-                       BT_ASSERT_DBG(msg_it->event_msg);
-
-                       /*
-                        * Check if we need to emit the delayed packet
-                        * beginning message instead of the event message.
-                        */
-                       if (G_UNLIKELY(msg_it->emit_delayed_packet_beginning_msg)) {
-                               *message = emit_delayed_packet_beg_msg(msg_it);
-                               if (!*message) {
-                                       status = CTF_MSG_ITER_STATUS_ERROR;
-                               }
-
-                               /*
-                                * Don't forget to emit the event message of
-                                * the event record that was just decoded.
-                                */
-                               msg_it->state = STATE_EMIT_QUEUED_MSG_EVENT;
-
-                       } else {
-                               *message = msg_it->event_msg;
-                               msg_it->event_msg = NULL;
-                       }
-                       goto end;
-               case STATE_EMIT_MSG_DISCARDED_EVENTS:
-                       /* create_msg_discared_events() logs errors */
-                       *message = create_msg_discarded_events(msg_it);
-
-                       if (!*message) {
-                               status = CTF_MSG_ITER_STATUS_ERROR;
-                       }
-
-                       goto end;
-               case STATE_EMIT_MSG_DISCARDED_PACKETS:
-                       /* create_msg_discared_packets() logs errors */
-                       *message = create_msg_discarded_packets(msg_it);
-
-                       if (!*message) {
-                               status = CTF_MSG_ITER_STATUS_ERROR;
-                       }
-
-                       goto end;
-               case STATE_EMIT_MSG_PACKET_BEGINNING:
-                       if (G_UNLIKELY(msg_it->meta.tc->quirks.barectf_event_before_packet)) {
-                               msg_it->emit_delayed_packet_beginning_msg = true;
-                               /*
-                                * There is no message to return yet as this
-                                * packet beginning message is delayed until we
-                                * decode the first event message of the
-                                * packet.
-                                */
-                               break;
-                       } else {
-                               /* create_msg_packet_beginning() logs errors */
-                               *message = create_msg_packet_beginning(msg_it, false);
-                               if (!*message) {
-                                       status = CTF_MSG_ITER_STATUS_ERROR;
-                               }
-                       }
-
-                       goto end;
-               case STATE_EMIT_MSG_PACKET_END_SINGLE:
-               case STATE_EMIT_MSG_PACKET_END_MULTI:
-                       /* create_msg_packet_end() logs errors */
-                       *message = create_msg_packet_end(msg_it);
-
-                       if (!*message) {
-                               status = CTF_MSG_ITER_STATUS_ERROR;
-                       }
-
-                       goto end;
-               case STATE_EMIT_MSG_STREAM_BEGINNING:
-                       /* create_msg_stream_beginning() logs errors */
-                       *message = create_msg_stream_beginning(msg_it);
-                       msg_it->emit_stream_beginning_message = false;
-                       msg_it->emit_stream_end_message = true;
-
-                       if (!*message) {
-                               status = CTF_MSG_ITER_STATUS_ERROR;
-                       }
-
-                       goto end;
-               case STATE_EMIT_MSG_STREAM_END:
-                       /* create_msg_stream_end() logs errors */
-                       *message = create_msg_stream_end(msg_it);
-                       msg_it->emit_stream_end_message = false;
-
-                       if (!*message) {
-                               status = CTF_MSG_ITER_STATUS_ERROR;
-                       }
-
-                       goto end;
-               case STATE_DONE:
-                       status = CTF_MSG_ITER_STATUS_EOF;
-                       goto end;
-               default:
-                       /* Non-emitting state: continue */
-                       break;
-               }
-       }
-
-end:
-       return status;
-}
-
-static
-enum ctf_msg_iter_status decode_until_state( struct ctf_msg_iter *msg_it,
-               enum state target_state_1, enum state target_state_2)
-{
-       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-       bt_self_component *self_comp = msg_it->self_comp;
-
-       BT_ASSERT_DBG(msg_it);
-
-       do {
-               /*
-                * Check if we reached the state at which we want to stop
-                * decoding.
-                */
-               if (msg_it->state == target_state_1 ||
-                               msg_it->state == target_state_2) {
-                       goto end;
-               }
-
-               status = handle_state(msg_it);
-               if (G_UNLIKELY(status == CTF_MSG_ITER_STATUS_AGAIN)) {
-                       BT_COMP_LOGD_STR("Medium returned CTF_MSG_ITER_STATUS_AGAIN.");
-                       goto end;
-               } else if (G_UNLIKELY(status != CTF_MSG_ITER_STATUS_OK)) {
-                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                               "Cannot handle state: msg-it-addr=%p, state=%s",
-                               msg_it, state_string(msg_it->state));
-                       goto end;
-               }
-
-               switch (msg_it->state) {
-               case STATE_INIT:
-               case STATE_SWITCH_PACKET:
-               case STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN:
-               case STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE:
-               case STATE_AFTER_TRACE_PACKET_HEADER:
-               case STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN:
-               case STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE:
-               case STATE_AFTER_STREAM_PACKET_CONTEXT:
-               case STATE_EMIT_MSG_STREAM_BEGINNING:
-               case STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS:
-               case STATE_EMIT_MSG_DISCARDED_EVENTS:
-               case STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS:
-               case STATE_EMIT_MSG_DISCARDED_PACKETS:
-               case STATE_EMIT_MSG_PACKET_BEGINNING:
-               case STATE_DSCOPE_EVENT_HEADER_BEGIN:
-               case STATE_DSCOPE_EVENT_HEADER_CONTINUE:
-               case STATE_AFTER_EVENT_HEADER:
-               case STATE_DSCOPE_EVENT_COMMON_CONTEXT_BEGIN:
-               case STATE_DSCOPE_EVENT_COMMON_CONTEXT_CONTINUE:
-               case STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN:
-               case STATE_DSCOPE_EVENT_SPEC_CONTEXT_CONTINUE:
-               case STATE_DSCOPE_EVENT_PAYLOAD_BEGIN:
-               case STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE:
-               case STATE_EMIT_MSG_EVENT:
-               case STATE_EMIT_QUEUED_MSG_EVENT:
-               case STATE_SKIP_PACKET_PADDING:
-               case STATE_EMIT_MSG_PACKET_END_MULTI:
-               case STATE_EMIT_MSG_PACKET_END_SINGLE:
-               case STATE_EMIT_QUEUED_MSG_PACKET_END:
-               case STATE_EMIT_MSG_STREAM_END:
-                       break;
-               case STATE_DONE:
-                       /* fall-through */
-               default:
-                       /* We should never get to the STATE_DONE state. */
-                       BT_COMP_LOGF("Unexpected state: msg-it-addr=%p, state=%s",
-                               msg_it, state_string(msg_it->state));
-                       bt_common_abort();
-               }
-       } while (true);
-
-end:
-       return status;
-}
-
-static
-enum ctf_msg_iter_status read_packet_header_context_fields(
-               struct ctf_msg_iter *msg_it)
-{
-       int ret;
-       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-
-       status = decode_until_state(msg_it, STATE_EMIT_MSG_PACKET_BEGINNING, -1);
-       if (status != CTF_MSG_ITER_STATUS_OK) {
-               goto end;
-       }
-
-       ret = set_current_packet_content_sizes(msg_it);
-       if (ret) {
-               status = CTF_MSG_ITER_STATUS_ERROR;
-               goto end;
-       }
-
-end:
-       return status;
-}
-
-BT_HIDDEN
-enum ctf_msg_iter_status ctf_msg_iter_seek(struct ctf_msg_iter *msg_it,
-               off_t offset)
-{
-       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-       enum ctf_msg_iter_medium_status medium_status;
-
-       BT_ASSERT(msg_it);
-       BT_ASSERT(offset >= 0);
-       BT_ASSERT(msg_it->medium.medops.seek);
-
-       medium_status = msg_it->medium.medops.seek(offset, msg_it->medium.data);
-       if (medium_status != CTF_MSG_ITER_MEDIUM_STATUS_OK) {
-               if (medium_status == CTF_MSG_ITER_MEDIUM_STATUS_EOF) {
-                       status = CTF_MSG_ITER_STATUS_EOF;
-               } else {
-                       status = CTF_MSG_ITER_STATUS_ERROR;
-                       goto end;
-               }
-       }
-
-       ctf_msg_iter_reset(msg_it);
-       msg_it->cur_packet_offset = offset;
-
-end:
-       return status;
-}
-
-static
-enum ctf_msg_iter_status clock_snapshot_at_msg_iter_state(
-               struct ctf_msg_iter *msg_it, enum state target_state_1,
-               enum state target_state_2, uint64_t *clock_snapshot)
-{
-       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
-
-       BT_ASSERT_DBG(msg_it);
-       BT_ASSERT_DBG(clock_snapshot);
-       status = decode_until_state(msg_it, target_state_1, target_state_2);
-       if (status != CTF_MSG_ITER_STATUS_OK) {
-               goto end;
-       }
-
-       *clock_snapshot = msg_it->default_clock_snapshot;
-end:
-       return status;
-}
-
-BT_HIDDEN
-enum ctf_msg_iter_status ctf_msg_iter_curr_packet_first_event_clock_snapshot(
-               struct ctf_msg_iter *msg_it, uint64_t *first_clock_snapshot)
-{
-       return clock_snapshot_at_msg_iter_state(msg_it,
-               STATE_AFTER_EVENT_HEADER, -1, first_clock_snapshot);
-}
-
-BT_HIDDEN
-enum ctf_msg_iter_status ctf_msg_iter_curr_packet_last_event_clock_snapshot(
-               struct ctf_msg_iter *msg_it, uint64_t *last_clock_snapshot)
-{
-       return clock_snapshot_at_msg_iter_state(msg_it,
-               STATE_EMIT_MSG_PACKET_END_SINGLE,
-               STATE_EMIT_MSG_PACKET_END_MULTI, last_clock_snapshot);
-}
-
-BT_HIDDEN
-enum ctf_msg_iter_status ctf_msg_iter_get_packet_properties(
-               struct ctf_msg_iter *msg_it,
-               struct ctf_msg_iter_packet_properties *props)
-{
-       enum ctf_msg_iter_status status;
-
-       BT_ASSERT_DBG(msg_it);
-       BT_ASSERT_DBG(props);
-       status = read_packet_header_context_fields(msg_it);
-       if (status != CTF_MSG_ITER_STATUS_OK) {
-               goto end;
-       }
-
-       props->exp_packet_total_size = msg_it->cur_exp_packet_total_size;
-       props->exp_packet_content_size = msg_it->cur_exp_packet_content_size;
-       props->stream_class_id = (uint64_t) msg_it->cur_stream_class_id;
-       props->data_stream_id = msg_it->cur_data_stream_id;
-       props->snapshots.discarded_events = msg_it->snapshots.discarded_events;
-       props->snapshots.packets = msg_it->snapshots.packets;
-       props->snapshots.beginning_clock = msg_it->snapshots.beginning_clock;
-       props->snapshots.end_clock = msg_it->snapshots.end_clock;
-
-end:
-       return status;
-}
-
-BT_HIDDEN
-void ctf_msg_iter_set_dry_run(struct ctf_msg_iter *msg_it,
-               bool val)
-{
-       msg_it->dry_run = val;
-}
diff --git a/src/plugins/ctf/common/msg-iter/msg-iter.cpp b/src/plugins/ctf/common/msg-iter/msg-iter.cpp
new file mode 100644 (file)
index 0000000..fd0cbf2
--- /dev/null
@@ -0,0 +1,3191 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright (c) 2015-2018 EfficiOS Inc. and Linux Foundation
+ * Copyright (c) 2015-2018 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Babeltrace - CTF message iterator
+ */
+
+#define BT_COMP_LOG_SELF_COMP (msg_it->self_comp)
+#define BT_LOG_OUTPUT_LEVEL (msg_it->log_level)
+#define BT_LOG_TAG "PLUGIN/CTF/MSG-ITER"
+#include "logging/comp-logging.h"
+
+#include <stdint.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include "common/assert.h"
+#include <string.h>
+#include <babeltrace2/babeltrace.h>
+#include "common/common.h"
+#include <glib.h>
+#include <stdlib.h>
+
+#include "msg-iter.hpp"
+#include "../bfcr/bfcr.hpp"
+
+struct ctf_msg_iter;
+
+/* A visit stack entry */
+struct stack_entry {
+       /*
+        * Current base field, one of:
+        *
+        *   * string
+        *   * structure
+        *   * array
+        *   * sequence
+        *   * variant
+        *
+        * Field is borrowed.
+        */
+       bt_field *base;
+
+       /* Index of next field to set */
+       size_t index;
+};
+
+struct ctf_msg_iter;
+
+/* Visit stack */
+struct stack {
+       struct ctf_msg_iter *msg_it;
+
+       /* Entries (struct stack_entry) */
+       GArray *entries;
+
+       /* Number of active entries */
+       size_t size;
+};
+
+/* State */
+enum state {
+       STATE_INIT,
+       STATE_SWITCH_PACKET,
+       STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN,
+       STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE,
+       STATE_AFTER_TRACE_PACKET_HEADER,
+       STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN,
+       STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE,
+       STATE_AFTER_STREAM_PACKET_CONTEXT,
+       STATE_EMIT_MSG_STREAM_BEGINNING,
+       STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS,
+       STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS,
+       STATE_EMIT_MSG_DISCARDED_EVENTS,
+       STATE_EMIT_MSG_DISCARDED_PACKETS,
+       STATE_EMIT_MSG_PACKET_BEGINNING,
+       STATE_DSCOPE_EVENT_HEADER_BEGIN,
+       STATE_DSCOPE_EVENT_HEADER_CONTINUE,
+       STATE_AFTER_EVENT_HEADER,
+       STATE_DSCOPE_EVENT_COMMON_CONTEXT_BEGIN,
+       STATE_DSCOPE_EVENT_COMMON_CONTEXT_CONTINUE,
+       STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN,
+       STATE_DSCOPE_EVENT_SPEC_CONTEXT_CONTINUE,
+       STATE_DSCOPE_EVENT_PAYLOAD_BEGIN,
+       STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE,
+       STATE_EMIT_MSG_EVENT,
+       STATE_EMIT_QUEUED_MSG_EVENT,
+       STATE_SKIP_PACKET_PADDING,
+       STATE_EMIT_MSG_PACKET_END_MULTI,
+       STATE_EMIT_MSG_PACKET_END_SINGLE,
+       STATE_EMIT_QUEUED_MSG_PACKET_END,
+       STATE_CHECK_EMIT_MSG_STREAM_END,
+       STATE_EMIT_MSG_STREAM_END,
+       STATE_DONE,
+};
+
+struct end_of_packet_snapshots {
+       uint64_t discarded_events;
+       uint64_t packets;
+       uint64_t beginning_clock;
+       uint64_t end_clock;
+};
+
+/* CTF message iterator */
+struct ctf_msg_iter {
+       /* Visit stack */
+       struct stack *stack;
+
+       /* Current message iterator to create messages (weak) */
+       bt_self_message_iterator *self_msg_iter;
+
+       /*
+        * True if library objects are unavailable during the decoding and
+        * should not be created/used.
+        */
+       bool dry_run;
+
+       /*
+        * Current dynamic scope field pointer.
+        *
+        * This is set by read_dscope_begin_state() and contains the
+        * value of one of the pointers in `dscopes` below.
+        */
+       bt_field *cur_dscope_field;
+
+       /*
+        * True if we're done filling a string field from a text
+        * array/sequence payload.
+        */
+       bool done_filling_string;
+
+       /* Trace and classes */
+       /* True to set IR fields */
+       bool set_ir_fields;
+
+       struct {
+               struct ctf_trace_class *tc;
+               struct ctf_stream_class *sc;
+               struct ctf_event_class *ec;
+       } meta;
+
+       /* Current packet (NULL if not created yet) */
+       bt_packet *packet;
+
+       /* Current stream (NULL if not set yet) */
+       bt_stream *stream;
+
+       /* Current event (NULL if not created yet) */
+       bt_event *event;
+
+       /* Current event message (NULL if not created yet) */
+       bt_message *event_msg;
+
+       /*
+        * True if we need to emit a packet beginning message before we emit
+        * the next event message or the packet end message.
+        */
+       bool emit_delayed_packet_beginning_msg;
+
+       /*
+        * True if this is the first packet we are reading, and therefore if we
+        * should emit a stream beginning message.
+        */
+       bool emit_stream_beginning_message;
+
+       /*
+        * True if we need to emit a stream end message at the end of the
+        * current stream. A live stream may never receive any data and thus
+        * never send a stream beginning message which removes the need to emit
+        * a stream end message.
+        */
+       bool emit_stream_end_message;
+
+       /* Database of current dynamic scopes */
+       struct {
+               bt_field *stream_packet_context;
+               bt_field *event_common_context;
+               bt_field *event_spec_context;
+               bt_field *event_payload;
+       } dscopes;
+
+       /* Current state */
+       enum state state;
+
+       /* Current medium buffer data */
+       struct {
+               /* Last address provided by medium */
+               const uint8_t *addr;
+
+               /* Buffer size provided by medium (bytes) */
+               size_t sz;
+
+               /* Offset within whole packet of addr (bits) */
+               size_t packet_offset;
+
+               /* Current position from addr (bits) */
+               size_t at;
+
+               /* Position of the last event header from addr (bits) */
+               size_t last_eh_at;
+       } buf;
+
+       /* Binary type reader */
+       struct bt_bfcr *bfcr;
+
+       /* Current medium data */
+       struct {
+               struct ctf_msg_iter_medium_ops medops;
+               size_t max_request_sz;
+               void *data;
+       } medium;
+
+       /* Current packet size (bits) (-1 if unknown) */
+       int64_t cur_exp_packet_total_size;
+
+       /* Current content size (bits) (-1 if unknown) */
+       int64_t cur_exp_packet_content_size;
+
+       /* Current stream class ID */
+       int64_t cur_stream_class_id;
+
+       /* Current event class ID */
+       int64_t cur_event_class_id;
+
+       /* Current data stream ID */
+       int64_t cur_data_stream_id;
+
+       /*
+        * Offset, in the underlying media, of the current packet's
+        * start (-1 if unknown).
+        */
+       off_t cur_packet_offset;
+
+       /* Default clock's current value */
+       uint64_t default_clock_snapshot;
+
+       /* End of current packet snapshots */
+       struct end_of_packet_snapshots snapshots;
+
+       /* End of previous packet snapshots */
+       struct end_of_packet_snapshots prev_packet_snapshots;
+
+       /* Stored values (for sequence lengths, variant tags) */
+       GArray *stored_values;
+
+       /* Iterator's current log level */
+       bt_logging_level log_level;
+
+       /* Iterator's owning self component, or `NULL` if none (query) */
+       bt_self_component *self_comp;
+};
+
+static inline
+const char *state_string(enum state state)
+{
+       switch (state) {
+       case STATE_INIT:
+               return "INIT";
+       case STATE_SWITCH_PACKET:
+               return "SWITCH_PACKET";
+       case STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN:
+               return "DSCOPE_TRACE_PACKET_HEADER_BEGIN";
+       case STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE:
+               return "DSCOPE_TRACE_PACKET_HEADER_CONTINUE";
+       case STATE_AFTER_TRACE_PACKET_HEADER:
+               return "AFTER_TRACE_PACKET_HEADER";
+       case STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN:
+               return "DSCOPE_STREAM_PACKET_CONTEXT_BEGIN";
+       case STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE:
+               return "DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE";
+       case STATE_AFTER_STREAM_PACKET_CONTEXT:
+               return "AFTER_STREAM_PACKET_CONTEXT";
+       case STATE_EMIT_MSG_STREAM_BEGINNING:
+               return "EMIT_MSG_STREAM_BEGINNING";
+       case STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS:
+               return "CHECK_EMIT_MSG_DISCARDED_EVENTS";
+       case STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS:
+               return "CHECK_EMIT_MSG_DISCARDED_PACKETS";
+       case STATE_EMIT_MSG_PACKET_BEGINNING:
+               return "EMIT_MSG_PACKET_BEGINNING";
+       case STATE_EMIT_MSG_DISCARDED_EVENTS:
+               return "EMIT_MSG_DISCARDED_EVENTS";
+       case STATE_EMIT_MSG_DISCARDED_PACKETS:
+               return "EMIT_MSG_DISCARDED_PACKETS";
+       case STATE_DSCOPE_EVENT_HEADER_BEGIN:
+               return "DSCOPE_EVENT_HEADER_BEGIN";
+       case STATE_DSCOPE_EVENT_HEADER_CONTINUE:
+               return "DSCOPE_EVENT_HEADER_CONTINUE";
+       case STATE_AFTER_EVENT_HEADER:
+               return "AFTER_EVENT_HEADER";
+       case STATE_DSCOPE_EVENT_COMMON_CONTEXT_BEGIN:
+               return "DSCOPE_EVENT_COMMON_CONTEXT_BEGIN";
+       case STATE_DSCOPE_EVENT_COMMON_CONTEXT_CONTINUE:
+               return "DSCOPE_EVENT_COMMON_CONTEXT_CONTINUE";
+       case STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN:
+               return "DSCOPE_EVENT_SPEC_CONTEXT_BEGIN";
+       case STATE_DSCOPE_EVENT_SPEC_CONTEXT_CONTINUE:
+               return "DSCOPE_EVENT_SPEC_CONTEXT_CONTINUE";
+       case STATE_DSCOPE_EVENT_PAYLOAD_BEGIN:
+               return "DSCOPE_EVENT_PAYLOAD_BEGIN";
+       case STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE:
+               return "DSCOPE_EVENT_PAYLOAD_CONTINUE";
+       case STATE_EMIT_MSG_EVENT:
+               return "EMIT_MSG_EVENT";
+       case STATE_EMIT_QUEUED_MSG_EVENT:
+               return "EMIT_QUEUED_MSG_EVENT";
+       case STATE_SKIP_PACKET_PADDING:
+               return "SKIP_PACKET_PADDING";
+       case STATE_EMIT_MSG_PACKET_END_MULTI:
+               return "EMIT_MSG_PACKET_END_MULTI";
+       case STATE_EMIT_MSG_PACKET_END_SINGLE:
+               return "EMIT_MSG_PACKET_END_SINGLE";
+       case STATE_EMIT_QUEUED_MSG_PACKET_END:
+               return "EMIT_QUEUED_MSG_PACKET_END";
+       case STATE_CHECK_EMIT_MSG_STREAM_END:
+               return "CHECK_EMIT_MSG_STREAM_END";
+       case STATE_EMIT_MSG_STREAM_END:
+               return "EMIT_MSG_STREAM_END";
+       case STATE_DONE:
+               return "DONE";
+       }
+
+       bt_common_abort();
+}
+
+static
+struct stack *stack_new(struct ctf_msg_iter *msg_it)
+{
+       bt_self_component *self_comp = msg_it->self_comp;
+       struct stack *stack = NULL;
+
+       stack = g_new0(struct stack, 1);
+       if (!stack) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Failed to allocate one stack.");
+               goto error;
+       }
+
+       stack->msg_it = msg_it;
+       stack->entries = g_array_new(FALSE, TRUE, sizeof(struct stack_entry));
+       if (!stack->entries) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Failed to allocate a GArray.");
+               goto error;
+       }
+
+       BT_COMP_LOGD("Created stack: msg-it-addr=%p, stack-addr=%p", msg_it, stack);
+       goto end;
+
+error:
+       g_free(stack);
+       stack = NULL;
+
+end:
+       return stack;
+}
+
+static
+void stack_destroy(struct stack *stack)
+{
+       struct ctf_msg_iter *msg_it;
+
+       BT_ASSERT_DBG(stack);
+       msg_it = stack->msg_it;
+       BT_COMP_LOGD("Destroying stack: addr=%p", stack);
+
+       if (stack->entries) {
+               g_array_free(stack->entries, TRUE);
+       }
+
+       g_free(stack);
+}
+
+static
+void stack_push(struct stack *stack, bt_field *base)
+{
+       struct stack_entry *entry;
+       struct ctf_msg_iter *msg_it;
+
+       BT_ASSERT_DBG(stack);
+       msg_it = stack->msg_it;
+       BT_ASSERT_DBG(base);
+       BT_COMP_LOGT("Pushing base field on stack: stack-addr=%p, "
+               "stack-size-before=%zu, stack-size-after=%zu",
+               stack, stack->size, stack->size + 1);
+
+       if (stack->entries->len == stack->size) {
+               g_array_set_size(stack->entries, stack->size + 1);
+       }
+
+       entry = &g_array_index(stack->entries, struct stack_entry, stack->size);
+       entry->base = base;
+       entry->index = 0;
+       stack->size++;
+}
+
+static inline
+unsigned int stack_size(struct stack *stack)
+{
+       BT_ASSERT_DBG(stack);
+       return stack->size;
+}
+
+static
+void stack_pop(struct stack *stack)
+{
+       struct ctf_msg_iter *msg_it;
+
+       BT_ASSERT_DBG(stack);
+       BT_ASSERT_DBG(stack_size(stack));
+       msg_it = stack->msg_it;
+       BT_COMP_LOGT("Popping from stack: "
+               "stack-addr=%p, stack-size-before=%zu, stack-size-after=%zu",
+               stack, stack->size, stack->size - 1);
+       stack->size--;
+}
+
+static inline
+struct stack_entry *stack_top(struct stack *stack)
+{
+       BT_ASSERT_DBG(stack);
+       BT_ASSERT_DBG(stack_size(stack));
+       return &g_array_index(stack->entries, struct stack_entry,
+               stack->size - 1);
+}
+
+static inline
+bool stack_empty(struct stack *stack)
+{
+       return stack_size(stack) == 0;
+}
+
+static
+void stack_clear(struct stack *stack)
+{
+       BT_ASSERT_DBG(stack);
+       stack->size = 0;
+}
+
+static inline
+enum ctf_msg_iter_status msg_iter_status_from_m_status(
+               enum ctf_msg_iter_medium_status m_status)
+{
+       /* They are the same */
+       return (ctf_msg_iter_status) m_status;
+}
+
+static inline
+size_t buf_size_bits(struct ctf_msg_iter *msg_it)
+{
+       return msg_it->buf.sz * 8;
+}
+
+static inline
+size_t buf_available_bits(struct ctf_msg_iter *msg_it)
+{
+       return buf_size_bits(msg_it) - msg_it->buf.at;
+}
+
+static inline
+size_t packet_at(struct ctf_msg_iter *msg_it)
+{
+       return msg_it->buf.packet_offset + msg_it->buf.at;
+}
+
+static inline
+void buf_consume_bits(struct ctf_msg_iter *msg_it, size_t incr)
+{
+       BT_COMP_LOGT("Advancing cursor: msg-it-addr=%p, cur-before=%zu, cur-after=%zu",
+               msg_it, msg_it->buf.at, msg_it->buf.at + incr);
+       msg_it->buf.at += incr;
+}
+
+static
+enum ctf_msg_iter_status request_medium_bytes(
+               struct ctf_msg_iter *msg_it)
+{
+       bt_self_component *self_comp = msg_it->self_comp;
+       uint8_t *buffer_addr = NULL;
+       size_t buffer_sz = 0;
+       enum ctf_msg_iter_medium_status m_status;
+
+       BT_COMP_LOGD("Calling user function (request bytes): msg-it-addr=%p, "
+               "request-size=%zu", msg_it, msg_it->medium.max_request_sz);
+       m_status = msg_it->medium.medops.request_bytes(
+               msg_it->medium.max_request_sz, &buffer_addr,
+               &buffer_sz, msg_it->medium.data);
+       BT_COMP_LOGD("User function returned: status=%s, buf-addr=%p, buf-size=%zu",
+               ctf_msg_iter_medium_status_string(m_status),
+               buffer_addr, buffer_sz);
+       if (m_status == CTF_MSG_ITER_MEDIUM_STATUS_OK) {
+               BT_ASSERT(buffer_sz != 0);
+
+               /* New packet offset is old one + old size (in bits) */
+               msg_it->buf.packet_offset += buf_size_bits(msg_it);
+
+               /* Restart at the beginning of the new medium buffer */
+               msg_it->buf.at = 0;
+               msg_it->buf.last_eh_at = SIZE_MAX;
+
+               /* New medium buffer size */
+               msg_it->buf.sz = buffer_sz;
+
+               /* New medium buffer address */
+               msg_it->buf.addr = buffer_addr;
+
+               BT_COMP_LOGD("User function returned new bytes: "
+                       "packet-offset=%zu, cur=%zu, size=%zu, addr=%p",
+                       msg_it->buf.packet_offset, msg_it->buf.at,
+                       msg_it->buf.sz, msg_it->buf.addr);
+               BT_COMP_LOGT_MEM(buffer_addr, buffer_sz, "Returned bytes at %p:",
+                       buffer_addr);
+       } else if (m_status == CTF_MSG_ITER_MEDIUM_STATUS_EOF) {
+               /*
+                * User returned end of stream: validate that we're not
+                * in the middle of a packet header, packet context, or
+                * event.
+                */
+               if (msg_it->cur_exp_packet_total_size >= 0) {
+                       if (packet_at(msg_it) ==
+                                       msg_it->cur_exp_packet_total_size) {
+                               goto end;
+                       }
+               } else {
+                       if (packet_at(msg_it) == 0) {
+                               goto end;
+                       }
+
+                       if (msg_it->buf.last_eh_at != SIZE_MAX &&
+                                       msg_it->buf.at == msg_it->buf.last_eh_at) {
+                               goto end;
+                       }
+               }
+
+               /* All other states are invalid */
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "User function returned %s, but message iterator is in an unexpected state: "
+                       "state=%s, cur-packet-size=%" PRId64 ", cur=%zu, "
+                       "packet-cur=%zu, last-eh-at=%zu",
+                       ctf_msg_iter_medium_status_string(m_status),
+                       state_string(msg_it->state),
+                       msg_it->cur_exp_packet_total_size,
+                       msg_it->buf.at, packet_at(msg_it),
+                       msg_it->buf.last_eh_at);
+               m_status = CTF_MSG_ITER_MEDIUM_STATUS_ERROR;
+       } else if (m_status < 0) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp, "User function failed: "
+                       "status=%s", ctf_msg_iter_medium_status_string(m_status));
+       }
+
+end:
+       return msg_iter_status_from_m_status(m_status);
+}
+
+static inline
+enum ctf_msg_iter_status buf_ensure_available_bits(
+               struct ctf_msg_iter *msg_it)
+{
+       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+
+       if (G_UNLIKELY(buf_available_bits(msg_it) == 0)) {
+               /*
+                * This _cannot_ return CTF_MSG_ITER_STATUS_OK
+                * _and_ no bits.
+                */
+               status = request_medium_bytes(msg_it);
+       }
+
+       return status;
+}
+
+static
+enum ctf_msg_iter_status read_dscope_begin_state(
+               struct ctf_msg_iter *msg_it,
+               struct ctf_field_class *dscope_fc,
+               enum state done_state, enum state continue_state,
+               bt_field *dscope_field)
+{
+       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+       bt_self_component *self_comp = msg_it->self_comp;
+       enum bt_bfcr_status bfcr_status;
+       size_t consumed_bits;
+
+       msg_it->cur_dscope_field = dscope_field;
+       BT_COMP_LOGT("Starting BFCR: msg-it-addr=%p, bfcr-addr=%p, fc-addr=%p",
+               msg_it, msg_it->bfcr, dscope_fc);
+       consumed_bits = bt_bfcr_start(msg_it->bfcr, dscope_fc,
+               msg_it->buf.addr, msg_it->buf.at, packet_at(msg_it),
+               msg_it->buf.sz, &bfcr_status);
+       BT_COMP_LOGT("BFCR consumed bits: size=%zu", consumed_bits);
+
+       switch (bfcr_status) {
+       case BT_BFCR_STATUS_OK:
+               /* Field class was read completely */
+               BT_COMP_LOGT_STR("Field was completely decoded.");
+               msg_it->state = done_state;
+               break;
+       case BT_BFCR_STATUS_EOF:
+               BT_COMP_LOGT_STR("BFCR needs more data to decode field completely.");
+               msg_it->state = continue_state;
+               break;
+       default:
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "BFCR failed to start: msg-it-addr=%p, bfcr-addr=%p, "
+                       "status=%s", msg_it, msg_it->bfcr,
+                       bt_bfcr_status_string(bfcr_status));
+               status = CTF_MSG_ITER_STATUS_ERROR;
+               goto end;
+       }
+
+       /* Consume bits now since we know we're not in an error state */
+       buf_consume_bits(msg_it, consumed_bits);
+
+end:
+       return status;
+}
+
+static
+enum ctf_msg_iter_status read_dscope_continue_state(
+               struct ctf_msg_iter *msg_it, enum state done_state)
+{
+       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+       bt_self_component *self_comp = msg_it->self_comp;
+       enum bt_bfcr_status bfcr_status;
+       size_t consumed_bits;
+
+       BT_COMP_LOGT("Continuing BFCR: msg-it-addr=%p, bfcr-addr=%p",
+               msg_it, msg_it->bfcr);
+
+       status = buf_ensure_available_bits(msg_it);
+       if (status != CTF_MSG_ITER_STATUS_OK) {
+               if (status < 0) {
+                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                               "Cannot ensure that buffer has at least one byte: "
+                               "msg-addr=%p, status=%s",
+                               msg_it, ctf_msg_iter_status_string(status));
+               } else {
+                       BT_COMP_LOGT("Cannot ensure that buffer has at least one byte: "
+                               "msg-addr=%p, status=%s",
+                               msg_it, ctf_msg_iter_status_string(status));
+               }
+
+               goto end;
+       }
+
+       consumed_bits = bt_bfcr_continue(msg_it->bfcr, msg_it->buf.addr,
+               msg_it->buf.sz, &bfcr_status);
+       BT_COMP_LOGT("BFCR consumed bits: size=%zu", consumed_bits);
+
+       switch (bfcr_status) {
+       case BT_BFCR_STATUS_OK:
+               /* Type was read completely. */
+               BT_COMP_LOGT_STR("Field was completely decoded.");
+               msg_it->state = done_state;
+               break;
+       case BT_BFCR_STATUS_EOF:
+               /* Stay in this continue state. */
+               BT_COMP_LOGT_STR("BFCR needs more data to decode field completely.");
+               break;
+       default:
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "BFCR failed to continue: msg-it-addr=%p, bfcr-addr=%p, "
+                       "status=%s", msg_it, msg_it->bfcr,
+                       bt_bfcr_status_string(bfcr_status));
+               status = CTF_MSG_ITER_STATUS_ERROR;
+               goto end;
+       }
+
+       /* Consume bits now since we know we're not in an error state. */
+       buf_consume_bits(msg_it, consumed_bits);
+end:
+       return status;
+}
+
+static
+void release_event_dscopes(struct ctf_msg_iter *msg_it)
+{
+       msg_it->dscopes.event_common_context = NULL;
+       msg_it->dscopes.event_spec_context = NULL;
+       msg_it->dscopes.event_payload = NULL;
+}
+
+static
+void release_all_dscopes(struct ctf_msg_iter *msg_it)
+{
+       msg_it->dscopes.stream_packet_context = NULL;
+
+       release_event_dscopes(msg_it);
+}
+
+static
+enum ctf_msg_iter_status switch_packet_state(struct ctf_msg_iter *msg_it)
+{
+       enum ctf_msg_iter_status status;
+       bt_self_component *self_comp = msg_it->self_comp;
+
+       /*
+        * We don't put the stream class here because we need to make
+        * sure that all the packets processed by the same message
+        * iterator refer to the same stream class (the first one).
+        */
+       BT_ASSERT(msg_it);
+
+       if (msg_it->cur_exp_packet_total_size != -1) {
+               msg_it->cur_packet_offset += msg_it->cur_exp_packet_total_size;
+       }
+
+       BT_COMP_LOGD("Switching packet: msg-it-addr=%p, cur=%zu, "
+               "packet-offset=%" PRId64, msg_it, msg_it->buf.at,
+               msg_it->cur_packet_offset);
+       stack_clear(msg_it->stack);
+       msg_it->meta.ec = NULL;
+       BT_PACKET_PUT_REF_AND_RESET(msg_it->packet);
+       BT_MESSAGE_PUT_REF_AND_RESET(msg_it->event_msg);
+       release_all_dscopes(msg_it);
+       msg_it->cur_dscope_field = NULL;
+
+       if (msg_it->medium.medops.switch_packet) {
+               enum ctf_msg_iter_medium_status medium_status;
+
+               medium_status = msg_it->medium.medops.switch_packet(msg_it->medium.data);
+               if (medium_status == CTF_MSG_ITER_MEDIUM_STATUS_EOF) {
+                       /* No more packets. */
+                       msg_it->state = STATE_CHECK_EMIT_MSG_STREAM_END;
+                       status = CTF_MSG_ITER_STATUS_OK;
+                       goto end;
+               } else if (medium_status != CTF_MSG_ITER_MEDIUM_STATUS_OK) {
+                       status = msg_iter_status_from_m_status(medium_status);
+                       goto end;
+               }
+
+               /*
+                * After the packet switch, the medium might want to give us a
+                * different buffer for the new packet.
+                */
+               status = request_medium_bytes(msg_it);
+               if (status != CTF_MSG_ITER_STATUS_OK) {
+                       goto end;
+               }
+       }
+
+       /*
+        * Adjust current buffer so that addr points to the beginning of the new
+        * packet.
+        */
+       if (msg_it->buf.addr) {
+               size_t consumed_bytes = (size_t) (msg_it->buf.at / CHAR_BIT);
+
+               /* Packets are assumed to start on a byte frontier. */
+               if (msg_it->buf.at % CHAR_BIT) {
+                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                               "Cannot switch packet: current position is not a multiple of 8: "
+                               "msg-it-addr=%p, cur=%zu", msg_it, msg_it->buf.at);
+                       status = CTF_MSG_ITER_STATUS_ERROR;
+                       goto end;
+               }
+
+               msg_it->buf.addr += consumed_bytes;
+               msg_it->buf.sz -= consumed_bytes;
+               msg_it->buf.at = 0;
+               msg_it->buf.packet_offset = 0;
+               BT_COMP_LOGD("Adjusted buffer: addr=%p, size=%zu",
+                       msg_it->buf.addr, msg_it->buf.sz);
+       }
+
+       msg_it->cur_exp_packet_content_size = -1;
+       msg_it->cur_exp_packet_total_size = -1;
+       msg_it->cur_stream_class_id = -1;
+       msg_it->cur_event_class_id = -1;
+       msg_it->cur_data_stream_id = -1;
+       msg_it->prev_packet_snapshots = msg_it->snapshots;
+       msg_it->snapshots.discarded_events = UINT64_C(-1);
+       msg_it->snapshots.packets = UINT64_C(-1);
+       msg_it->snapshots.beginning_clock = UINT64_C(-1);
+       msg_it->snapshots.end_clock = UINT64_C(-1);
+       msg_it->state = STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN;
+
+       status = CTF_MSG_ITER_STATUS_OK;
+end:
+       return status;
+}
+
+static
+enum ctf_msg_iter_status read_packet_header_begin_state(
+               struct ctf_msg_iter *msg_it)
+{
+       struct ctf_field_class *packet_header_fc = NULL;
+       bt_self_component *self_comp = msg_it->self_comp;
+       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+
+       /*
+        * Make sure at least one bit is available for this packet. An
+        * empty packet is impossible. If we reach the end of the medium
+        * at this point, then it's considered the end of the stream.
+        */
+       status = buf_ensure_available_bits(msg_it);
+       switch (status) {
+       case CTF_MSG_ITER_STATUS_OK:
+               break;
+       case CTF_MSG_ITER_STATUS_EOF:
+               status = CTF_MSG_ITER_STATUS_OK;
+               msg_it->state = STATE_CHECK_EMIT_MSG_STREAM_END;
+               goto end;
+       default:
+               goto end;
+       }
+
+       /* Packet header class is common to the whole trace class. */
+       packet_header_fc = msg_it->meta.tc->packet_header_fc;
+       if (!packet_header_fc) {
+               msg_it->state = STATE_AFTER_TRACE_PACKET_HEADER;
+               goto end;
+       }
+
+       msg_it->cur_stream_class_id = -1;
+       msg_it->cur_event_class_id = -1;
+       msg_it->cur_data_stream_id = -1;
+       BT_COMP_LOGD("Decoding packet header field: "
+               "msg-it-addr=%p, trace-class-addr=%p, fc-addr=%p",
+               msg_it, msg_it->meta.tc, packet_header_fc);
+       status = read_dscope_begin_state(msg_it, packet_header_fc,
+               STATE_AFTER_TRACE_PACKET_HEADER,
+               STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE, NULL);
+       if (status < 0) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Cannot decode packet header field: "
+                       "msg-it-addr=%p, trace-class-addr=%p, "
+                       "fc-addr=%p",
+                       msg_it, msg_it->meta.tc, packet_header_fc);
+       }
+
+end:
+       return status;
+}
+
+static
+enum ctf_msg_iter_status read_packet_header_continue_state(
+               struct ctf_msg_iter *msg_it)
+{
+       return read_dscope_continue_state(msg_it,
+               STATE_AFTER_TRACE_PACKET_HEADER);
+}
+
+static inline
+enum ctf_msg_iter_status set_current_stream_class(struct ctf_msg_iter *msg_it)
+{
+       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+       bt_self_component *self_comp = msg_it->self_comp;
+       struct ctf_stream_class *new_stream_class = NULL;
+
+       if (msg_it->cur_stream_class_id == -1) {
+               /*
+                * No current stream class ID field, therefore only one
+                * stream class.
+                */
+               if (msg_it->meta.tc->stream_classes->len != 1) {
+                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                               "Need exactly one stream class since there's "
+                               "no stream class ID field: "
+                               "msg-it-addr=%p", msg_it);
+                       status = CTF_MSG_ITER_STATUS_ERROR;
+                       goto end;
+               }
+
+               new_stream_class = (ctf_stream_class *) msg_it->meta.tc->stream_classes->pdata[0];
+               msg_it->cur_stream_class_id = new_stream_class->id;
+       }
+
+       new_stream_class = ctf_trace_class_borrow_stream_class_by_id(
+               msg_it->meta.tc, msg_it->cur_stream_class_id);
+       if (!new_stream_class) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "No stream class with ID of stream class ID to use in trace class: "
+                       "msg-it-addr=%p, stream-class-id=%" PRIu64 ", "
+                       "trace-class-addr=%p",
+                       msg_it, msg_it->cur_stream_class_id, msg_it->meta.tc);
+               status = CTF_MSG_ITER_STATUS_ERROR;
+               goto end;
+       }
+
+       if (msg_it->meta.sc) {
+               if (new_stream_class != msg_it->meta.sc) {
+                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                               "Two packets refer to two different stream classes within the same packet sequence: "
+                               "msg-it-addr=%p, prev-stream-class-addr=%p, "
+                               "prev-stream-class-id=%" PRId64 ", "
+                               "next-stream-class-addr=%p, "
+                               "next-stream-class-id=%" PRId64 ", "
+                               "trace-addr=%p",
+                               msg_it, msg_it->meta.sc,
+                               msg_it->meta.sc->id,
+                               new_stream_class,
+                               new_stream_class->id,
+                               msg_it->meta.tc);
+                       status = CTF_MSG_ITER_STATUS_ERROR;
+                       goto end;
+               }
+       } else {
+               msg_it->meta.sc = new_stream_class;
+       }
+
+       BT_COMP_LOGD("Set current stream class: "
+               "msg-it-addr=%p, stream-class-addr=%p, "
+               "stream-class-id=%" PRId64,
+               msg_it, msg_it->meta.sc, msg_it->meta.sc->id);
+
+end:
+       return status;
+}
+
+static inline
+enum ctf_msg_iter_status set_current_stream(struct ctf_msg_iter *msg_it)
+{
+       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+       bt_self_component *self_comp = msg_it->self_comp;
+       bt_stream *stream = NULL;
+
+       BT_COMP_LOGD("Calling user function (get stream): msg-it-addr=%p, "
+               "stream-class-addr=%p, stream-class-id=%" PRId64,
+               msg_it, msg_it->meta.sc,
+               msg_it->meta.sc->id);
+       stream = msg_it->medium.medops.borrow_stream(
+               msg_it->meta.sc->ir_sc, msg_it->cur_data_stream_id,
+               msg_it->medium.data);
+       bt_stream_get_ref(stream);
+       BT_COMP_LOGD("User function returned: stream-addr=%p", stream);
+       if (!stream) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "User function failed to return a stream object for the given stream class.");
+               status = CTF_MSG_ITER_STATUS_ERROR;
+               goto end;
+       }
+
+       if (msg_it->stream && stream != msg_it->stream) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "User function returned a different stream than the previous one for the same sequence of packets.");
+               status = CTF_MSG_ITER_STATUS_ERROR;
+               goto end;
+       }
+
+       BT_STREAM_MOVE_REF(msg_it->stream, stream);
+
+end:
+       bt_stream_put_ref(stream);
+       return status;
+}
+
+static inline
+enum ctf_msg_iter_status set_current_packet(struct ctf_msg_iter *msg_it)
+{
+       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+       bt_self_component *self_comp = msg_it->self_comp;
+       bt_packet *packet = NULL;
+
+       BT_COMP_LOGD("Creating packet from stream: "
+               "msg-it-addr=%p, stream-addr=%p, "
+               "stream-class-addr=%p, "
+               "stream-class-id=%" PRId64,
+               msg_it, msg_it->stream, msg_it->meta.sc,
+               msg_it->meta.sc->id);
+
+       /* Create packet */
+       BT_ASSERT(msg_it->stream);
+       packet = bt_packet_create(msg_it->stream);
+       if (!packet) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Cannot create packet from stream: "
+                       "msg-it-addr=%p, stream-addr=%p, "
+                       "stream-class-addr=%p, "
+                       "stream-class-id=%" PRId64,
+                       msg_it, msg_it->stream, msg_it->meta.sc,
+                       msg_it->meta.sc->id);
+               goto error;
+       }
+
+       goto end;
+
+error:
+       BT_PACKET_PUT_REF_AND_RESET(packet);
+       status = CTF_MSG_ITER_STATUS_ERROR;
+
+end:
+       BT_PACKET_MOVE_REF(msg_it->packet, packet);
+       return status;
+}
+
+static
+enum ctf_msg_iter_status after_packet_header_state(
+               struct ctf_msg_iter *msg_it)
+{
+       enum ctf_msg_iter_status status;
+
+       status = set_current_stream_class(msg_it);
+       if (status != CTF_MSG_ITER_STATUS_OK) {
+               goto end;
+       }
+
+       if (!msg_it->dry_run) {
+               status = set_current_stream(msg_it);
+               if (status != CTF_MSG_ITER_STATUS_OK) {
+                       goto end;
+               }
+
+               status = set_current_packet(msg_it);
+               if (status != CTF_MSG_ITER_STATUS_OK) {
+                       goto end;
+               }
+       }
+
+       msg_it->state = STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN;
+
+       status = CTF_MSG_ITER_STATUS_OK;
+
+end:
+       return status;
+}
+
+static
+enum ctf_msg_iter_status read_packet_context_begin_state(
+               struct ctf_msg_iter *msg_it)
+{
+       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+       bt_self_component *self_comp = msg_it->self_comp;
+       struct ctf_field_class *packet_context_fc;
+
+       BT_ASSERT(msg_it->meta.sc);
+       packet_context_fc = msg_it->meta.sc->packet_context_fc;
+       if (!packet_context_fc) {
+               BT_COMP_LOGD("No packet packet context field class in stream class: continuing: "
+                       "msg-it-addr=%p, stream-class-addr=%p, "
+                       "stream-class-id=%" PRId64,
+                       msg_it, msg_it->meta.sc,
+                       msg_it->meta.sc->id);
+               msg_it->state = STATE_AFTER_STREAM_PACKET_CONTEXT;
+               goto end;
+       }
+
+       if (packet_context_fc->in_ir && !msg_it->dry_run) {
+               BT_ASSERT(!msg_it->dscopes.stream_packet_context);
+               BT_ASSERT(msg_it->packet);
+               msg_it->dscopes.stream_packet_context =
+                       bt_packet_borrow_context_field(msg_it->packet);
+               BT_ASSERT(msg_it->dscopes.stream_packet_context);
+       }
+
+       BT_COMP_LOGD("Decoding packet context field: "
+               "msg-it-addr=%p, stream-class-addr=%p, "
+               "stream-class-id=%" PRId64 ", fc-addr=%p",
+               msg_it, msg_it->meta.sc,
+               msg_it->meta.sc->id, packet_context_fc);
+       status = read_dscope_begin_state(msg_it, packet_context_fc,
+               STATE_AFTER_STREAM_PACKET_CONTEXT,
+               STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE,
+               msg_it->dscopes.stream_packet_context);
+       if (status < 0) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Cannot decode packet context field: "
+                       "msg-it-addr=%p, stream-class-addr=%p, "
+                       "stream-class-id=%" PRId64 ", fc-addr=%p",
+                       msg_it, msg_it->meta.sc,
+                       msg_it->meta.sc->id,
+                       packet_context_fc);
+       }
+
+end:
+       return status;
+}
+
+static
+enum ctf_msg_iter_status read_packet_context_continue_state(
+               struct ctf_msg_iter *msg_it)
+{
+       return read_dscope_continue_state(msg_it,
+                       STATE_AFTER_STREAM_PACKET_CONTEXT);
+}
+
+static
+enum ctf_msg_iter_status set_current_packet_content_sizes(
+               struct ctf_msg_iter *msg_it)
+{
+       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+       bt_self_component *self_comp = msg_it->self_comp;
+
+       if (msg_it->cur_exp_packet_total_size == -1) {
+               if (msg_it->cur_exp_packet_content_size != -1) {
+                       msg_it->cur_exp_packet_total_size =
+                               msg_it->cur_exp_packet_content_size;
+               }
+       } else {
+               if (msg_it->cur_exp_packet_content_size == -1) {
+                       msg_it->cur_exp_packet_content_size =
+                               msg_it->cur_exp_packet_total_size;
+               }
+       }
+
+       BT_ASSERT((msg_it->cur_exp_packet_total_size >= 0 &&
+               msg_it->cur_exp_packet_content_size >= 0) ||
+               (msg_it->cur_exp_packet_total_size < 0 &&
+               msg_it->cur_exp_packet_content_size < 0));
+
+       if (msg_it->cur_exp_packet_content_size >
+                       msg_it->cur_exp_packet_total_size) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Invalid packet or content size: "
+                       "content size is greater than packet size: "
+                       "msg-it-addr=%p, packet-context-field-addr=%p, "
+                       "packet-size=%" PRId64 ", content-size=%" PRId64,
+                       msg_it, msg_it->dscopes.stream_packet_context,
+                       msg_it->cur_exp_packet_total_size,
+                       msg_it->cur_exp_packet_content_size);
+               status = CTF_MSG_ITER_STATUS_ERROR;
+               goto end;
+       }
+
+       BT_COMP_LOGD("Set current packet and content sizes: "
+               "msg-it-addr=%p, packet-size=%" PRIu64 ", content-size=%" PRIu64,
+               msg_it, msg_it->cur_exp_packet_total_size,
+               msg_it->cur_exp_packet_content_size);
+
+end:
+       return status;
+}
+
+static
+enum ctf_msg_iter_status after_packet_context_state(struct ctf_msg_iter *msg_it)
+{
+       enum ctf_msg_iter_status status;
+
+       status = set_current_packet_content_sizes(msg_it);
+       if (status != CTF_MSG_ITER_STATUS_OK) {
+               goto end;
+       }
+
+       if (msg_it->emit_stream_beginning_message) {
+               msg_it->state = STATE_EMIT_MSG_STREAM_BEGINNING;
+       } else {
+               msg_it->state = STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS;
+       }
+
+end:
+       return status;
+}
+
+static
+enum ctf_msg_iter_status read_event_header_begin_state(struct ctf_msg_iter *msg_it)
+{
+       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+       bt_self_component *self_comp = msg_it->self_comp;
+       struct ctf_field_class *event_header_fc = NULL;
+
+       /* Reset the position of the last event header */
+       msg_it->buf.last_eh_at = msg_it->buf.at;
+       msg_it->cur_event_class_id = -1;
+
+       /* Check if we have some content left */
+       if (msg_it->cur_exp_packet_content_size >= 0) {
+               if (G_UNLIKELY(packet_at(msg_it) ==
+                               msg_it->cur_exp_packet_content_size)) {
+                       /* No more events! */
+                       BT_COMP_LOGD("Reached end of packet: msg-it-addr=%p, "
+                               "cur=%zu", msg_it, packet_at(msg_it));
+                       msg_it->state = STATE_EMIT_MSG_PACKET_END_MULTI;
+                       goto end;
+               } else if (G_UNLIKELY(packet_at(msg_it) >
+                               msg_it->cur_exp_packet_content_size)) {
+                       /* That's not supposed to happen */
+                       BT_COMP_LOGD("Before decoding event header field: cursor is passed the packet's content: "
+                               "msg-it-addr=%p, content-size=%" PRId64 ", "
+                               "cur=%zu", msg_it,
+                               msg_it->cur_exp_packet_content_size,
+                               packet_at(msg_it));
+                       status = CTF_MSG_ITER_STATUS_ERROR;
+                       goto end;
+               }
+       } else {
+               /*
+                * "Infinite" content: we're done when the medium has
+                * nothing else for us.
+                */
+               status = buf_ensure_available_bits(msg_it);
+               switch (status) {
+               case CTF_MSG_ITER_STATUS_OK:
+                       break;
+               case CTF_MSG_ITER_STATUS_EOF:
+                       status = CTF_MSG_ITER_STATUS_OK;
+                       msg_it->state = STATE_EMIT_MSG_PACKET_END_SINGLE;
+                       goto end;
+               default:
+                       goto end;
+               }
+       }
+
+       release_event_dscopes(msg_it);
+       BT_ASSERT(msg_it->meta.sc);
+       event_header_fc = msg_it->meta.sc->event_header_fc;
+       if (!event_header_fc) {
+               msg_it->state = STATE_AFTER_EVENT_HEADER;
+               goto end;
+       }
+
+       BT_COMP_LOGD("Decoding event header field: "
+               "msg-it-addr=%p, stream-class-addr=%p, "
+               "stream-class-id=%" PRId64 ", "
+               "fc-addr=%p",
+               msg_it, msg_it->meta.sc,
+               msg_it->meta.sc->id,
+               event_header_fc);
+       status = read_dscope_begin_state(msg_it, event_header_fc,
+               STATE_AFTER_EVENT_HEADER,
+               STATE_DSCOPE_EVENT_HEADER_CONTINUE, NULL);
+       if (status < 0) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Cannot decode event header field: "
+                       "msg-it-addr=%p, stream-class-addr=%p, "
+                       "stream-class-id=%" PRId64 ", fc-addr=%p",
+                       msg_it, msg_it->meta.sc,
+                       msg_it->meta.sc->id,
+                       event_header_fc);
+       }
+
+end:
+       return status;
+}
+
+static
+enum ctf_msg_iter_status read_event_header_continue_state(
+               struct ctf_msg_iter *msg_it)
+{
+       return read_dscope_continue_state(msg_it,
+               STATE_AFTER_EVENT_HEADER);
+}
+
+static inline
+enum ctf_msg_iter_status set_current_event_class(struct ctf_msg_iter *msg_it)
+{
+       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+       bt_self_component *self_comp = msg_it->self_comp;
+
+       struct ctf_event_class *new_event_class = NULL;
+
+       if (msg_it->cur_event_class_id == -1) {
+               /*
+                * No current event class ID field, therefore only one
+                * event class.
+                */
+               if (msg_it->meta.sc->event_classes->len != 1) {
+                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                               "Need exactly one event class since there's no event class ID field: "
+                               "msg-it-addr=%p", msg_it);
+                       status = CTF_MSG_ITER_STATUS_ERROR;
+                       goto end;
+               }
+
+               new_event_class = (ctf_event_class *) msg_it->meta.sc->event_classes->pdata[0];
+               msg_it->cur_event_class_id = new_event_class->id;
+       }
+
+       new_event_class = ctf_stream_class_borrow_event_class_by_id(
+               msg_it->meta.sc, msg_it->cur_event_class_id);
+       if (!new_event_class) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "No event class with ID of event class ID to use in stream class: "
+                       "msg-it-addr=%p, stream-class-id=%" PRIu64 ", "
+                       "event-class-id=%" PRIu64 ", "
+                       "trace-class-addr=%p",
+                       msg_it, msg_it->meta.sc->id, msg_it->cur_event_class_id,
+                       msg_it->meta.tc);
+               status = CTF_MSG_ITER_STATUS_ERROR;
+               goto end;
+       }
+
+       msg_it->meta.ec = new_event_class;
+       BT_COMP_LOGD("Set current event class: "
+               "msg-it-addr=%p, event-class-addr=%p, "
+               "event-class-id=%" PRId64 ", "
+               "event-class-name=\"%s\"",
+               msg_it, msg_it->meta.ec, msg_it->meta.ec->id,
+               msg_it->meta.ec->name->str);
+
+end:
+       return status;
+}
+
+static inline
+enum ctf_msg_iter_status set_current_event_message(
+               struct ctf_msg_iter *msg_it)
+{
+       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+       bt_self_component *self_comp = msg_it->self_comp;
+       bt_message *msg = NULL;
+
+       BT_ASSERT_DBG(msg_it->meta.ec);
+       BT_ASSERT_DBG(msg_it->packet);
+       BT_COMP_LOGD("Creating event message from event class and packet: "
+               "msg-it-addr=%p, ec-addr=%p, ec-name=\"%s\", packet-addr=%p",
+               msg_it, msg_it->meta.ec,
+               msg_it->meta.ec->name->str,
+               msg_it->packet);
+       BT_ASSERT_DBG(msg_it->self_msg_iter);
+       BT_ASSERT_DBG(msg_it->meta.sc);
+
+       if (bt_stream_class_borrow_default_clock_class(msg_it->meta.sc->ir_sc)) {
+               msg = bt_message_event_create_with_packet_and_default_clock_snapshot(
+                       msg_it->self_msg_iter, msg_it->meta.ec->ir_ec,
+                       msg_it->packet, msg_it->default_clock_snapshot);
+       } else {
+               msg = bt_message_event_create_with_packet(msg_it->self_msg_iter,
+                       msg_it->meta.ec->ir_ec, msg_it->packet);
+       }
+
+       if (!msg) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Cannot create event message: "
+                       "msg-it-addr=%p, ec-addr=%p, ec-name=\"%s\", "
+                       "packet-addr=%p",
+                       msg_it, msg_it->meta.ec,
+                       msg_it->meta.ec->name->str,
+                       msg_it->packet);
+               goto error;
+       }
+
+       goto end;
+
+error:
+       BT_MESSAGE_PUT_REF_AND_RESET(msg);
+       status = CTF_MSG_ITER_STATUS_ERROR;
+
+end:
+       BT_MESSAGE_MOVE_REF(msg_it->event_msg, msg);
+       return status;
+}
+
+static
+enum ctf_msg_iter_status after_event_header_state(
+               struct ctf_msg_iter *msg_it)
+{
+       enum ctf_msg_iter_status status;
+
+       status = set_current_event_class(msg_it);
+       if (status != CTF_MSG_ITER_STATUS_OK) {
+               goto end;
+       }
+
+       if (G_UNLIKELY(msg_it->dry_run)) {
+               goto next_state;
+       }
+
+       status = set_current_event_message(msg_it);
+       if (status != CTF_MSG_ITER_STATUS_OK) {
+               goto end;
+       }
+
+       msg_it->event = bt_message_event_borrow_event(
+               msg_it->event_msg);
+       BT_ASSERT_DBG(msg_it->event);
+
+next_state:
+       msg_it->state = STATE_DSCOPE_EVENT_COMMON_CONTEXT_BEGIN;
+
+end:
+       return status;
+}
+
+static
+enum ctf_msg_iter_status read_event_common_context_begin_state(
+               struct ctf_msg_iter *msg_it)
+{
+       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+       bt_self_component *self_comp = msg_it->self_comp;
+       struct ctf_field_class *event_common_context_fc;
+
+       event_common_context_fc = msg_it->meta.sc->event_common_context_fc;
+       if (!event_common_context_fc) {
+               msg_it->state = STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN;
+               goto end;
+       }
+
+       if (event_common_context_fc->in_ir && !msg_it->dry_run) {
+               BT_ASSERT_DBG(!msg_it->dscopes.event_common_context);
+               msg_it->dscopes.event_common_context =
+                       bt_event_borrow_common_context_field(
+                               msg_it->event);
+               BT_ASSERT_DBG(msg_it->dscopes.event_common_context);
+       }
+
+       BT_COMP_LOGT("Decoding event common context field: "
+               "msg-it-addr=%p, stream-class-addr=%p, "
+               "stream-class-id=%" PRId64 ", "
+               "fc-addr=%p",
+               msg_it, msg_it->meta.sc,
+               msg_it->meta.sc->id,
+               event_common_context_fc);
+       status = read_dscope_begin_state(msg_it, event_common_context_fc,
+               STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN,
+               STATE_DSCOPE_EVENT_COMMON_CONTEXT_CONTINUE,
+               msg_it->dscopes.event_common_context);
+       if (status < 0) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Cannot decode event common context field: "
+                       "msg-it-addr=%p, stream-class-addr=%p, "
+                       "stream-class-id=%" PRId64 ", fc-addr=%p",
+                       msg_it, msg_it->meta.sc,
+                       msg_it->meta.sc->id,
+                       event_common_context_fc);
+       }
+
+end:
+       return status;
+}
+
+static
+enum ctf_msg_iter_status read_event_common_context_continue_state(
+               struct ctf_msg_iter *msg_it)
+{
+       return read_dscope_continue_state(msg_it,
+               STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN);
+}
+
+static
+enum ctf_msg_iter_status read_event_spec_context_begin_state(
+               struct ctf_msg_iter *msg_it)
+{
+       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+       bt_self_component *self_comp = msg_it->self_comp;
+       struct ctf_field_class *event_spec_context_fc;
+
+       event_spec_context_fc = msg_it->meta.ec->spec_context_fc;
+       if (!event_spec_context_fc) {
+               msg_it->state = STATE_DSCOPE_EVENT_PAYLOAD_BEGIN;
+               goto end;
+       }
+
+       if (event_spec_context_fc->in_ir && !msg_it->dry_run) {
+               BT_ASSERT_DBG(!msg_it->dscopes.event_spec_context);
+               msg_it->dscopes.event_spec_context =
+                       bt_event_borrow_specific_context_field(
+                               msg_it->event);
+               BT_ASSERT_DBG(msg_it->dscopes.event_spec_context);
+       }
+
+       BT_COMP_LOGT("Decoding event specific context field: "
+               "msg-it-addr=%p, event-class-addr=%p, "
+               "event-class-name=\"%s\", event-class-id=%" PRId64 ", "
+               "fc-addr=%p",
+               msg_it, msg_it->meta.ec,
+               msg_it->meta.ec->name->str,
+               msg_it->meta.ec->id,
+               event_spec_context_fc);
+       status = read_dscope_begin_state(msg_it, event_spec_context_fc,
+               STATE_DSCOPE_EVENT_PAYLOAD_BEGIN,
+               STATE_DSCOPE_EVENT_SPEC_CONTEXT_CONTINUE,
+               msg_it->dscopes.event_spec_context);
+       if (status < 0) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Cannot decode event specific context field: "
+                       "msg-it-addr=%p, event-class-addr=%p, "
+                       "event-class-name=\"%s\", "
+                       "event-class-id=%" PRId64 ", fc-addr=%p",
+                       msg_it, msg_it->meta.ec,
+                       msg_it->meta.ec->name->str,
+                       msg_it->meta.ec->id,
+                       event_spec_context_fc);
+       }
+
+end:
+       return status;
+}
+
+static
+enum ctf_msg_iter_status read_event_spec_context_continue_state(
+               struct ctf_msg_iter *msg_it)
+{
+       return read_dscope_continue_state(msg_it,
+               STATE_DSCOPE_EVENT_PAYLOAD_BEGIN);
+}
+
+static
+enum ctf_msg_iter_status read_event_payload_begin_state(
+               struct ctf_msg_iter *msg_it)
+{
+       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+       bt_self_component *self_comp = msg_it->self_comp;
+       struct ctf_field_class *event_payload_fc;
+
+       event_payload_fc = msg_it->meta.ec->payload_fc;
+       if (!event_payload_fc) {
+               msg_it->state = STATE_EMIT_MSG_EVENT;
+               goto end;
+       }
+
+       if (event_payload_fc->in_ir && !msg_it->dry_run) {
+               BT_ASSERT_DBG(!msg_it->dscopes.event_payload);
+               msg_it->dscopes.event_payload =
+                       bt_event_borrow_payload_field(
+                               msg_it->event);
+               BT_ASSERT_DBG(msg_it->dscopes.event_payload);
+       }
+
+       BT_COMP_LOGT("Decoding event payload field: "
+               "msg-it-addr=%p, event-class-addr=%p, "
+               "event-class-name=\"%s\", event-class-id=%" PRId64 ", "
+               "fc-addr=%p",
+               msg_it, msg_it->meta.ec,
+               msg_it->meta.ec->name->str,
+               msg_it->meta.ec->id,
+               event_payload_fc);
+       status = read_dscope_begin_state(msg_it, event_payload_fc,
+               STATE_EMIT_MSG_EVENT,
+               STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE,
+               msg_it->dscopes.event_payload);
+       if (status < 0) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Cannot decode event payload field: "
+                       "msg-it-addr=%p, event-class-addr=%p, "
+                       "event-class-name=\"%s\", "
+                       "event-class-id=%" PRId64 ", fc-addr=%p",
+                       msg_it, msg_it->meta.ec,
+                       msg_it->meta.ec->name->str,
+                       msg_it->meta.ec->id,
+                       event_payload_fc);
+       }
+
+end:
+       return status;
+}
+
+static
+enum ctf_msg_iter_status read_event_payload_continue_state(
+               struct ctf_msg_iter *msg_it)
+{
+       return read_dscope_continue_state(msg_it, STATE_EMIT_MSG_EVENT);
+}
+
+static
+enum ctf_msg_iter_status skip_packet_padding_state(struct ctf_msg_iter *msg_it)
+{
+       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+       size_t bits_to_skip;
+       const enum state next_state = STATE_SWITCH_PACKET;
+
+       BT_ASSERT(msg_it->cur_exp_packet_total_size > 0);
+       bits_to_skip = msg_it->cur_exp_packet_total_size - packet_at(msg_it);
+       if (bits_to_skip == 0) {
+               msg_it->state = next_state;
+               goto end;
+       } else {
+               size_t bits_to_consume;
+
+               BT_COMP_LOGD("Trying to skip %zu bits of padding: msg-it-addr=%p, size=%zu",
+                       bits_to_skip, msg_it, bits_to_skip);
+               status = buf_ensure_available_bits(msg_it);
+               if (status != CTF_MSG_ITER_STATUS_OK) {
+                       goto end;
+               }
+
+               bits_to_consume = MIN(buf_available_bits(msg_it), bits_to_skip);
+               BT_COMP_LOGD("Skipping %zu bits of padding: msg-it-addr=%p, size=%zu",
+                       bits_to_consume, msg_it, bits_to_consume);
+               buf_consume_bits(msg_it, bits_to_consume);
+               bits_to_skip = msg_it->cur_exp_packet_total_size -
+                       packet_at(msg_it);
+               if (bits_to_skip == 0) {
+                       msg_it->state = next_state;
+                       goto end;
+               }
+       }
+
+end:
+       return status;
+}
+
+static
+enum ctf_msg_iter_status check_emit_msg_discarded_events(
+               struct ctf_msg_iter *msg_it)
+{
+       msg_it->state = STATE_EMIT_MSG_DISCARDED_EVENTS;
+
+       if (!msg_it->meta.sc->has_discarded_events) {
+               msg_it->state = STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS;
+               goto end;
+       }
+
+       if (msg_it->prev_packet_snapshots.discarded_events == UINT64_C(-1)) {
+               if (msg_it->snapshots.discarded_events == 0 ||
+                               msg_it->snapshots.discarded_events == UINT64_C(-1)) {
+                       /*
+                        * Stream's first packet with no discarded
+                        * events or no information about discarded
+                        * events: do not emit.
+                        */
+                       msg_it->state = STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS;
+               }
+       } else {
+               /*
+                * If the previous packet has a value for this counter,
+                * then this counter is defined for the whole stream.
+                */
+               BT_ASSERT(msg_it->snapshots.discarded_events != UINT64_C(-1));
+
+               if (msg_it->snapshots.discarded_events -
+                               msg_it->prev_packet_snapshots.discarded_events == 0) {
+                       /*
+                        * No discarded events since previous packet: do
+                        * not emit.
+                        */
+                       msg_it->state = STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS;
+               }
+       }
+
+end:
+       return CTF_MSG_ITER_STATUS_OK;
+}
+
+static
+enum ctf_msg_iter_status check_emit_msg_discarded_packets(
+               struct ctf_msg_iter *msg_it)
+{
+       msg_it->state = STATE_EMIT_MSG_DISCARDED_PACKETS;
+
+       if (!msg_it->meta.sc->has_discarded_packets) {
+               msg_it->state = STATE_EMIT_MSG_PACKET_BEGINNING;
+               goto end;
+       }
+
+       if (msg_it->prev_packet_snapshots.packets == UINT64_C(-1)) {
+               /*
+                * Stream's first packet or no information about
+                * discarded packets: do not emit. In other words, if
+                * this is the first packet and its sequence number is
+                * not 0, do not consider that packets were previously
+                * lost: we might be reading a partial stream (LTTng
+                * snapshot for example).
+                */
+               msg_it->state = STATE_EMIT_MSG_PACKET_BEGINNING;
+       } else {
+               /*
+                * If the previous packet has a value for this counter,
+                * then this counter is defined for the whole stream.
+                */
+               BT_ASSERT(msg_it->snapshots.packets != UINT64_C(-1));
+
+               if (msg_it->snapshots.packets -
+                               msg_it->prev_packet_snapshots.packets <= 1) {
+                       /*
+                        * No discarded packets since previous packet:
+                        * do not emit.
+                        */
+                       msg_it->state = STATE_EMIT_MSG_PACKET_BEGINNING;
+               }
+       }
+
+end:
+       return CTF_MSG_ITER_STATUS_OK;
+}
+
+static inline
+enum state check_emit_msg_stream_end(struct ctf_msg_iter *msg_it)
+{
+       enum state next_state;
+
+       if (msg_it->emit_stream_end_message) {
+               next_state = STATE_EMIT_MSG_STREAM_END;
+       } else {
+               next_state = STATE_DONE;
+       }
+
+       return next_state;
+}
+
+static inline
+enum ctf_msg_iter_status handle_state(struct ctf_msg_iter *msg_it)
+{
+       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+       const enum state state = msg_it->state;
+
+       BT_COMP_LOGT("Handling state: msg-it-addr=%p, state=%s",
+               msg_it, state_string(state));
+
+       // TODO: optimalize!
+       switch (state) {
+       case STATE_INIT:
+               msg_it->state = STATE_SWITCH_PACKET;
+               break;
+       case STATE_SWITCH_PACKET:
+               status = switch_packet_state(msg_it);
+               break;
+       case STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN:
+               status = read_packet_header_begin_state(msg_it);
+               break;
+       case STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE:
+               status = read_packet_header_continue_state(msg_it);
+               break;
+       case STATE_AFTER_TRACE_PACKET_HEADER:
+               status = after_packet_header_state(msg_it);
+               break;
+       case STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN:
+               status = read_packet_context_begin_state(msg_it);
+               break;
+       case STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE:
+               status = read_packet_context_continue_state(msg_it);
+               break;
+       case STATE_AFTER_STREAM_PACKET_CONTEXT:
+               status = after_packet_context_state(msg_it);
+               break;
+       case STATE_EMIT_MSG_STREAM_BEGINNING:
+               msg_it->state = STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS;
+               break;
+       case STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS:
+               status = check_emit_msg_discarded_events(msg_it);
+               break;
+       case STATE_EMIT_MSG_DISCARDED_EVENTS:
+               msg_it->state = STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS;
+               break;
+       case STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS:
+               status = check_emit_msg_discarded_packets(msg_it);
+               break;
+       case STATE_EMIT_MSG_DISCARDED_PACKETS:
+               msg_it->state = STATE_EMIT_MSG_PACKET_BEGINNING;
+               break;
+       case STATE_EMIT_MSG_PACKET_BEGINNING:
+               msg_it->state = STATE_DSCOPE_EVENT_HEADER_BEGIN;
+               break;
+       case STATE_DSCOPE_EVENT_HEADER_BEGIN:
+               status = read_event_header_begin_state(msg_it);
+               break;
+       case STATE_DSCOPE_EVENT_HEADER_CONTINUE:
+               status = read_event_header_continue_state(msg_it);
+               break;
+       case STATE_AFTER_EVENT_HEADER:
+               status = after_event_header_state(msg_it);
+               break;
+       case STATE_DSCOPE_EVENT_COMMON_CONTEXT_BEGIN:
+               status = read_event_common_context_begin_state(msg_it);
+               break;
+       case STATE_DSCOPE_EVENT_COMMON_CONTEXT_CONTINUE:
+               status = read_event_common_context_continue_state(msg_it);
+               break;
+       case STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN:
+               status = read_event_spec_context_begin_state(msg_it);
+               break;
+       case STATE_DSCOPE_EVENT_SPEC_CONTEXT_CONTINUE:
+               status = read_event_spec_context_continue_state(msg_it);
+               break;
+       case STATE_DSCOPE_EVENT_PAYLOAD_BEGIN:
+               status = read_event_payload_begin_state(msg_it);
+               break;
+       case STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE:
+               status = read_event_payload_continue_state(msg_it);
+               break;
+       case STATE_EMIT_MSG_EVENT:
+               msg_it->state = STATE_DSCOPE_EVENT_HEADER_BEGIN;
+               break;
+       case STATE_EMIT_QUEUED_MSG_EVENT:
+               msg_it->state = STATE_EMIT_MSG_EVENT;
+               break;
+       case STATE_SKIP_PACKET_PADDING:
+               status = skip_packet_padding_state(msg_it);
+               break;
+       case STATE_EMIT_MSG_PACKET_END_MULTI:
+               msg_it->state = STATE_SKIP_PACKET_PADDING;
+               break;
+       case STATE_EMIT_MSG_PACKET_END_SINGLE:
+               msg_it->state = STATE_EMIT_MSG_STREAM_END;
+               break;
+       case STATE_EMIT_QUEUED_MSG_PACKET_END:
+               msg_it->state = STATE_EMIT_MSG_PACKET_END_SINGLE;
+               break;
+       case STATE_CHECK_EMIT_MSG_STREAM_END:
+               msg_it->state = check_emit_msg_stream_end(msg_it);
+               break;
+       case STATE_EMIT_MSG_STREAM_END:
+               msg_it->state = STATE_DONE;
+               break;
+       case STATE_DONE:
+               break;
+       default:
+               BT_COMP_LOGF("Unknown CTF plugin message iterator state: "
+                       "msg-it-addr=%p, state=%d", msg_it, msg_it->state);
+               bt_common_abort();
+       }
+
+       BT_COMP_LOGT("Handled state: msg-it-addr=%p, status=%s, "
+               "prev-state=%s, cur-state=%s",
+               msg_it, ctf_msg_iter_status_string(status),
+               state_string(state), state_string(msg_it->state));
+       return status;
+}
+
+BT_HIDDEN
+void ctf_msg_iter_reset_for_next_stream_file(struct ctf_msg_iter *msg_it)
+{
+       BT_ASSERT(msg_it);
+       BT_COMP_LOGD("Resetting message iterator: addr=%p", msg_it);
+       stack_clear(msg_it->stack);
+       msg_it->meta.sc = NULL;
+       msg_it->meta.ec = NULL;
+       BT_PACKET_PUT_REF_AND_RESET(msg_it->packet);
+       BT_STREAM_PUT_REF_AND_RESET(msg_it->stream);
+       BT_MESSAGE_PUT_REF_AND_RESET(msg_it->event_msg);
+       release_all_dscopes(msg_it);
+       msg_it->cur_dscope_field = NULL;
+
+       msg_it->buf.addr = NULL;
+       msg_it->buf.sz = 0;
+       msg_it->buf.at = 0;
+       msg_it->buf.last_eh_at = SIZE_MAX;
+       msg_it->buf.packet_offset = 0;
+       msg_it->state = STATE_INIT;
+       msg_it->cur_exp_packet_content_size = -1;
+       msg_it->cur_exp_packet_total_size = -1;
+       msg_it->cur_packet_offset = -1;
+       msg_it->cur_event_class_id = -1;
+       msg_it->snapshots.beginning_clock = UINT64_C(-1);
+       msg_it->snapshots.end_clock = UINT64_C(-1);
+}
+
+/**
+ * Resets the internal state of a CTF message iterator.
+ */
+BT_HIDDEN
+void ctf_msg_iter_reset(struct ctf_msg_iter *msg_it)
+{
+       ctf_msg_iter_reset_for_next_stream_file(msg_it);
+       msg_it->cur_stream_class_id = -1;
+       msg_it->cur_data_stream_id = -1;
+       msg_it->snapshots.discarded_events = UINT64_C(-1);
+       msg_it->snapshots.packets = UINT64_C(-1);
+       msg_it->prev_packet_snapshots.discarded_events = UINT64_C(-1);
+       msg_it->prev_packet_snapshots.packets = UINT64_C(-1);
+       msg_it->prev_packet_snapshots.beginning_clock = UINT64_C(-1);
+       msg_it->prev_packet_snapshots.end_clock = UINT64_C(-1);
+       msg_it->emit_stream_beginning_message = true;
+       msg_it->emit_stream_end_message = false;
+}
+
+static
+bt_field *borrow_next_field(struct ctf_msg_iter *msg_it)
+{
+       bt_field *next_field = NULL;
+       bt_field *base_field;
+       const bt_field_class *base_fc;
+       bt_field_class_type base_fc_type;
+       size_t index;
+
+       BT_ASSERT_DBG(!stack_empty(msg_it->stack));
+       index = stack_top(msg_it->stack)->index;
+       base_field = stack_top(msg_it->stack)->base;
+       BT_ASSERT_DBG(base_field);
+       base_fc = bt_field_borrow_class_const(base_field);
+       BT_ASSERT_DBG(base_fc);
+       base_fc_type = bt_field_class_get_type(base_fc);
+
+       if (base_fc_type == BT_FIELD_CLASS_TYPE_STRUCTURE) {
+               BT_ASSERT_DBG(index <
+                       bt_field_class_structure_get_member_count(
+                               bt_field_borrow_class_const(
+                                               base_field)));
+               next_field =
+                       bt_field_structure_borrow_member_field_by_index(
+                               base_field, index);
+       } else if (bt_field_class_type_is(base_fc_type,
+                       BT_FIELD_CLASS_TYPE_ARRAY)) {
+               BT_ASSERT_DBG(index < bt_field_array_get_length(base_field));
+               next_field = bt_field_array_borrow_element_field_by_index(
+                       base_field, index);
+       } else if (bt_field_class_type_is(base_fc_type,
+                       BT_FIELD_CLASS_TYPE_VARIANT)) {
+               BT_ASSERT_DBG(index == 0);
+               next_field = bt_field_variant_borrow_selected_option_field(
+                       base_field);
+       } else {
+               bt_common_abort();
+       }
+
+       BT_ASSERT_DBG(next_field);
+       return next_field;
+}
+
+static
+void update_default_clock(struct ctf_msg_iter *msg_it, uint64_t new_val,
+               uint64_t new_val_size)
+{
+       uint64_t new_val_mask;
+       uint64_t cur_value_masked;
+
+       BT_ASSERT_DBG(new_val_size > 0);
+
+       /*
+        * Special case for a 64-bit new value, which is the limit
+        * of a clock value as of this version: overwrite the
+        * current value directly.
+        */
+       if (new_val_size == 64) {
+               msg_it->default_clock_snapshot = new_val;
+               goto end;
+       }
+
+       new_val_mask = (1ULL << new_val_size) - 1;
+       cur_value_masked = msg_it->default_clock_snapshot & new_val_mask;
+
+       if (new_val < cur_value_masked) {
+               /*
+                * It looks like a wrap happened on the number of bits
+                * of the requested new value. Assume that the clock
+                * value wrapped only one time.
+                */
+               msg_it->default_clock_snapshot += new_val_mask + 1;
+       }
+
+       /* Clear the low bits of the current clock value. */
+       msg_it->default_clock_snapshot &= ~new_val_mask;
+
+       /* Set the low bits of the current clock value. */
+       msg_it->default_clock_snapshot |= new_val;
+
+end:
+       BT_COMP_LOGT("Updated default clock's value from integer field's value: "
+               "value=%" PRIu64, msg_it->default_clock_snapshot);
+}
+
+static
+enum bt_bfcr_status bfcr_unsigned_int_cb(uint64_t value,
+               struct ctf_field_class *fc, void *data)
+{
+       ctf_msg_iter *msg_it = (ctf_msg_iter *) data;
+       bt_self_component *self_comp = msg_it->self_comp;
+       enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
+
+       bt_field *field = NULL;
+       ctf_field_class_int *int_fc = ctf_field_class_as_int(fc);
+
+       BT_COMP_LOGT("Unsigned integer function called from BFCR: "
+               "msg-it-addr=%p, bfcr-addr=%p, fc-addr=%p, "
+               "fc-type=%d, fc-in-ir=%d, value=%" PRIu64,
+               msg_it, msg_it->bfcr, fc, fc->type, fc->in_ir, value);
+
+       if (G_LIKELY(int_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE)) {
+               goto update_def_clock;
+       }
+
+       switch (int_fc->meaning) {
+       case CTF_FIELD_CLASS_MEANING_EVENT_CLASS_ID:
+               msg_it->cur_event_class_id = value;
+               break;
+       case CTF_FIELD_CLASS_MEANING_DATA_STREAM_ID:
+               msg_it->cur_data_stream_id = value;
+               break;
+       case CTF_FIELD_CLASS_MEANING_PACKET_BEGINNING_TIME:
+               msg_it->snapshots.beginning_clock = value;
+               break;
+       case CTF_FIELD_CLASS_MEANING_PACKET_END_TIME:
+               msg_it->snapshots.end_clock = value;
+               break;
+       case CTF_FIELD_CLASS_MEANING_STREAM_CLASS_ID:
+               msg_it->cur_stream_class_id = value;
+               break;
+       case CTF_FIELD_CLASS_MEANING_MAGIC:
+               if (value != 0xc1fc1fc1) {
+                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                               "Invalid CTF magic number: msg-it-addr=%p, "
+                               "magic=%" PRIx64, msg_it, value);
+                       status = BT_BFCR_STATUS_ERROR;
+                       goto end;
+               }
+
+               break;
+       case CTF_FIELD_CLASS_MEANING_PACKET_COUNTER_SNAPSHOT:
+               msg_it->snapshots.packets = value;
+               break;
+       case CTF_FIELD_CLASS_MEANING_DISC_EV_REC_COUNTER_SNAPSHOT:
+               msg_it->snapshots.discarded_events = value;
+               break;
+       case CTF_FIELD_CLASS_MEANING_EXP_PACKET_TOTAL_SIZE:
+               msg_it->cur_exp_packet_total_size = value;
+               break;
+       case CTF_FIELD_CLASS_MEANING_EXP_PACKET_CONTENT_SIZE:
+               msg_it->cur_exp_packet_content_size = value;
+               break;
+       default:
+               bt_common_abort();
+       }
+
+update_def_clock:
+       if (G_UNLIKELY(int_fc->mapped_clock_class)) {
+               update_default_clock(msg_it, value, int_fc->base.size);
+       }
+
+       if (G_UNLIKELY(int_fc->storing_index >= 0)) {
+               g_array_index(msg_it->stored_values, uint64_t,
+                       (uint64_t) int_fc->storing_index) = value;
+       }
+
+       if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
+               goto end;
+       }
+
+       field = borrow_next_field(msg_it);
+       BT_ASSERT_DBG(field);
+       BT_ASSERT_DBG(bt_field_borrow_class_const(field) == fc->ir_fc);
+       BT_ASSERT_DBG(bt_field_class_type_is(bt_field_get_class_type(field),
+               BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER));
+       bt_field_integer_unsigned_set_value(field, value);
+       stack_top(msg_it->stack)->index++;
+
+end:
+       return status;
+}
+
+static
+enum bt_bfcr_status bfcr_unsigned_int_char_cb(uint64_t value,
+               struct ctf_field_class *fc, void *data)
+{
+       int ret;
+       ctf_msg_iter *msg_it = (ctf_msg_iter *) data;
+       bt_self_component *self_comp = msg_it->self_comp;
+       enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
+       bt_field *string_field = NULL;
+       ctf_field_class_int *int_fc = ctf_field_class_as_int(fc);
+       char str[2] = {'\0', '\0'};
+
+       BT_COMP_LOGT("Unsigned integer character function called from BFCR: "
+               "msg-it-addr=%p, bfcr-addr=%p, fc-addr=%p, "
+               "fc-type=%d, fc-in-ir=%d, value=%" PRIu64,
+               msg_it, msg_it->bfcr, fc, fc->type, fc->in_ir, value);
+       BT_ASSERT_DBG(int_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE);
+       BT_ASSERT_DBG(!int_fc->mapped_clock_class);
+       BT_ASSERT_DBG(int_fc->storing_index < 0);
+
+       if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
+               goto end;
+       }
+
+       if (msg_it->done_filling_string) {
+               goto end;
+       }
+
+       if (value == 0) {
+               msg_it->done_filling_string = true;
+               goto end;
+       }
+
+       string_field = stack_top(msg_it->stack)->base;
+       BT_ASSERT_DBG(bt_field_get_class_type(string_field) ==
+               BT_FIELD_CLASS_TYPE_STRING);
+
+       /* Append character */
+       str[0] = (char) value;
+       ret = bt_field_string_append_with_length(string_field, str, 1);
+       if (ret) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Cannot append character to string field's value: "
+                       "msg-it-addr=%p, field-addr=%p, ret=%d",
+                       msg_it, string_field, ret);
+               status = BT_BFCR_STATUS_ERROR;
+               goto end;
+       }
+
+end:
+       return status;
+}
+
+static
+enum bt_bfcr_status bfcr_signed_int_cb(int64_t value,
+               struct ctf_field_class *fc, void *data)
+{
+       enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
+       bt_field *field = NULL;
+       ctf_msg_iter *msg_it = (ctf_msg_iter *) data;
+       ctf_field_class_int *int_fc = ctf_field_class_as_int(fc);
+
+       BT_COMP_LOGT("Signed integer function called from BFCR: "
+               "msg-it-addr=%p, bfcr-addr=%p, fc-addr=%p, "
+               "fc-type=%d, fc-in-ir=%d, value=%" PRId64,
+               msg_it, msg_it->bfcr, fc, fc->type, fc->in_ir, value);
+       BT_ASSERT_DBG(int_fc->meaning == CTF_FIELD_CLASS_MEANING_NONE);
+
+       if (G_UNLIKELY(int_fc->storing_index >= 0)) {
+               g_array_index(msg_it->stored_values, uint64_t,
+                       (uint64_t) int_fc->storing_index) = (uint64_t) value;
+       }
+
+       if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
+               goto end;
+       }
+
+       field = borrow_next_field(msg_it);
+       BT_ASSERT_DBG(field);
+       BT_ASSERT_DBG(bt_field_borrow_class_const(field) == fc->ir_fc);
+       BT_ASSERT_DBG(bt_field_class_type_is(bt_field_get_class_type(field),
+               BT_FIELD_CLASS_TYPE_SIGNED_INTEGER));
+       bt_field_integer_signed_set_value(field, value);
+       stack_top(msg_it->stack)->index++;
+
+end:
+       return status;
+}
+
+static
+enum bt_bfcr_status bfcr_floating_point_cb(double value,
+               struct ctf_field_class *fc, void *data)
+{
+       enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
+       bt_field *field = NULL;
+       ctf_msg_iter *msg_it = (ctf_msg_iter *) data;
+       bt_field_class_type type;
+
+       BT_COMP_LOGT("Floating point number function called from BFCR: "
+               "msg-it-addr=%p, bfcr-addr=%p, fc-addr=%p, "
+               "fc-type=%d, fc-in-ir=%d, value=%f",
+               msg_it, msg_it->bfcr, fc, fc->type, fc->in_ir, value);
+
+       if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
+               goto end;
+       }
+
+       field = borrow_next_field(msg_it);
+       type = bt_field_get_class_type(field);
+       BT_ASSERT_DBG(field);
+       BT_ASSERT_DBG(bt_field_borrow_class_const(field) == fc->ir_fc);
+       BT_ASSERT_DBG(bt_field_class_type_is(type, BT_FIELD_CLASS_TYPE_REAL));
+
+       if (type == BT_FIELD_CLASS_TYPE_SINGLE_PRECISION_REAL) {
+               bt_field_real_single_precision_set_value(field, (float) value);
+       } else {
+               bt_field_real_double_precision_set_value(field, value);
+       }
+       stack_top(msg_it->stack)->index++;
+
+end:
+       return status;
+}
+
+static
+enum bt_bfcr_status bfcr_string_begin_cb(
+               struct ctf_field_class *fc, void *data)
+{
+       bt_field *field = NULL;
+       ctf_msg_iter *msg_it = (ctf_msg_iter *) data;
+
+       BT_COMP_LOGT("String (beginning) function called from BFCR: "
+               "msg-it-addr=%p, bfcr-addr=%p, fc-addr=%p, "
+               "fc-type=%d, fc-in-ir=%d",
+               msg_it, msg_it->bfcr, fc, fc->type, fc->in_ir);
+
+       if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
+               goto end;
+       }
+
+       field = borrow_next_field(msg_it);
+       BT_ASSERT_DBG(field);
+       BT_ASSERT_DBG(bt_field_borrow_class_const(field) == fc->ir_fc);
+       BT_ASSERT_DBG(bt_field_get_class_type(field) ==
+                 BT_FIELD_CLASS_TYPE_STRING);
+       bt_field_string_clear(field);
+
+       /*
+        * Push on stack. Not a compound class per se, but we know that
+        * only bfcr_string_cb() may be called between this call and a
+        * subsequent call to bfcr_string_end_cb().
+        */
+       stack_push(msg_it->stack, field);
+
+end:
+       return BT_BFCR_STATUS_OK;
+}
+
+static
+enum bt_bfcr_status bfcr_string_cb(const char *value,
+               size_t len, struct ctf_field_class *fc, void *data)
+{
+       enum bt_bfcr_status status = BT_BFCR_STATUS_OK;
+       bt_field *field = NULL;
+       ctf_msg_iter *msg_it = (ctf_msg_iter *) data;
+       bt_self_component *self_comp = msg_it->self_comp;
+       int ret;
+
+       BT_COMP_LOGT("String (substring) function called from BFCR: "
+               "msg-it-addr=%p, bfcr-addr=%p, fc-addr=%p, "
+               "fc-type=%d, fc-in-ir=%d, string-length=%zu",
+               msg_it, msg_it->bfcr, fc, fc->type, fc->in_ir,
+               len);
+
+       if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
+               goto end;
+       }
+
+       field = stack_top(msg_it->stack)->base;
+       BT_ASSERT_DBG(field);
+
+       /* Append current substring */
+       ret = bt_field_string_append_with_length(field, value, len);
+       if (ret) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Cannot append substring to string field's value: "
+                       "msg-it-addr=%p, field-addr=%p, string-length=%zu, "
+                       "ret=%d", msg_it, field, len, ret);
+               status = BT_BFCR_STATUS_ERROR;
+               goto end;
+       }
+
+end:
+       return status;
+}
+
+static
+enum bt_bfcr_status bfcr_string_end_cb(
+               struct ctf_field_class *fc, void *data)
+{
+       ctf_msg_iter *msg_it = (ctf_msg_iter *) data;
+
+       BT_COMP_LOGT("String (end) function called from BFCR: "
+               "msg-it-addr=%p, bfcr-addr=%p, fc-addr=%p, "
+               "fc-type=%d, fc-in-ir=%d",
+               msg_it, msg_it->bfcr, fc, fc->type, fc->in_ir);
+
+       if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
+               goto end;
+       }
+
+       /* Pop string field */
+       stack_pop(msg_it->stack);
+
+       /* Go to next field */
+       stack_top(msg_it->stack)->index++;
+
+end:
+       return BT_BFCR_STATUS_OK;
+}
+
+static
+enum bt_bfcr_status bfcr_compound_begin_cb(
+               struct ctf_field_class *fc, void *data)
+{
+       ctf_msg_iter *msg_it = (ctf_msg_iter *) data;
+       bt_field *field;
+
+       BT_COMP_LOGT("Compound (beginning) function called from BFCR: "
+               "msg-it-addr=%p, bfcr-addr=%p, fc-addr=%p, "
+               "fc-type=%d, fc-in-ir=%d",
+               msg_it, msg_it->bfcr, fc, fc->type, fc->in_ir);
+
+       if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
+               goto end;
+       }
+
+       /* Borrow field */
+       if (stack_empty(msg_it->stack)) {
+               /* Root: already set by read_dscope_begin_state() */
+               field = msg_it->cur_dscope_field;
+       } else {
+               field = borrow_next_field(msg_it);
+               BT_ASSERT_DBG(field);
+       }
+
+       /* Push field */
+       BT_ASSERT_DBG(field);
+       BT_ASSERT_DBG(bt_field_borrow_class_const(field) == fc->ir_fc);
+       stack_push(msg_it->stack, field);
+
+       /*
+        * Change BFCR "unsigned int" callback if it's a text
+        * array/sequence.
+        */
+       if (fc->type == CTF_FIELD_CLASS_TYPE_ARRAY ||
+                       fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE) {
+               ctf_field_class_array_base *array_fc = ctf_field_class_as_array_base(fc);
+
+               if (array_fc->is_text) {
+                       BT_ASSERT_DBG(bt_field_get_class_type(field) ==
+                                 BT_FIELD_CLASS_TYPE_STRING);
+                       msg_it->done_filling_string = false;
+                       bt_field_string_clear(field);
+                       bt_bfcr_set_unsigned_int_cb(msg_it->bfcr,
+                               bfcr_unsigned_int_char_cb);
+               }
+       }
+
+end:
+       return BT_BFCR_STATUS_OK;
+}
+
+static
+enum bt_bfcr_status bfcr_compound_end_cb(
+               struct ctf_field_class *fc, void *data)
+{
+       ctf_msg_iter *msg_it = (ctf_msg_iter *) data;
+
+       BT_COMP_LOGT("Compound (end) function called from BFCR: "
+               "msg-it-addr=%p, bfcr-addr=%p, fc-addr=%p, "
+               "fc-type=%d, fc-in-ir=%d",
+               msg_it, msg_it->bfcr, fc, fc->type, fc->in_ir);
+
+       if (G_UNLIKELY(!fc->in_ir || msg_it->dry_run)) {
+               goto end;
+       }
+
+       BT_ASSERT_DBG(!stack_empty(msg_it->stack));
+       BT_ASSERT_DBG(bt_field_borrow_class_const(stack_top(msg_it->stack)->base) ==
+               fc->ir_fc);
+
+       /*
+        * Reset BFCR "unsigned int" callback if it's a text
+        * array/sequence.
+        */
+       if (fc->type == CTF_FIELD_CLASS_TYPE_ARRAY ||
+                       fc->type == CTF_FIELD_CLASS_TYPE_SEQUENCE) {
+               ctf_field_class_array_base *array_fc = ctf_field_class_as_array_base(fc);
+
+               if (array_fc->is_text) {
+                       BT_ASSERT_DBG(bt_field_get_class_type(
+                               stack_top(msg_it->stack)->base) ==
+                               BT_FIELD_CLASS_TYPE_STRING);
+                       bt_bfcr_set_unsigned_int_cb(msg_it->bfcr,
+                               bfcr_unsigned_int_cb);
+               }
+       }
+
+       /* Pop stack */
+       stack_pop(msg_it->stack);
+
+       /* If the stack is not empty, increment the base's index */
+       if (!stack_empty(msg_it->stack)) {
+               stack_top(msg_it->stack)->index++;
+       }
+
+end:
+       return BT_BFCR_STATUS_OK;
+}
+
+static
+int64_t bfcr_get_sequence_length_cb(struct ctf_field_class *fc, void *data)
+{
+       bt_field *seq_field;
+       ctf_msg_iter *msg_it = (ctf_msg_iter *) data;
+       bt_self_component *self_comp = msg_it->self_comp;
+       struct ctf_field_class_sequence *seq_fc = ctf_field_class_as_sequence(fc);
+       int64_t length;
+       int ret;
+
+       length = (uint64_t) g_array_index(msg_it->stored_values, uint64_t,
+               seq_fc->stored_length_index);
+
+       if (G_UNLIKELY(msg_it->dry_run)){
+               goto end;
+       }
+
+       seq_field = stack_top(msg_it->stack)->base;
+       BT_ASSERT_DBG(seq_field);
+
+       /*
+        * bfcr_get_sequence_length_cb() also gets called back for a
+        * text sequence, but the destination field is a string field.
+        * Only set the field's sequence length if the destination field
+        * is a sequence field.
+        */
+       if (!seq_fc->base.is_text) {
+               BT_ASSERT_DBG(bt_field_class_type_is(
+                       bt_field_get_class_type(seq_field),
+                               BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY));
+               ret = bt_field_array_dynamic_set_length(seq_field,
+                       (uint64_t) length);
+               if (ret) {
+                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                               "Cannot set dynamic array field's length field: "
+                               "msg-it-addr=%p, field-addr=%p, "
+                               "length=%" PRIu64, msg_it, seq_field, length);
+                       length = -1;
+               }
+       }
+
+end:
+       return length;
+}
+
+static
+struct ctf_field_class *bfcr_borrow_variant_selected_field_class_cb(
+               struct ctf_field_class *fc, void *data)
+{
+       int ret;
+       uint64_t i;
+       int64_t option_index = -1;
+       ctf_msg_iter *msg_it = (ctf_msg_iter *) data;
+       ctf_field_class_variant *var_fc = ctf_field_class_as_variant(fc);
+       struct ctf_named_field_class *selected_option = NULL;
+       bt_self_component *self_comp = msg_it->self_comp;
+       struct ctf_field_class *ret_fc = NULL;
+       union {
+               uint64_t u;
+               int64_t i;
+       } tag;
+
+       /* Get variant's tag */
+       tag.u = g_array_index(msg_it->stored_values, uint64_t,
+               var_fc->stored_tag_index);
+
+       /*
+        * Check each range to find the selected option's index.
+        */
+       if (var_fc->tag_fc->base.is_signed) {
+               for (i = 0; i < var_fc->ranges->len; i++) {
+                       struct ctf_field_class_variant_range *range =
+                               ctf_field_class_variant_borrow_range_by_index(
+                                       var_fc, i);
+
+                       if (tag.i >= range->range.lower.i &&
+                                       tag.i <= range->range.upper.i) {
+                               option_index = (int64_t) range->option_index;
+                               break;
+                       }
+               }
+       } else {
+               for (i = 0; i < var_fc->ranges->len; i++) {
+                       struct ctf_field_class_variant_range *range =
+                               ctf_field_class_variant_borrow_range_by_index(
+                                       var_fc, i);
+
+                       if (tag.u >= range->range.lower.u &&
+                                       tag.u <= range->range.upper.u) {
+                               option_index = (int64_t) range->option_index;
+                               break;
+                       }
+               }
+       }
+
+       if (option_index < 0) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Cannot find variant field class's option: "
+                       "msg-it-addr=%p, var-fc-addr=%p, u-tag=%" PRIu64 ", "
+                       "i-tag=%" PRId64, msg_it, var_fc, tag.u, tag.i);
+               ret_fc = NULL;
+               goto end;
+       }
+
+       selected_option = ctf_field_class_variant_borrow_option_by_index(
+               var_fc, (uint64_t) option_index);
+
+       if (selected_option->fc->in_ir && !msg_it->dry_run) {
+               bt_field *var_field = stack_top(msg_it->stack)->base;
+
+               ret = bt_field_variant_select_option_by_index(
+                       var_field, option_index);
+               if (ret) {
+                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                               "Cannot select variant field's option field: "
+                               "msg-it-addr=%p, var-field-addr=%p, "
+                               "opt-index=%" PRId64, msg_it, var_field,
+                               option_index);
+                       ret_fc = NULL;
+                       goto end;
+               }
+       }
+
+       ret_fc = selected_option->fc;
+
+end:
+       return ret_fc;
+}
+
+static
+bt_message *create_msg_stream_beginning(struct ctf_msg_iter *msg_it)
+{
+       bt_self_component *self_comp = msg_it->self_comp;
+       bt_message *msg;
+
+       BT_ASSERT(msg_it->stream);
+       BT_ASSERT(msg_it->self_msg_iter);
+       msg = bt_message_stream_beginning_create(msg_it->self_msg_iter,
+               msg_it->stream);
+       if (!msg) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Cannot create stream beginning message: "
+                       "msg-it-addr=%p, stream-addr=%p",
+                       msg_it, msg_it->stream);
+       }
+
+       return msg;
+}
+
+static
+bt_message *create_msg_stream_end(struct ctf_msg_iter *msg_it)
+{
+       bt_self_component *self_comp = msg_it->self_comp;
+       bt_message *msg;
+
+       if (!msg_it->stream) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Cannot create stream end message because stream is NULL: "
+                       "msg-it-addr=%p", msg_it);
+               msg = NULL;
+               goto end;
+       }
+
+       BT_ASSERT(msg_it->self_msg_iter);
+       msg = bt_message_stream_end_create(msg_it->self_msg_iter,
+               msg_it->stream);
+       if (!msg) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Cannot create stream end message: "
+                       "msg-it-addr=%p, stream-addr=%p",
+                       msg_it, msg_it->stream);
+       }
+
+end:
+       return msg;
+}
+
+static
+bt_message *create_msg_packet_beginning(struct ctf_msg_iter *msg_it,
+               bool use_default_cs)
+{
+       bt_self_component *self_comp = msg_it->self_comp;
+       bt_message *msg;
+       const bt_stream_class *sc = msg_it->meta.sc->ir_sc;
+
+       BT_ASSERT(msg_it->packet);
+       BT_ASSERT(sc);
+       BT_ASSERT(msg_it->self_msg_iter);
+
+       if (msg_it->meta.sc->packets_have_ts_begin) {
+               BT_ASSERT(msg_it->snapshots.beginning_clock != UINT64_C(-1));
+               uint64_t raw_cs_value;
+
+               /*
+                * Either use the decoded packet `timestamp_begin` field or the
+                * current stream's default clock_snapshot.
+                */
+               if (use_default_cs) {
+                       raw_cs_value = msg_it->default_clock_snapshot;
+               } else {
+                       raw_cs_value = msg_it->snapshots.beginning_clock;
+               }
+
+               msg = bt_message_packet_beginning_create_with_default_clock_snapshot(
+                       msg_it->self_msg_iter, msg_it->packet,
+                       raw_cs_value);
+       } else {
+               msg = bt_message_packet_beginning_create(msg_it->self_msg_iter,
+                       msg_it->packet);
+       }
+
+       if (!msg) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Cannot create packet beginning message: "
+                       "msg-it-addr=%p, packet-addr=%p",
+                       msg_it, msg_it->packet);
+               goto end;
+       }
+
+end:
+       return msg;
+}
+
+static
+bt_message *emit_delayed_packet_beg_msg(struct ctf_msg_iter *msg_it)
+{
+       bool packet_beg_ts_need_fix_up;
+
+       msg_it->emit_delayed_packet_beginning_msg = false;
+
+       /*
+        * Only fix the packet's timestamp_begin if it's larger than the first
+        * event of the packet. If there was no event in the packet, the
+        * `default_clock_snapshot` field will be either equal or greater than
+        * `snapshots.beginning_clock` so there is not fix needed.
+        */
+       packet_beg_ts_need_fix_up =
+               msg_it->default_clock_snapshot < msg_it->snapshots.beginning_clock;
+
+       /* create_msg_packet_beginning() logs errors */
+       return create_msg_packet_beginning(msg_it, packet_beg_ts_need_fix_up);
+}
+
+
+static
+bt_message *create_msg_packet_end(struct ctf_msg_iter *msg_it)
+{
+       bt_message *msg;
+       bool update_default_cs = true;
+       bt_self_component *self_comp = msg_it->self_comp;
+
+       if (!msg_it->packet) {
+               msg = NULL;
+               goto end;
+       }
+
+       /*
+        * Check if we need to emit the delayed packet
+        * beginning message instead of the packet end message.
+        */
+       if (G_UNLIKELY(msg_it->emit_delayed_packet_beginning_msg)) {
+               msg = emit_delayed_packet_beg_msg(msg_it);
+               /* Don't forget to emit the packet end message. */
+               msg_it->state = STATE_EMIT_QUEUED_MSG_PACKET_END;
+               goto end;
+       }
+
+       /* Check if may be affected by lttng-crash timestamp_end quirk. */
+       if (G_UNLIKELY(msg_it->meta.tc->quirks.lttng_crash)) {
+               /*
+                * Check if the `timestamp_begin` field is non-zero but
+                * `timestamp_end` is zero. It means the trace is affected by
+                * the lttng-crash packet `timestamp_end` quirk and must be
+                * fixed up by omitting to update the default clock snapshot to
+                * the `timestamp_end` as is typically done.
+                */
+               if (msg_it->snapshots.beginning_clock != 0 &&
+                               msg_it->snapshots.end_clock == 0) {
+                       update_default_cs = false;
+               }
+       }
+
+       /*
+        * Check if may be affected by lttng event-after-packet `timestamp_end`
+        * quirk.
+        */
+       if (msg_it->meta.tc->quirks.lttng_event_after_packet) {
+               /*
+                * Check if `timestamp_end` is smaller then the current
+                * default_clock_snapshot (which is set to the last event
+                * decoded). It means the trace is affected by the lttng
+                * `event-after-packet` packet `timestamp_end` quirk and must
+                * be fixed up by omitting to update the default clock snapshot
+                * to the `timestamp_end` as is typically done.
+                */
+               if (msg_it->snapshots.end_clock < msg_it->default_clock_snapshot) {
+                       update_default_cs = false;
+               }
+       }
+
+       /* Update default clock from packet's end time. */
+       if (msg_it->snapshots.end_clock != UINT64_C(-1) && update_default_cs) {
+               msg_it->default_clock_snapshot = msg_it->snapshots.end_clock;
+       }
+
+       BT_ASSERT(msg_it->self_msg_iter);
+
+       if (msg_it->meta.sc->packets_have_ts_end) {
+               BT_ASSERT(msg_it->snapshots.end_clock != UINT64_C(-1));
+               msg = bt_message_packet_end_create_with_default_clock_snapshot(
+                       msg_it->self_msg_iter, msg_it->packet,
+                       msg_it->default_clock_snapshot);
+       } else {
+               msg = bt_message_packet_end_create(msg_it->self_msg_iter,
+                       msg_it->packet);
+       }
+
+       if (!msg) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Cannot create packet end message: "
+                       "msg-it-addr=%p, packet-addr=%p",
+                       msg_it, msg_it->packet);
+               goto end;
+
+       }
+
+       BT_PACKET_PUT_REF_AND_RESET(msg_it->packet);
+
+end:
+       return msg;
+}
+
+static
+bt_message *create_msg_discarded_events(struct ctf_msg_iter *msg_it)
+{
+       bt_message *msg;
+       bt_self_component *self_comp = msg_it->self_comp;
+       uint64_t beginning_raw_value = UINT64_C(-1);
+       uint64_t end_raw_value = UINT64_C(-1);
+
+       BT_ASSERT(msg_it->self_msg_iter);
+       BT_ASSERT(msg_it->stream);
+       BT_ASSERT(msg_it->meta.sc->has_discarded_events);
+
+       if (msg_it->meta.sc->discarded_events_have_default_cs) {
+               if (msg_it->prev_packet_snapshots.discarded_events == UINT64_C(-1)) {
+                       /*
+                        * We discarded events, but before (and possibly
+                        * including) the current packet: use this packet's time
+                        * range, and do not have a specific count.
+                        */
+                       beginning_raw_value = msg_it->snapshots.beginning_clock;
+                       end_raw_value = msg_it->snapshots.end_clock;
+               } else {
+                       beginning_raw_value = msg_it->prev_packet_snapshots.end_clock;
+                       end_raw_value = msg_it->snapshots.end_clock;
+               }
+
+               BT_ASSERT(beginning_raw_value != UINT64_C(-1));
+               BT_ASSERT(end_raw_value != UINT64_C(-1));
+               msg = bt_message_discarded_events_create_with_default_clock_snapshots(
+                       msg_it->self_msg_iter, msg_it->stream, beginning_raw_value,
+                       end_raw_value);
+       } else {
+               msg = bt_message_discarded_events_create(msg_it->self_msg_iter,
+                       msg_it->stream);
+       }
+
+       if (!msg) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Cannot create discarded events message: "
+                       "msg-it-addr=%p, stream-addr=%p",
+                       msg_it, msg_it->stream);
+               goto end;
+       }
+
+       if (msg_it->prev_packet_snapshots.discarded_events != UINT64_C(-1)) {
+               bt_message_discarded_events_set_count(msg,
+                       msg_it->snapshots.discarded_events -
+                       msg_it->prev_packet_snapshots.discarded_events);
+       }
+
+end:
+       return msg;
+}
+
+static
+bt_message *create_msg_discarded_packets(struct ctf_msg_iter *msg_it)
+{
+       bt_message *msg;
+       bt_self_component *self_comp = msg_it->self_comp;
+
+       BT_ASSERT(msg_it->self_msg_iter);
+       BT_ASSERT(msg_it->stream);
+       BT_ASSERT(msg_it->meta.sc->has_discarded_packets);
+       BT_ASSERT(msg_it->prev_packet_snapshots.packets !=
+               UINT64_C(-1));
+
+       if (msg_it->meta.sc->discarded_packets_have_default_cs) {
+               BT_ASSERT(msg_it->prev_packet_snapshots.end_clock != UINT64_C(-1));
+               BT_ASSERT(msg_it->snapshots.beginning_clock != UINT64_C(-1));
+               msg = bt_message_discarded_packets_create_with_default_clock_snapshots(
+                       msg_it->self_msg_iter, msg_it->stream,
+                       msg_it->prev_packet_snapshots.end_clock,
+                       msg_it->snapshots.beginning_clock);
+       } else {
+               msg = bt_message_discarded_packets_create(msg_it->self_msg_iter,
+                       msg_it->stream);
+       }
+
+       if (!msg) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Cannot create discarded packets message: "
+                       "msg-it-addr=%p, stream-addr=%p",
+                       msg_it, msg_it->stream);
+               goto end;
+       }
+
+       bt_message_discarded_packets_set_count(msg,
+               msg_it->snapshots.packets -
+                       msg_it->prev_packet_snapshots.packets - 1);
+
+end:
+       return msg;
+}
+
+BT_HIDDEN
+struct ctf_msg_iter *ctf_msg_iter_create(
+               struct ctf_trace_class *tc,
+               size_t max_request_sz,
+               struct ctf_msg_iter_medium_ops medops, void *data,
+               bt_logging_level log_level,
+               bt_self_component *self_comp,
+               bt_self_message_iterator *self_msg_iter)
+{
+       struct ctf_msg_iter *msg_it = NULL;
+       struct bt_bfcr_cbs cbs = {
+               .classes = {
+                       .signed_int = bfcr_signed_int_cb,
+                       .unsigned_int = bfcr_unsigned_int_cb,
+                       .floating_point = bfcr_floating_point_cb,
+                       .string_begin = bfcr_string_begin_cb,
+                       .string = bfcr_string_cb,
+                       .string_end = bfcr_string_end_cb,
+                       .compound_begin = bfcr_compound_begin_cb,
+                       .compound_end = bfcr_compound_end_cb,
+               },
+               .query = {
+                       .get_sequence_length = bfcr_get_sequence_length_cb,
+                       .borrow_variant_selected_field_class = bfcr_borrow_variant_selected_field_class_cb,
+               },
+       };
+
+       BT_ASSERT(tc);
+       BT_ASSERT(medops.request_bytes);
+       BT_ASSERT(medops.borrow_stream);
+       BT_ASSERT(max_request_sz > 0);
+
+       BT_COMP_LOG_CUR_LVL(BT_LOG_DEBUG, log_level, self_comp,
+               "Creating CTF plugin message iterator: "
+               "trace-addr=%p, max-request-size=%zu, "
+               "data=%p, log-level=%s", tc, max_request_sz, data,
+               bt_common_logging_level_string(log_level));
+       msg_it = g_new0(struct ctf_msg_iter, 1);
+       if (!msg_it) {
+               BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, log_level, self_comp,
+                       "Failed to allocate one CTF plugin message iterator.");
+               goto end;
+       }
+       msg_it->self_comp = self_comp;
+       msg_it->self_msg_iter = self_msg_iter;
+       msg_it->log_level = log_level;
+       msg_it->meta.tc = tc;
+       msg_it->medium.medops = medops;
+       msg_it->medium.max_request_sz = max_request_sz;
+       msg_it->medium.data = data;
+       msg_it->stack = stack_new(msg_it);
+       msg_it->stored_values = g_array_new(FALSE, TRUE, sizeof(uint64_t));
+       g_array_set_size(msg_it->stored_values, tc->stored_value_count);
+
+       if (!msg_it->stack) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Failed to create field stack.");
+               goto error;
+       }
+
+       msg_it->bfcr = bt_bfcr_create(cbs, msg_it, log_level, NULL);
+       if (!msg_it->bfcr) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Failed to create binary class reader (BFCR).");
+               goto error;
+       }
+
+       ctf_msg_iter_reset(msg_it);
+       BT_COMP_LOGD("Created CTF plugin message iterator: "
+               "trace-addr=%p, max-request-size=%zu, "
+               "data=%p, msg-it-addr=%p, log-level=%s",
+               tc, max_request_sz, data, msg_it,
+               bt_common_logging_level_string(log_level));
+       msg_it->cur_packet_offset = 0;
+
+end:
+       return msg_it;
+
+error:
+       ctf_msg_iter_destroy(msg_it);
+       msg_it = NULL;
+       goto end;
+}
+
+void ctf_msg_iter_destroy(struct ctf_msg_iter *msg_it)
+{
+       BT_PACKET_PUT_REF_AND_RESET(msg_it->packet);
+       BT_STREAM_PUT_REF_AND_RESET(msg_it->stream);
+       release_all_dscopes(msg_it);
+
+       BT_COMP_LOGD("Destroying CTF plugin message iterator: addr=%p", msg_it);
+
+       if (msg_it->stack) {
+               BT_COMP_LOGD_STR("Destroying field stack.");
+               stack_destroy(msg_it->stack);
+       }
+
+       if (msg_it->bfcr) {
+               BT_COMP_LOGD("Destroying BFCR: bfcr-addr=%p", msg_it->bfcr);
+               bt_bfcr_destroy(msg_it->bfcr);
+       }
+
+       if (msg_it->stored_values) {
+               g_array_free(msg_it->stored_values, TRUE);
+       }
+
+       g_free(msg_it);
+}
+
+enum ctf_msg_iter_status ctf_msg_iter_get_next_message(
+               struct ctf_msg_iter *msg_it,
+               const bt_message **message)
+{
+       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+       bt_self_component *self_comp = msg_it->self_comp;
+
+       BT_ASSERT_DBG(msg_it);
+       BT_ASSERT_DBG(message);
+       BT_COMP_LOGD("Getting next message: msg-it-addr=%p", msg_it);
+
+       while (true) {
+               status = handle_state(msg_it);
+               if (G_UNLIKELY(status == CTF_MSG_ITER_STATUS_AGAIN)) {
+                       BT_COMP_LOGD_STR("Medium returned CTF_MSG_ITER_STATUS_AGAIN.");
+                       goto end;
+               } else if (G_UNLIKELY(status != CTF_MSG_ITER_STATUS_OK)) {
+                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                               "Cannot handle state: msg-it-addr=%p, state=%s",
+                               msg_it, state_string(msg_it->state));
+                       goto end;
+               }
+
+               switch (msg_it->state) {
+               case STATE_EMIT_MSG_EVENT:
+                       BT_ASSERT_DBG(msg_it->event_msg);
+
+                       /*
+                        * Check if we need to emit the delayed packet
+                        * beginning message instead of the event message.
+                        */
+                       if (G_UNLIKELY(msg_it->emit_delayed_packet_beginning_msg)) {
+                               *message = emit_delayed_packet_beg_msg(msg_it);
+                               if (!*message) {
+                                       status = CTF_MSG_ITER_STATUS_ERROR;
+                               }
+
+                               /*
+                                * Don't forget to emit the event message of
+                                * the event record that was just decoded.
+                                */
+                               msg_it->state = STATE_EMIT_QUEUED_MSG_EVENT;
+
+                       } else {
+                               *message = msg_it->event_msg;
+                               msg_it->event_msg = NULL;
+                       }
+                       goto end;
+               case STATE_EMIT_MSG_DISCARDED_EVENTS:
+                       /* create_msg_discared_events() logs errors */
+                       *message = create_msg_discarded_events(msg_it);
+
+                       if (!*message) {
+                               status = CTF_MSG_ITER_STATUS_ERROR;
+                       }
+
+                       goto end;
+               case STATE_EMIT_MSG_DISCARDED_PACKETS:
+                       /* create_msg_discared_packets() logs errors */
+                       *message = create_msg_discarded_packets(msg_it);
+
+                       if (!*message) {
+                               status = CTF_MSG_ITER_STATUS_ERROR;
+                       }
+
+                       goto end;
+               case STATE_EMIT_MSG_PACKET_BEGINNING:
+                       if (G_UNLIKELY(msg_it->meta.tc->quirks.barectf_event_before_packet)) {
+                               msg_it->emit_delayed_packet_beginning_msg = true;
+                               /*
+                                * There is no message to return yet as this
+                                * packet beginning message is delayed until we
+                                * decode the first event message of the
+                                * packet.
+                                */
+                               break;
+                       } else {
+                               /* create_msg_packet_beginning() logs errors */
+                               *message = create_msg_packet_beginning(msg_it, false);
+                               if (!*message) {
+                                       status = CTF_MSG_ITER_STATUS_ERROR;
+                               }
+                       }
+
+                       goto end;
+               case STATE_EMIT_MSG_PACKET_END_SINGLE:
+               case STATE_EMIT_MSG_PACKET_END_MULTI:
+                       /* create_msg_packet_end() logs errors */
+                       *message = create_msg_packet_end(msg_it);
+
+                       if (!*message) {
+                               status = CTF_MSG_ITER_STATUS_ERROR;
+                       }
+
+                       goto end;
+               case STATE_EMIT_MSG_STREAM_BEGINNING:
+                       /* create_msg_stream_beginning() logs errors */
+                       *message = create_msg_stream_beginning(msg_it);
+                       msg_it->emit_stream_beginning_message = false;
+                       msg_it->emit_stream_end_message = true;
+
+                       if (!*message) {
+                               status = CTF_MSG_ITER_STATUS_ERROR;
+                       }
+
+                       goto end;
+               case STATE_EMIT_MSG_STREAM_END:
+                       /* create_msg_stream_end() logs errors */
+                       *message = create_msg_stream_end(msg_it);
+                       msg_it->emit_stream_end_message = false;
+
+                       if (!*message) {
+                               status = CTF_MSG_ITER_STATUS_ERROR;
+                       }
+
+                       goto end;
+               case STATE_DONE:
+                       status = CTF_MSG_ITER_STATUS_EOF;
+                       goto end;
+               default:
+                       /* Non-emitting state: continue */
+                       break;
+               }
+       }
+
+end:
+       return status;
+}
+
+static
+enum ctf_msg_iter_status decode_until_state( struct ctf_msg_iter *msg_it,
+               enum state target_state_1, enum state target_state_2)
+{
+       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+       bt_self_component *self_comp = msg_it->self_comp;
+
+       BT_ASSERT_DBG(msg_it);
+
+       do {
+               /*
+                * Check if we reached the state at which we want to stop
+                * decoding.
+                */
+               if (msg_it->state == target_state_1 ||
+                               msg_it->state == target_state_2) {
+                       goto end;
+               }
+
+               status = handle_state(msg_it);
+               if (G_UNLIKELY(status == CTF_MSG_ITER_STATUS_AGAIN)) {
+                       BT_COMP_LOGD_STR("Medium returned CTF_MSG_ITER_STATUS_AGAIN.");
+                       goto end;
+               } else if (G_UNLIKELY(status != CTF_MSG_ITER_STATUS_OK)) {
+                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                               "Cannot handle state: msg-it-addr=%p, state=%s",
+                               msg_it, state_string(msg_it->state));
+                       goto end;
+               }
+
+               switch (msg_it->state) {
+               case STATE_INIT:
+               case STATE_SWITCH_PACKET:
+               case STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN:
+               case STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE:
+               case STATE_AFTER_TRACE_PACKET_HEADER:
+               case STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN:
+               case STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE:
+               case STATE_AFTER_STREAM_PACKET_CONTEXT:
+               case STATE_EMIT_MSG_STREAM_BEGINNING:
+               case STATE_CHECK_EMIT_MSG_DISCARDED_EVENTS:
+               case STATE_EMIT_MSG_DISCARDED_EVENTS:
+               case STATE_CHECK_EMIT_MSG_DISCARDED_PACKETS:
+               case STATE_EMIT_MSG_DISCARDED_PACKETS:
+               case STATE_EMIT_MSG_PACKET_BEGINNING:
+               case STATE_DSCOPE_EVENT_HEADER_BEGIN:
+               case STATE_DSCOPE_EVENT_HEADER_CONTINUE:
+               case STATE_AFTER_EVENT_HEADER:
+               case STATE_DSCOPE_EVENT_COMMON_CONTEXT_BEGIN:
+               case STATE_DSCOPE_EVENT_COMMON_CONTEXT_CONTINUE:
+               case STATE_DSCOPE_EVENT_SPEC_CONTEXT_BEGIN:
+               case STATE_DSCOPE_EVENT_SPEC_CONTEXT_CONTINUE:
+               case STATE_DSCOPE_EVENT_PAYLOAD_BEGIN:
+               case STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE:
+               case STATE_EMIT_MSG_EVENT:
+               case STATE_EMIT_QUEUED_MSG_EVENT:
+               case STATE_SKIP_PACKET_PADDING:
+               case STATE_EMIT_MSG_PACKET_END_MULTI:
+               case STATE_EMIT_MSG_PACKET_END_SINGLE:
+               case STATE_EMIT_QUEUED_MSG_PACKET_END:
+               case STATE_EMIT_MSG_STREAM_END:
+                       break;
+               case STATE_DONE:
+                       /* fall-through */
+               default:
+                       /* We should never get to the STATE_DONE state. */
+                       BT_COMP_LOGF("Unexpected state: msg-it-addr=%p, state=%s",
+                               msg_it, state_string(msg_it->state));
+                       bt_common_abort();
+               }
+       } while (true);
+
+end:
+       return status;
+}
+
+static
+enum ctf_msg_iter_status read_packet_header_context_fields(
+               struct ctf_msg_iter *msg_it)
+{
+       int ret;
+       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+
+       status = decode_until_state(msg_it, STATE_EMIT_MSG_PACKET_BEGINNING, (state) -1);
+       if (status != CTF_MSG_ITER_STATUS_OK) {
+               goto end;
+       }
+
+       ret = set_current_packet_content_sizes(msg_it);
+       if (ret) {
+               status = CTF_MSG_ITER_STATUS_ERROR;
+               goto end;
+       }
+
+end:
+       return status;
+}
+
+BT_HIDDEN
+enum ctf_msg_iter_status ctf_msg_iter_seek(struct ctf_msg_iter *msg_it,
+               off_t offset)
+{
+       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+       enum ctf_msg_iter_medium_status medium_status;
+
+       BT_ASSERT(msg_it);
+       BT_ASSERT(offset >= 0);
+       BT_ASSERT(msg_it->medium.medops.seek);
+
+       medium_status = msg_it->medium.medops.seek(offset, msg_it->medium.data);
+       if (medium_status != CTF_MSG_ITER_MEDIUM_STATUS_OK) {
+               if (medium_status == CTF_MSG_ITER_MEDIUM_STATUS_EOF) {
+                       status = CTF_MSG_ITER_STATUS_EOF;
+               } else {
+                       status = CTF_MSG_ITER_STATUS_ERROR;
+                       goto end;
+               }
+       }
+
+       ctf_msg_iter_reset(msg_it);
+       msg_it->cur_packet_offset = offset;
+
+end:
+       return status;
+}
+
+static
+enum ctf_msg_iter_status clock_snapshot_at_msg_iter_state(
+               struct ctf_msg_iter *msg_it, enum state target_state_1,
+               enum state target_state_2, uint64_t *clock_snapshot)
+{
+       enum ctf_msg_iter_status status = CTF_MSG_ITER_STATUS_OK;
+
+       BT_ASSERT_DBG(msg_it);
+       BT_ASSERT_DBG(clock_snapshot);
+       status = decode_until_state(msg_it, target_state_1, target_state_2);
+       if (status != CTF_MSG_ITER_STATUS_OK) {
+               goto end;
+       }
+
+       *clock_snapshot = msg_it->default_clock_snapshot;
+end:
+       return status;
+}
+
+BT_HIDDEN
+enum ctf_msg_iter_status ctf_msg_iter_curr_packet_first_event_clock_snapshot(
+               struct ctf_msg_iter *msg_it, uint64_t *first_clock_snapshot)
+{
+       return clock_snapshot_at_msg_iter_state(msg_it,
+               STATE_AFTER_EVENT_HEADER, (state) -1, first_clock_snapshot);
+}
+
+BT_HIDDEN
+enum ctf_msg_iter_status ctf_msg_iter_curr_packet_last_event_clock_snapshot(
+               struct ctf_msg_iter *msg_it, uint64_t *last_clock_snapshot)
+{
+       return clock_snapshot_at_msg_iter_state(msg_it,
+               STATE_EMIT_MSG_PACKET_END_SINGLE,
+               STATE_EMIT_MSG_PACKET_END_MULTI, last_clock_snapshot);
+}
+
+BT_HIDDEN
+enum ctf_msg_iter_status ctf_msg_iter_get_packet_properties(
+               struct ctf_msg_iter *msg_it,
+               struct ctf_msg_iter_packet_properties *props)
+{
+       enum ctf_msg_iter_status status;
+
+       BT_ASSERT_DBG(msg_it);
+       BT_ASSERT_DBG(props);
+       status = read_packet_header_context_fields(msg_it);
+       if (status != CTF_MSG_ITER_STATUS_OK) {
+               goto end;
+       }
+
+       props->exp_packet_total_size = msg_it->cur_exp_packet_total_size;
+       props->exp_packet_content_size = msg_it->cur_exp_packet_content_size;
+       props->stream_class_id = (uint64_t) msg_it->cur_stream_class_id;
+       props->data_stream_id = msg_it->cur_data_stream_id;
+       props->snapshots.discarded_events = msg_it->snapshots.discarded_events;
+       props->snapshots.packets = msg_it->snapshots.packets;
+       props->snapshots.beginning_clock = msg_it->snapshots.beginning_clock;
+       props->snapshots.end_clock = msg_it->snapshots.end_clock;
+
+end:
+       return status;
+}
+
+BT_HIDDEN
+void ctf_msg_iter_set_dry_run(struct ctf_msg_iter *msg_it,
+               bool val)
+{
+       msg_it->dry_run = val;
+}
diff --git a/src/plugins/ctf/common/msg-iter/msg-iter.h b/src/plugins/ctf/common/msg-iter/msg-iter.h
deleted file mode 100644 (file)
index edba684..0000000
+++ /dev/null
@@ -1,360 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright (c) 2015-2016 EfficiOS Inc. and Linux Foundation
- * Copyright (c) 2015-2016 Philippe Proulx <pproulx@efficios.com>
- *
- * Babeltrace - CTF message iterator
- */
-
-#ifndef CTF_MSG_ITER_H
-#define CTF_MSG_ITER_H
-
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stddef.h>
-#include <babeltrace2/babeltrace.h>
-#include "common/macros.h"
-
-#include "../metadata/ctf-meta.h"
-
-/**
- * @file ctf-msg-iter.h
- *
- * CTF message iterator
- *
- * This is a common internal API used by CTF source plugins. It allows
- * one to get messages from a user-provided medium.
- */
-
-/**
- * Medium operations status codes.  These use the same values as
- * libbabeltrace2.
- */
-enum ctf_msg_iter_medium_status {
-       /**
-        * End of file.
-        *
-        * The medium function called by the message iterator
-        * function reached the end of the file.
-        */
-       CTF_MSG_ITER_MEDIUM_STATUS_EOF = 1,
-
-       /**
-        * There is no data available right now, try again later.
-        */
-       CTF_MSG_ITER_MEDIUM_STATUS_AGAIN = 11,
-
-       /** General error. */
-       CTF_MSG_ITER_MEDIUM_STATUS_ERROR = -1,
-
-       /** Memory error. */
-       CTF_MSG_ITER_MEDIUM_STATUS_MEMORY_ERROR = -12,
-
-       /** Everything okay. */
-       CTF_MSG_ITER_MEDIUM_STATUS_OK = 0,
-};
-
-/**
- * CTF message iterator API status code.
- */
-enum ctf_msg_iter_status {
-       /**
-        * End of file.
-        *
-        * The medium function called by the message iterator
-        * function reached the end of the file.
-        */
-       CTF_MSG_ITER_STATUS_EOF = CTF_MSG_ITER_MEDIUM_STATUS_EOF,
-
-       /**
-        * There is no data available right now, try again later.
-        *
-        * Some condition resulted in the
-        * ctf_msg_iter_medium_ops::request_bytes() user function not
-        * having access to any data now. You should retry calling the
-        * last called message iterator function once the situation
-        * is resolved.
-        */
-       CTF_MSG_ITER_STATUS_AGAIN = CTF_MSG_ITER_MEDIUM_STATUS_AGAIN,
-
-       /** General error. */
-       CTF_MSG_ITER_STATUS_ERROR = CTF_MSG_ITER_MEDIUM_STATUS_ERROR,
-
-       /** Memory error. */
-       CTF_MSG_ITER_STATUS_MEMORY_ERROR  = CTF_MSG_ITER_MEDIUM_STATUS_MEMORY_ERROR,
-
-       /** Everything okay. */
-       CTF_MSG_ITER_STATUS_OK = CTF_MSG_ITER_MEDIUM_STATUS_OK,
-};
-
-/**
- * Medium operations.
- *
- * Those user functions are called by the message iterator
- * functions to request medium actions.
- */
-struct ctf_msg_iter_medium_ops {
-       /**
-        * Returns the next byte buffer to be used by the binary file
-        * reader to deserialize binary data.
-        *
-        * This function \em must be defined.
-        *
-        * The purpose of this function is to return a buffer of bytes
-        * to the message iterator, of a maximum of \p request_sz
-        * bytes. If this function cannot return a buffer of at least
-        * \p request_sz bytes, it may return a smaller buffer. In
-        * either cases, \p buffer_sz must be set to the returned buffer
-        * size (in bytes).
-        *
-        * The returned buffer's ownership remains the medium, in that
-        * it won't be freed by the message iterator functions. The
-        * returned buffer won't be modified by the message
-        * iterator functions either.
-        *
-        * When this function is called for the first time for a given
-        * file, the offset within the file is considered to be 0. The
-        * next times this function is called, the returned buffer's
-        * byte offset within the complete file must be the previous
-        * offset plus the last returned value of \p buffer_sz by this
-        * medium.
-        *
-        * This function must return one of the following statuses:
-        *
-        *   - <b>#CTF_MSG_ITER_MEDIUM_STATUS_OK</b>: Everything
-        *     is okay, i.e. \p buffer_sz is set to a positive value
-        *     reflecting the number of available bytes in the buffer
-        *     starting at the address written in \p buffer_addr.
-        *   - <b>#CTF_MSG_ITER_MEDIUM_STATUS_AGAIN</b>: No data is
-        *     available right now. In this case, the message
-        *     iterator function called by the user returns
-        *     #CTF_MSG_ITER_STATUS_AGAIN, and it is the user's
-        *     responsibility to make sure enough data becomes available
-        *     before calling the \em same message iterator
-        *     function again to continue the decoding process.
-        *   - <b>#CTF_MSG_ITER_MEDIUM_STATUS_EOF</b>: The end of
-        *     the file was reached, and no more data will ever be
-        *     available for this file. In this case, the message
-        *     iterator function called by the user returns
-        *     #CTF_MSG_ITER_STATUS_EOF. This must \em not be
-        *     returned when returning at least one byte of data to the
-        *     caller, i.e. this must be returned when there's
-        *     absolutely nothing left; should the request size be
-        *     larger than what's left in the file, this function must
-        *     return what's left, setting \p buffer_sz to the number of
-        *     remaining bytes, and return
-        *     #CTF_MSG_ITER_MEDIUM_STATUS_EOF on the \em following
-        *     call.
-        *   - <b>#CTF_MSG_ITER_MEDIUM_STATUS_ERROR</b>: A fatal
-        *     error occurred during this operation. In this case, the
-        *     message iterator function called by the user returns
-        *     #CTF_MSG_ITER_STATUS_ERROR.
-        *
-        * If #CTF_MSG_ITER_MEDIUM_STATUS_OK is not returned, the
-        * values of \p buffer_sz and \p buffer_addr are \em ignored by
-        * the caller.
-        *
-        * @param request_sz    Requested buffer size (bytes)
-        * @param buffer_addr   Returned buffer address
-        * @param buffer_sz     Returned buffer's size (bytes)
-        * @param data          User data
-        * @returns             Status code (see description above)
-        */
-       enum ctf_msg_iter_medium_status (* request_bytes)(size_t request_sz,
-                       uint8_t **buffer_addr, size_t *buffer_sz, void *data);
-
-       /**
-        * Repositions the underlying stream's position.
-        *
-        * This *optional* method repositions the underlying stream
-        * to a given absolute position in the medium.
-        *
-        * @param offset        Offset to use for the given directive
-        * @param data          User data
-        * @returns             One of #ctf_msg_iter_medium_status values
-        */
-       enum ctf_msg_iter_medium_status (* seek)(off_t offset, void *data);
-
-       /**
-        * Called when the message iterator wishes to inform the medium that it
-        * is about to start a new packet.
-        *
-        * After the iterator has called switch_packet, the following call to
-        * request_bytes must return the content at the start of the next
-        * packet.       */
-       enum ctf_msg_iter_medium_status (* switch_packet)(void *data);
-
-       /**
-        * Returns a stream instance (weak reference) for the given
-        * stream class.
-        *
-        * This is called after a packet header is read, and the
-        * corresponding stream class is found by the message
-        * iterator.
-        *
-        * @param stream_class  Stream class of the stream to get
-        * @param stream_id     Stream (instance) ID of the stream
-        *                      to get (-1ULL if not available)
-        * @param data          User data
-        * @returns             Stream instance (weak reference) or
-        *                      \c NULL on error
-        */
-       bt_stream * (* borrow_stream)(bt_stream_class *stream_class,
-                       int64_t stream_id, void *data);
-};
-
-/** CTF message iterator. */
-struct ctf_msg_iter;
-
-/**
- * Creates a CTF message iterator.
- *
- * Upon successful completion, the reference count of \p trace is
- * incremented.
- *
- * @param trace                        Trace to read
- * @param max_request_sz       Maximum buffer size, in bytes, to
- *                             request to
- *                             ctf_msg_iter_medium_ops::request_bytes()
- *                             at a time
- * @param medops               Medium operations
- * @param medops_data          User data (passed to medium operations)
- * @returns                    New CTF message iterator on
- *                             success, or \c NULL on error
- */
-BT_HIDDEN
-struct ctf_msg_iter *ctf_msg_iter_create(
-               struct ctf_trace_class *tc,
-               size_t max_request_sz,
-               struct ctf_msg_iter_medium_ops medops,
-               void *medops_data,
-               bt_logging_level log_level,
-               bt_self_component *self_comp,
-               bt_self_message_iterator *self_msg_iter);
-
-/**
- * Destroys a CTF message iterator, freeing all internal resources.
- *
- * The registered trace's reference count is decremented.
- *
- * @param msg_iter             CTF message iterator
- */
-BT_HIDDEN
-void ctf_msg_iter_destroy(struct ctf_msg_iter *msg_iter);
-
-/**
- * Returns the next message from a CTF message iterator.
- *
- * Upon successful completion, #CTF_MSG_ITER_STATUS_OK is
- * returned, and the next message is written to \p msg.
- * In this case, the caller is responsible for calling
- * bt_message_put() on the returned message.
- *
- * If this function returns #CTF_MSG_ITER_STATUS_AGAIN, the caller
- * should make sure that data becomes available to its medium, and
- * call this function again, until another status is returned.
- *
- * @param msg_iter             CTF message iterator
- * @param message              Returned message if the function's
- *                             return value is #CTF_MSG_ITER_STATUS_OK
- * @returns                    One of #ctf_msg_iter_status values
- */
-BT_HIDDEN
-enum ctf_msg_iter_status ctf_msg_iter_get_next_message(
-               struct ctf_msg_iter *msg_it,
-               const bt_message **message);
-
-struct ctf_msg_iter_packet_properties {
-       int64_t exp_packet_total_size;
-       int64_t exp_packet_content_size;
-       uint64_t stream_class_id;
-       int64_t data_stream_id;
-
-       struct {
-               uint64_t discarded_events;
-               uint64_t packets;
-               uint64_t beginning_clock;
-               uint64_t end_clock;
-       } snapshots;
-};
-
-BT_HIDDEN
-enum ctf_msg_iter_status ctf_msg_iter_get_packet_properties(
-               struct ctf_msg_iter *msg_it,
-               struct ctf_msg_iter_packet_properties *props);
-
-BT_HIDDEN
-enum ctf_msg_iter_status ctf_msg_iter_curr_packet_first_event_clock_snapshot(
-               struct ctf_msg_iter *msg_it, uint64_t *first_event_cs);
-
-BT_HIDDEN
-enum ctf_msg_iter_status ctf_msg_iter_curr_packet_last_event_clock_snapshot(
-               struct ctf_msg_iter *msg_it, uint64_t *last_event_cs);
-
-BT_HIDDEN
-enum ctf_msg_iter_status ctf_msg_iter_seek(
-               struct ctf_msg_iter *msg_it, off_t offset);
-
-/*
- * Resets the iterator so that the next requested medium bytes are
- * assumed to be the first bytes of a new stream. Depending on
- * ctf_msg_iter_set_emit_stream_beginning_message(), the first message
- * which this iterator emits after calling ctf_msg_iter_reset() is of
- * type `CTF_MESSAGE_TYPE_STREAM_BEGINNING`.
- */
-BT_HIDDEN
-void ctf_msg_iter_reset(struct ctf_msg_iter *msg_it);
-
-/*
- * Like ctf_msg_iter_reset(), but preserves stream-dependent state.
- */
-BT_HIDDEN
-void ctf_msg_iter_reset_for_next_stream_file(struct ctf_msg_iter *msg_it);
-
-BT_HIDDEN
-void ctf_msg_iter_set_dry_run(struct ctf_msg_iter *msg_it,
-               bool val);
-
-static inline
-const char *ctf_msg_iter_medium_status_string(
-               enum ctf_msg_iter_medium_status status)
-{
-       switch (status) {
-       case CTF_MSG_ITER_MEDIUM_STATUS_EOF:
-               return "EOF";
-       case CTF_MSG_ITER_MEDIUM_STATUS_AGAIN:
-               return "AGAIN";
-       case CTF_MSG_ITER_MEDIUM_STATUS_ERROR:
-               return "ERROR";
-       case CTF_MSG_ITER_MEDIUM_STATUS_MEMORY_ERROR:
-               return "MEMORY_ERROR";
-       case CTF_MSG_ITER_MEDIUM_STATUS_OK:
-               return "OK";
-       }
-
-       bt_common_abort();
-}
-
-static inline
-const char *ctf_msg_iter_status_string(
-               enum ctf_msg_iter_status status)
-{
-       switch (status) {
-       case CTF_MSG_ITER_STATUS_EOF:
-               return "EOF";
-       case CTF_MSG_ITER_STATUS_AGAIN:
-               return "AGAIN";
-       case CTF_MSG_ITER_STATUS_ERROR:
-               return "ERROR";
-       case CTF_MSG_ITER_STATUS_MEMORY_ERROR:
-               return "MEMORY_ERROR";
-       case CTF_MSG_ITER_STATUS_OK:
-               return "OK";
-       }
-
-       bt_common_abort();
-}
-
-#endif /* CTF_MSG_ITER_H */
diff --git a/src/plugins/ctf/common/msg-iter/msg-iter.hpp b/src/plugins/ctf/common/msg-iter/msg-iter.hpp
new file mode 100644 (file)
index 0000000..393da16
--- /dev/null
@@ -0,0 +1,360 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright (c) 2015-2016 EfficiOS Inc. and Linux Foundation
+ * Copyright (c) 2015-2016 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Babeltrace - CTF message iterator
+ */
+
+#ifndef CTF_MSG_ITER_H
+#define CTF_MSG_ITER_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <babeltrace2/babeltrace.h>
+#include "common/macros.h"
+
+#include "../metadata/ctf-meta.hpp"
+
+/**
+ * @file ctf-msg-iter.h
+ *
+ * CTF message iterator
+ *
+ * This is a common internal API used by CTF source plugins. It allows
+ * one to get messages from a user-provided medium.
+ */
+
+/**
+ * Medium operations status codes.  These use the same values as
+ * libbabeltrace2.
+ */
+enum ctf_msg_iter_medium_status {
+       /**
+        * End of file.
+        *
+        * The medium function called by the message iterator
+        * function reached the end of the file.
+        */
+       CTF_MSG_ITER_MEDIUM_STATUS_EOF = 1,
+
+       /**
+        * There is no data available right now, try again later.
+        */
+       CTF_MSG_ITER_MEDIUM_STATUS_AGAIN = 11,
+
+       /** General error. */
+       CTF_MSG_ITER_MEDIUM_STATUS_ERROR = -1,
+
+       /** Memory error. */
+       CTF_MSG_ITER_MEDIUM_STATUS_MEMORY_ERROR = -12,
+
+       /** Everything okay. */
+       CTF_MSG_ITER_MEDIUM_STATUS_OK = 0,
+};
+
+/**
+ * CTF message iterator API status code.
+ */
+enum ctf_msg_iter_status {
+       /**
+        * End of file.
+        *
+        * The medium function called by the message iterator
+        * function reached the end of the file.
+        */
+       CTF_MSG_ITER_STATUS_EOF = CTF_MSG_ITER_MEDIUM_STATUS_EOF,
+
+       /**
+        * There is no data available right now, try again later.
+        *
+        * Some condition resulted in the
+        * ctf_msg_iter_medium_ops::request_bytes() user function not
+        * having access to any data now. You should retry calling the
+        * last called message iterator function once the situation
+        * is resolved.
+        */
+       CTF_MSG_ITER_STATUS_AGAIN = CTF_MSG_ITER_MEDIUM_STATUS_AGAIN,
+
+       /** General error. */
+       CTF_MSG_ITER_STATUS_ERROR = CTF_MSG_ITER_MEDIUM_STATUS_ERROR,
+
+       /** Memory error. */
+       CTF_MSG_ITER_STATUS_MEMORY_ERROR  = CTF_MSG_ITER_MEDIUM_STATUS_MEMORY_ERROR,
+
+       /** Everything okay. */
+       CTF_MSG_ITER_STATUS_OK = CTF_MSG_ITER_MEDIUM_STATUS_OK,
+};
+
+/**
+ * Medium operations.
+ *
+ * Those user functions are called by the message iterator
+ * functions to request medium actions.
+ */
+struct ctf_msg_iter_medium_ops {
+       /**
+        * Returns the next byte buffer to be used by the binary file
+        * reader to deserialize binary data.
+        *
+        * This function \em must be defined.
+        *
+        * The purpose of this function is to return a buffer of bytes
+        * to the message iterator, of a maximum of \p request_sz
+        * bytes. If this function cannot return a buffer of at least
+        * \p request_sz bytes, it may return a smaller buffer. In
+        * either cases, \p buffer_sz must be set to the returned buffer
+        * size (in bytes).
+        *
+        * The returned buffer's ownership remains the medium, in that
+        * it won't be freed by the message iterator functions. The
+        * returned buffer won't be modified by the message
+        * iterator functions either.
+        *
+        * When this function is called for the first time for a given
+        * file, the offset within the file is considered to be 0. The
+        * next times this function is called, the returned buffer's
+        * byte offset within the complete file must be the previous
+        * offset plus the last returned value of \p buffer_sz by this
+        * medium.
+        *
+        * This function must return one of the following statuses:
+        *
+        *   - <b>#CTF_MSG_ITER_MEDIUM_STATUS_OK</b>: Everything
+        *     is okay, i.e. \p buffer_sz is set to a positive value
+        *     reflecting the number of available bytes in the buffer
+        *     starting at the address written in \p buffer_addr.
+        *   - <b>#CTF_MSG_ITER_MEDIUM_STATUS_AGAIN</b>: No data is
+        *     available right now. In this case, the message
+        *     iterator function called by the user returns
+        *     #CTF_MSG_ITER_STATUS_AGAIN, and it is the user's
+        *     responsibility to make sure enough data becomes available
+        *     before calling the \em same message iterator
+        *     function again to continue the decoding process.
+        *   - <b>#CTF_MSG_ITER_MEDIUM_STATUS_EOF</b>: The end of
+        *     the file was reached, and no more data will ever be
+        *     available for this file. In this case, the message
+        *     iterator function called by the user returns
+        *     #CTF_MSG_ITER_STATUS_EOF. This must \em not be
+        *     returned when returning at least one byte of data to the
+        *     caller, i.e. this must be returned when there's
+        *     absolutely nothing left; should the request size be
+        *     larger than what's left in the file, this function must
+        *     return what's left, setting \p buffer_sz to the number of
+        *     remaining bytes, and return
+        *     #CTF_MSG_ITER_MEDIUM_STATUS_EOF on the \em following
+        *     call.
+        *   - <b>#CTF_MSG_ITER_MEDIUM_STATUS_ERROR</b>: A fatal
+        *     error occurred during this operation. In this case, the
+        *     message iterator function called by the user returns
+        *     #CTF_MSG_ITER_STATUS_ERROR.
+        *
+        * If #CTF_MSG_ITER_MEDIUM_STATUS_OK is not returned, the
+        * values of \p buffer_sz and \p buffer_addr are \em ignored by
+        * the caller.
+        *
+        * @param request_sz    Requested buffer size (bytes)
+        * @param buffer_addr   Returned buffer address
+        * @param buffer_sz     Returned buffer's size (bytes)
+        * @param data          User data
+        * @returns             Status code (see description above)
+        */
+       enum ctf_msg_iter_medium_status (* request_bytes)(size_t request_sz,
+                       uint8_t **buffer_addr, size_t *buffer_sz, void *data);
+
+       /**
+        * Repositions the underlying stream's position.
+        *
+        * This *optional* method repositions the underlying stream
+        * to a given absolute position in the medium.
+        *
+        * @param offset        Offset to use for the given directive
+        * @param data          User data
+        * @returns             One of #ctf_msg_iter_medium_status values
+        */
+       enum ctf_msg_iter_medium_status (* seek)(off_t offset, void *data);
+
+       /**
+        * Called when the message iterator wishes to inform the medium that it
+        * is about to start a new packet.
+        *
+        * After the iterator has called switch_packet, the following call to
+        * request_bytes must return the content at the start of the next
+        * packet.       */
+       enum ctf_msg_iter_medium_status (* switch_packet)(void *data);
+
+       /**
+        * Returns a stream instance (weak reference) for the given
+        * stream class.
+        *
+        * This is called after a packet header is read, and the
+        * corresponding stream class is found by the message
+        * iterator.
+        *
+        * @param stream_class  Stream class of the stream to get
+        * @param stream_id     Stream (instance) ID of the stream
+        *                      to get (-1ULL if not available)
+        * @param data          User data
+        * @returns             Stream instance (weak reference) or
+        *                      \c NULL on error
+        */
+       bt_stream * (* borrow_stream)(bt_stream_class *stream_class,
+                       int64_t stream_id, void *data);
+};
+
+/** CTF message iterator. */
+struct ctf_msg_iter;
+
+/**
+ * Creates a CTF message iterator.
+ *
+ * Upon successful completion, the reference count of \p trace is
+ * incremented.
+ *
+ * @param trace                        Trace to read
+ * @param max_request_sz       Maximum buffer size, in bytes, to
+ *                             request to
+ *                             ctf_msg_iter_medium_ops::request_bytes()
+ *                             at a time
+ * @param medops               Medium operations
+ * @param medops_data          User data (passed to medium operations)
+ * @returns                    New CTF message iterator on
+ *                             success, or \c NULL on error
+ */
+BT_HIDDEN
+struct ctf_msg_iter *ctf_msg_iter_create(
+               struct ctf_trace_class *tc,
+               size_t max_request_sz,
+               struct ctf_msg_iter_medium_ops medops,
+               void *medops_data,
+               bt_logging_level log_level,
+               bt_self_component *self_comp,
+               bt_self_message_iterator *self_msg_iter);
+
+/**
+ * Destroys a CTF message iterator, freeing all internal resources.
+ *
+ * The registered trace's reference count is decremented.
+ *
+ * @param msg_iter             CTF message iterator
+ */
+BT_HIDDEN
+void ctf_msg_iter_destroy(struct ctf_msg_iter *msg_iter);
+
+/**
+ * Returns the next message from a CTF message iterator.
+ *
+ * Upon successful completion, #CTF_MSG_ITER_STATUS_OK is
+ * returned, and the next message is written to \p msg.
+ * In this case, the caller is responsible for calling
+ * bt_message_put() on the returned message.
+ *
+ * If this function returns #CTF_MSG_ITER_STATUS_AGAIN, the caller
+ * should make sure that data becomes available to its medium, and
+ * call this function again, until another status is returned.
+ *
+ * @param msg_iter             CTF message iterator
+ * @param message              Returned message if the function's
+ *                             return value is #CTF_MSG_ITER_STATUS_OK
+ * @returns                    One of #ctf_msg_iter_status values
+ */
+BT_HIDDEN
+enum ctf_msg_iter_status ctf_msg_iter_get_next_message(
+               struct ctf_msg_iter *msg_it,
+               const bt_message **message);
+
+struct ctf_msg_iter_packet_properties {
+       int64_t exp_packet_total_size;
+       int64_t exp_packet_content_size;
+       uint64_t stream_class_id;
+       int64_t data_stream_id;
+
+       struct {
+               uint64_t discarded_events;
+               uint64_t packets;
+               uint64_t beginning_clock;
+               uint64_t end_clock;
+       } snapshots;
+};
+
+BT_HIDDEN
+enum ctf_msg_iter_status ctf_msg_iter_get_packet_properties(
+               struct ctf_msg_iter *msg_it,
+               struct ctf_msg_iter_packet_properties *props);
+
+BT_HIDDEN
+enum ctf_msg_iter_status ctf_msg_iter_curr_packet_first_event_clock_snapshot(
+               struct ctf_msg_iter *msg_it, uint64_t *first_event_cs);
+
+BT_HIDDEN
+enum ctf_msg_iter_status ctf_msg_iter_curr_packet_last_event_clock_snapshot(
+               struct ctf_msg_iter *msg_it, uint64_t *last_event_cs);
+
+BT_HIDDEN
+enum ctf_msg_iter_status ctf_msg_iter_seek(
+               struct ctf_msg_iter *msg_it, off_t offset);
+
+/*
+ * Resets the iterator so that the next requested medium bytes are
+ * assumed to be the first bytes of a new stream. Depending on
+ * ctf_msg_iter_set_emit_stream_beginning_message(), the first message
+ * which this iterator emits after calling ctf_msg_iter_reset() is of
+ * type `CTF_MESSAGE_TYPE_STREAM_BEGINNING`.
+ */
+BT_HIDDEN
+void ctf_msg_iter_reset(struct ctf_msg_iter *msg_it);
+
+/*
+ * Like ctf_msg_iter_reset(), but preserves stream-dependent state.
+ */
+BT_HIDDEN
+void ctf_msg_iter_reset_for_next_stream_file(struct ctf_msg_iter *msg_it);
+
+BT_HIDDEN
+void ctf_msg_iter_set_dry_run(struct ctf_msg_iter *msg_it,
+               bool val);
+
+static inline
+const char *ctf_msg_iter_medium_status_string(
+               enum ctf_msg_iter_medium_status status)
+{
+       switch (status) {
+       case CTF_MSG_ITER_MEDIUM_STATUS_EOF:
+               return "EOF";
+       case CTF_MSG_ITER_MEDIUM_STATUS_AGAIN:
+               return "AGAIN";
+       case CTF_MSG_ITER_MEDIUM_STATUS_ERROR:
+               return "ERROR";
+       case CTF_MSG_ITER_MEDIUM_STATUS_MEMORY_ERROR:
+               return "MEMORY_ERROR";
+       case CTF_MSG_ITER_MEDIUM_STATUS_OK:
+               return "OK";
+       }
+
+       bt_common_abort();
+}
+
+static inline
+const char *ctf_msg_iter_status_string(
+               enum ctf_msg_iter_status status)
+{
+       switch (status) {
+       case CTF_MSG_ITER_STATUS_EOF:
+               return "EOF";
+       case CTF_MSG_ITER_STATUS_AGAIN:
+               return "AGAIN";
+       case CTF_MSG_ITER_STATUS_ERROR:
+               return "ERROR";
+       case CTF_MSG_ITER_STATUS_MEMORY_ERROR:
+               return "MEMORY_ERROR";
+       case CTF_MSG_ITER_STATUS_OK:
+               return "OK";
+       }
+
+       bt_common_abort();
+}
+
+#endif /* CTF_MSG_ITER_H */
diff --git a/src/plugins/ctf/common/print.h b/src/plugins/ctf/common/print.h
deleted file mode 100644 (file)
index a2f744e..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright (c) 2016 Philippe Proulx <pproulx@efficios.com>
- *
- * Define PRINT_PREFIX and PRINT_ERR_STREAM, then include this file.
- */
-
-#ifndef CTF_BTR_PRINT_H
-#define CTF_BTR_PRINT_H
-
-#include <stdio.h>
-#include "common/macros.h"
-
-#define PERR(fmt, ...)                                                 \
-       do {                                                            \
-               if (PRINT_ERR_STREAM) {                                 \
-                       fprintf(PRINT_ERR_STREAM,                       \
-                               "Error: " PRINT_PREFIX ": " fmt,        \
-                               ##__VA_ARGS__);                         \
-               }                                                       \
-       } while (0)
-
-#define PWARN(fmt, ...)                                                        \
-       do {                                                            \
-               if (PRINT_ERR_STREAM) {                                 \
-                       fprintf(PRINT_ERR_STREAM,                       \
-                               "Warning: " PRINT_PREFIX ": " fmt,      \
-                               ##__VA_ARGS__);                         \
-               }                                                       \
-       } while (0)
-
-#define PDBG(fmt, ...)                                                 \
-       do {                                                            \
-               if (babeltrace_debug) {                                 \
-                       fprintf(stderr,                                 \
-                               "Debug: " PRINT_PREFIX ": " fmt,        \
-                               ##__VA_ARGS__);                         \
-               }                                                       \
-       } while (0)
-
-#endif /* CTF_BTR_PRINT_H */
diff --git a/src/plugins/ctf/common/print.hpp b/src/plugins/ctf/common/print.hpp
new file mode 100644 (file)
index 0000000..a2f744e
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright (c) 2016 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Define PRINT_PREFIX and PRINT_ERR_STREAM, then include this file.
+ */
+
+#ifndef CTF_BTR_PRINT_H
+#define CTF_BTR_PRINT_H
+
+#include <stdio.h>
+#include "common/macros.h"
+
+#define PERR(fmt, ...)                                                 \
+       do {                                                            \
+               if (PRINT_ERR_STREAM) {                                 \
+                       fprintf(PRINT_ERR_STREAM,                       \
+                               "Error: " PRINT_PREFIX ": " fmt,        \
+                               ##__VA_ARGS__);                         \
+               }                                                       \
+       } while (0)
+
+#define PWARN(fmt, ...)                                                        \
+       do {                                                            \
+               if (PRINT_ERR_STREAM) {                                 \
+                       fprintf(PRINT_ERR_STREAM,                       \
+                               "Warning: " PRINT_PREFIX ": " fmt,      \
+                               ##__VA_ARGS__);                         \
+               }                                                       \
+       } while (0)
+
+#define PDBG(fmt, ...)                                                 \
+       do {                                                            \
+               if (babeltrace_debug) {                                 \
+                       fprintf(stderr,                                 \
+                               "Debug: " PRINT_PREFIX ": " fmt,        \
+                               ##__VA_ARGS__);                         \
+               }                                                       \
+       } while (0)
+
+#endif /* CTF_BTR_PRINT_H */
index 5303df763a897fe87d908c95c82a4b746c25119b..ef6e3992f5e17c57efb00e37d8c47e4b5d0ccdaa 100644 (file)
@@ -4,14 +4,14 @@ noinst_LTLIBRARIES = libbabeltrace2-plugin-ctf-fs-sink.la
 
 libbabeltrace2_plugin_ctf_fs_sink_la_LIBADD =
 libbabeltrace2_plugin_ctf_fs_sink_la_SOURCES = \
-       fs-sink.c \
-       fs-sink.h \
-       fs-sink-ctf-meta.h \
-       translate-trace-ir-to-ctf-ir.c \
-       translate-trace-ir-to-ctf-ir.h \
-       translate-ctf-ir-to-tsdl.c \
-       translate-ctf-ir-to-tsdl.h \
-       fs-sink-stream.c \
-       fs-sink-stream.h \
-       fs-sink-trace.c \
-       fs-sink-trace.h
+       fs-sink.cpp \
+       fs-sink.hpp \
+       fs-sink-ctf-meta.hpp \
+       translate-trace-ir-to-ctf-ir.cpp \
+       translate-trace-ir-to-ctf-ir.hpp \
+       translate-ctf-ir-to-tsdl.cpp \
+       translate-ctf-ir-to-tsdl.hpp \
+       fs-sink-stream.cpp \
+       fs-sink-stream.hpp \
+       fs-sink-trace.cpp \
+       fs-sink-trace.hpp
diff --git a/src/plugins/ctf/fs-sink/fs-sink-ctf-meta.h b/src/plugins/ctf/fs-sink/fs-sink-ctf-meta.h
deleted file mode 100644 (file)
index e2045da..0000000
+++ /dev/null
@@ -1,940 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2018-2019 Philippe Proulx <pproulx@efficios.com>
- */
-
-#ifndef BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_CTF_META_H
-#define BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_CTF_META_H
-
-#include <babeltrace2/babeltrace.h>
-#include "common/common.h"
-#include "common/assert.h"
-#include "common/uuid.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_BOOL,
-       FS_SINK_CTF_FIELD_CLASS_TYPE_BIT_ARRAY,
-       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_OPTION,
-       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_bool {
-       struct fs_sink_ctf_field_class_bit_array base;
-};
-
-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_option {
-       struct fs_sink_ctf_field_class base;
-       struct fs_sink_ctf_field_class *content_fc;
-       GString *tag_ref;
-};
-
-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;
-
-struct fs_sink_ctf_stream_class {
-       /* Weak */
-       struct fs_sink_ctf_trace *trace;
-
-       /* Weak */
-       const bt_stream_class *ir_sc;
-
-       /* Weak */
-       const bt_clock_class *default_clock_class;
-
-       GString *default_clock_class_name;
-       bool has_packets;
-       bool packets_have_ts_begin;
-       bool packets_have_ts_end;
-       bool has_discarded_events;
-       bool discarded_events_has_ts;
-       bool discarded_packets_has_ts;
-
-       /* 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 {
-       /* Weak */
-       const bt_trace *ir_trace;
-
-       /* Weak */
-       const bt_trace_class *ir_tc;
-
-       bt_uuid_t uuid;
-
-       /* 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 = bt_field_class_type_is(ir_fc_type,
-               BT_FIELD_CLASS_TYPE_SIGNED_INTEGER);
-}
-
-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_bit_array *
-fs_sink_ctf_field_class_bit_array_create(
-               const bt_field_class *ir_fc, uint64_t index_in_parent)
-{
-       struct fs_sink_ctf_field_class_bit_array *fc =
-               g_new0(struct fs_sink_ctf_field_class_bit_array, 1);
-
-       BT_ASSERT(fc);
-       _fs_sink_ctf_field_class_bit_array_init((void *) fc,
-               FS_SINK_CTF_FIELD_CLASS_TYPE_BIT_ARRAY, ir_fc,
-               (unsigned int) bt_field_class_bit_array_get_length(ir_fc),
-               index_in_parent);
-       return fc;
-}
-
-static inline
-struct fs_sink_ctf_field_class_bool *fs_sink_ctf_field_class_bool_create(
-               const bt_field_class *ir_fc, uint64_t index_in_parent)
-{
-       struct fs_sink_ctf_field_class_bool *fc =
-               g_new0(struct fs_sink_ctf_field_class_bool, 1);
-
-       BT_ASSERT(fc);
-
-       /*
-        * CTF 1.8 has no boolean field class type, so this component
-        * translates it to an 8-bit unsigned integer field class.
-        */
-       _fs_sink_ctf_field_class_bit_array_init((void *) fc,
-               FS_SINK_CTF_FIELD_CLASS_TYPE_BOOL, ir_fc,
-               8, index_in_parent);
-       return fc;
-}
-
-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_get_type(ir_fc) ==
-                       BT_FIELD_CLASS_TYPE_SINGLE_PRECISION_REAL ? 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_option *fs_sink_ctf_field_class_option_create_empty(
-               const bt_field_class *ir_fc, uint64_t index_in_parent)
-{
-       struct fs_sink_ctf_field_class_option *fc =
-               g_new0(struct fs_sink_ctf_field_class_option, 1);
-
-       BT_ASSERT(fc);
-       _fs_sink_ctf_field_class_init((void *) fc,
-               FS_SINK_CTF_FIELD_CLASS_TYPE_OPTION, ir_fc,
-               1, index_in_parent);
-       fc->tag_ref = g_string_new(NULL);
-       BT_ASSERT(fc->tag_ref);
-       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_get_type(fc->base.ir_fc) ==
-               BT_FIELD_CLASS_TYPE_VARIANT_WITHOUT_SELECTOR_FIELD;
-       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_array_static_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_get_type(ir_fc) ==
-                       BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY_WITHOUT_LENGTH_FIELD;
-       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_bit_array_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_bool_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_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_option_destroy(
-               struct fs_sink_ctf_field_class_option *fc)
-{
-       BT_ASSERT(fc);
-       _fs_sink_ctf_field_class_fini((void *) fc);
-       fs_sink_ctf_field_class_destroy(fc->content_fc);
-
-       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_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_BOOL:
-               _fs_sink_ctf_field_class_bool_destroy((void *) fc);
-               break;
-       case FS_SINK_CTF_FIELD_CLASS_TYPE_BIT_ARRAY:
-               _fs_sink_ctf_field_class_bit_array_destroy((void *) fc);
-               break;
-       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_OPTION:
-               _fs_sink_ctf_field_class_option_destroy((void *) fc);
-               break;
-       case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT:
-               _fs_sink_ctf_field_class_variant_destroy((void *) fc);
-               break;
-       default:
-               bt_common_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_DBG(fc);
-       BT_ASSERT_DBG(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_DBG(fc);
-       BT_ASSERT_DBG(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_DBG(fc);
-       BT_ASSERT_DBG(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_DBG(fc);
-       BT_ASSERT_DBG(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 *trace,
-               const bt_stream_class *ir_sc)
-{
-       struct fs_sink_ctf_stream_class *sc =
-               g_new0(struct fs_sink_ctf_stream_class, 1);
-
-       BT_ASSERT(trace);
-       BT_ASSERT(ir_sc);
-       BT_ASSERT(sc);
-       sc->trace = trace;
-       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);
-       sc->has_packets = bt_stream_class_supports_packets(ir_sc);
-       sc->packets_have_ts_begin =
-               bt_stream_class_packets_have_beginning_default_clock_snapshot(
-                       ir_sc);
-       sc->packets_have_ts_end =
-               bt_stream_class_packets_have_end_default_clock_snapshot(ir_sc);
-       sc->has_discarded_events =
-               bt_stream_class_supports_discarded_events(ir_sc);
-
-       if (sc->has_discarded_events) {
-               sc->discarded_events_has_ts =
-                       bt_stream_class_discarded_events_have_default_clock_snapshots(
-                               ir_sc);
-       }
-
-       if (bt_stream_class_supports_discarded_packets(ir_sc)) {
-               sc->discarded_packets_has_ts =
-                       bt_stream_class_discarded_packets_have_default_clock_snapshots(
-                               ir_sc);
-       }
-
-       g_ptr_array_add(trace->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_destroy(struct fs_sink_ctf_trace *trace)
-{
-       if (!trace) {
-               return;
-       }
-
-       if (trace->stream_classes) {
-               g_ptr_array_free(trace->stream_classes, TRUE);
-               trace->stream_classes = NULL;
-       }
-
-       g_free(trace);
-}
-
-static inline
-struct fs_sink_ctf_trace *fs_sink_ctf_trace_create(const bt_trace *ir_trace)
-{
-       struct fs_sink_ctf_trace *trace =
-               g_new0(struct fs_sink_ctf_trace, 1);
-
-       BT_ASSERT(trace);
-
-       bt_uuid_generate(trace->uuid);
-
-       trace->ir_trace = ir_trace;
-       trace->ir_tc = bt_trace_borrow_class_const(ir_trace);
-       trace->stream_classes = g_ptr_array_new_with_free_func(
-               (GDestroyNotify) fs_sink_ctf_stream_class_destroy);
-       BT_ASSERT(trace->stream_classes);
-
-       return trace;
-}
-
-#endif /* BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_CTF_META_H */
diff --git a/src/plugins/ctf/fs-sink/fs-sink-ctf-meta.hpp b/src/plugins/ctf/fs-sink/fs-sink-ctf-meta.hpp
new file mode 100644 (file)
index 0000000..8e30ab9
--- /dev/null
@@ -0,0 +1,1043 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2018-2019 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#ifndef BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_CTF_META_H
+#define BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_CTF_META_H
+
+#include <babeltrace2/babeltrace.h>
+#include "common/common.h"
+#include "common/assert.h"
+#include "common/uuid.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_BOOL,
+       FS_SINK_CTF_FIELD_CLASS_TYPE_BIT_ARRAY,
+       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_OPTION,
+       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_bool {
+       struct fs_sink_ctf_field_class_bit_array base;
+};
+
+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_option {
+       struct fs_sink_ctf_field_class base;
+       struct fs_sink_ctf_field_class *content_fc;
+       GString *tag_ref;
+};
+
+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;
+};
+
+static inline
+fs_sink_ctf_field_class_bit_array *fs_sink_ctf_field_class_as_bit_array(
+               fs_sink_ctf_field_class *fc)
+{
+       BT_ASSERT_DBG(!fc || (fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_BIT_ARRAY ||
+               fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_INT ||
+               fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_BOOL ||
+               fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_FLOAT));
+
+       return (fs_sink_ctf_field_class_bit_array *) fc;
+}
+
+static inline
+fs_sink_ctf_field_class_bool *fs_sink_ctf_field_class_as_bool(
+               fs_sink_ctf_field_class *fc)
+{
+       BT_ASSERT_DBG(!fc || fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_BOOL);
+
+       return (fs_sink_ctf_field_class_bool *) fc;
+}
+
+static inline
+fs_sink_ctf_field_class_int *fs_sink_ctf_field_class_as_int(
+               fs_sink_ctf_field_class *fc)
+{
+       BT_ASSERT_DBG(!fc || fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_INT);
+
+       return (fs_sink_ctf_field_class_int *) fc;
+}
+
+static inline
+fs_sink_ctf_field_class_float *fs_sink_ctf_field_class_as_float(
+               fs_sink_ctf_field_class *fc)
+{
+       BT_ASSERT_DBG(!fc || fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_FLOAT);
+
+       return (fs_sink_ctf_field_class_float *) fc;
+}
+
+static inline
+fs_sink_ctf_field_class_string *fs_sink_ctf_field_class_as_string(
+               fs_sink_ctf_field_class *fc)
+{
+       BT_ASSERT_DBG(!fc || fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_STRING);
+
+       return (fs_sink_ctf_field_class_string *) fc;
+}
+
+static inline
+fs_sink_ctf_field_class_struct *fs_sink_ctf_field_class_as_struct(
+               fs_sink_ctf_field_class *fc)
+{
+       BT_ASSERT_DBG(!fc || fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT);
+
+       return (fs_sink_ctf_field_class_struct *) fc;
+}
+
+static inline
+fs_sink_ctf_field_class_array_base *fs_sink_ctf_field_class_as_array_base(
+               fs_sink_ctf_field_class *fc)
+{
+       BT_ASSERT_DBG(!fc || (fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY ||
+               fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE));
+
+       return (fs_sink_ctf_field_class_array_base *) fc;
+}
+
+static inline
+fs_sink_ctf_field_class_array *fs_sink_ctf_field_class_as_array(
+               fs_sink_ctf_field_class *fc)
+{
+       BT_ASSERT_DBG(!fc || fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY);
+
+       return (fs_sink_ctf_field_class_array *) fc;
+}
+
+static inline
+fs_sink_ctf_field_class_sequence *fs_sink_ctf_field_class_as_sequence(
+               fs_sink_ctf_field_class *fc)
+{
+       BT_ASSERT_DBG(!fc || fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE);
+
+       return (fs_sink_ctf_field_class_sequence *) fc;
+}
+
+static inline
+fs_sink_ctf_field_class_option *fs_sink_ctf_field_class_as_option(
+               fs_sink_ctf_field_class *fc)
+{
+       BT_ASSERT_DBG(!fc || fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_OPTION);
+
+       return (fs_sink_ctf_field_class_option *) fc;
+}
+
+static inline
+fs_sink_ctf_field_class_variant *fs_sink_ctf_field_class_as_variant(
+               fs_sink_ctf_field_class *fc)
+{
+       BT_ASSERT_DBG(!fc || fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT);
+
+       return (fs_sink_ctf_field_class_variant *) fc;
+}
+
+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;
+
+struct fs_sink_ctf_stream_class {
+       /* Weak */
+       struct fs_sink_ctf_trace *trace;
+
+       /* Weak */
+       const bt_stream_class *ir_sc;
+
+       /* Weak */
+       const bt_clock_class *default_clock_class;
+
+       GString *default_clock_class_name;
+       bool has_packets;
+       bool packets_have_ts_begin;
+       bool packets_have_ts_end;
+       bool has_discarded_events;
+       bool discarded_events_has_ts;
+       bool discarded_packets_has_ts;
+
+       /* 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 {
+       /* Weak */
+       const bt_trace *ir_trace;
+
+       /* Weak */
+       const bt_trace_class *ir_tc;
+
+       bt_uuid_t uuid;
+
+       /* 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(&fc->base, 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(&fc->base, type, ir_fc,
+               (unsigned int) bt_field_class_integer_get_field_value_range(
+                       ir_fc),
+               index_in_parent);
+       fc->is_signed = bt_field_class_type_is(ir_fc_type,
+               BT_FIELD_CLASS_TYPE_SIGNED_INTEGER);
+}
+
+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_bit_array *
+fs_sink_ctf_field_class_bit_array_create(
+               const bt_field_class *ir_fc, uint64_t index_in_parent)
+{
+       struct fs_sink_ctf_field_class_bit_array *fc =
+               g_new0(struct fs_sink_ctf_field_class_bit_array, 1);
+
+       BT_ASSERT(fc);
+       _fs_sink_ctf_field_class_bit_array_init(fc,
+               FS_SINK_CTF_FIELD_CLASS_TYPE_BIT_ARRAY, ir_fc,
+               (unsigned int) bt_field_class_bit_array_get_length(ir_fc),
+               index_in_parent);
+       return fc;
+}
+
+static inline
+struct fs_sink_ctf_field_class_bool *fs_sink_ctf_field_class_bool_create(
+               const bt_field_class *ir_fc, uint64_t index_in_parent)
+{
+       struct fs_sink_ctf_field_class_bool *fc =
+               g_new0(struct fs_sink_ctf_field_class_bool, 1);
+
+       BT_ASSERT(fc);
+
+       /*
+        * CTF 1.8 has no boolean field class type, so this component
+        * translates it to an 8-bit unsigned integer field class.
+        */
+       _fs_sink_ctf_field_class_bit_array_init(&fc->base,
+               FS_SINK_CTF_FIELD_CLASS_TYPE_BOOL, ir_fc,
+               8, index_in_parent);
+       return fc;
+}
+
+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(&fc->base,
+               FS_SINK_CTF_FIELD_CLASS_TYPE_FLOAT,
+               ir_fc,
+               bt_field_class_get_type(ir_fc) ==
+                       BT_FIELD_CLASS_TYPE_SINGLE_PRECISION_REAL ? 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(&fc->base,
+               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(&fc->base,
+               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_option *fs_sink_ctf_field_class_option_create_empty(
+               const bt_field_class *ir_fc, uint64_t index_in_parent)
+{
+       struct fs_sink_ctf_field_class_option *fc =
+               g_new0(struct fs_sink_ctf_field_class_option, 1);
+
+       BT_ASSERT(fc);
+       _fs_sink_ctf_field_class_init(&fc->base,
+               FS_SINK_CTF_FIELD_CLASS_TYPE_OPTION, ir_fc,
+               1, index_in_parent);
+       fc->tag_ref = g_string_new(NULL);
+       BT_ASSERT(fc->tag_ref);
+       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(&fc->base,
+               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_get_type(fc->base.ir_fc) ==
+               BT_FIELD_CLASS_TYPE_VARIANT_WITHOUT_SELECTOR_FIELD;
+       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(&fc->base.base,
+               FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY, ir_fc,
+               1, index_in_parent);
+       fc->length = bt_field_class_array_static_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(&fc->base.base,
+               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_get_type(ir_fc) ==
+                       BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY_WITHOUT_LENGTH_FIELD;
+       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_bit_array_destroy(
+               struct fs_sink_ctf_field_class_bit_array *fc)
+{
+       BT_ASSERT(fc);
+       _fs_sink_ctf_field_class_fini(&fc->base);
+       g_free(fc);
+}
+
+static inline
+void _fs_sink_ctf_field_class_bool_destroy(
+               struct fs_sink_ctf_field_class_bool *fc)
+{
+       BT_ASSERT(fc);
+       _fs_sink_ctf_field_class_fini(&fc->base.base);
+       g_free(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(&fc->base.base);
+       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(&fc->base.base);
+       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(&fc->base);
+       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(&fc->base);
+
+       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(&fc->base);
+       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(&fc->base);
+       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(&fc->base);
+
+       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_option_destroy(
+               struct fs_sink_ctf_field_class_option *fc)
+{
+       BT_ASSERT(fc);
+       _fs_sink_ctf_field_class_fini(&fc->base);
+       fs_sink_ctf_field_class_destroy(fc->content_fc);
+
+       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_variant_destroy(
+               struct fs_sink_ctf_field_class_variant *fc)
+{
+       BT_ASSERT(fc);
+       _fs_sink_ctf_field_class_fini(&fc->base);
+
+       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_BOOL:
+               _fs_sink_ctf_field_class_bool_destroy(fs_sink_ctf_field_class_as_bool(fc));
+               break;
+       case FS_SINK_CTF_FIELD_CLASS_TYPE_BIT_ARRAY:
+               _fs_sink_ctf_field_class_bit_array_destroy(fs_sink_ctf_field_class_as_bit_array(fc));
+               break;
+       case FS_SINK_CTF_FIELD_CLASS_TYPE_INT:
+               _fs_sink_ctf_field_class_int_destroy(fs_sink_ctf_field_class_as_int(fc));
+               break;
+       case FS_SINK_CTF_FIELD_CLASS_TYPE_FLOAT:
+               _fs_sink_ctf_field_class_float_destroy(fs_sink_ctf_field_class_as_float(fc));
+               break;
+       case FS_SINK_CTF_FIELD_CLASS_TYPE_STRING:
+               _fs_sink_ctf_field_class_string_destroy(fs_sink_ctf_field_class_as_string(fc));
+               break;
+       case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT:
+               _fs_sink_ctf_field_class_struct_destroy(fs_sink_ctf_field_class_as_struct(fc));
+               break;
+       case FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY:
+               _fs_sink_ctf_field_class_array_destroy(fs_sink_ctf_field_class_as_array(fc));
+               break;
+       case FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE:
+               _fs_sink_ctf_field_class_sequence_destroy(fs_sink_ctf_field_class_as_sequence(fc));
+               break;
+       case FS_SINK_CTF_FIELD_CLASS_TYPE_OPTION:
+               _fs_sink_ctf_field_class_option_destroy(fs_sink_ctf_field_class_as_option(fc));
+               break;
+       case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT:
+               _fs_sink_ctf_field_class_variant_destroy(fs_sink_ctf_field_class_as_variant(fc));
+               break;
+       default:
+               bt_common_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_DBG(fc);
+       BT_ASSERT_DBG(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_DBG(fc);
+       BT_ASSERT_DBG(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 = fs_sink_ctf_field_class_as_int(
+               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_DBG(fc);
+       BT_ASSERT_DBG(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_DBG(fc);
+       BT_ASSERT_DBG(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 *trace,
+               const bt_stream_class *ir_sc)
+{
+       struct fs_sink_ctf_stream_class *sc =
+               g_new0(struct fs_sink_ctf_stream_class, 1);
+
+       BT_ASSERT(trace);
+       BT_ASSERT(ir_sc);
+       BT_ASSERT(sc);
+       sc->trace = trace;
+       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);
+       sc->has_packets = bt_stream_class_supports_packets(ir_sc);
+       sc->packets_have_ts_begin =
+               bt_stream_class_packets_have_beginning_default_clock_snapshot(
+                       ir_sc);
+       sc->packets_have_ts_end =
+               bt_stream_class_packets_have_end_default_clock_snapshot(ir_sc);
+       sc->has_discarded_events =
+               bt_stream_class_supports_discarded_events(ir_sc);
+
+       if (sc->has_discarded_events) {
+               sc->discarded_events_has_ts =
+                       bt_stream_class_discarded_events_have_default_clock_snapshots(
+                               ir_sc);
+       }
+
+       if (bt_stream_class_supports_discarded_packets(ir_sc)) {
+               sc->discarded_packets_has_ts =
+                       bt_stream_class_discarded_packets_have_default_clock_snapshots(
+                               ir_sc);
+       }
+
+       g_ptr_array_add(trace->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_destroy(struct fs_sink_ctf_trace *trace)
+{
+       if (!trace) {
+               return;
+       }
+
+       if (trace->stream_classes) {
+               g_ptr_array_free(trace->stream_classes, TRUE);
+               trace->stream_classes = NULL;
+       }
+
+       g_free(trace);
+}
+
+static inline
+struct fs_sink_ctf_trace *fs_sink_ctf_trace_create(const bt_trace *ir_trace)
+{
+       struct fs_sink_ctf_trace *trace =
+               g_new0(struct fs_sink_ctf_trace, 1);
+
+       BT_ASSERT(trace);
+
+       bt_uuid_generate(trace->uuid);
+
+       trace->ir_trace = ir_trace;
+       trace->ir_tc = bt_trace_borrow_class_const(ir_trace);
+       trace->stream_classes = g_ptr_array_new_with_free_func(
+               (GDestroyNotify) fs_sink_ctf_stream_class_destroy);
+       BT_ASSERT(trace->stream_classes);
+
+       return trace;
+}
+
+#endif /* BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_CTF_META_H */
diff --git a/src/plugins/ctf/fs-sink/fs-sink-stream.c b/src/plugins/ctf/fs-sink/fs-sink-stream.c
deleted file mode 100644 (file)
index b14e6d5..0000000
+++ /dev/null
@@ -1,724 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
- */
-
-#define BT_COMP_LOG_SELF_COMP (stream->trace->fs_sink->self_comp)
-#define BT_LOG_OUTPUT_LEVEL (stream->log_level)
-#define BT_LOG_TAG "PLUGIN/SINK.CTF.FS/STREAM"
-#include "logging/comp-logging.h"
-
-#include <babeltrace2/babeltrace.h>
-#include <stdio.h>
-#include <stdbool.h>
-#include <glib.h>
-#include "common/assert.h"
-#include "ctfser/ctfser.h"
-#include "compat/endian.h"
-
-#include "fs-sink.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_printf(name, "%s-%u", san_base->str, 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->log_level = trace->log_level;
-       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->fs_sink,
-               trace->trace, 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,
-               stream->log_level);
-       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_bool_field(struct fs_sink_stream *stream,
-               struct fs_sink_ctf_field_class_int *fc, const bt_field *field)
-{
-       /*
-        * CTF 1.8 has no boolean field class type, so this component
-        * translates this boolean field to an 8-bit unsigned integer
-        * field which has the value 0 (false) or 1 (true).
-        */
-       return bt_ctfser_write_unsigned_int(&stream->ctfser,
-               bt_field_bool_get_value(field) ? 1 : 0,
-               fc->base.base.alignment, fc->base.size, BYTE_ORDER);
-}
-
-static inline
-int write_bit_array_field(struct fs_sink_stream *stream,
-               struct fs_sink_ctf_field_class_bit_array *fc,
-               const bt_field *field)
-{
-       /*
-        * CTF 1.8 has no bit array field class type, so this component
-        * translates this bit array field to an unsigned integer field.
-        */
-       return bt_ctfser_write_unsigned_int(&stream->ctfser,
-               bt_field_bit_array_get_value_as_integer(field),
-               fc->base.alignment, fc->size, BYTE_ORDER);
-}
-
-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_integer_signed_get_value(field),
-                       fc->base.base.alignment, fc->base.size, BYTE_ORDER);
-       } else {
-               ret = bt_ctfser_write_unsigned_int(&stream->ctfser,
-                       bt_field_integer_unsigned_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;
-
-       if (fc->base.size == 32) {
-               val = (double) bt_field_real_single_precision_get_value(field);
-               ret = bt_ctfser_write_float32(&stream->ctfser, val,
-                       fc->base.base.alignment, BYTE_ORDER);
-       } else {
-               val = bt_field_real_double_precision_get_value(field);
-               ret = bt_ctfser_write_float64(&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 (G_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 (G_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 (G_LIKELY(align_struct)) {
-               ret = bt_ctfser_align_offset_in_current_packet(&stream->ctfser,
-                       fc->base.alignment);
-               if (G_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 (G_UNLIKELY(ret)) {
-                       goto end;
-               }
-       }
-
-end:
-       return ret;
-}
-
-static inline
-int write_option_field(struct fs_sink_stream *stream,
-               struct fs_sink_ctf_field_class_option *fc,
-               const bt_field *field)
-{
-       int ret;
-       const bt_field *content_field =
-               bt_field_option_borrow_field_const(field);
-
-       ret = bt_ctfser_write_unsigned_int(&stream->ctfser,
-               content_field ? 1 : 0, 8, 8, BYTE_ORDER);
-       if (G_UNLIKELY(ret)) {
-               goto end;
-       }
-
-       /*
-        * CTF 1.8 has no option field class type, so this component
-        * translates the option field class to a variant field class
-        * where the options are:
-        *
-        * * An empty structure field class (field occupies 0 bits).
-        * * The optional field class itself.
-        *
-        * If `content_field` is `NULL`, do not write anything (empty
-        * structure).
-        */
-       if (content_field) {
-               ret = write_field(stream, fc->content_fc, content_field);
-       }
-
-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_index(field);
-       int ret;
-
-       if (fc->tag_is_before) {
-               ret = bt_ctfser_write_unsigned_int(&stream->ctfser,
-                       opt_index, 8, 16, BYTE_ORDER);
-               if (G_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_BOOL:
-               ret = write_bool_field(stream, (void *) fc, field);
-               break;
-       case FS_SINK_CTF_FIELD_CLASS_TYPE_BIT_ARRAY:
-               ret = write_bit_array_field(stream, (void *) fc, field);
-               break;
-       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_OPTION:
-               ret = write_option_field(stream, (void *) fc, field);
-               break;
-       case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT:
-               ret = write_variant_field(stream, (void *) fc, field);
-               break;
-       default:
-               bt_common_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 (G_UNLIKELY(ret)) {
-               goto end;
-       }
-
-       /* Time */
-       if (stream->sc->default_clock_class) {
-               BT_ASSERT_DBG(cs);
-               ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser,
-                       bt_clock_snapshot_get_value(cs), 8, 64, BYTE_ORDER);
-               if (G_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 (G_UNLIKELY(ret)) {
-               goto end;
-       }
-
-       /* Common context */
-       if (stream->sc->event_common_context_fc) {
-               field = bt_event_borrow_common_context_field_const(event);
-               BT_ASSERT_DBG(field);
-               ret = write_struct_field(stream,
-                       (void *) stream->sc->event_common_context_fc,
-                       field, true);
-               if (G_UNLIKELY(ret)) {
-                       goto end;
-               }
-       }
-
-       /* Specific context */
-       if (ec->spec_context_fc) {
-               field = bt_event_borrow_specific_context_field_const(event);
-               BT_ASSERT_DBG(field);
-               ret = write_struct_field(stream, (void *) ec->spec_context_fc,
-                       field, true);
-               if (G_UNLIKELY(ret)) {
-                       goto end;
-               }
-       }
-
-       /* Specific context */
-       if (ec->payload_fc) {
-               field = bt_event_borrow_payload_field_const(event);
-               BT_ASSERT_DBG(field);
-               ret = write_struct_field(stream, (void *) ec->payload_fc,
-                       field, true);
-               if (G_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->packets_have_ts_begin) {
-               /* 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;
-               }
-       }
-
-       if (stream->sc->packets_have_ts_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;
-               }
-       }
-
-       if (stream->sc->has_discarded_events) {
-               /* 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_ASSERT(stream->packet_state.packet);
-               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 */
-       ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser,
-               UINT64_C(0xc1fc1fc1), 8, 32, BYTE_ORDER);
-       if (ret) {
-               BT_COMP_LOGE("Error writing packet header magic: stream-file-name=%s",
-                       stream->file_name->str);
-               goto end;
-       }
-
-       /* Packet header: UUID */
-       for (i = 0; i < BT_UUID_LEN; i++) {
-               ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser,
-                       (uint64_t) stream->sc->trace->uuid[i], 8, 8, BYTE_ORDER);
-               if (ret) {
-                       BT_COMP_LOGE("Error writing packet header UUID: stream-file-name=%s",
-                               stream->file_name->str);
-                       goto end;
-               }
-       }
-
-       /* Packet header: stream class ID */
-       ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser,
-               bt_stream_class_get_id(stream->sc->ir_sc), 8, 64, BYTE_ORDER);
-       if (ret) {
-               BT_COMP_LOGE("Error writing packet header stream class id: "
-                       "stream-file-name=%s, stream-class-id=%"PRIu64,
-                       stream->file_name->str,
-                       bt_stream_class_get_id(stream->sc->ir_sc));
-               goto end;
-       }
-
-       /* Packet header: stream ID */
-       ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser,
-               bt_stream_get_id(stream->ir_stream), 8, 64, BYTE_ORDER);
-       if (ret) {
-               BT_COMP_LOGE("Error writing packet header stream id: "
-                       "stream-file-name=%s, stream-id=%"PRIu64,
-                       stream->file_name->str,
-                       bt_stream_get_id(stream->ir_stream));
-               goto end;
-       }
-
-       /* 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;
-}
diff --git a/src/plugins/ctf/fs-sink/fs-sink-stream.cpp b/src/plugins/ctf/fs-sink/fs-sink-stream.cpp
new file mode 100644 (file)
index 0000000..da0a608
--- /dev/null
@@ -0,0 +1,736 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#define BT_COMP_LOG_SELF_COMP (stream->trace->fs_sink->self_comp)
+#define BT_LOG_OUTPUT_LEVEL (stream->log_level)
+#define BT_LOG_TAG "PLUGIN/SINK.CTF.FS/STREAM"
+#include "logging/comp-logging.h"
+
+#include <babeltrace2/babeltrace.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <glib.h>
+#include "common/assert.h"
+#include "ctfser/ctfser.h"
+#include "compat/endian.h"
+
+#include "fs-sink.hpp"
+#include "fs-sink-trace.hpp"
+#include "fs-sink-stream.hpp"
+#include "translate-trace-ir-to-ctf-ir.hpp"
+
+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 = (fs_sink_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_printf(name, "%s-%u", san_base->str, 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->log_level = trace->log_level;
+       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->fs_sink,
+               trace->trace, 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,
+               stream->log_level);
+       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_bool_field(struct fs_sink_stream *stream,
+               struct fs_sink_ctf_field_class_bool *fc, const bt_field *field)
+{
+       /*
+        * CTF 1.8 has no boolean field class type, so this component
+        * translates this boolean field to an 8-bit unsigned integer
+        * field which has the value 0 (false) or 1 (true).
+        */
+       return bt_ctfser_write_unsigned_int(&stream->ctfser,
+               bt_field_bool_get_value(field) ? 1 : 0,
+               fc->base.base.alignment, fc->base.size, BYTE_ORDER);
+}
+
+static inline
+int write_bit_array_field(struct fs_sink_stream *stream,
+               struct fs_sink_ctf_field_class_bit_array *fc,
+               const bt_field *field)
+{
+       /*
+        * CTF 1.8 has no bit array field class type, so this component
+        * translates this bit array field to an unsigned integer field.
+        */
+       return bt_ctfser_write_unsigned_int(&stream->ctfser,
+               bt_field_bit_array_get_value_as_integer(field),
+               fc->base.alignment, fc->size, BYTE_ORDER);
+}
+
+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_integer_signed_get_value(field),
+                       fc->base.base.alignment, fc->base.size, BYTE_ORDER);
+       } else {
+               ret = bt_ctfser_write_unsigned_int(&stream->ctfser,
+                       bt_field_integer_unsigned_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;
+
+       if (fc->base.size == 32) {
+               val = (double) bt_field_real_single_precision_get_value(field);
+               ret = bt_ctfser_write_float32(&stream->ctfser, val,
+                       fc->base.base.alignment, BYTE_ORDER);
+       } else {
+               val = bt_field_real_double_precision_get_value(field);
+               ret = bt_ctfser_write_float64(&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_base_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 (G_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 (G_UNLIKELY(ret)) {
+                       goto end;
+               }
+       }
+
+       ret = write_array_base_field_elements(stream, &fc->base, 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 (G_LIKELY(align_struct)) {
+               ret = bt_ctfser_align_offset_in_current_packet(&stream->ctfser,
+                       fc->base.alignment);
+               if (G_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 (G_UNLIKELY(ret)) {
+                       goto end;
+               }
+       }
+
+end:
+       return ret;
+}
+
+static inline
+int write_option_field(struct fs_sink_stream *stream,
+               struct fs_sink_ctf_field_class_option *fc,
+               const bt_field *field)
+{
+       int ret;
+       const bt_field *content_field =
+               bt_field_option_borrow_field_const(field);
+
+       ret = bt_ctfser_write_unsigned_int(&stream->ctfser,
+               content_field ? 1 : 0, 8, 8, BYTE_ORDER);
+       if (G_UNLIKELY(ret)) {
+               goto end;
+       }
+
+       /*
+        * CTF 1.8 has no option field class type, so this component
+        * translates the option field class to a variant field class
+        * where the options are:
+        *
+        * * An empty structure field class (field occupies 0 bits).
+        * * The optional field class itself.
+        *
+        * If `content_field` is `NULL`, do not write anything (empty
+        * structure).
+        */
+       if (content_field) {
+               ret = write_field(stream, fc->content_fc, content_field);
+       }
+
+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_index(field);
+       int ret;
+
+       if (fc->tag_is_before) {
+               ret = bt_ctfser_write_unsigned_int(&stream->ctfser,
+                       opt_index, 8, 16, BYTE_ORDER);
+               if (G_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_BOOL:
+               ret = write_bool_field(stream,
+                       fs_sink_ctf_field_class_as_bool(fc), field);
+               break;
+       case FS_SINK_CTF_FIELD_CLASS_TYPE_BIT_ARRAY:
+               ret = write_bit_array_field(stream,
+                       fs_sink_ctf_field_class_as_bit_array(fc), field);
+               break;
+       case FS_SINK_CTF_FIELD_CLASS_TYPE_INT:
+               ret = write_int_field(stream,
+                       fs_sink_ctf_field_class_as_int(fc), field);
+               break;
+       case FS_SINK_CTF_FIELD_CLASS_TYPE_FLOAT:
+               ret = write_float_field(stream,
+                       fs_sink_ctf_field_class_as_float(fc), field);
+               break;
+       case FS_SINK_CTF_FIELD_CLASS_TYPE_STRING:
+               ret = write_string_field(stream,
+                       fs_sink_ctf_field_class_as_string(fc), field);
+               break;
+       case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT:
+               ret = write_struct_field(stream,
+                       fs_sink_ctf_field_class_as_struct(fc), field, true);
+               break;
+       case FS_SINK_CTF_FIELD_CLASS_TYPE_ARRAY:
+               ret = write_array_base_field_elements(stream,
+                       fs_sink_ctf_field_class_as_array_base(fc), field);
+               break;
+       case FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE:
+               ret = write_sequence_field(stream,
+                       fs_sink_ctf_field_class_as_sequence(fc), field);
+               break;
+       case FS_SINK_CTF_FIELD_CLASS_TYPE_OPTION:
+               ret = write_option_field(stream,
+                       fs_sink_ctf_field_class_as_option(fc), field);
+               break;
+       case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT:
+               ret = write_variant_field(stream,
+                       fs_sink_ctf_field_class_as_variant(fc), field);
+               break;
+       default:
+               bt_common_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 (G_UNLIKELY(ret)) {
+               goto end;
+       }
+
+       /* Time */
+       if (stream->sc->default_clock_class) {
+               BT_ASSERT_DBG(cs);
+               ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser,
+                       bt_clock_snapshot_get_value(cs), 8, 64, BYTE_ORDER);
+               if (G_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 (G_UNLIKELY(ret)) {
+               goto end;
+       }
+
+       /* Common context */
+       if (stream->sc->event_common_context_fc) {
+               field = bt_event_borrow_common_context_field_const(event);
+               BT_ASSERT_DBG(field);
+               ret = write_struct_field(stream,
+                       fs_sink_ctf_field_class_as_struct(stream->sc->event_common_context_fc),
+                       field, true);
+               if (G_UNLIKELY(ret)) {
+                       goto end;
+               }
+       }
+
+       /* Specific context */
+       if (ec->spec_context_fc) {
+               field = bt_event_borrow_specific_context_field_const(event);
+               BT_ASSERT_DBG(field);
+               ret = write_struct_field(stream,
+                       fs_sink_ctf_field_class_as_struct(ec->spec_context_fc),
+                       field, true);
+               if (G_UNLIKELY(ret)) {
+                       goto end;
+               }
+       }
+
+       /* Specific context */
+       if (ec->payload_fc) {
+               field = bt_event_borrow_payload_field_const(event);
+               BT_ASSERT_DBG(field);
+               ret = write_struct_field(stream,
+                       fs_sink_ctf_field_class_as_struct(ec->payload_fc),
+                       field, true);
+               if (G_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->packets_have_ts_begin) {
+               /* 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;
+               }
+       }
+
+       if (stream->sc->packets_have_ts_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;
+               }
+       }
+
+       if (stream->sc->has_discarded_events) {
+               /* 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_ASSERT(stream->packet_state.packet);
+               packet_context_field = bt_packet_borrow_context_field_const(
+                       stream->packet_state.packet);
+               BT_ASSERT(packet_context_field);
+               ret = write_struct_field(stream,
+                       fs_sink_ctf_field_class_as_struct(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 */
+       ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser,
+               UINT64_C(0xc1fc1fc1), 8, 32, BYTE_ORDER);
+       if (ret) {
+               BT_COMP_LOGE("Error writing packet header magic: stream-file-name=%s",
+                       stream->file_name->str);
+               goto end;
+       }
+
+       /* Packet header: UUID */
+       for (i = 0; i < BT_UUID_LEN; i++) {
+               ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser,
+                       (uint64_t) stream->sc->trace->uuid[i], 8, 8, BYTE_ORDER);
+               if (ret) {
+                       BT_COMP_LOGE("Error writing packet header UUID: stream-file-name=%s",
+                               stream->file_name->str);
+                       goto end;
+               }
+       }
+
+       /* Packet header: stream class ID */
+       ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser,
+               bt_stream_class_get_id(stream->sc->ir_sc), 8, 64, BYTE_ORDER);
+       if (ret) {
+               BT_COMP_LOGE("Error writing packet header stream class id: "
+                       "stream-file-name=%s, stream-class-id=%" PRIu64,
+                       stream->file_name->str,
+                       bt_stream_class_get_id(stream->sc->ir_sc));
+               goto end;
+       }
+
+       /* Packet header: stream ID */
+       ret = bt_ctfser_write_byte_aligned_unsigned_int(&stream->ctfser,
+               bt_stream_get_id(stream->ir_stream), 8, 64, BYTE_ORDER);
+       if (ret) {
+               BT_COMP_LOGE("Error writing packet header stream id: "
+                       "stream-file-name=%s, stream-id=%" PRIu64,
+                       stream->file_name->str,
+                       bt_stream_get_id(stream->ir_stream));
+               goto end;
+       }
+
+       /* 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;
+}
diff --git a/src/plugins/ctf/fs-sink/fs-sink-stream.h b/src/plugins/ctf/fs-sink/fs-sink-stream.h
deleted file mode 100644 (file)
index a6acdd5..0000000
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
- */
-
-#ifndef BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_STREAM_H
-#define BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_STREAM_H
-
-#include "common/macros.h"
-#include <babeltrace2/babeltrace.h>
-#include "ctfser/ctfser.h"
-#include <glib.h>
-#include <stdbool.h>
-#include <stdint.h>
-
-#include "fs-sink-ctf-meta.h"
-
-struct fs_sink_trace;
-
-struct fs_sink_stream {
-       bt_logging_level log_level;
-       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;
-
-       /* Current packet's state */
-       struct {
-               /*
-                * True if we're, for this stream, within an opened
-                * packet (got a packet beginning message, but no
-                * packet end message yet).
-                */
-               bool is_open;
-
-               /*
-                * Current beginning default clock snapshot for the
-                * current packet (`UINT64_C(-1)` if not set).
-                */
-               uint64_t beginning_cs;
-
-               /*
-                * Current end default clock snapshot for the current
-                * packet (`UINT64_C(-1)` if not set).
-                */
-               uint64_t end_cs;
-
-               /*
-                * Current packet's content size (bits) for the current
-                * packet.
-                */
-               uint64_t content_size;
-
-               /*
-                * Current packet's total size (bits) for the current
-                * packet.
-                */
-               uint64_t total_size;
-
-               /*
-                * Discarded events (free running) counter for the
-                * current packet.
-                */
-               uint64_t discarded_events_counter;
-
-               /* Sequence number (free running) of the current packet */
-               uint64_t seq_num;
-
-               /*
-                * Offset of the packet context structure within the
-                * current packet (bits).
-                */
-               uint64_t context_offset_bits;
-
-               /*
-                * Owned by this; `NULL` if the current packet is closed
-                * or if the trace IR stream does not support packets.
-                */
-               const bt_packet *packet;
-       } packet_state;
-
-       /* Previous packet's state */
-       struct {
-               /* End default clock snapshot (`UINT64_C(-1)` if not set) */
-               uint64_t end_cs;
-
-               /* Discarded events (free running) counter */
-               uint64_t discarded_events_counter;
-
-               /* Sequence number (free running) */
-               uint64_t seq_num;
-       } prev_packet_state;
-
-       /* State to handle discarded events */
-       struct {
-               /*
-                * True if we're in the time range given by a previously
-                * received discarded events message. In this case,
-                * `beginning_cs` and `end_cs` below contain the
-                * beginning and end clock snapshots for this range.
-                *
-                * This is used to validate that, when receiving a
-                * packet end message, the current discarded events time
-                * range matches what's expected for CTF 1.8, that is:
-                *
-                * * Its beginning time is the previous packet's end
-                *   time (or the current packet's beginning time if
-                *   this is the first packet).
-                *
-                * * Its end time is the current packet's end time.
-                */
-               bool in_range;
-
-               /*
-                * Beginning and end times of the time range given by a
-                * previously received discarded events message.
-                */
-               uint64_t beginning_cs;
-               uint64_t end_cs;
-       } discarded_events_state;
-
-       /* State to handle discarded packets */
-       struct {
-               /*
-                * True if we're in the time range given by a previously
-                * received discarded packets message. In this case,
-                * `beginning_cs` and `end_cs` below contain the
-                * beginning and end clock snapshots for this range.
-                *
-                * This is used to validate that, when receiving a
-                * packet beginning message, the current discarded
-                * packets time range matches what's expected for CTF
-                * 1.8, that is:
-                *
-                * * Its beginning time is the previous packet's end
-                *   time.
-                *
-                * * Its end time is the current packet's beginning
-                *   time.
-                */
-               bool in_range;
-
-               /*
-                * Beginning and end times of the time range given by a
-                * previously received discarded packets message.
-                */
-               uint64_t beginning_cs;
-               uint64_t end_cs;
-       } discarded_packets_state;
-};
-
-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 */
diff --git a/src/plugins/ctf/fs-sink/fs-sink-stream.hpp b/src/plugins/ctf/fs-sink/fs-sink-stream.hpp
new file mode 100644 (file)
index 0000000..3a1e170
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#ifndef BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_STREAM_H
+#define BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_STREAM_H
+
+#include "common/macros.h"
+#include <babeltrace2/babeltrace.h>
+#include "ctfser/ctfser.h"
+#include <glib.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "fs-sink-ctf-meta.hpp"
+
+struct fs_sink_trace;
+
+struct fs_sink_stream {
+       bt_logging_level log_level;
+       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;
+
+       /* Current packet's state */
+       struct {
+               /*
+                * True if we're, for this stream, within an opened
+                * packet (got a packet beginning message, but no
+                * packet end message yet).
+                */
+               bool is_open;
+
+               /*
+                * Current beginning default clock snapshot for the
+                * current packet (`UINT64_C(-1)` if not set).
+                */
+               uint64_t beginning_cs;
+
+               /*
+                * Current end default clock snapshot for the current
+                * packet (`UINT64_C(-1)` if not set).
+                */
+               uint64_t end_cs;
+
+               /*
+                * Current packet's content size (bits) for the current
+                * packet.
+                */
+               uint64_t content_size;
+
+               /*
+                * Current packet's total size (bits) for the current
+                * packet.
+                */
+               uint64_t total_size;
+
+               /*
+                * Discarded events (free running) counter for the
+                * current packet.
+                */
+               uint64_t discarded_events_counter;
+
+               /* Sequence number (free running) of the current packet */
+               uint64_t seq_num;
+
+               /*
+                * Offset of the packet context structure within the
+                * current packet (bits).
+                */
+               uint64_t context_offset_bits;
+
+               /*
+                * Owned by this; `NULL` if the current packet is closed
+                * or if the trace IR stream does not support packets.
+                */
+               const bt_packet *packet;
+       } packet_state;
+
+       /* Previous packet's state */
+       struct {
+               /* End default clock snapshot (`UINT64_C(-1)` if not set) */
+               uint64_t end_cs;
+
+               /* Discarded events (free running) counter */
+               uint64_t discarded_events_counter;
+
+               /* Sequence number (free running) */
+               uint64_t seq_num;
+       } prev_packet_state;
+
+       /* State to handle discarded events */
+       struct {
+               /*
+                * True if we're in the time range given by a previously
+                * received discarded events message. In this case,
+                * `beginning_cs` and `end_cs` below contain the
+                * beginning and end clock snapshots for this range.
+                *
+                * This is used to validate that, when receiving a
+                * packet end message, the current discarded events time
+                * range matches what's expected for CTF 1.8, that is:
+                *
+                * * Its beginning time is the previous packet's end
+                *   time (or the current packet's beginning time if
+                *   this is the first packet).
+                *
+                * * Its end time is the current packet's end time.
+                */
+               bool in_range;
+
+               /*
+                * Beginning and end times of the time range given by a
+                * previously received discarded events message.
+                */
+               uint64_t beginning_cs;
+               uint64_t end_cs;
+       } discarded_events_state;
+
+       /* State to handle discarded packets */
+       struct {
+               /*
+                * True if we're in the time range given by a previously
+                * received discarded packets message. In this case,
+                * `beginning_cs` and `end_cs` below contain the
+                * beginning and end clock snapshots for this range.
+                *
+                * This is used to validate that, when receiving a
+                * packet beginning message, the current discarded
+                * packets time range matches what's expected for CTF
+                * 1.8, that is:
+                *
+                * * Its beginning time is the previous packet's end
+                *   time.
+                *
+                * * Its end time is the current packet's beginning
+                *   time.
+                */
+               bool in_range;
+
+               /*
+                * Beginning and end times of the time range given by a
+                * previously received discarded packets message.
+                */
+               uint64_t beginning_cs;
+               uint64_t end_cs;
+       } discarded_packets_state;
+};
+
+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 */
diff --git a/src/plugins/ctf/fs-sink/fs-sink-trace.c b/src/plugins/ctf/fs-sink/fs-sink-trace.c
deleted file mode 100644 (file)
index 60f668e..0000000
+++ /dev/null
@@ -1,620 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
- */
-
-#define BT_COMP_LOG_SELF_COMP (trace->fs_sink->self_comp)
-#define BT_LOG_OUTPUT_LEVEL (trace->log_level)
-#define BT_LOG_TAG "PLUGIN/SINK.CTF.FS/TRACE"
-#include "logging/comp-logging.h"
-
-#include <babeltrace2/babeltrace.h>
-#include <stdio.h>
-#include <stdbool.h>
-#include <glib.h>
-#include "common/assert.h"
-#include "ctfser/ctfser.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;
-}
-
-/*
- * Find a path based on `path` that doesn't exist yet.  First, try `path`
- * itself, then try with incrementing suffixes.
- */
-
-static
-GString *make_unique_trace_path(const char *path)
-{
-       GString *unique_path;
-       unsigned int suffix = 0;
-
-       unique_path = g_string_new(path);
-
-       while (g_file_test(unique_path->str, G_FILE_TEST_EXISTS)) {
-               g_string_printf(unique_path, "%s-%u", path, suffix);
-               suffix++;
-       }
-
-       return unique_path;
-}
-
-/*
- * Disable `deprecated-declarations` warnings for
- * lttng_validate_datetime() because we're using `GTimeVal` and
- * g_time_val_from_iso8601() which are deprecated since GLib 2.56
- * (Babeltrace supports older versions too).
- */
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-
-/*
- * Validate that the input string `datetime` is an ISO8601-compliant string (the
- * format used by LTTng in the metadata).
- */
-
-static
-int lttng_validate_datetime(const struct fs_sink_trace *trace,
-               const char *datetime)
-{
-       GTimeVal tv;
-       int ret = -1;
-
-       /*
-        * We are using g_time_val_from_iso8601, as the safer/more modern
-        * alternative, g_date_time_new_from_iso8601, is only available in
-        * glib >= 2.56, and this is sufficient for our use case of validating
-        * the format.
-        */
-       if (!g_time_val_from_iso8601(datetime, &tv)) {
-               BT_COMP_LOGI("Couldn't parse datetime as ISO 8601: date=\"%s\"", datetime);
-               goto end;
-       }
-
-       ret = 0;
-
-end:
-       return ret;
-}
-
-#pragma GCC diagnostic pop
-
-static
-int append_lttng_trace_path_ust_uid(const struct fs_sink_trace *trace,
-               GString *path, const bt_trace *tc)
-{
-       const bt_value *v;
-       int ret;
-
-       v = bt_trace_borrow_environment_entry_value_by_name_const(tc, "tracer_buffering_id");
-       if (!v || !bt_value_is_signed_integer(v)) {
-               BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_buffering_id\"");
-               goto error;
-       }
-
-       g_string_append_printf(path, G_DIR_SEPARATOR_S "%" PRId64,
-               bt_value_integer_signed_get(v));
-
-       v = bt_trace_borrow_environment_entry_value_by_name_const(tc, "architecture_bit_width");
-       if (!v || !bt_value_is_signed_integer(v)) {
-               BT_COMP_LOGI_STR("Couldn't get environment value: name=\"architecture_bit_width\"");
-               goto error;
-       }
-
-       g_string_append_printf(path, G_DIR_SEPARATOR_S "%" PRIu64 "-bit",
-               bt_value_integer_signed_get(v));
-
-       ret = 0;
-       goto end;
-
-error:
-       ret = -1;
-
-end:
-       return ret;
-}
-
-static
-int append_lttng_trace_path_ust_pid(const struct fs_sink_trace *trace,
-               GString *path, const bt_trace *tc)
-{
-       const bt_value *v;
-       const char *datetime;
-       int ret;
-
-       v = bt_trace_borrow_environment_entry_value_by_name_const(tc, "procname");
-       if (!v || !bt_value_is_string(v)) {
-               BT_COMP_LOGI_STR("Couldn't get environment value: name=\"procname\"");
-               goto error;
-       }
-
-       g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", bt_value_string_get(v));
-
-       v = bt_trace_borrow_environment_entry_value_by_name_const(tc, "vpid");
-       if (!v || !bt_value_is_signed_integer(v)) {
-               BT_COMP_LOGI_STR("Couldn't get environment value: name=\"vpid\"");
-               goto error;
-       }
-
-       g_string_append_printf(path, "-%" PRId64, bt_value_integer_signed_get(v));
-
-       v = bt_trace_borrow_environment_entry_value_by_name_const(tc, "vpid_datetime");
-       if (!v || !bt_value_is_string(v)) {
-               BT_COMP_LOGI_STR("Couldn't get environment value: name=\"vpid_datetime\"");
-               goto error;
-       }
-
-       datetime = bt_value_string_get(v);
-
-       if (lttng_validate_datetime(trace, datetime)) {
-               goto error;
-       }
-
-       g_string_append_printf(path, "-%s", datetime);
-
-       ret = 0;
-       goto end;
-
-error:
-       ret = -1;
-
-end:
-       return ret;
-}
-
-/*
- * Try to build a trace path based on environment values put in the trace
- * environment by the LTTng tracer, starting with version 2.11.
- */
-static
-GString *make_lttng_trace_path_rel(const struct fs_sink_trace *trace)
-{
-       const bt_value *v;
-       const char *tracer_name, *domain, *datetime;
-       int64_t tracer_major, tracer_minor;
-       GString *path;
-
-       path = g_string_new(NULL);
-       if (!path) {
-               goto error;
-       }
-
-       v = bt_trace_borrow_environment_entry_value_by_name_const(
-               trace->ir_trace, "tracer_name");
-       if (!v || !bt_value_is_string(v)) {
-               BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_name\"");
-               goto error;
-       }
-
-       tracer_name = bt_value_string_get(v);
-
-       if (!g_str_equal(tracer_name, "lttng-ust")
-                       && !g_str_equal(tracer_name, "lttng-modules")) {
-               BT_COMP_LOGI("Unrecognized tracer name: name=\"%s\"", tracer_name);
-               goto error;
-       }
-
-       v = bt_trace_borrow_environment_entry_value_by_name_const(
-               trace->ir_trace, "tracer_major");
-       if (!v || !bt_value_is_signed_integer(v)) {
-               BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_major\"");
-               goto error;
-       }
-
-       tracer_major = bt_value_integer_signed_get(v);
-
-       v = bt_trace_borrow_environment_entry_value_by_name_const(
-               trace->ir_trace, "tracer_minor");
-       if (!v || !bt_value_is_signed_integer(v)) {
-               BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_minor\"");
-               goto error;
-       }
-
-       tracer_minor = bt_value_integer_signed_get(v);
-
-       if (!(tracer_major >= 3 || (tracer_major == 2 && tracer_minor >= 11))) {
-               BT_COMP_LOGI("Unsupported LTTng version for automatic trace path: major=%" PRId64 ", minor=%" PRId64,
-                       tracer_major, tracer_minor);
-               goto error;
-       }
-
-       v = bt_trace_borrow_environment_entry_value_by_name_const(
-               trace->ir_trace, "hostname");
-       if (!v || !bt_value_is_string(v)) {
-               BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_hostname\"");
-               goto error;
-       }
-
-       g_string_assign(path, bt_value_string_get(v));
-
-       v = bt_trace_borrow_environment_entry_value_by_name_const(
-               trace->ir_trace, "trace_name");
-       if (!v || !bt_value_is_string(v)) {
-               BT_COMP_LOGI_STR("Couldn't get environment value: name=\"trace_name\"");
-               goto error;
-       }
-
-       g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", bt_value_string_get(v));
-
-       v = bt_trace_borrow_environment_entry_value_by_name_const(
-               trace->ir_trace, "trace_creation_datetime");
-       if (!v || !bt_value_is_string(v)) {
-               BT_COMP_LOGI_STR("Couldn't get environment value: name=\"trace_creation_datetime\"");
-               goto error;
-       }
-
-       datetime = bt_value_string_get(v);
-
-       if (lttng_validate_datetime(trace, datetime)) {
-               goto error;
-       }
-
-       g_string_append_printf(path, "-%s", datetime);
-
-       v = bt_trace_borrow_environment_entry_value_by_name_const(
-               trace->ir_trace, "domain");
-       if (!v || !bt_value_is_string(v)) {
-               BT_COMP_LOGI_STR("Couldn't get environment value: name=\"domain\"");
-               goto error;
-       }
-
-       domain = bt_value_string_get(v);
-       g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", domain);
-
-       if (g_str_equal(domain, "ust")) {
-               const char *tracer_buffering_scheme;
-
-               v = bt_trace_borrow_environment_entry_value_by_name_const(
-                       trace->ir_trace, "tracer_buffering_scheme");
-               if (!v || !bt_value_is_string(v)) {
-                       BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_buffering_scheme\"");
-                       goto error;
-               }
-
-               tracer_buffering_scheme = bt_value_string_get(v);
-               g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", tracer_buffering_scheme);
-
-               if (g_str_equal(tracer_buffering_scheme, "uid")) {
-                       if (append_lttng_trace_path_ust_uid(trace, path,
-                                       trace->ir_trace)) {
-                               goto error;
-                       }
-               } else if (g_str_equal(tracer_buffering_scheme, "pid")){
-                       if (append_lttng_trace_path_ust_pid(trace, path,
-                                       trace->ir_trace)) {
-                               goto error;
-                       }
-               } else {
-                       /* Unknown buffering scheme. */
-                       BT_COMP_LOGI("Unknown buffering scheme: tracer_buffering_scheme=\"%s\"", tracer_buffering_scheme);
-                       goto error;
-               }
-       } else if (!g_str_equal(domain, "kernel")) {
-               /* Unknown domain. */
-               BT_COMP_LOGI("Unknown domain: domain=\"%s\"", domain);
-               goto error;
-       }
-
-       goto end;
-
-error:
-       if (path) {
-               g_string_free(path, TRUE);
-               path = NULL;
-       }
-
-end:
-       return path;
-}
-
-/* Build the relative output path for `trace`. */
-
-static
-GString *make_trace_path_rel(const struct fs_sink_trace *trace)
-{
-       GString *path = NULL;
-       const char *trace_name;
-
-       BT_ASSERT(!trace->fs_sink->assume_single_trace);
-
-       /* First, try to build a path using environment fields written by LTTng. */
-       path = make_lttng_trace_path_rel(trace);
-       if (path) {
-               goto end;
-       }
-
-       /* Otherwise, use the trace name, if available. */
-       trace_name = bt_trace_get_name(trace->ir_trace);
-       if (trace_name) {
-               path = g_string_new(trace_name);
-               goto end;
-       }
-
-       /* Otherwise, use "trace". */
-       path = g_string_new("trace");
-
-end:
-       return path;
-}
-
-/*
- * Compute the trace output path for `trace`, rooted at `output_base_directory`.
- */
-
-static
-GString *make_trace_path(const struct fs_sink_trace *trace, const char *output_base_directory)
-{
-       GString *rel_path = NULL;
-       GString *rel_path_san = NULL;
-       GString *full_path = NULL;
-       GString *unique_full_path = NULL;
-
-       if (trace->fs_sink->assume_single_trace) {
-               /* Use output directory directly */
-               unique_full_path = g_string_new(output_base_directory);
-               if (!unique_full_path) {
-                       goto end;
-               }
-       } else {
-               rel_path = make_trace_path_rel(trace);
-               if (!rel_path) {
-                       goto end;
-               }
-
-               rel_path_san = sanitize_trace_path(rel_path->str);
-               if (!rel_path_san) {
-                       goto end;
-               }
-
-               full_path = g_string_new(NULL);
-               if (!full_path) {
-                       goto end;
-               }
-
-               g_string_printf(full_path, "%s" G_DIR_SEPARATOR_S "%s",
-                       output_base_directory, rel_path_san->str);
-
-               unique_full_path = make_unique_trace_path(full_path->str);
-       }
-
-end:
-       if (rel_path) {
-               g_string_free(rel_path, TRUE);
-       }
-
-       if (rel_path_san) {
-               g_string_free(rel_path_san, TRUE);
-       }
-
-       if (full_path) {
-               g_string_free(full_path, TRUE);
-       }
-
-       return unique_full_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_ctf_ir_to_tsdl(trace->trace, tsdl);
-
-       BT_ASSERT(trace->metadata_path);
-       fh = fopen(trace->metadata_path->str, "wb");
-       if (!fh) {
-               BT_COMP_LOGF_ERRNO("In trace destruction listener: "
-                       "cannot open metadata file for writing",
-                       ": path=\"%s\"", trace->metadata_path->str);
-               bt_common_abort();
-       }
-
-       len = fwrite(tsdl->str, sizeof(*tsdl->str), tsdl->len, fh);
-       if (len != tsdl->len) {
-               BT_COMP_LOGF_ERRNO("In trace destruction listener: "
-                       "cannot write metadata file",
-                       ": path=\"%s\"", trace->metadata_path->str);
-               bt_common_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 (fh) {
-               int ret = fclose(fh);
-
-               if (ret != 0) {
-                       BT_COMP_LOGW_ERRNO("In trace destruction listener: "
-                               "cannot close metadata file",
-                               ": path=\"%s\"", trace->metadata_path->str);
-               }
-       }
-
-       g_string_free(trace->metadata_path, TRUE);
-       trace->metadata_path = NULL;
-
-       fs_sink_ctf_trace_destroy(trace->trace);
-       trace->trace = NULL;
-       g_free(trace);
-
-       g_string_free(tsdl, TRUE);
-
-end:
-       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);
-       bt_trace_add_listener_status trace_status;
-
-       if (!trace) {
-               goto end;
-       }
-
-       trace->log_level = fs_sink->log_level;
-       trace->fs_sink = fs_sink;
-       trace->ir_trace = ir_trace;
-       trace->ir_trace_destruction_listener_id = UINT64_C(-1);
-       trace->trace = translate_trace_trace_ir_to_ctf_ir(fs_sink, ir_trace);
-       if (!trace->trace) {
-               goto error;
-       }
-
-       trace->path = make_trace_path(trace, fs_sink->output_dir_path->str);
-       BT_ASSERT(trace->path);
-       ret = g_mkdir_with_parents(trace->path->str, 0755);
-       if (ret) {
-               BT_COMP_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;
-}
diff --git a/src/plugins/ctf/fs-sink/fs-sink-trace.cpp b/src/plugins/ctf/fs-sink/fs-sink-trace.cpp
new file mode 100644 (file)
index 0000000..2be7d5e
--- /dev/null
@@ -0,0 +1,620 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#define BT_COMP_LOG_SELF_COMP (trace->fs_sink->self_comp)
+#define BT_LOG_OUTPUT_LEVEL (trace->log_level)
+#define BT_LOG_TAG "PLUGIN/SINK.CTF.FS/TRACE"
+#include "logging/comp-logging.h"
+
+#include <babeltrace2/babeltrace.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <glib.h>
+#include "common/assert.h"
+#include "ctfser/ctfser.h"
+
+#include "translate-trace-ir-to-ctf-ir.hpp"
+#include "translate-ctf-ir-to-tsdl.hpp"
+#include "fs-sink.hpp"
+#include "fs-sink-trace.hpp"
+#include "fs-sink-stream.hpp"
+
+/*
+ * 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;
+}
+
+/*
+ * Find a path based on `path` that doesn't exist yet.  First, try `path`
+ * itself, then try with incrementing suffixes.
+ */
+
+static
+GString *make_unique_trace_path(const char *path)
+{
+       GString *unique_path;
+       unsigned int suffix = 0;
+
+       unique_path = g_string_new(path);
+
+       while (g_file_test(unique_path->str, G_FILE_TEST_EXISTS)) {
+               g_string_printf(unique_path, "%s-%u", path, suffix);
+               suffix++;
+       }
+
+       return unique_path;
+}
+
+/*
+ * Disable `deprecated-declarations` warnings for
+ * lttng_validate_datetime() because we're using `GTimeVal` and
+ * g_time_val_from_iso8601() which are deprecated since GLib 2.56
+ * (Babeltrace supports older versions too).
+ */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+
+/*
+ * Validate that the input string `datetime` is an ISO8601-compliant string (the
+ * format used by LTTng in the metadata).
+ */
+
+static
+int lttng_validate_datetime(const struct fs_sink_trace *trace,
+               const char *datetime)
+{
+       GTimeVal tv;
+       int ret = -1;
+
+       /*
+        * We are using g_time_val_from_iso8601, as the safer/more modern
+        * alternative, g_date_time_new_from_iso8601, is only available in
+        * glib >= 2.56, and this is sufficient for our use case of validating
+        * the format.
+        */
+       if (!g_time_val_from_iso8601(datetime, &tv)) {
+               BT_COMP_LOGI("Couldn't parse datetime as ISO 8601: date=\"%s\"", datetime);
+               goto end;
+       }
+
+       ret = 0;
+
+end:
+       return ret;
+}
+
+#pragma GCC diagnostic pop
+
+static
+int append_lttng_trace_path_ust_uid(const struct fs_sink_trace *trace,
+               GString *path, const bt_trace *tc)
+{
+       const bt_value *v;
+       int ret;
+
+       v = bt_trace_borrow_environment_entry_value_by_name_const(tc, "tracer_buffering_id");
+       if (!v || !bt_value_is_signed_integer(v)) {
+               BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_buffering_id\"");
+               goto error;
+       }
+
+       g_string_append_printf(path, G_DIR_SEPARATOR_S "%" PRId64,
+               bt_value_integer_signed_get(v));
+
+       v = bt_trace_borrow_environment_entry_value_by_name_const(tc, "architecture_bit_width");
+       if (!v || !bt_value_is_signed_integer(v)) {
+               BT_COMP_LOGI_STR("Couldn't get environment value: name=\"architecture_bit_width\"");
+               goto error;
+       }
+
+       g_string_append_printf(path, G_DIR_SEPARATOR_S "%" PRIu64 "-bit",
+               bt_value_integer_signed_get(v));
+
+       ret = 0;
+       goto end;
+
+error:
+       ret = -1;
+
+end:
+       return ret;
+}
+
+static
+int append_lttng_trace_path_ust_pid(const struct fs_sink_trace *trace,
+               GString *path, const bt_trace *tc)
+{
+       const bt_value *v;
+       const char *datetime;
+       int ret;
+
+       v = bt_trace_borrow_environment_entry_value_by_name_const(tc, "procname");
+       if (!v || !bt_value_is_string(v)) {
+               BT_COMP_LOGI_STR("Couldn't get environment value: name=\"procname\"");
+               goto error;
+       }
+
+       g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", bt_value_string_get(v));
+
+       v = bt_trace_borrow_environment_entry_value_by_name_const(tc, "vpid");
+       if (!v || !bt_value_is_signed_integer(v)) {
+               BT_COMP_LOGI_STR("Couldn't get environment value: name=\"vpid\"");
+               goto error;
+       }
+
+       g_string_append_printf(path, "-%" PRId64, bt_value_integer_signed_get(v));
+
+       v = bt_trace_borrow_environment_entry_value_by_name_const(tc, "vpid_datetime");
+       if (!v || !bt_value_is_string(v)) {
+               BT_COMP_LOGI_STR("Couldn't get environment value: name=\"vpid_datetime\"");
+               goto error;
+       }
+
+       datetime = bt_value_string_get(v);
+
+       if (lttng_validate_datetime(trace, datetime)) {
+               goto error;
+       }
+
+       g_string_append_printf(path, "-%s", datetime);
+
+       ret = 0;
+       goto end;
+
+error:
+       ret = -1;
+
+end:
+       return ret;
+}
+
+/*
+ * Try to build a trace path based on environment values put in the trace
+ * environment by the LTTng tracer, starting with version 2.11.
+ */
+static
+GString *make_lttng_trace_path_rel(const struct fs_sink_trace *trace)
+{
+       const bt_value *v;
+       const char *tracer_name, *domain, *datetime;
+       int64_t tracer_major, tracer_minor;
+       GString *path;
+
+       path = g_string_new(NULL);
+       if (!path) {
+               goto error;
+       }
+
+       v = bt_trace_borrow_environment_entry_value_by_name_const(
+               trace->ir_trace, "tracer_name");
+       if (!v || !bt_value_is_string(v)) {
+               BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_name\"");
+               goto error;
+       }
+
+       tracer_name = bt_value_string_get(v);
+
+       if (!g_str_equal(tracer_name, "lttng-ust")
+                       && !g_str_equal(tracer_name, "lttng-modules")) {
+               BT_COMP_LOGI("Unrecognized tracer name: name=\"%s\"", tracer_name);
+               goto error;
+       }
+
+       v = bt_trace_borrow_environment_entry_value_by_name_const(
+               trace->ir_trace, "tracer_major");
+       if (!v || !bt_value_is_signed_integer(v)) {
+               BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_major\"");
+               goto error;
+       }
+
+       tracer_major = bt_value_integer_signed_get(v);
+
+       v = bt_trace_borrow_environment_entry_value_by_name_const(
+               trace->ir_trace, "tracer_minor");
+       if (!v || !bt_value_is_signed_integer(v)) {
+               BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_minor\"");
+               goto error;
+       }
+
+       tracer_minor = bt_value_integer_signed_get(v);
+
+       if (!(tracer_major >= 3 || (tracer_major == 2 && tracer_minor >= 11))) {
+               BT_COMP_LOGI("Unsupported LTTng version for automatic trace path: major=%" PRId64 ", minor=%" PRId64,
+                       tracer_major, tracer_minor);
+               goto error;
+       }
+
+       v = bt_trace_borrow_environment_entry_value_by_name_const(
+               trace->ir_trace, "hostname");
+       if (!v || !bt_value_is_string(v)) {
+               BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_hostname\"");
+               goto error;
+       }
+
+       g_string_assign(path, bt_value_string_get(v));
+
+       v = bt_trace_borrow_environment_entry_value_by_name_const(
+               trace->ir_trace, "trace_name");
+       if (!v || !bt_value_is_string(v)) {
+               BT_COMP_LOGI_STR("Couldn't get environment value: name=\"trace_name\"");
+               goto error;
+       }
+
+       g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", bt_value_string_get(v));
+
+       v = bt_trace_borrow_environment_entry_value_by_name_const(
+               trace->ir_trace, "trace_creation_datetime");
+       if (!v || !bt_value_is_string(v)) {
+               BT_COMP_LOGI_STR("Couldn't get environment value: name=\"trace_creation_datetime\"");
+               goto error;
+       }
+
+       datetime = bt_value_string_get(v);
+
+       if (lttng_validate_datetime(trace, datetime)) {
+               goto error;
+       }
+
+       g_string_append_printf(path, "-%s", datetime);
+
+       v = bt_trace_borrow_environment_entry_value_by_name_const(
+               trace->ir_trace, "domain");
+       if (!v || !bt_value_is_string(v)) {
+               BT_COMP_LOGI_STR("Couldn't get environment value: name=\"domain\"");
+               goto error;
+       }
+
+       domain = bt_value_string_get(v);
+       g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", domain);
+
+       if (g_str_equal(domain, "ust")) {
+               const char *tracer_buffering_scheme;
+
+               v = bt_trace_borrow_environment_entry_value_by_name_const(
+                       trace->ir_trace, "tracer_buffering_scheme");
+               if (!v || !bt_value_is_string(v)) {
+                       BT_COMP_LOGI_STR("Couldn't get environment value: name=\"tracer_buffering_scheme\"");
+                       goto error;
+               }
+
+               tracer_buffering_scheme = bt_value_string_get(v);
+               g_string_append_printf(path, G_DIR_SEPARATOR_S "%s", tracer_buffering_scheme);
+
+               if (g_str_equal(tracer_buffering_scheme, "uid")) {
+                       if (append_lttng_trace_path_ust_uid(trace, path,
+                                       trace->ir_trace)) {
+                               goto error;
+                       }
+               } else if (g_str_equal(tracer_buffering_scheme, "pid")){
+                       if (append_lttng_trace_path_ust_pid(trace, path,
+                                       trace->ir_trace)) {
+                               goto error;
+                       }
+               } else {
+                       /* Unknown buffering scheme. */
+                       BT_COMP_LOGI("Unknown buffering scheme: tracer_buffering_scheme=\"%s\"", tracer_buffering_scheme);
+                       goto error;
+               }
+       } else if (!g_str_equal(domain, "kernel")) {
+               /* Unknown domain. */
+               BT_COMP_LOGI("Unknown domain: domain=\"%s\"", domain);
+               goto error;
+       }
+
+       goto end;
+
+error:
+       if (path) {
+               g_string_free(path, TRUE);
+               path = NULL;
+       }
+
+end:
+       return path;
+}
+
+/* Build the relative output path for `trace`. */
+
+static
+GString *make_trace_path_rel(const struct fs_sink_trace *trace)
+{
+       GString *path = NULL;
+       const char *trace_name;
+
+       BT_ASSERT(!trace->fs_sink->assume_single_trace);
+
+       /* First, try to build a path using environment fields written by LTTng. */
+       path = make_lttng_trace_path_rel(trace);
+       if (path) {
+               goto end;
+       }
+
+       /* Otherwise, use the trace name, if available. */
+       trace_name = bt_trace_get_name(trace->ir_trace);
+       if (trace_name) {
+               path = g_string_new(trace_name);
+               goto end;
+       }
+
+       /* Otherwise, use "trace". */
+       path = g_string_new("trace");
+
+end:
+       return path;
+}
+
+/*
+ * Compute the trace output path for `trace`, rooted at `output_base_directory`.
+ */
+
+static
+GString *make_trace_path(const struct fs_sink_trace *trace, const char *output_base_directory)
+{
+       GString *rel_path = NULL;
+       GString *rel_path_san = NULL;
+       GString *full_path = NULL;
+       GString *unique_full_path = NULL;
+
+       if (trace->fs_sink->assume_single_trace) {
+               /* Use output directory directly */
+               unique_full_path = g_string_new(output_base_directory);
+               if (!unique_full_path) {
+                       goto end;
+               }
+       } else {
+               rel_path = make_trace_path_rel(trace);
+               if (!rel_path) {
+                       goto end;
+               }
+
+               rel_path_san = sanitize_trace_path(rel_path->str);
+               if (!rel_path_san) {
+                       goto end;
+               }
+
+               full_path = g_string_new(NULL);
+               if (!full_path) {
+                       goto end;
+               }
+
+               g_string_printf(full_path, "%s" G_DIR_SEPARATOR_S "%s",
+                       output_base_directory, rel_path_san->str);
+
+               unique_full_path = make_unique_trace_path(full_path->str);
+       }
+
+end:
+       if (rel_path) {
+               g_string_free(rel_path, TRUE);
+       }
+
+       if (rel_path_san) {
+               g_string_free(rel_path_san, TRUE);
+       }
+
+       if (full_path) {
+               g_string_free(full_path, TRUE);
+       }
+
+       return unique_full_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_ctf_ir_to_tsdl(trace->trace, tsdl);
+
+       BT_ASSERT(trace->metadata_path);
+       fh = fopen(trace->metadata_path->str, "wb");
+       if (!fh) {
+               BT_COMP_LOGF_ERRNO("In trace destruction listener: "
+                       "cannot open metadata file for writing",
+                       ": path=\"%s\"", trace->metadata_path->str);
+               bt_common_abort();
+       }
+
+       len = fwrite(tsdl->str, sizeof(*tsdl->str), tsdl->len, fh);
+       if (len != tsdl->len) {
+               BT_COMP_LOGF_ERRNO("In trace destruction listener: "
+                       "cannot write metadata file",
+                       ": path=\"%s\"", trace->metadata_path->str);
+               bt_common_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 (fh) {
+               int ret = fclose(fh);
+
+               if (ret != 0) {
+                       BT_COMP_LOGW_ERRNO("In trace destruction listener: "
+                               "cannot close metadata file",
+                               ": path=\"%s\"", trace->metadata_path->str);
+               }
+       }
+
+       g_string_free(trace->metadata_path, TRUE);
+       trace->metadata_path = NULL;
+
+       fs_sink_ctf_trace_destroy(trace->trace);
+       trace->trace = NULL;
+       g_free(trace);
+
+       g_string_free(tsdl, TRUE);
+
+end:
+       return;
+}
+
+static
+void ir_trace_destruction_listener(const bt_trace *ir_trace, void *data)
+{
+       struct fs_sink_trace *trace = (fs_sink_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);
+       bt_trace_add_listener_status trace_status;
+
+       if (!trace) {
+               goto end;
+       }
+
+       trace->log_level = fs_sink->log_level;
+       trace->fs_sink = fs_sink;
+       trace->ir_trace = ir_trace;
+       trace->ir_trace_destruction_listener_id = UINT64_C(-1);
+       trace->trace = translate_trace_trace_ir_to_ctf_ir(fs_sink, ir_trace);
+       if (!trace->trace) {
+               goto error;
+       }
+
+       trace->path = make_trace_path(trace, fs_sink->output_dir_path->str);
+       BT_ASSERT(trace->path);
+       ret = g_mkdir_with_parents(trace->path->str, 0755);
+       if (ret) {
+               BT_COMP_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;
+}
diff --git a/src/plugins/ctf/fs-sink/fs-sink-trace.h b/src/plugins/ctf/fs-sink/fs-sink-trace.h
deleted file mode 100644 (file)
index 89c38ef..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
- */
-
-#ifndef BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_TRACE_H
-#define BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_TRACE_H
-
-#include "common/macros.h"
-#include <babeltrace2/babeltrace.h>
-#include "ctfser/ctfser.h"
-#include <glib.h>
-#include <stdint.h>
-
-#include "fs-sink-ctf-meta.h"
-
-struct fs_sink_comp;
-
-struct fs_sink_trace {
-       bt_logging_level log_level;
-       struct fs_sink_comp *fs_sink;
-
-       /* Owned by this */
-       struct fs_sink_ctf_trace *trace;
-
-       /*
-        * Weak reference: this object does not own it, and `trace`
-        * above does not own its trace IR trace and 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;
-
-       bt_listener_id 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 */
diff --git a/src/plugins/ctf/fs-sink/fs-sink-trace.hpp b/src/plugins/ctf/fs-sink/fs-sink-trace.hpp
new file mode 100644 (file)
index 0000000..ae33573
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#ifndef BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_TRACE_H
+#define BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_TRACE_H
+
+#include "common/macros.h"
+#include <babeltrace2/babeltrace.h>
+#include "ctfser/ctfser.h"
+#include <glib.h>
+#include <stdint.h>
+
+#include "fs-sink-ctf-meta.hpp"
+
+struct fs_sink_comp;
+
+struct fs_sink_trace {
+       bt_logging_level log_level;
+       struct fs_sink_comp *fs_sink;
+
+       /* Owned by this */
+       struct fs_sink_ctf_trace *trace;
+
+       /*
+        * Weak reference: this object does not own it, and `trace`
+        * above does not own its trace IR trace and 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;
+
+       bt_listener_id 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 */
diff --git a/src/plugins/ctf/fs-sink/fs-sink.c b/src/plugins/ctf/fs-sink/fs-sink.c
deleted file mode 100644 (file)
index 4b07750..0000000
+++ /dev/null
@@ -1,1142 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
- */
-
-#define BT_COMP_LOG_SELF_COMP (fs_sink->self_comp)
-#define BT_LOG_OUTPUT_LEVEL (fs_sink->log_level)
-#define BT_LOG_TAG "PLUGIN/SINK.CTF.FS"
-#include "logging/comp-logging.h"
-
-#include <babeltrace2/babeltrace.h>
-#include <stdio.h>
-#include <stdbool.h>
-#include <glib.h>
-#include "common/assert.h"
-#include "ctfser/ctfser.h"
-#include "plugins/common/param-validation/param-validation.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_component_class_initialize_method_status ensure_output_dir_exists(
-               struct fs_sink_comp *fs_sink)
-{
-       bt_component_class_initialize_method_status status =
-               BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
-       int ret;
-
-       ret = g_mkdir_with_parents(fs_sink->output_dir_path->str, 0755);
-       if (ret) {
-               BT_COMP_LOGE_APPEND_CAUSE_ERRNO(fs_sink->self_comp,
-                       "Cannot create directories for output directory",
-                       ": output-dir-path=\"%s\"",
-                       fs_sink->output_dir_path->str);
-               status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
-               goto end;
-       }
-
-end:
-       return status;
-}
-
-static struct bt_param_validation_map_value_entry_descr fs_sink_params_descr[] = {
-       { "path", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, { .type = BT_VALUE_TYPE_STRING } },
-       { "assume-single-trace", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_BOOL } },
-       { "ignore-discarded-events", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_BOOL } },
-       { "ignore-discarded-packets", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_BOOL } },
-       { "quiet", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_BOOL } },
-       BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
-};
-
-static
-bt_component_class_initialize_method_status
-configure_component(struct fs_sink_comp *fs_sink, const bt_value *params)
-{
-       bt_component_class_initialize_method_status status;
-       const bt_value *value;
-       enum bt_param_validation_status validation_status;
-       gchar *validation_error;
-
-       validation_status = bt_param_validation_validate(params,
-               fs_sink_params_descr, &validation_error);
-       if (validation_status == BT_PARAM_VALIDATION_STATUS_VALIDATION_ERROR) {
-               status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
-               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                       "%s", validation_error);
-               goto end;
-       } else if (validation_status == BT_PARAM_VALIDATION_STATUS_MEMORY_ERROR) {
-               status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
-               goto end;
-       }
-
-       value = bt_value_map_borrow_entry_value_const(params, "path");
-       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) {
-               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) {
-               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) {
-               fs_sink->ignore_discarded_packets =
-                       (bool) bt_value_bool_get(value);
-       }
-
-       value = bt_value_map_borrow_entry_value_const(params,
-               "quiet");
-       if (value) {
-               fs_sink->quiet = (bool) bt_value_bool_get(value);
-       }
-
-       status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
-
-end:
-       g_free(validation_error);
-       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_MESSAGE_ITERATOR_PUT_REF_AND_RESET(
-               fs_sink->upstream_iter);
-       g_free(fs_sink);
-
-end:
-       return;
-}
-
-BT_HIDDEN
-bt_component_class_initialize_method_status ctf_fs_sink_init(
-               bt_self_component_sink *self_comp_sink,
-               bt_self_component_sink_configuration *config,
-               const bt_value *params,
-               void *init_method_data)
-{
-       bt_component_class_initialize_method_status status;
-       bt_self_component_add_port_status add_port_status;
-       struct fs_sink_comp *fs_sink = NULL;
-       bt_self_component *self_comp =
-               bt_self_component_sink_as_self_component(self_comp_sink);
-       bt_logging_level log_level = bt_component_get_logging_level(
-               bt_self_component_as_component(self_comp));
-
-       fs_sink = g_new0(struct fs_sink_comp, 1);
-       if (!fs_sink) {
-               BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, log_level, self_comp,
-                       "Failed to allocate one CTF FS sink structure.");
-               BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
-                       self_comp, "Failed to allocate one CTF FS sink structure.");
-               status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
-               goto end;
-       }
-
-       fs_sink->log_level = log_level;
-       fs_sink->self_comp = self_comp;
-       fs_sink->output_dir_path = g_string_new(NULL);
-       status = configure_component(fs_sink, params);
-       if (status != BT_COMPONENT_CLASS_INITIALIZE_METHOD_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_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Single trace mode, but output path exists: output-path=\"%s\"",
-                       fs_sink->output_dir_path->str);
-               status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
-               goto end;
-       }
-
-       status = ensure_output_dir_exists(fs_sink);
-       if (status != BT_COMPONENT_CLASS_INITIALIZE_METHOD_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_COMP_LOGE_APPEND_CAUSE(self_comp, "Failed to allocate one GHashTable.");
-               status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
-               goto end;
-       }
-
-       add_port_status = bt_self_component_sink_add_input_port(
-               self_comp_sink, in_port_name, NULL, NULL);
-       if (add_port_status != BT_SELF_COMPONENT_ADD_PORT_STATUS_OK) {
-               status = (int) add_port_status;
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Failed to add input port.");
-               goto end;
-       }
-
-       bt_self_component_set_data(self_comp, fs_sink);
-
-end:
-       if (status != BT_COMPONENT_CLASS_INITIALIZE_METHOD_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 (G_UNLIKELY(!trace)) {
-               if (fs_sink->assume_single_trace &&
-                               g_hash_table_size(fs_sink->traces) > 0) {
-                       BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                               "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 (G_UNLIKELY(!stream)) {
-               stream = fs_sink_stream_create(trace, ir_stream);
-               if (!stream) {
-                       goto end;
-               }
-       }
-
-end:
-       return stream;
-}
-
-static inline
-bt_component_class_sink_consume_method_status handle_event_msg(
-               struct fs_sink_comp *fs_sink, const bt_message *msg)
-{
-       int ret;
-       bt_component_class_sink_consume_method_status status =
-               BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_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 (G_UNLIKELY(!stream)) {
-               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                       "Failed to borrow stream.");
-               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
-               goto end;
-       }
-
-       ret = try_translate_event_class_trace_ir_to_ctf_ir(fs_sink,
-               stream->sc, bt_event_borrow_class_const(ir_event), &ec);
-       if (ret) {
-               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                       "Failed to translate event class to CTF IR.");
-               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
-               goto end;
-       }
-
-       BT_ASSERT_DBG(ec);
-
-       if (stream->sc->default_clock_class) {
-               cs = bt_message_event_borrow_default_clock_snapshot_const(
-                       msg);
-       }
-
-       /*
-        * If this event's stream does not support packets, then we
-        * lazily create artificial packets.
-        *
-        * The size of an artificial packet is arbitrarily at least
-        * 4 MiB (it usually is greater because we close it when
-        * comes the time to write a new event and the packet's content
-        * size is >= 4 MiB), except the last one which can be smaller.
-        */
-       if (G_UNLIKELY(!stream->sc->has_packets)) {
-               if (stream->packet_state.is_open &&
-                               bt_ctfser_get_offset_in_current_packet_bits(&stream->ctfser) / 8 >=
-                               4 * 1024 * 1024) {
-                       /*
-                        * Stream's current packet is larger than 4 MiB:
-                        * close it. A new packet will be opened just
-                        * below.
-                        */
-                       ret = fs_sink_stream_close_packet(stream, NULL);
-                       if (ret) {
-                               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                                       "Failed to close packet.");
-                               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
-                               goto end;
-                       }
-               }
-
-               if (!stream->packet_state.is_open) {
-                       /* Stream's packet is not currently opened: open it */
-                       ret = fs_sink_stream_open_packet(stream, NULL, NULL);
-                       if (ret) {
-                               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                                       "Failed to open packet.");
-                               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
-                               goto end;
-                       }
-               }
-       }
-
-       BT_ASSERT_DBG(stream->packet_state.is_open);
-       ret = fs_sink_stream_write_event(stream, cs, ir_event, ec);
-       if (G_UNLIKELY(ret)) {
-               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                       "Failed to write event.");
-               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
-               goto end;
-       }
-
-end:
-       return status;
-}
-
-static inline
-bt_component_class_sink_consume_method_status handle_packet_beginning_msg(
-               struct fs_sink_comp *fs_sink, const bt_message *msg)
-{
-       int ret;
-       bt_component_class_sink_consume_method_status status =
-               BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_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 (G_UNLIKELY(!stream)) {
-               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                       "Failed to borrow stream.");
-               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
-               goto end;
-       }
-
-       if (stream->sc->packets_have_ts_begin) {
-               cs = bt_message_packet_beginning_borrow_default_clock_snapshot_const(
-                       msg);
-               BT_ASSERT(cs);
-       }
-
-       /*
-        * If we previously received a discarded events message with
-        * a time range, make sure that its beginning time matches what's
-        * expected for CTF 1.8, that is:
-        *
-        * * Its beginning time is the previous packet's end
-        *   time (or the current packet's beginning time if
-        *   this is the first packet).
-        *
-        * We check this here instead of in handle_packet_end_msg()
-        * because we want to catch any incompatible message as early as
-        * possible to report the error.
-        *
-        * Validation of the discarded events message's end time is
-        * performed in handle_packet_end_msg().
-        */
-       if (stream->discarded_events_state.in_range) {
-               uint64_t expected_cs;
-
-               /*
-                * `stream->discarded_events_state.in_range` is only set
-                * when the stream class's discarded events have a time
-                * range.
-                *
-                * It is required that the packet beginning and end
-                * messages for this stream class have times when
-                * discarded events have a time range.
-                */
-               BT_ASSERT(stream->sc->discarded_events_has_ts);
-               BT_ASSERT(stream->sc->packets_have_ts_begin);
-               BT_ASSERT(stream->sc->packets_have_ts_end);
-
-               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_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                               "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_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
-                       goto end;
-               }
-       }
-
-       /*
-        * If we previously received a discarded packets message with a
-        * time range, make sure that its beginning and end times match
-        * what's expected for CTF 1.8, that is:
-        *
-        * * Its beginning time is the previous packet's end time.
-        *
-        * * Its end time is the current packet's beginning time.
-        */
-       if (stream->discarded_packets_state.in_range) {
-               uint64_t expected_end_cs;
-
-               /*
-                * `stream->discarded_packets_state.in_range` is only
-                * set when the stream class's discarded packets have a
-                * time range.
-                *
-                * It is required that the packet beginning and end
-                * messages for this stream class have times when
-                * discarded packets have a time range.
-                */
-               BT_ASSERT(stream->sc->discarded_packets_has_ts);
-               BT_ASSERT(stream->sc->packets_have_ts_begin);
-               BT_ASSERT(stream->sc->packets_have_ts_end);
-
-               /*
-                * It is not supported to have a discarded packets
-                * message _before_ the first packet: we cannot validate
-                * that its beginning time is compatible with CTF 1.8 in
-                * this case.
-                */
-               if (stream->prev_packet_state.end_cs == UINT64_C(-1)) {
-                       BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                               "Incompatible discarded packets message "
-                               "occurring 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_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
-                       goto end;
-               }
-
-               if (stream->discarded_packets_state.beginning_cs !=
-                               stream->prev_packet_state.end_cs) {
-                       BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                               "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_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
-                       goto end;
-               }
-
-               expected_end_cs = bt_clock_snapshot_get_value(cs);
-
-               if (stream->discarded_packets_state.end_cs !=
-                               expected_end_cs) {
-                       BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                               "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.end_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_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
-                       goto end;
-               }
-       }
-
-       /*
-        * We're not in a discarded packets time range anymore since we
-        * require that the discarded packets time ranges go from one
-        * packet's end time to the next packet's beginning time, and
-        * we're handling a packet beginning message here.
-        */
-       stream->discarded_packets_state.in_range = false;
-
-       ret = fs_sink_stream_open_packet(stream, cs, ir_packet);
-       if (ret) {
-               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                       "Failed to open packet.");
-               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
-               goto end;
-       }
-
-end:
-       return status;
-}
-
-static inline
-bt_component_class_sink_consume_method_status handle_packet_end_msg(
-               struct fs_sink_comp *fs_sink, const bt_message *msg)
-{
-       int ret;
-       bt_component_class_sink_consume_method_status status =
-               BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_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 (G_UNLIKELY(!stream)) {
-               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                       "Failed to borrow stream.");
-               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
-               goto end;
-       }
-
-       if (stream->sc->packets_have_ts_end) {
-               cs = bt_message_packet_end_borrow_default_clock_snapshot_const(
-                       msg);
-               BT_ASSERT(cs);
-       }
-
-       /*
-        * If we previously received a discarded events message with
-        * a time range, make sure that its end time matches what's
-        * expected for CTF 1.8, that is:
-        *
-        * * Its end time is the current packet's end time.
-        *
-        * Validation of the discarded events message's beginning time
-        * is performed in handle_packet_beginning_msg().
-        */
-       if (stream->discarded_events_state.in_range) {
-               uint64_t expected_cs;
-
-               /*
-                * `stream->discarded_events_state.in_range` is only set
-                * when the stream class's discarded events have a time
-                * range.
-                *
-                * It is required that the packet beginning and end
-                * messages for this stream class have times when
-                * discarded events have a time range.
-                */
-               BT_ASSERT(stream->sc->discarded_events_has_ts);
-               BT_ASSERT(stream->sc->packets_have_ts_begin);
-               BT_ASSERT(stream->sc->packets_have_ts_end);
-
-               expected_cs = bt_clock_snapshot_get_value(cs);
-
-               if (stream->discarded_events_state.end_cs != expected_cs) {
-                       BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                               "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_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
-                       goto end;
-               }
-       }
-
-       ret = fs_sink_stream_close_packet(stream, cs);
-       if (ret) {
-               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                       "Failed to close packet.");
-               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
-               goto end;
-       }
-
-       /*
-        * We're not in a discarded events time range anymore since we
-        * require that the discarded events time ranges go from one
-        * packet's end time to the next packet's end time, and we're
-        * handling a packet end message here.
-        */
-       stream->discarded_events_state.in_range = false;
-
-end:
-       return status;
-}
-
-static inline
-bt_component_class_sink_consume_method_status handle_stream_beginning_msg(
-               struct fs_sink_comp *fs_sink, const bt_message *msg)
-{
-       bt_component_class_sink_consume_method_status status =
-               BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK;
-       const bt_stream *ir_stream =
-               bt_message_stream_beginning_borrow_stream_const(msg);
-       const bt_stream_class *ir_sc =
-               bt_stream_borrow_class_const(ir_stream);
-       struct fs_sink_stream *stream;
-       bool packets_have_beginning_end_cs =
-               bt_stream_class_packets_have_beginning_default_clock_snapshot(ir_sc) &&
-               bt_stream_class_packets_have_end_default_clock_snapshot(ir_sc);
-
-       /*
-        * Not supported: discarded events or discarded packets support
-        * without packets support. Packets are the way to know where
-        * discarded events/packets occurred in CTF 1.8.
-        */
-       if (!bt_stream_class_supports_packets(ir_sc)) {
-               BT_ASSERT(!bt_stream_class_supports_discarded_packets(ir_sc));
-
-               if (!fs_sink->ignore_discarded_events &&
-                               bt_stream_class_supports_discarded_events(ir_sc)) {
-                       BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                               "Unsupported stream: "
-                               "stream does not support packets, "
-                               "but supports discarded events: "
-                               "stream-addr=%p, "
-                               "stream-id=%" PRIu64 ", "
-                               "stream-name=\"%s\"",
-                               ir_stream, bt_stream_get_id(ir_stream),
-                               bt_stream_get_name(ir_stream));
-                       status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
-                       goto end;
-               }
-       }
-
-       /*
-        * Not supported: discarded events with default clock snapshots,
-        * but packet beginning/end without default clock snapshot.
-        */
-       if (!fs_sink->ignore_discarded_events &&
-                       bt_stream_class_discarded_events_have_default_clock_snapshots(ir_sc) &&
-                       !packets_have_beginning_end_cs) {
-               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                       "Unsupported stream: discarded events have "
-                       "default clock snapshots, but packets have no "
-                       "beginning and/or end default clock snapshots: "
-                       "stream-addr=%p, "
-                       "stream-id=%" PRIu64 ", "
-                       "stream-name=\"%s\"",
-                       ir_stream, bt_stream_get_id(ir_stream),
-                       bt_stream_get_name(ir_stream));
-               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
-               goto end;
-       }
-
-       /*
-        * Not supported: discarded packets with default clock
-        * snapshots, but packet beginning/end without default clock
-        * snapshot.
-        */
-       if (!fs_sink->ignore_discarded_packets &&
-                       bt_stream_class_discarded_packets_have_default_clock_snapshots(ir_sc) &&
-                       !packets_have_beginning_end_cs) {
-               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                       "Unsupported stream: discarded packets have "
-                       "default clock snapshots, but packets have no "
-                       "beginning and/or end default clock snapshots: "
-                       "stream-addr=%p, "
-                       "stream-id=%" PRIu64 ", "
-                       "stream-name=\"%s\"",
-                       ir_stream, bt_stream_get_id(ir_stream),
-                       bt_stream_get_name(ir_stream));
-               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
-               goto end;
-       }
-
-       stream = borrow_stream(fs_sink, ir_stream);
-       if (!stream) {
-               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                       "Failed to borrow stream.");
-               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
-               goto end;
-       }
-
-       BT_COMP_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_component_class_sink_consume_method_status handle_stream_end_msg(
-               struct fs_sink_comp *fs_sink, const bt_message *msg)
-{
-       bt_component_class_sink_consume_method_status status =
-               BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_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) {
-               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                       "Failed to borrow stream.");
-               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
-               goto end;
-       }
-
-       if (G_UNLIKELY(!stream->sc->has_packets &&
-                       stream->packet_state.is_open)) {
-               /* Close stream's current artificial packet */
-               int ret = fs_sink_stream_close_packet(stream, NULL);
-
-               if (ret) {
-                       BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                               "Failed to close packet.");
-                       status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
-                       goto end;
-               }
-       }
-
-       BT_COMP_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_component_class_sink_consume_method_status handle_discarded_events_msg(
-               struct fs_sink_comp *fs_sink, const bt_message *msg)
-{
-       bt_component_class_sink_consume_method_status status =
-               BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_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) {
-               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                       "Failed to borrow stream.");
-               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
-               goto end;
-       }
-
-       if (fs_sink->ignore_discarded_events) {
-               BT_COMP_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_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                       "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_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
-               goto end;
-       }
-
-       /*
-        * If we're currently in an opened packet (got a packet
-        * beginning message, but no packet end message yet), we do not
-        * support having a discarded events message with a time range
-        * because we require that the discarded events message's time
-        * range go from a packet's end time to the next packet's end
-        * time.
-        */
-       if (stream->packet_state.is_open &&
-                       stream->sc->discarded_events_has_ts) {
-               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                       "Unsupported discarded events message with "
-                       "default clock snapshots occurring 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_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
-               goto end;
-       }
-
-       if (stream->sc->discarded_events_has_ts) {
-               /*
-                * Make the stream's state be in the time range of a
-                * discarded events message since we have the message's
-                * time range (`stream->sc->discarded_events_has_ts`).
-                */
-               stream->discarded_events_state.in_range = true;
-
-               /*
-                * The clock snapshot values will be validated when
-                * handling the next packet beginning and end messages
-                * (next calls to handle_packet_beginning_msg() and
-                * handle_packet_end_msg()).
-                */
-               cs = bt_message_discarded_events_borrow_beginning_default_clock_snapshot_const(
-                       msg);
-               BT_ASSERT(cs);
-               stream->discarded_events_state.beginning_cs =
-                       bt_clock_snapshot_get_value(cs);
-               cs = bt_message_discarded_events_borrow_end_default_clock_snapshot_const(
-                       msg);
-               BT_ASSERT(cs);
-               stream->discarded_events_state.end_cs = bt_clock_snapshot_get_value(cs);
-       }
-
-       avail = bt_message_discarded_events_get_count(msg, &count);
-       if (avail != BT_PROPERTY_AVAILABILITY_AVAILABLE) {
-               /*
-                * There's no specific count of discarded events: set it
-                * to 1 so that we know that we at least discarded
-                * something.
-                */
-               count = 1;
-       }
-
-       stream->packet_state.discarded_events_counter += count;
-
-end:
-       return status;
-}
-
-static inline
-bt_component_class_sink_consume_method_status handle_discarded_packets_msg(
-               struct fs_sink_comp *fs_sink, const bt_message *msg)
-{
-       bt_component_class_sink_consume_method_status status =
-               BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_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) {
-               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                       "Failed to borrow stream.");
-               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
-               goto end;
-       }
-
-       if (fs_sink->ignore_discarded_packets) {
-               BT_COMP_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_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                       "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_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
-               goto end;
-       }
-
-       /*
-        * Discarded packets messages are guaranteed to occur between
-        * packets.
-        */
-       BT_ASSERT(!stream->packet_state.is_open);
-
-       if (stream->sc->discarded_packets_has_ts) {
-               /*
-                * Make the stream's state be in the time range of a
-                * discarded packets message since we have the message's
-                * time range (`stream->sc->discarded_packets_has_ts`).
-                */
-               stream->discarded_packets_state.in_range = true;
-
-               /*
-                * The clock snapshot values will be validated when
-                * handling the next packet beginning message (next call
-                * to handle_packet_beginning_msg()).
-                */
-               cs = bt_message_discarded_packets_borrow_beginning_default_clock_snapshot_const(
-                       msg);
-               BT_ASSERT(cs);
-               stream->discarded_packets_state.beginning_cs =
-                       bt_clock_snapshot_get_value(cs);
-               cs = bt_message_discarded_packets_borrow_end_default_clock_snapshot_const(
-                       msg);
-               BT_ASSERT(cs);
-               stream->discarded_packets_state.end_cs =
-                       bt_clock_snapshot_get_value(cs);
-       }
-
-       avail = bt_message_discarded_packets_get_count(msg, &count);
-       if (avail != BT_PROPERTY_AVAILABILITY_AVAILABLE) {
-               /*
-                * There's no specific count of discarded packets: set
-                * it to 1 so that we know that we at least discarded
-                * something.
-                */
-               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_component_class_sink_consume_method_status ctf_fs_sink_consume(
-               bt_self_component_sink *self_comp)
-{
-       bt_component_class_sink_consume_method_status status =
-               BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK;
-       struct fs_sink_comp *fs_sink;
-       bt_message_iterator_next_status next_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_DBG(fs_sink);
-       BT_ASSERT_DBG(fs_sink->upstream_iter);
-
-       /* Consume messages */
-       next_status = bt_message_iterator_next(
-               fs_sink->upstream_iter, &msgs, &msg_count);
-       if (next_status < 0) {
-               status = (int) next_status;
-               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                       "Failed to get next message from upstream iterator.");
-               goto end;
-       }
-
-       switch (next_status) {
-       case BT_MESSAGE_ITERATOR_NEXT_STATUS_OK:
-       {
-               uint64_t i;
-
-               for (i = 0; i < msg_count; i++) {
-                       const bt_message *msg = msgs[i];
-
-                       BT_ASSERT_DBG(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_COMP_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_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:
-                               bt_common_abort();
-                       }
-
-                       BT_MESSAGE_PUT_REF_AND_RESET(msgs[i]);
-
-                       if (status != BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK) {
-                               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                                       "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_NEXT_STATUS_AGAIN:
-               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_AGAIN;
-               break;
-       case BT_MESSAGE_ITERATOR_NEXT_STATUS_END:
-               /* TODO: Finalize all traces (should already be done?) */
-               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_END;
-               break;
-       default:
-               break;
-       }
-
-       goto end;
-
-error:
-       BT_ASSERT(status != BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK);
-       put_messages(msgs, msg_count);
-
-end:
-       return status;
-}
-
-BT_HIDDEN
-bt_component_class_sink_graph_is_configured_method_status
-ctf_fs_sink_graph_is_configured(
-               bt_self_component_sink *self_comp)
-{
-       bt_component_class_sink_graph_is_configured_method_status status;
-       bt_message_iterator_create_from_sink_component_status
-               msg_iter_status;
-       struct fs_sink_comp *fs_sink = bt_self_component_get_data(
-                       bt_self_component_sink_as_self_component(self_comp));
-
-       msg_iter_status =
-               bt_message_iterator_create_from_sink_component(
-                       self_comp,
-                       bt_self_component_sink_borrow_input_port_by_name(
-                               self_comp, in_port_name), &fs_sink->upstream_iter);
-       if (msg_iter_status != BT_MESSAGE_ITERATOR_CREATE_FROM_SINK_COMPONENT_STATUS_OK) {
-               status = (int) msg_iter_status;
-               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
-                       "Failed to create upstream iterator.");
-               goto end;
-       }
-
-       status = BT_COMPONENT_CLASS_SINK_GRAPH_IS_CONFIGURED_METHOD_STATUS_OK;
-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);
-}
diff --git a/src/plugins/ctf/fs-sink/fs-sink.cpp b/src/plugins/ctf/fs-sink/fs-sink.cpp
new file mode 100644 (file)
index 0000000..42a616c
--- /dev/null
@@ -0,0 +1,1147 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#define BT_COMP_LOG_SELF_COMP (fs_sink->self_comp)
+#define BT_LOG_OUTPUT_LEVEL (fs_sink->log_level)
+#define BT_LOG_TAG "PLUGIN/SINK.CTF.FS"
+#include "logging/comp-logging.h"
+
+#include <babeltrace2/babeltrace.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <glib.h>
+#include "common/assert.h"
+#include "ctfser/ctfser.h"
+#include "plugins/common/param-validation/param-validation.h"
+
+#include "fs-sink.hpp"
+#include "fs-sink-trace.hpp"
+#include "fs-sink-stream.hpp"
+#include "fs-sink-ctf-meta.hpp"
+#include "translate-trace-ir-to-ctf-ir.hpp"
+#include "translate-ctf-ir-to-tsdl.hpp"
+
+static
+const char * const in_port_name = "in";
+
+static
+bt_component_class_initialize_method_status ensure_output_dir_exists(
+               struct fs_sink_comp *fs_sink)
+{
+       bt_component_class_initialize_method_status status =
+               BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
+       int ret;
+
+       ret = g_mkdir_with_parents(fs_sink->output_dir_path->str, 0755);
+       if (ret) {
+               BT_COMP_LOGE_APPEND_CAUSE_ERRNO(fs_sink->self_comp,
+                       "Cannot create directories for output directory",
+                       ": output-dir-path=\"%s\"",
+                       fs_sink->output_dir_path->str);
+               status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
+               goto end;
+       }
+
+end:
+       return status;
+}
+
+static bt_param_validation_map_value_entry_descr fs_sink_params_descr[] = {
+       { "path", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY,
+               { bt_param_validation_value_descr::string_t } },
+       { "assume-single-trace", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL,
+               { bt_param_validation_value_descr::bool_t } },
+       { "ignore-discarded-events", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL,
+               { bt_param_validation_value_descr::bool_t } },
+       { "ignore-discarded-packets", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL,
+               { bt_param_validation_value_descr::bool_t } },
+       { "quiet", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL,
+               { bt_param_validation_value_descr::bool_t } },
+       BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
+};
+
+static
+bt_component_class_initialize_method_status
+configure_component(struct fs_sink_comp *fs_sink, const bt_value *params)
+{
+       bt_component_class_initialize_method_status status;
+       const bt_value *value;
+       enum bt_param_validation_status validation_status;
+       gchar *validation_error;
+
+       validation_status = bt_param_validation_validate(params,
+               fs_sink_params_descr, &validation_error);
+       if (validation_status == BT_PARAM_VALIDATION_STATUS_VALIDATION_ERROR) {
+               status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
+               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
+                       "%s", validation_error);
+               goto end;
+       } else if (validation_status == BT_PARAM_VALIDATION_STATUS_MEMORY_ERROR) {
+               status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
+               goto end;
+       }
+
+       value = bt_value_map_borrow_entry_value_const(params, "path");
+       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) {
+               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) {
+               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) {
+               fs_sink->ignore_discarded_packets =
+                       (bool) bt_value_bool_get(value);
+       }
+
+       value = bt_value_map_borrow_entry_value_const(params,
+               "quiet");
+       if (value) {
+               fs_sink->quiet = (bool) bt_value_bool_get(value);
+       }
+
+       status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
+
+end:
+       g_free(validation_error);
+       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_MESSAGE_ITERATOR_PUT_REF_AND_RESET(
+               fs_sink->upstream_iter);
+       g_free(fs_sink);
+
+end:
+       return;
+}
+
+BT_HIDDEN
+bt_component_class_initialize_method_status ctf_fs_sink_init(
+               bt_self_component_sink *self_comp_sink,
+               bt_self_component_sink_configuration *config,
+               const bt_value *params,
+               void *init_method_data)
+{
+       bt_component_class_initialize_method_status status;
+       bt_self_component_add_port_status add_port_status;
+       struct fs_sink_comp *fs_sink = NULL;
+       bt_self_component *self_comp =
+               bt_self_component_sink_as_self_component(self_comp_sink);
+       bt_logging_level log_level = bt_component_get_logging_level(
+               bt_self_component_as_component(self_comp));
+
+       fs_sink = g_new0(struct fs_sink_comp, 1);
+       if (!fs_sink) {
+               BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, log_level, self_comp,
+                       "Failed to allocate one CTF FS sink structure.");
+               BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
+                       self_comp, "Failed to allocate one CTF FS sink structure.");
+               status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
+               goto end;
+       }
+
+       fs_sink->log_level = log_level;
+       fs_sink->self_comp = self_comp;
+       fs_sink->output_dir_path = g_string_new(NULL);
+       status = configure_component(fs_sink, params);
+       if (status != BT_COMPONENT_CLASS_INITIALIZE_METHOD_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_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Single trace mode, but output path exists: output-path=\"%s\"",
+                       fs_sink->output_dir_path->str);
+               status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
+               goto end;
+       }
+
+       status = ensure_output_dir_exists(fs_sink);
+       if (status != BT_COMPONENT_CLASS_INITIALIZE_METHOD_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_COMP_LOGE_APPEND_CAUSE(self_comp, "Failed to allocate one GHashTable.");
+               status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
+               goto end;
+       }
+
+       add_port_status = bt_self_component_sink_add_input_port(
+               self_comp_sink, in_port_name, NULL, NULL);
+       if (add_port_status != BT_SELF_COMPONENT_ADD_PORT_STATUS_OK) {
+               status = (bt_component_class_initialize_method_status) add_port_status;
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Failed to add input port.");
+               goto end;
+       }
+
+       bt_self_component_set_data(self_comp, fs_sink);
+
+end:
+       if (status != BT_COMPONENT_CLASS_INITIALIZE_METHOD_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 = (fs_sink_trace *) g_hash_table_lookup(fs_sink->traces, ir_trace);
+       if (G_UNLIKELY(!trace)) {
+               if (fs_sink->assume_single_trace &&
+                               g_hash_table_size(fs_sink->traces) > 0) {
+                       BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
+                               "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 = (fs_sink_stream *) g_hash_table_lookup(trace->streams, ir_stream);
+       if (G_UNLIKELY(!stream)) {
+               stream = fs_sink_stream_create(trace, ir_stream);
+               if (!stream) {
+                       goto end;
+               }
+       }
+
+end:
+       return stream;
+}
+
+static inline
+bt_component_class_sink_consume_method_status handle_event_msg(
+               struct fs_sink_comp *fs_sink, const bt_message *msg)
+{
+       int ret;
+       bt_component_class_sink_consume_method_status status =
+               BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_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 (G_UNLIKELY(!stream)) {
+               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
+                       "Failed to borrow stream.");
+               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+               goto end;
+       }
+
+       ret = try_translate_event_class_trace_ir_to_ctf_ir(fs_sink,
+               stream->sc, bt_event_borrow_class_const(ir_event), &ec);
+       if (ret) {
+               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
+                       "Failed to translate event class to CTF IR.");
+               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+               goto end;
+       }
+
+       BT_ASSERT_DBG(ec);
+
+       if (stream->sc->default_clock_class) {
+               cs = bt_message_event_borrow_default_clock_snapshot_const(
+                       msg);
+       }
+
+       /*
+        * If this event's stream does not support packets, then we
+        * lazily create artificial packets.
+        *
+        * The size of an artificial packet is arbitrarily at least
+        * 4 MiB (it usually is greater because we close it when
+        * comes the time to write a new event and the packet's content
+        * size is >= 4 MiB), except the last one which can be smaller.
+        */
+       if (G_UNLIKELY(!stream->sc->has_packets)) {
+               if (stream->packet_state.is_open &&
+                               bt_ctfser_get_offset_in_current_packet_bits(&stream->ctfser) / 8 >=
+                               4 * 1024 * 1024) {
+                       /*
+                        * Stream's current packet is larger than 4 MiB:
+                        * close it. A new packet will be opened just
+                        * below.
+                        */
+                       ret = fs_sink_stream_close_packet(stream, NULL);
+                       if (ret) {
+                               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
+                                       "Failed to close packet.");
+                               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+                               goto end;
+                       }
+               }
+
+               if (!stream->packet_state.is_open) {
+                       /* Stream's packet is not currently opened: open it */
+                       ret = fs_sink_stream_open_packet(stream, NULL, NULL);
+                       if (ret) {
+                               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
+                                       "Failed to open packet.");
+                               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+                               goto end;
+                       }
+               }
+       }
+
+       BT_ASSERT_DBG(stream->packet_state.is_open);
+       ret = fs_sink_stream_write_event(stream, cs, ir_event, ec);
+       if (G_UNLIKELY(ret)) {
+               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
+                       "Failed to write event.");
+               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+               goto end;
+       }
+
+end:
+       return status;
+}
+
+static inline
+bt_component_class_sink_consume_method_status handle_packet_beginning_msg(
+               struct fs_sink_comp *fs_sink, const bt_message *msg)
+{
+       int ret;
+       bt_component_class_sink_consume_method_status status =
+               BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_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 (G_UNLIKELY(!stream)) {
+               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
+                       "Failed to borrow stream.");
+               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+               goto end;
+       }
+
+       if (stream->sc->packets_have_ts_begin) {
+               cs = bt_message_packet_beginning_borrow_default_clock_snapshot_const(
+                       msg);
+               BT_ASSERT(cs);
+       }
+
+       /*
+        * If we previously received a discarded events message with
+        * a time range, make sure that its beginning time matches what's
+        * expected for CTF 1.8, that is:
+        *
+        * * Its beginning time is the previous packet's end
+        *   time (or the current packet's beginning time if
+        *   this is the first packet).
+        *
+        * We check this here instead of in handle_packet_end_msg()
+        * because we want to catch any incompatible message as early as
+        * possible to report the error.
+        *
+        * Validation of the discarded events message's end time is
+        * performed in handle_packet_end_msg().
+        */
+       if (stream->discarded_events_state.in_range) {
+               uint64_t expected_cs;
+
+               /*
+                * `stream->discarded_events_state.in_range` is only set
+                * when the stream class's discarded events have a time
+                * range.
+                *
+                * It is required that the packet beginning and end
+                * messages for this stream class have times when
+                * discarded events have a time range.
+                */
+               BT_ASSERT(stream->sc->discarded_events_has_ts);
+               BT_ASSERT(stream->sc->packets_have_ts_begin);
+               BT_ASSERT(stream->sc->packets_have_ts_end);
+
+               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_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
+                               "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_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+                       goto end;
+               }
+       }
+
+       /*
+        * If we previously received a discarded packets message with a
+        * time range, make sure that its beginning and end times match
+        * what's expected for CTF 1.8, that is:
+        *
+        * * Its beginning time is the previous packet's end time.
+        *
+        * * Its end time is the current packet's beginning time.
+        */
+       if (stream->discarded_packets_state.in_range) {
+               uint64_t expected_end_cs;
+
+               /*
+                * `stream->discarded_packets_state.in_range` is only
+                * set when the stream class's discarded packets have a
+                * time range.
+                *
+                * It is required that the packet beginning and end
+                * messages for this stream class have times when
+                * discarded packets have a time range.
+                */
+               BT_ASSERT(stream->sc->discarded_packets_has_ts);
+               BT_ASSERT(stream->sc->packets_have_ts_begin);
+               BT_ASSERT(stream->sc->packets_have_ts_end);
+
+               /*
+                * It is not supported to have a discarded packets
+                * message _before_ the first packet: we cannot validate
+                * that its beginning time is compatible with CTF 1.8 in
+                * this case.
+                */
+               if (stream->prev_packet_state.end_cs == UINT64_C(-1)) {
+                       BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
+                               "Incompatible discarded packets message "
+                               "occurring 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_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+                       goto end;
+               }
+
+               if (stream->discarded_packets_state.beginning_cs !=
+                               stream->prev_packet_state.end_cs) {
+                       BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
+                               "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_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+                       goto end;
+               }
+
+               expected_end_cs = bt_clock_snapshot_get_value(cs);
+
+               if (stream->discarded_packets_state.end_cs !=
+                               expected_end_cs) {
+                       BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
+                               "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.end_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_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+                       goto end;
+               }
+       }
+
+       /*
+        * We're not in a discarded packets time range anymore since we
+        * require that the discarded packets time ranges go from one
+        * packet's end time to the next packet's beginning time, and
+        * we're handling a packet beginning message here.
+        */
+       stream->discarded_packets_state.in_range = false;
+
+       ret = fs_sink_stream_open_packet(stream, cs, ir_packet);
+       if (ret) {
+               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
+                       "Failed to open packet.");
+               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+               goto end;
+       }
+
+end:
+       return status;
+}
+
+static inline
+bt_component_class_sink_consume_method_status handle_packet_end_msg(
+               struct fs_sink_comp *fs_sink, const bt_message *msg)
+{
+       int ret;
+       bt_component_class_sink_consume_method_status status =
+               BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_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 (G_UNLIKELY(!stream)) {
+               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
+                       "Failed to borrow stream.");
+               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+               goto end;
+       }
+
+       if (stream->sc->packets_have_ts_end) {
+               cs = bt_message_packet_end_borrow_default_clock_snapshot_const(
+                       msg);
+               BT_ASSERT(cs);
+       }
+
+       /*
+        * If we previously received a discarded events message with
+        * a time range, make sure that its end time matches what's
+        * expected for CTF 1.8, that is:
+        *
+        * * Its end time is the current packet's end time.
+        *
+        * Validation of the discarded events message's beginning time
+        * is performed in handle_packet_beginning_msg().
+        */
+       if (stream->discarded_events_state.in_range) {
+               uint64_t expected_cs;
+
+               /*
+                * `stream->discarded_events_state.in_range` is only set
+                * when the stream class's discarded events have a time
+                * range.
+                *
+                * It is required that the packet beginning and end
+                * messages for this stream class have times when
+                * discarded events have a time range.
+                */
+               BT_ASSERT(stream->sc->discarded_events_has_ts);
+               BT_ASSERT(stream->sc->packets_have_ts_begin);
+               BT_ASSERT(stream->sc->packets_have_ts_end);
+
+               expected_cs = bt_clock_snapshot_get_value(cs);
+
+               if (stream->discarded_events_state.end_cs != expected_cs) {
+                       BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
+                               "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_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+                       goto end;
+               }
+       }
+
+       ret = fs_sink_stream_close_packet(stream, cs);
+       if (ret) {
+               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
+                       "Failed to close packet.");
+               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+               goto end;
+       }
+
+       /*
+        * We're not in a discarded events time range anymore since we
+        * require that the discarded events time ranges go from one
+        * packet's end time to the next packet's end time, and we're
+        * handling a packet end message here.
+        */
+       stream->discarded_events_state.in_range = false;
+
+end:
+       return status;
+}
+
+static inline
+bt_component_class_sink_consume_method_status handle_stream_beginning_msg(
+               struct fs_sink_comp *fs_sink, const bt_message *msg)
+{
+       bt_component_class_sink_consume_method_status status =
+               BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK;
+       const bt_stream *ir_stream =
+               bt_message_stream_beginning_borrow_stream_const(msg);
+       const bt_stream_class *ir_sc =
+               bt_stream_borrow_class_const(ir_stream);
+       struct fs_sink_stream *stream;
+       bool packets_have_beginning_end_cs =
+               bt_stream_class_packets_have_beginning_default_clock_snapshot(ir_sc) &&
+               bt_stream_class_packets_have_end_default_clock_snapshot(ir_sc);
+
+       /*
+        * Not supported: discarded events or discarded packets support
+        * without packets support. Packets are the way to know where
+        * discarded events/packets occurred in CTF 1.8.
+        */
+       if (!bt_stream_class_supports_packets(ir_sc)) {
+               BT_ASSERT(!bt_stream_class_supports_discarded_packets(ir_sc));
+
+               if (!fs_sink->ignore_discarded_events &&
+                               bt_stream_class_supports_discarded_events(ir_sc)) {
+                       BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
+                               "Unsupported stream: "
+                               "stream does not support packets, "
+                               "but supports discarded events: "
+                               "stream-addr=%p, "
+                               "stream-id=%" PRIu64 ", "
+                               "stream-name=\"%s\"",
+                               ir_stream, bt_stream_get_id(ir_stream),
+                               bt_stream_get_name(ir_stream));
+                       status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+                       goto end;
+               }
+       }
+
+       /*
+        * Not supported: discarded events with default clock snapshots,
+        * but packet beginning/end without default clock snapshot.
+        */
+       if (!fs_sink->ignore_discarded_events &&
+                       bt_stream_class_discarded_events_have_default_clock_snapshots(ir_sc) &&
+                       !packets_have_beginning_end_cs) {
+               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
+                       "Unsupported stream: discarded events have "
+                       "default clock snapshots, but packets have no "
+                       "beginning and/or end default clock snapshots: "
+                       "stream-addr=%p, "
+                       "stream-id=%" PRIu64 ", "
+                       "stream-name=\"%s\"",
+                       ir_stream, bt_stream_get_id(ir_stream),
+                       bt_stream_get_name(ir_stream));
+               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+               goto end;
+       }
+
+       /*
+        * Not supported: discarded packets with default clock
+        * snapshots, but packet beginning/end without default clock
+        * snapshot.
+        */
+       if (!fs_sink->ignore_discarded_packets &&
+                       bt_stream_class_discarded_packets_have_default_clock_snapshots(ir_sc) &&
+                       !packets_have_beginning_end_cs) {
+               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
+                       "Unsupported stream: discarded packets have "
+                       "default clock snapshots, but packets have no "
+                       "beginning and/or end default clock snapshots: "
+                       "stream-addr=%p, "
+                       "stream-id=%" PRIu64 ", "
+                       "stream-name=\"%s\"",
+                       ir_stream, bt_stream_get_id(ir_stream),
+                       bt_stream_get_name(ir_stream));
+               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+               goto end;
+       }
+
+       stream = borrow_stream(fs_sink, ir_stream);
+       if (!stream) {
+               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
+                       "Failed to borrow stream.");
+               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+               goto end;
+       }
+
+       BT_COMP_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_component_class_sink_consume_method_status handle_stream_end_msg(
+               struct fs_sink_comp *fs_sink, const bt_message *msg)
+{
+       bt_component_class_sink_consume_method_status status =
+               BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_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) {
+               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
+                       "Failed to borrow stream.");
+               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+               goto end;
+       }
+
+       if (G_UNLIKELY(!stream->sc->has_packets &&
+                       stream->packet_state.is_open)) {
+               /* Close stream's current artificial packet */
+               int ret = fs_sink_stream_close_packet(stream, NULL);
+
+               if (ret) {
+                       BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
+                               "Failed to close packet.");
+                       status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+                       goto end;
+               }
+       }
+
+       BT_COMP_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_component_class_sink_consume_method_status handle_discarded_events_msg(
+               struct fs_sink_comp *fs_sink, const bt_message *msg)
+{
+       bt_component_class_sink_consume_method_status status =
+               BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_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) {
+               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
+                       "Failed to borrow stream.");
+               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+               goto end;
+       }
+
+       if (fs_sink->ignore_discarded_events) {
+               BT_COMP_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_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
+                       "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_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+               goto end;
+       }
+
+       /*
+        * If we're currently in an opened packet (got a packet
+        * beginning message, but no packet end message yet), we do not
+        * support having a discarded events message with a time range
+        * because we require that the discarded events message's time
+        * range go from a packet's end time to the next packet's end
+        * time.
+        */
+       if (stream->packet_state.is_open &&
+                       stream->sc->discarded_events_has_ts) {
+               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
+                       "Unsupported discarded events message with "
+                       "default clock snapshots occurring 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_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+               goto end;
+       }
+
+       if (stream->sc->discarded_events_has_ts) {
+               /*
+                * Make the stream's state be in the time range of a
+                * discarded events message since we have the message's
+                * time range (`stream->sc->discarded_events_has_ts`).
+                */
+               stream->discarded_events_state.in_range = true;
+
+               /*
+                * The clock snapshot values will be validated when
+                * handling the next packet beginning and end messages
+                * (next calls to handle_packet_beginning_msg() and
+                * handle_packet_end_msg()).
+                */
+               cs = bt_message_discarded_events_borrow_beginning_default_clock_snapshot_const(
+                       msg);
+               BT_ASSERT(cs);
+               stream->discarded_events_state.beginning_cs =
+                       bt_clock_snapshot_get_value(cs);
+               cs = bt_message_discarded_events_borrow_end_default_clock_snapshot_const(
+                       msg);
+               BT_ASSERT(cs);
+               stream->discarded_events_state.end_cs = bt_clock_snapshot_get_value(cs);
+       }
+
+       avail = bt_message_discarded_events_get_count(msg, &count);
+       if (avail != BT_PROPERTY_AVAILABILITY_AVAILABLE) {
+               /*
+                * There's no specific count of discarded events: set it
+                * to 1 so that we know that we at least discarded
+                * something.
+                */
+               count = 1;
+       }
+
+       stream->packet_state.discarded_events_counter += count;
+
+end:
+       return status;
+}
+
+static inline
+bt_component_class_sink_consume_method_status handle_discarded_packets_msg(
+               struct fs_sink_comp *fs_sink, const bt_message *msg)
+{
+       bt_component_class_sink_consume_method_status status =
+               BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_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) {
+               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
+                       "Failed to borrow stream.");
+               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+               goto end;
+       }
+
+       if (fs_sink->ignore_discarded_packets) {
+               BT_COMP_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_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
+                       "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_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
+               goto end;
+       }
+
+       /*
+        * Discarded packets messages are guaranteed to occur between
+        * packets.
+        */
+       BT_ASSERT(!stream->packet_state.is_open);
+
+       if (stream->sc->discarded_packets_has_ts) {
+               /*
+                * Make the stream's state be in the time range of a
+                * discarded packets message since we have the message's
+                * time range (`stream->sc->discarded_packets_has_ts`).
+                */
+               stream->discarded_packets_state.in_range = true;
+
+               /*
+                * The clock snapshot values will be validated when
+                * handling the next packet beginning message (next call
+                * to handle_packet_beginning_msg()).
+                */
+               cs = bt_message_discarded_packets_borrow_beginning_default_clock_snapshot_const(
+                       msg);
+               BT_ASSERT(cs);
+               stream->discarded_packets_state.beginning_cs =
+                       bt_clock_snapshot_get_value(cs);
+               cs = bt_message_discarded_packets_borrow_end_default_clock_snapshot_const(
+                       msg);
+               BT_ASSERT(cs);
+               stream->discarded_packets_state.end_cs =
+                       bt_clock_snapshot_get_value(cs);
+       }
+
+       avail = bt_message_discarded_packets_get_count(msg, &count);
+       if (avail != BT_PROPERTY_AVAILABILITY_AVAILABLE) {
+               /*
+                * There's no specific count of discarded packets: set
+                * it to 1 so that we know that we at least discarded
+                * something.
+                */
+               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_component_class_sink_consume_method_status ctf_fs_sink_consume(
+               bt_self_component_sink *self_comp)
+{
+       bt_component_class_sink_consume_method_status status =
+               BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK;
+       struct fs_sink_comp *fs_sink;
+       bt_message_iterator_next_status next_status;
+       uint64_t msg_count = 0;
+       bt_message_array_const msgs;
+
+       fs_sink = (fs_sink_comp *) bt_self_component_get_data(
+                       bt_self_component_sink_as_self_component(self_comp));
+       BT_ASSERT_DBG(fs_sink);
+       BT_ASSERT_DBG(fs_sink->upstream_iter);
+
+       /* Consume messages */
+       next_status = bt_message_iterator_next(
+               fs_sink->upstream_iter, &msgs, &msg_count);
+       if (next_status < 0) {
+               status = (bt_component_class_sink_consume_method_status) next_status;
+               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
+                       "Failed to get next message from upstream iterator.");
+               goto end;
+       }
+
+       switch (next_status) {
+       case BT_MESSAGE_ITERATOR_NEXT_STATUS_OK:
+       {
+               uint64_t i;
+
+               for (i = 0; i < msg_count; i++) {
+                       const bt_message *msg = msgs[i];
+
+                       BT_ASSERT_DBG(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_COMP_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_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:
+                               bt_common_abort();
+                       }
+
+                       BT_MESSAGE_PUT_REF_AND_RESET(msgs[i]);
+
+                       if (status != BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK) {
+                               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
+                                       "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_NEXT_STATUS_AGAIN:
+               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_AGAIN;
+               break;
+       case BT_MESSAGE_ITERATOR_NEXT_STATUS_END:
+               /* TODO: Finalize all traces (should already be done?) */
+               status = BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_END;
+               break;
+       default:
+               break;
+       }
+
+       goto end;
+
+error:
+       BT_ASSERT(status != BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK);
+       put_messages(msgs, msg_count);
+
+end:
+       return status;
+}
+
+BT_HIDDEN
+bt_component_class_sink_graph_is_configured_method_status
+ctf_fs_sink_graph_is_configured(
+               bt_self_component_sink *self_comp)
+{
+       bt_component_class_sink_graph_is_configured_method_status status;
+       bt_message_iterator_create_from_sink_component_status
+               msg_iter_status;
+       fs_sink_comp *fs_sink = (fs_sink_comp *) bt_self_component_get_data(
+                       bt_self_component_sink_as_self_component(self_comp));
+
+       msg_iter_status =
+               bt_message_iterator_create_from_sink_component(
+                       self_comp,
+                       bt_self_component_sink_borrow_input_port_by_name(
+                               self_comp, in_port_name), &fs_sink->upstream_iter);
+       if (msg_iter_status != BT_MESSAGE_ITERATOR_CREATE_FROM_SINK_COMPONENT_STATUS_OK) {
+               status = (bt_component_class_sink_graph_is_configured_method_status) msg_iter_status;
+               BT_COMP_LOGE_APPEND_CAUSE(fs_sink->self_comp,
+                       "Failed to create upstream iterator.");
+               goto end;
+       }
+
+       status = BT_COMPONENT_CLASS_SINK_GRAPH_IS_CONFIGURED_METHOD_STATUS_OK;
+end:
+       return status;
+}
+
+BT_HIDDEN
+void ctf_fs_sink_finalize(bt_self_component_sink *self_comp)
+{
+       fs_sink_comp *fs_sink = (fs_sink_comp *) bt_self_component_get_data(
+                       bt_self_component_sink_as_self_component(self_comp));
+
+       destroy_fs_sink_comp(fs_sink);
+}
diff --git a/src/plugins/ctf/fs-sink/fs-sink.h b/src/plugins/ctf/fs-sink/fs-sink.h
deleted file mode 100644 (file)
index 983ca01..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
- */
-
-#ifndef BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_H
-#define BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_H
-
-#include "common/macros.h"
-#include <babeltrace2/babeltrace.h>
-#include <stdbool.h>
-#include <glib.h>
-
-struct fs_sink_comp {
-       bt_logging_level log_level;
-       bt_self_component *self_comp;
-
-       /* Owned by this */
-       bt_message_iterator *upstream_iter;
-
-       /* Base output directory path */
-       GString *output_dir_path;
-
-       /*
-        * True if the component assumes that it will only write a
-        * single CTF trace (which can contain one or more data
-        * streams). This makes the component write the stream files
-        * directly in the output directory (`output_dir_path` above).
-        */
-       bool assume_single_trace;
-
-       /* True to completely ignore discarded events messages */
-       bool ignore_discarded_events;
-
-       /* True to completely ignore discarded packets messages */
-       bool ignore_discarded_packets;
-
-       /*
-        * True to make the component quiet (nothing printed to the
-        * standard output).
-        */
-       bool quiet;
-
-       /*
-        * Hash table of `const bt_trace *` (weak) to
-        * `struct fs_sink_trace *` (owned by hash table).
-        */
-       GHashTable *traces;
-};
-
-BT_HIDDEN
-bt_component_class_initialize_method_status ctf_fs_sink_init(
-               bt_self_component_sink *component,
-               bt_self_component_sink_configuration *config,
-               const bt_value *params,
-               void *init_method_data);
-
-BT_HIDDEN
-bt_component_class_sink_consume_method_status ctf_fs_sink_consume(
-               bt_self_component_sink *component);
-
-BT_HIDDEN
-bt_component_class_sink_graph_is_configured_method_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 */
diff --git a/src/plugins/ctf/fs-sink/fs-sink.hpp b/src/plugins/ctf/fs-sink/fs-sink.hpp
new file mode 100644 (file)
index 0000000..983ca01
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#ifndef BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_H
+#define BABELTRACE_PLUGIN_CTF_FS_SINK_FS_SINK_H
+
+#include "common/macros.h"
+#include <babeltrace2/babeltrace.h>
+#include <stdbool.h>
+#include <glib.h>
+
+struct fs_sink_comp {
+       bt_logging_level log_level;
+       bt_self_component *self_comp;
+
+       /* Owned by this */
+       bt_message_iterator *upstream_iter;
+
+       /* Base output directory path */
+       GString *output_dir_path;
+
+       /*
+        * True if the component assumes that it will only write a
+        * single CTF trace (which can contain one or more data
+        * streams). This makes the component write the stream files
+        * directly in the output directory (`output_dir_path` above).
+        */
+       bool assume_single_trace;
+
+       /* True to completely ignore discarded events messages */
+       bool ignore_discarded_events;
+
+       /* True to completely ignore discarded packets messages */
+       bool ignore_discarded_packets;
+
+       /*
+        * True to make the component quiet (nothing printed to the
+        * standard output).
+        */
+       bool quiet;
+
+       /*
+        * Hash table of `const bt_trace *` (weak) to
+        * `struct fs_sink_trace *` (owned by hash table).
+        */
+       GHashTable *traces;
+};
+
+BT_HIDDEN
+bt_component_class_initialize_method_status ctf_fs_sink_init(
+               bt_self_component_sink *component,
+               bt_self_component_sink_configuration *config,
+               const bt_value *params,
+               void *init_method_data);
+
+BT_HIDDEN
+bt_component_class_sink_consume_method_status ctf_fs_sink_consume(
+               bt_self_component_sink *component);
+
+BT_HIDDEN
+bt_component_class_sink_graph_is_configured_method_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 */
diff --git a/src/plugins/ctf/fs-sink/translate-ctf-ir-to-tsdl.c b/src/plugins/ctf/fs-sink/translate-ctf-ir-to-tsdl.c
deleted file mode 100644 (file)
index 85f6bee..0000000
+++ /dev/null
@@ -1,977 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
- */
-
-#include "translate-ctf-ir-to-tsdl.h"
-
-#include <babeltrace2/babeltrace.h>
-#include "common/macros.h"
-#include <stdio.h>
-#include <stdbool.h>
-#include <string.h>
-#include <glib.h>
-#include "common/assert.h"
-#include "compat/endian.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,
-               "\"" BT_UUID_FMT "\"",
-               BT_UUID_FMT_VALUES(uuid));
-}
-
-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:
-                       bt_common_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_bool_field_class(struct ctx *ctx,
-               __attribute__((unused)) struct fs_sink_ctf_field_class_bool *fc)
-{
-       /*
-        * CTF 1.8 has no boolean field class type, so this component
-        * translates it to an 8-bit unsigned integer field class.
-        */
-       append_integer_field_class_from_props(ctx, fc->base.size,
-               fc->base.base.alignment, false,
-               BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL,
-               NULL, NULL, false);
-}
-
-static
-void append_bit_array_field_class(struct ctx *ctx,
-               struct fs_sink_ctf_field_class_bit_array *fc)
-{
-       /*
-        * CTF 1.8 has no bit array field class type, so this component
-        * translates it to an unsigned integer field class with an
-        * hexadecimal base.
-        */
-       append_integer_field_class_from_props(ctx, fc->size,
-               fc->base.alignment, false,
-               BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL,
-               NULL, NULL, false);
-}
-
-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 = bt_field_class_type_is(type,
-               BT_FIELD_CLASS_TYPE_SIGNED_INTEGER);
-
-       if (bt_field_class_type_is(type, BT_FIELD_CLASS_TYPE_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 (bt_field_class_type_is(type, BT_FIELD_CLASS_TYPE_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_enumeration_mapping *mapping;
-                       const bt_field_class_enumeration_unsigned_mapping *u_mapping;
-                       const bt_field_class_enumeration_signed_mapping *s_mapping;
-                       const bt_integer_range_set *ranges;
-                       const bt_integer_range_set_unsigned *u_ranges;
-                       const bt_integer_range_set_signed *s_ranges;
-                       uint64_t range_count;
-                       uint64_t range_i;
-
-                       if (is_signed) {
-                               s_mapping = bt_field_class_enumeration_signed_borrow_mapping_by_index_const(
-                                       ir_fc, i);
-                               mapping = bt_field_class_enumeration_signed_mapping_as_mapping_const(
-                                       s_mapping);
-                               s_ranges = bt_field_class_enumeration_signed_mapping_borrow_ranges_const(
-                                       s_mapping);
-                               ranges = bt_integer_range_set_signed_as_range_set_const(
-                                       s_ranges);
-                       } else {
-                               u_mapping = bt_field_class_enumeration_unsigned_borrow_mapping_by_index_const(
-                                       ir_fc, i);
-                               mapping = bt_field_class_enumeration_unsigned_mapping_as_mapping_const(
-                                       u_mapping);
-                               u_ranges = bt_field_class_enumeration_unsigned_mapping_borrow_ranges_const(
-                                       u_mapping);
-                               ranges = bt_integer_range_set_unsigned_as_range_set_const(
-                                       u_ranges);
-                       }
-
-                       label = bt_field_class_enumeration_mapping_get_label(
-                               mapping);
-                       range_count = bt_integer_range_set_get_range_count(
-                               ranges);
-
-                       for (range_i = 0; range_i < range_count; range_i++) {
-                               append_indent(ctx);
-                               g_string_append(ctx->tsdl, "\"");
-                               append_quoted_string_content(ctx, label);
-                               g_string_append(ctx->tsdl, "\" = ");
-
-                               if (is_signed) {
-                                       const bt_integer_range_signed *range;
-                                       int64_t lower, upper;
-
-                                       range = bt_integer_range_set_signed_borrow_range_by_index_const(
-                                               s_ranges, range_i);
-                                       lower = bt_integer_range_signed_get_lower(
-                                               range);
-                                       upper = bt_integer_range_signed_get_upper(
-                                               range);
-
-                                       if (lower == upper) {
-                                               g_string_append_printf(
-                                                       ctx->tsdl, "%" PRId64,
-                                                       lower);
-                                       } else {
-                                               g_string_append_printf(
-                                                       ctx->tsdl, "%" PRId64 " ... %" PRId64,
-                                                       lower, upper);
-                                       }
-                               } else {
-                                       const bt_integer_range_unsigned *range;
-                                       uint64_t lower, upper;
-
-                                       range = bt_integer_range_set_unsigned_borrow_range_by_index_const(
-                                               u_ranges, range_i);
-                                       lower = bt_integer_range_unsigned_get_lower(
-                                               range);
-                                       upper = bt_integer_range_unsigned_get_upper(
-                                               range);
-
-                                       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_get_type(fc->base.base.ir_fc) ==
-                       BT_FIELD_CLASS_TYPE_SINGLE_PRECISION_REAL) {
-               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 = "";
-
-       BT_ASSERT(fc);
-
-       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;
-
-               /*
-                * For sequence, option, and variant field classes, if
-                * the length/tag field class is generated before, write
-                * it now before the dependent field class.
-                */
-               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_OPTION) {
-                       struct fs_sink_ctf_field_class_option *opt_fc =
-                               (void *) fc;
-
-                       /*
-                        * CTF 1.8 does not support the option field
-                        * class type. To write something anyway, this
-                        * component translates this type to a variant
-                        * field class where the options are:
-                        *
-                        * * An empty structure field class.
-                        * * The optional field class itself.
-                        *
-                        * The "tag" is always generated/before in that
-                        * case (an 8-bit unsigned enumeration field
-                        * class).
-                        */
-                       append_indent(ctx);
-                       g_string_append(ctx->tsdl,
-                               "/* The enumeration and variant field classes "
-                               "below were a trace IR option field class. */\n");
-                       append_indent(ctx);
-                       g_string_append(ctx->tsdl, "enum : ");
-                       append_integer_field_class_from_props(ctx,
-                               8, 8, false,
-                               BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL,
-                               NULL, NULL, false);
-                       g_string_append(ctx->tsdl, " {\n");
-                       ctx->indent_level++;
-                       append_indent(ctx);
-                       g_string_append(ctx->tsdl, "none = 0,\n");
-                       append_indent(ctx);
-                       g_string_append(ctx->tsdl, "content = 1,\n");
-                       append_end_block(ctx);
-                       g_string_append_printf(ctx->tsdl, " %s;\n",
-                               opt_fc->tag_ref->str);
-               } 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 *option_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",
-                                               option_named_fc->name->str, i);
-                               }
-
-                               append_end_block(ctx);
-                               g_string_append_printf(ctx->tsdl, " %s;\n",
-                                       var_fc->tag_ref->str);
-                       }
-               } else if (fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_BOOL) {
-                       append_indent(ctx);
-                       g_string_append(ctx->tsdl,
-                               "/* The integer field class below was a trace IR boolean field class. */\n");
-               } else if (fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_BIT_ARRAY) {
-                       append_indent(ctx);
-                       g_string_append(ctx->tsdl,
-                               "/* The integer field class below was a trace IR bit array field class. */\n");
-               }
-
-               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_option_field_class(struct ctx *ctx,
-               struct fs_sink_ctf_field_class_option *opt_fc)
-{
-       g_string_append_printf(ctx->tsdl, "variant <%s> {\n",
-               opt_fc->tag_ref->str);
-       ctx->indent_level++;
-       append_indent(ctx);
-       g_string_append(ctx->tsdl, "struct { } none;\n");
-       append_indent(ctx);
-       append_member(ctx, "content", opt_fc->content_fc);
-       append_end_block(ctx);
-}
-
-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_BOOL:
-               append_bool_field_class(ctx, (void *) fc);
-               break;
-       case FS_SINK_CTF_FIELD_CLASS_TYPE_BIT_ARRAY:
-               append_bit_array_field_class(ctx, (void *) fc);
-               break;
-       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_OPTION:
-               append_option_field_class(ctx, (void *) fc);
-               break;
-       case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT:
-               append_variant_field_class(ctx, (void *) fc);
-               break;
-       default:
-               bt_common_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:
-                       bt_common_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->packets_have_ts_begin) {
-               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);
-       }
-
-       if (sc->packets_have_ts_end) {
-               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);
-       }
-
-       if (sc->has_discarded_events) {
-               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);
-       }
-
-       /*
-        * Unconditionnally write the packet sequence number as, even if
-        * there's no possible discarded packets message, it's still
-        * useful information to have.
-        */
-       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_ctf_ir_to_tsdl(struct fs_sink_ctf_trace *trace,
-               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, trace->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 environment */
-       count = bt_trace_get_environment_entry_count(trace->ir_trace);
-       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_borrow_environment_entry_by_index_const(
-                               trace->ir_trace, i, &name, &val);
-                       append_indent(&ctx);
-                       g_string_append_printf(tsdl, "%s = ", name);
-
-                       switch (bt_value_get_type(val)) {
-                       case BT_VALUE_TYPE_SIGNED_INTEGER:
-                               g_string_append_printf(tsdl, "%" PRId64,
-                                       bt_value_integer_signed_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_trace_ir_to_ctf_ir().
-                                */
-                               bt_common_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 < trace->stream_classes->len; i++) {
-               append_stream_class(&ctx, trace->stream_classes->pdata[i]);
-       }
-}
diff --git a/src/plugins/ctf/fs-sink/translate-ctf-ir-to-tsdl.cpp b/src/plugins/ctf/fs-sink/translate-ctf-ir-to-tsdl.cpp
new file mode 100644 (file)
index 0000000..c93db6a
--- /dev/null
@@ -0,0 +1,978 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include "translate-ctf-ir-to-tsdl.hpp"
+
+#include <babeltrace2/babeltrace.h>
+#include "common/macros.h"
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <glib.h>
+#include "common/assert.h"
+#include "compat/endian.h"
+
+#include "fs-sink-ctf-meta.hpp"
+
+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,
+               "\"" BT_UUID_FMT "\"",
+               BT_UUID_FMT_VALUES(uuid));
+}
+
+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:
+                       bt_common_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_bool_field_class(struct ctx *ctx,
+               __attribute__((unused)) struct fs_sink_ctf_field_class_bool *fc)
+{
+       /*
+        * CTF 1.8 has no boolean field class type, so this component
+        * translates it to an 8-bit unsigned integer field class.
+        */
+       append_integer_field_class_from_props(ctx, fc->base.size,
+               fc->base.base.alignment, false,
+               BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL,
+               NULL, NULL, false);
+}
+
+static
+void append_bit_array_field_class(struct ctx *ctx,
+               struct fs_sink_ctf_field_class_bit_array *fc)
+{
+       /*
+        * CTF 1.8 has no bit array field class type, so this component
+        * translates it to an unsigned integer field class with an
+        * hexadecimal base.
+        */
+       append_integer_field_class_from_props(ctx, fc->size,
+               fc->base.alignment, false,
+               BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL,
+               NULL, NULL, false);
+}
+
+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 = bt_field_class_type_is(type,
+               BT_FIELD_CLASS_TYPE_SIGNED_INTEGER);
+
+       if (bt_field_class_type_is(type, BT_FIELD_CLASS_TYPE_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 (bt_field_class_type_is(type, BT_FIELD_CLASS_TYPE_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_enumeration_mapping *mapping;
+                       const bt_field_class_enumeration_unsigned_mapping *u_mapping;
+                       const bt_field_class_enumeration_signed_mapping *s_mapping;
+                       const bt_integer_range_set *ranges;
+                       const bt_integer_range_set_unsigned *u_ranges;
+                       const bt_integer_range_set_signed *s_ranges;
+                       uint64_t range_count;
+                       uint64_t range_i;
+
+                       if (is_signed) {
+                               s_mapping = bt_field_class_enumeration_signed_borrow_mapping_by_index_const(
+                                       ir_fc, i);
+                               mapping = bt_field_class_enumeration_signed_mapping_as_mapping_const(
+                                       s_mapping);
+                               s_ranges = bt_field_class_enumeration_signed_mapping_borrow_ranges_const(
+                                       s_mapping);
+                               ranges = bt_integer_range_set_signed_as_range_set_const(
+                                       s_ranges);
+                       } else {
+                               u_mapping = bt_field_class_enumeration_unsigned_borrow_mapping_by_index_const(
+                                       ir_fc, i);
+                               mapping = bt_field_class_enumeration_unsigned_mapping_as_mapping_const(
+                                       u_mapping);
+                               u_ranges = bt_field_class_enumeration_unsigned_mapping_borrow_ranges_const(
+                                       u_mapping);
+                               ranges = bt_integer_range_set_unsigned_as_range_set_const(
+                                       u_ranges);
+                       }
+
+                       label = bt_field_class_enumeration_mapping_get_label(
+                               mapping);
+                       range_count = bt_integer_range_set_get_range_count(
+                               ranges);
+
+                       for (range_i = 0; range_i < range_count; range_i++) {
+                               append_indent(ctx);
+                               g_string_append(ctx->tsdl, "\"");
+                               append_quoted_string_content(ctx, label);
+                               g_string_append(ctx->tsdl, "\" = ");
+
+                               if (is_signed) {
+                                       const bt_integer_range_signed *range;
+                                       int64_t lower, upper;
+
+                                       range = bt_integer_range_set_signed_borrow_range_by_index_const(
+                                               s_ranges, range_i);
+                                       lower = bt_integer_range_signed_get_lower(
+                                               range);
+                                       upper = bt_integer_range_signed_get_upper(
+                                               range);
+
+                                       if (lower == upper) {
+                                               g_string_append_printf(
+                                                       ctx->tsdl, "%" PRId64,
+                                                       lower);
+                                       } else {
+                                               g_string_append_printf(
+                                                       ctx->tsdl, "%" PRId64 " ... %" PRId64,
+                                                       lower, upper);
+                                       }
+                               } else {
+                                       const bt_integer_range_unsigned *range;
+                                       uint64_t lower, upper;
+
+                                       range = bt_integer_range_set_unsigned_borrow_range_by_index_const(
+                                               u_ranges, range_i);
+                                       lower = bt_integer_range_unsigned_get_lower(
+                                               range);
+                                       upper = bt_integer_range_unsigned_get_upper(
+                                               range);
+
+                                       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_get_type(fc->base.base.ir_fc) ==
+                       BT_FIELD_CLASS_TYPE_SINGLE_PRECISION_REAL) {
+               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_string *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 = "";
+
+       BT_ASSERT(fc);
+
+       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 =
+                               fs_sink_ctf_field_class_as_array(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 =
+                               fs_sink_ctf_field_class_as_sequence(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;
+
+               /*
+                * For sequence, option, and variant field classes, if
+                * the length/tag field class is generated before, write
+                * it now before the dependent field class.
+                */
+               if (fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE) {
+                       struct fs_sink_ctf_field_class_sequence *seq_fc =
+                               fs_sink_ctf_field_class_as_sequence(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_OPTION) {
+                       struct fs_sink_ctf_field_class_option *opt_fc =
+                               fs_sink_ctf_field_class_as_option(fc);
+
+                       /*
+                        * CTF 1.8 does not support the option field
+                        * class type. To write something anyway, this
+                        * component translates this type to a variant
+                        * field class where the options are:
+                        *
+                        * * An empty structure field class.
+                        * * The optional field class itself.
+                        *
+                        * The "tag" is always generated/before in that
+                        * case (an 8-bit unsigned enumeration field
+                        * class).
+                        */
+                       append_indent(ctx);
+                       g_string_append(ctx->tsdl,
+                               "/* The enumeration and variant field classes "
+                               "below were a trace IR option field class. */\n");
+                       append_indent(ctx);
+                       g_string_append(ctx->tsdl, "enum : ");
+                       append_integer_field_class_from_props(ctx,
+                               8, 8, false,
+                               BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL,
+                               NULL, NULL, false);
+                       g_string_append(ctx->tsdl, " {\n");
+                       ctx->indent_level++;
+                       append_indent(ctx);
+                       g_string_append(ctx->tsdl, "none = 0,\n");
+                       append_indent(ctx);
+                       g_string_append(ctx->tsdl, "content = 1,\n");
+                       append_end_block(ctx);
+                       g_string_append_printf(ctx->tsdl, " %s;\n",
+                               opt_fc->tag_ref->str);
+               } else if (fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT) {
+                       struct fs_sink_ctf_field_class_variant *var_fc =
+                               fs_sink_ctf_field_class_as_variant(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 *option_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",
+                                               option_named_fc->name->str, i);
+                               }
+
+                               append_end_block(ctx);
+                               g_string_append_printf(ctx->tsdl, " %s;\n",
+                                       var_fc->tag_ref->str);
+                       }
+               } else if (fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_BOOL) {
+                       append_indent(ctx);
+                       g_string_append(ctx->tsdl,
+                               "/* The integer field class below was a trace IR boolean field class. */\n");
+               } else if (fc->type == FS_SINK_CTF_FIELD_CLASS_TYPE_BIT_ARRAY) {
+                       append_indent(ctx);
+                       g_string_append(ctx->tsdl,
+                               "/* The integer field class below was a trace IR bit array field class. */\n");
+               }
+
+               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_option_field_class(struct ctx *ctx,
+               struct fs_sink_ctf_field_class_option *opt_fc)
+{
+       g_string_append_printf(ctx->tsdl, "variant <%s> {\n",
+               opt_fc->tag_ref->str);
+       ctx->indent_level++;
+       append_indent(ctx);
+       g_string_append(ctx->tsdl, "struct { } none;\n");
+       append_indent(ctx);
+       append_member(ctx, "content", opt_fc->content_fc);
+       append_end_block(ctx);
+}
+
+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_BOOL:
+               append_bool_field_class(ctx, fs_sink_ctf_field_class_as_bool(fc));
+               break;
+       case FS_SINK_CTF_FIELD_CLASS_TYPE_BIT_ARRAY:
+               append_bit_array_field_class(ctx, fs_sink_ctf_field_class_as_bit_array(fc));
+               break;
+       case FS_SINK_CTF_FIELD_CLASS_TYPE_INT:
+               append_integer_field_class(ctx, fs_sink_ctf_field_class_as_int(fc));
+               break;
+       case FS_SINK_CTF_FIELD_CLASS_TYPE_FLOAT:
+               append_float_field_class(ctx, fs_sink_ctf_field_class_as_float(fc));
+               break;
+       case FS_SINK_CTF_FIELD_CLASS_TYPE_STRING:
+               append_string_field_class(ctx, fs_sink_ctf_field_class_as_string(fc));
+               break;
+       case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT:
+               append_struct_field_class(ctx, fs_sink_ctf_field_class_as_struct(fc));
+               break;
+       case FS_SINK_CTF_FIELD_CLASS_TYPE_OPTION:
+               append_option_field_class(ctx, fs_sink_ctf_field_class_as_option(fc));
+               break;
+       case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT:
+               append_variant_field_class(ctx, fs_sink_ctf_field_class_as_variant(fc));
+               break;
+       default:
+               bt_common_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:
+                       bt_common_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->packets_have_ts_begin) {
+               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);
+       }
+
+       if (sc->packets_have_ts_end) {
+               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);
+       }
+
+       if (sc->has_discarded_events) {
+               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);
+       }
+
+       /*
+        * Unconditionnally write the packet sequence number as, even if
+        * there's no possible discarded packets message, it's still
+        * useful information to have.
+        */
+       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,
+                       fs_sink_ctf_field_class_as_struct(sc->packet_context_fc));
+               fs_sink_ctf_field_class_struct_align_at_least(
+                       fs_sink_ctf_field_class_as_struct(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, 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,
+                       (fs_sink_ctf_event_class *) sc->event_classes->pdata[i]);
+       }
+}
+
+BT_HIDDEN
+void translate_trace_ctf_ir_to_tsdl(struct fs_sink_ctf_trace *trace,
+               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, trace->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 environment */
+       count = bt_trace_get_environment_entry_count(trace->ir_trace);
+       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_borrow_environment_entry_by_index_const(
+                               trace->ir_trace, i, &name, &val);
+                       append_indent(&ctx);
+                       g_string_append_printf(tsdl, "%s = ", name);
+
+                       switch (bt_value_get_type(val)) {
+                       case BT_VALUE_TYPE_SIGNED_INTEGER:
+                               g_string_append_printf(tsdl, "%" PRId64,
+                                       bt_value_integer_signed_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_trace_ir_to_ctf_ir().
+                                */
+                               bt_common_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 < trace->stream_classes->len; i++) {
+               append_stream_class(&ctx,
+                       (fs_sink_ctf_stream_class *) trace->stream_classes->pdata[i]);
+       }
+}
diff --git a/src/plugins/ctf/fs-sink/translate-ctf-ir-to-tsdl.h b/src/plugins/ctf/fs-sink/translate-ctf-ir-to-tsdl.h
deleted file mode 100644 (file)
index 151e3c6..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
- */
-
-#ifndef BABELTRACE_PLUGIN_CTF_FS_SINK_TRANSLATE_CTF_IR_TO_TSDL_H
-#define BABELTRACE_PLUGIN_CTF_FS_SINK_TRANSLATE_CTF_IR_TO_TSDL_H
-
-#include <glib.h>
-
-#include "common/macros.h"
-#include "fs-sink-ctf-meta.h"
-
-BT_HIDDEN
-void translate_trace_ctf_ir_to_tsdl(struct fs_sink_ctf_trace *trace,
-               GString *tsdl);
-
-#endif /* BABELTRACE_PLUGIN_CTF_FS_SINK_TRANSLATE_CTF_IR_TO_TSDL_H */
diff --git a/src/plugins/ctf/fs-sink/translate-ctf-ir-to-tsdl.hpp b/src/plugins/ctf/fs-sink/translate-ctf-ir-to-tsdl.hpp
new file mode 100644 (file)
index 0000000..b8a73e7
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#ifndef BABELTRACE_PLUGIN_CTF_FS_SINK_TRANSLATE_CTF_IR_TO_TSDL_H
+#define BABELTRACE_PLUGIN_CTF_FS_SINK_TRANSLATE_CTF_IR_TO_TSDL_H
+
+#include <glib.h>
+
+#include "common/macros.h"
+#include "fs-sink-ctf-meta.hpp"
+
+BT_HIDDEN
+void translate_trace_ctf_ir_to_tsdl(struct fs_sink_ctf_trace *trace,
+               GString *tsdl);
+
+#endif /* BABELTRACE_PLUGIN_CTF_FS_SINK_TRANSLATE_CTF_IR_TO_TSDL_H */
diff --git a/src/plugins/ctf/fs-sink/translate-trace-ir-to-ctf-ir.c b/src/plugins/ctf/fs-sink/translate-trace-ir-to-ctf-ir.c
deleted file mode 100644 (file)
index 19ca9dc..0000000
+++ /dev/null
@@ -1,1877 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
- */
-
-#define BT_COMP_LOG_SELF_COMP (ctx->self_comp)
-#define BT_LOG_OUTPUT_LEVEL (ctx->log_level)
-#define BT_LOG_TAG "PLUGIN/SINK.CTF.FS/TRANSLATE-TRACE-IR-TO-CTF-IR"
-#include "logging/comp-logging.h"
-
-#include "translate-trace-ir-to-ctf-ir.h"
-
-#include <babeltrace2/babeltrace.h>
-#include "common/macros.h"
-#include "common/common.h"
-#include "common/assert.h"
-#include <stdio.h>
-#include <stdbool.h>
-#include <string.h>
-#include <glib.h>
-
-#include "fs-sink.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 {
-       bt_logging_level log_level;
-       bt_self_component *self_comp;
-
-       /* Weak */
-       struct fs_sink_ctf_stream_class *cur_sc;
-
-       /* Weak */
-       struct fs_sink_ctf_event_class *cur_ec;
-
-       bt_field_path_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 const char *reserved_tsdl_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",
-};
-
-static inline
-bool ist_valid_identifier(const char *name)
-{
-       const char *at;
-       uint64_t i;
-       bool ist_valid = true;
-
-       /* Make sure the name is not a reserved keyword */
-       for (i = 0; i < sizeof(reserved_tsdl_keywords) / sizeof(*reserved_tsdl_keywords);
-                       i++) {
-               if (strcmp(name, reserved_tsdl_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((unsigned char) 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((unsigned char) *at) && *at != '_') {
-                       ist_valid = false;
-                       goto end;
-               }
-       }
-
-end:
-       return ist_valid;
-}
-
-static inline
-bool must_protect_identifier(const char *name)
-{
-       uint64_t i;
-       bool must_protect = false;
-
-       /* Protect a reserved keyword */
-       for (i = 0; i < sizeof(reserved_tsdl_keywords) / sizeof(*reserved_tsdl_keywords);
-                       i++) {
-               if (strcmp(name, reserved_tsdl_keywords[i]) == 0) {
-                       must_protect = true;
-                       goto end;
-               }
-       }
-
-       /* Protect an identifier which already starts with `_` */
-       if (name[0] == '_') {
-               must_protect = true;
-               goto end;
-       }
-
-end:
-       return must_protect;
-}
-
-static inline
-int cur_path_stack_push(struct ctx *ctx,
-               uint64_t index_in_parent, const char *name,
-               bool force_protect_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(NULL);
-
-       if (name) {
-               if (force_protect_name) {
-                       g_string_assign(field_path_elem->name, "_");
-               }
-
-               g_string_append(field_path_elem->name, name);
-
-               if (ctx->cur_scope == BT_FIELD_PATH_SCOPE_PACKET_CONTEXT) {
-                       if (is_reserved_member_name(name, "packet_size") ||
-                                       is_reserved_member_name(name, "content_size") ||
-                                       is_reserved_member_name(name, "timestamp_begin") ||
-                                       is_reserved_member_name(name, "timestamp_end") ||
-                                       is_reserved_member_name(name, "events_discarded") ||
-                                       is_reserved_member_name(name, "packet_seq_num")) {
-                               BT_COMP_LOGE("Unsupported reserved TSDL structure field class member "
-                                       "or variant field class option name: name=\"%s\"",
-                                       name);
-                               ret = -1;
-                               goto end;
-                       }
-               }
-
-               if (!ist_valid_identifier(field_path_elem->name->str)) {
-                       ret = -1;
-                       BT_COMP_LOGE("Unsupported non-TSDL structure field class member "
-                               "or variant field class option name: name=\"%s\"",
-                               field_path_elem->name->str);
-                       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);
-}
-
-/*
- * 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,
-               struct fs_sink_ctf_field_class **user_tgt_fc)
-{
-       int ret = 0;
-       struct fs_sink_ctf_field_class *tgt_fc = NULL;
-       uint64_t i;
-       int64_t si;
-       const char *tgt_fc_name = NULL;
-       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_FIELD_PATH_SCOPE_PACKET_CONTEXT:
-               BT_ASSERT(ctx->cur_sc);
-               tgt_fc = ctx->cur_sc->packet_context_fc;
-               break;
-       case BT_FIELD_PATH_SCOPE_EVENT_COMMON_CONTEXT:
-               BT_ASSERT(ctx->cur_sc);
-               tgt_fc = ctx->cur_sc->event_common_context_fc;
-               break;
-       case BT_FIELD_PATH_SCOPE_EVENT_SPECIFIC_CONTEXT:
-               BT_ASSERT(ctx->cur_ec);
-               tgt_fc = ctx->cur_ec->spec_context_fc;
-               break;
-       case BT_FIELD_PATH_SCOPE_EVENT_PAYLOAD:
-               BT_ASSERT(ctx->cur_ec);
-               tgt_fc = ctx->cur_ec->payload_fc;
-               break;
-       default:
-               bt_common_abort();
-       }
-
-       i = 0;
-
-       while (i < bt_field_path_get_item_count(tgt_ir_field_path)) {
-               const bt_field_path_item *fp_item =
-                       bt_field_path_borrow_item_by_index_const(
-                               tgt_ir_field_path, i);
-               struct fs_sink_ctf_named_field_class *named_fc = NULL;
-
-               BT_ASSERT(tgt_fc);
-               BT_ASSERT(fp_item);
-
-               if (bt_field_path_item_get_type(fp_item) ==
-                               BT_FIELD_PATH_ITEM_TYPE_CURRENT_OPTION_CONTENT) {
-                       /* Not supported by CTF 1.8 */
-                       ret = -1;
-                       goto end;
-               }
-
-               switch (tgt_fc->type) {
-               case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT:
-                       BT_ASSERT(bt_field_path_item_get_type(fp_item) ==
-                               BT_FIELD_PATH_ITEM_TYPE_INDEX);
-                       named_fc = fs_sink_ctf_field_class_struct_borrow_member_by_index(
-                               (void *) tgt_fc,
-                               bt_field_path_item_index_get_index(fp_item));
-                       break;
-               case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT:
-                       BT_ASSERT(bt_field_path_item_get_type(fp_item) ==
-                               BT_FIELD_PATH_ITEM_TYPE_INDEX);
-                       named_fc = fs_sink_ctf_field_class_variant_borrow_option_by_index(
-                               (void *) tgt_fc,
-                               bt_field_path_item_index_get_index(fp_item));
-                       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;
-
-                       BT_ASSERT(bt_field_path_item_get_type(fp_item) ==
-                               BT_FIELD_PATH_ITEM_TYPE_CURRENT_ARRAY_ELEMENT);
-                       tgt_fc = array_base_fc->elem_fc;
-                       break;
-               }
-               default:
-                       bt_common_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 = NULL;
-               struct fs_sink_ctf_field_class_variant *var_fc = NULL;
-               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);
-
-                                       if (user_tgt_fc) {
-                                               *user_tgt_fc = tgt_fc;
-                                       }
-                               } 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,
-               struct fs_sink_ctf_field_class **user_tgt_fc)
-{
-       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_FIELD_PATH_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_FIELD_PATH_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_FIELD_PATH_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_FIELD_PATH_SCOPE_EVENT_PAYLOAD:
-               BT_ASSERT(ctx->cur_ec);
-               fc = ctx->cur_ec->payload_fc;
-               g_string_assign(tgt_field_ref, "event.fields");
-               break;
-       default:
-               bt_common_abort();
-       }
-
-       BT_ASSERT(fc);
-
-       for (i = 0; i < bt_field_path_get_item_count(tgt_ir_field_path); i++) {
-               const bt_field_path_item *fp_item =
-                       bt_field_path_borrow_item_by_index_const(
-                               tgt_ir_field_path, i);
-               struct fs_sink_ctf_named_field_class *named_fc = NULL;
-
-               if (bt_field_path_item_get_type(fp_item) !=
-                               BT_FIELD_PATH_ITEM_TYPE_INDEX) {
-                       /* Not supported by TSDL 1.8 */
-                       ret = -1;
-                       goto end;
-               }
-
-               switch (fc->type) {
-               case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT:
-                       BT_ASSERT(bt_field_path_item_get_type(fp_item) ==
-                               BT_FIELD_PATH_ITEM_TYPE_INDEX);
-                       named_fc = fs_sink_ctf_field_class_struct_borrow_member_by_index(
-                               (void *) fc,
-                               bt_field_path_item_index_get_index(fp_item));
-                       break;
-               case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT:
-                       BT_ASSERT(bt_field_path_item_get_type(fp_item) ==
-                               BT_FIELD_PATH_ITEM_TYPE_INDEX);
-                       named_fc = fs_sink_ctf_field_class_variant_borrow_option_by_index(
-                               (void *) fc,
-                               bt_field_path_item_index_get_index(fp_item));
-                       break;
-               default:
-                       bt_common_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;
-       }
-
-       if (user_tgt_fc) {
-               *user_tgt_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,
-               struct fs_sink_ctf_field_class **user_tgt_fc)
-{
-       int ret;
-       bt_field_path_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, user_tgt_fc);
-               if (ret) {
-                       ret = create_absolute_field_ref(ctx, tgt_ir_field_path,
-                               tgt_field_ref, user_tgt_fc);
-                       if (ret) {
-                               *create_before = true;
-                               goto end;
-                       }
-               }
-       } else {
-               ret = create_absolute_field_ref(ctx, tgt_ir_field_path,
-                       tgt_field_ref, user_tgt_fc);
-
-               /* 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_OPTION:
-       {
-               struct fs_sink_ctf_field_class_option *opt_fc =
-                       (void *) parent_fc;
-
-               BT_ASSERT(!opt_fc->content_fc);
-               opt_fc->content_fc = fc;
-               opt_fc->base.alignment = fc->alignment;
-               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:
-               bt_common_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, true, memb_ir_fc,
-                       (void *) struct_fc);
-               if (ret) {
-                       BT_COMP_LOGE("Cannot translate structure field class member: "
-                               "name=\"%s\"", name);
-                       goto end;
-               }
-
-               ret = translate_field_class(ctx);
-               if (ret) {
-                       BT_COMP_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;
-}
-
-/*
- * This function protects a given variant FC option name (with the `_`
- * prefix) if required. On success, `name_buf->str` contains the variant
- * FC option name to use (original option name or protected if
- * required).
- *
- * One of the goals of `sink.ctf.fs` is to write a CTF trace which is as
- * close as possible to an original CTF trace as decoded by
- * `src.ctf.fs`.
- *
- * This scenario is valid in CTF 1.8:
- *
- *     enum {
- *         HELLO,
- *         MEOW
- *     } tag;
- *
- *     variant <tag> {
- *         int HELLO;
- *         string MEOW;
- *     };
- *
- * Once in trace IR, the enumeration FC mapping names and variant FC
- * option names are kept as is. For this reason, we don't want to
- * protect the variant FC option names here (by prepending `_`): this
- * would make the variant FC option name and the enumeration FC mapping
- * name not match.
- *
- * This scenario is also valid in CTF 1.8:
- *
- *     enum {
- *         _HELLO,
- *         MEOW
- *     } tag;
- *
- *     variant <tag> {
- *         int _HELLO;
- *         string MEOW;
- *     };
- *
- * Once in trace IR, the enumeration FC mapping names are kept as is,
- * but the `_HELLO` variant FC option name becomes `HELLO` (unprotected
- * for presentation, as recommended by CTF 1.8). When going back to
- * TSDL, we need to protect `HELLO` so that it becomes `_HELLO` to match
- * the corresponding enumeration FC mapping name.
- *
- * This scenario is also valid in CTF 1.8:
- *
- *     enum {
- *         __HELLO,
- *         MEOW
- *     } tag;
- *
- *     variant <tag> {
- *         int __HELLO;
- *         string MEOW;
- *     };
- *
- * Once in trace IR, the enumeration FC mapping names are kept as is,
- * but the `__HELLO` variant FC option name becomes `_HELLO`
- * (unprotected). When going back to TSDL, we need to protect `_HELLO`
- * so that it becomes `__HELLO` to match the corresponding enumeration
- * FC mapping name.
- *
- * `src.ctf.fs` always uses the _same_ integer range sets for a selector
- * FC mapping and a corresponding variant FC option. We can use that
- * fact to find the original variant FC option names by matching variant
- * FC options and enumeration FC mappings by range set.
- */
-static
-int maybe_protect_variant_option_name(const bt_field_class *ir_var_fc,
-               const bt_field_class *ir_tag_fc, uint64_t opt_i,
-               GString *name_buf)
-{
-       int ret = 0;
-       uint64_t i;
-       bt_field_class_type ir_var_fc_type;
-       const void *opt_ranges = NULL;
-       const char *mapping_label = NULL;
-       const char *ir_opt_name;
-       const bt_field_class_variant_option *base_var_opt;
-       bool force_protect = false;
-
-       ir_var_fc_type = bt_field_class_get_type(ir_var_fc);
-       base_var_opt = bt_field_class_variant_borrow_option_by_index_const(
-               ir_var_fc, opt_i);
-       BT_ASSERT(base_var_opt);
-       ir_opt_name = bt_field_class_variant_option_get_name(base_var_opt);
-       BT_ASSERT(ir_opt_name);
-
-       /*
-        * Check if the variant FC option name is required to be
-        * protected (reserved TSDL keyword or starts with `_`). In that
-        * case, the name of the selector FC mapping we find must match
-        * exactly the protected name.
-        */
-       force_protect = must_protect_identifier(ir_opt_name);
-       if (force_protect) {
-               g_string_assign(name_buf, "_");
-               g_string_append(name_buf, ir_opt_name);
-       } else {
-               g_string_assign(name_buf, ir_opt_name);
-       }
-
-       /* Borrow option's ranges */
-       if (ir_var_fc_type == BT_FIELD_CLASS_TYPE_VARIANT_WITHOUT_SELECTOR_FIELD) {
-               /* No ranges: we're done */
-               goto end;
-       } if (ir_var_fc_type == BT_FIELD_CLASS_TYPE_VARIANT_WITH_UNSIGNED_INTEGER_SELECTOR_FIELD) {
-               const bt_field_class_variant_with_selector_field_integer_unsigned_option *var_opt =
-                       bt_field_class_variant_with_selector_field_integer_unsigned_borrow_option_by_index_const(
-                               ir_var_fc, opt_i);
-               opt_ranges =
-                       bt_field_class_variant_with_selector_field_integer_unsigned_option_borrow_ranges_const(
-                               var_opt);
-       } else {
-               const bt_field_class_variant_with_selector_field_integer_signed_option *var_opt =
-                       bt_field_class_variant_with_selector_field_integer_signed_borrow_option_by_index_const(
-                               ir_var_fc, opt_i);
-               opt_ranges =
-                       bt_field_class_variant_with_selector_field_integer_signed_option_borrow_ranges_const(
-                               var_opt);
-       }
-
-       /* Find corresponding mapping by range set in selector FC */
-       for (i = 0; i < bt_field_class_enumeration_get_mapping_count(ir_tag_fc);
-                       i++) {
-               if (ir_var_fc_type == BT_FIELD_CLASS_TYPE_VARIANT_WITH_UNSIGNED_INTEGER_SELECTOR_FIELD) {
-                       const bt_field_class_enumeration_mapping *mapping_base;
-                       const bt_field_class_enumeration_unsigned_mapping *mapping;
-                       const bt_integer_range_set_unsigned *mapping_ranges;
-
-                       mapping = bt_field_class_enumeration_unsigned_borrow_mapping_by_index_const(
-                               ir_tag_fc, i);
-                       mapping_ranges = bt_field_class_enumeration_unsigned_mapping_borrow_ranges_const(
-                               mapping);
-
-                       if (bt_integer_range_set_unsigned_is_equal(opt_ranges,
-                                       mapping_ranges)) {
-                               /* We have a winner */
-                               mapping_base =
-                                       bt_field_class_enumeration_unsigned_mapping_as_mapping_const(
-                                               mapping);
-                               mapping_label =
-                                       bt_field_class_enumeration_mapping_get_label(
-                                               mapping_base);
-                               break;
-                       }
-               } else {
-                       const bt_field_class_enumeration_mapping *mapping_base;
-                       const bt_field_class_enumeration_signed_mapping *mapping;
-                       const bt_integer_range_set_signed *mapping_ranges;
-
-                       mapping = bt_field_class_enumeration_signed_borrow_mapping_by_index_const(
-                               ir_tag_fc, i);
-                       mapping_ranges = bt_field_class_enumeration_signed_mapping_borrow_ranges_const(
-                               mapping);
-
-                       if (bt_integer_range_set_signed_is_equal(opt_ranges,
-                                       mapping_ranges)) {
-                               /* We have a winner */
-                               mapping_base =
-                                       bt_field_class_enumeration_signed_mapping_as_mapping_const(
-                                               mapping);
-                               mapping_label =
-                                       bt_field_class_enumeration_mapping_get_label(
-                                               mapping_base);
-                               break;
-                       }
-               }
-       }
-
-       if (!mapping_label) {
-               /* Range set not found: invalid selector for CTF 1.8 */
-               ret = -1;
-               goto end;
-       }
-
-       /*
-        * If the enumeration FC mapping name is not the same as the
-        * variant FC option name and we didn't protect already, try
-        * protecting the option name and check again.
-        */
-       if (strcmp(mapping_label, name_buf->str) != 0) {
-               if (force_protect) {
-                       ret = -1;
-                       goto end;
-               }
-
-               if (mapping_label[0] == '\0') {
-                       ret = -1;
-                       goto end;
-               }
-
-               g_string_assign(name_buf, "_");
-               g_string_append(name_buf, ir_opt_name);
-
-               if (strcmp(mapping_label, name_buf->str) != 0) {
-                       ret = -1;
-                       goto end;
-               }
-       }
-
-end:
-       return ret;
-}
-
-static inline
-int translate_option_field_class(struct ctx *ctx)
-{
-       struct fs_sink_ctf_field_class_option *fc =
-               fs_sink_ctf_field_class_option_create_empty(
-                       cur_path_stack_top(ctx)->ir_fc,
-                       cur_path_stack_top(ctx)->index_in_parent);
-       const bt_field_class *content_ir_fc =
-               bt_field_class_option_borrow_field_class_const(fc->base.ir_fc);
-       int ret;
-
-       BT_ASSERT(fc);
-
-       /*
-        * CTF 1.8 does not support the option field class type. To
-        * write something anyway, this component translates this type
-        * to a variant field class where the options are:
-        *
-        * * An empty structure field class.
-        * * The optional field class itself.
-        *
-        * The "tag" is always generated/before in that case (an 8-bit
-        * unsigned enumeration field class).
-        */
-       append_to_parent_field_class(ctx, (void *) fc);
-       ret = cur_path_stack_push(ctx, UINT64_C(-1), NULL, false, content_ir_fc,
-               (void *) fc);
-       if (ret) {
-               BT_COMP_LOGE_STR("Cannot translate option field class content.");
-               goto end;
-       }
-
-       ret = translate_field_class(ctx);
-       if (ret) {
-               BT_COMP_LOGE_STR("Cannot translate option field class content.");
-               goto end;
-       }
-
-       cur_path_stack_pop(ctx);
-       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_field_class_type ir_fc_type;
-       const bt_field_path *ir_selector_field_path = NULL;
-       struct fs_sink_ctf_field_class *tgt_fc = NULL;
-       GString *name_buf = g_string_new(NULL);
-       bt_value *prot_opt_names = bt_value_array_create();
-       uint64_t opt_count;
-
-       BT_ASSERT(fc);
-       BT_ASSERT(name_buf);
-       BT_ASSERT(prot_opt_names);
-       ir_fc_type = bt_field_class_get_type(fc->base.ir_fc);
-       opt_count = bt_field_class_variant_get_option_count(fc->base.ir_fc);
-
-       if (bt_field_class_type_is(ir_fc_type,
-                       BT_FIELD_CLASS_TYPE_VARIANT_WITH_SELECTOR_FIELD)) {
-               ir_selector_field_path = bt_field_class_variant_with_selector_field_borrow_selector_field_path_const(
-                       fc->base.ir_fc);
-               BT_ASSERT(ir_selector_field_path);
-       }
-
-       /* Resolve tag field class before appending to parent */
-       resolve_field_class(ctx, ir_selector_field_path, fc->tag_ref,
-               &fc->tag_is_before, &tgt_fc);
-
-       if (ir_selector_field_path && tgt_fc) {
-               uint64_t mapping_count;
-               uint64_t option_count;
-
-               /* CTF 1.8: selector FC must be an enumeration FC */
-               bt_field_class_type type = bt_field_class_get_type(
-                       tgt_fc->ir_fc);
-
-               if (!bt_field_class_type_is(type,
-                               BT_FIELD_CLASS_TYPE_ENUMERATION)) {
-                       fc->tag_is_before = true;
-                       goto validate_opts;
-               }
-
-               /*
-                * Call maybe_protect_variant_option_name() for each
-                * option below. In that case we also want selector FC
-                * to contain as many mappings as the variant FC has
-                * options.
-                */
-               mapping_count = bt_field_class_enumeration_get_mapping_count(
-                       tgt_fc->ir_fc);
-               option_count = bt_field_class_variant_get_option_count(
-                       fc->base.ir_fc);
-
-               if (mapping_count != option_count) {
-                       fc->tag_is_before = true;
-                       goto validate_opts;
-               }
-       } else {
-               /*
-                * No compatible selector field class for CTF 1.8:
-                * create the appropriate selector field class.
-                */
-               fc->tag_is_before = true;
-               goto validate_opts;
-       }
-
-validate_opts:
-       /*
-        * First pass: detect any option name clash with option name
-        * protection. In that case, we don't fail: just create the
-        * selector field class before the variant field class.
-        *
-        * After this, `prot_opt_names` contains the final option names,
-        * potentially protected if needed. They can still be invalid
-        * TSDL identifiers however; this will be checked by
-        * cur_path_stack_push().
-        */
-       for (i = 0; i < opt_count; i++) {
-               if (!fc->tag_is_before) {
-                       BT_ASSERT(tgt_fc->ir_fc);
-                       ret = maybe_protect_variant_option_name(fc->base.ir_fc,
-                               tgt_fc->ir_fc, i, name_buf);
-                       if (ret) {
-                               fc->tag_is_before = true;
-                       }
-               }
-
-               ret = bt_value_array_append_string_element(prot_opt_names,
-                       name_buf->str);
-               if (ret) {
-                       goto end;
-               }
-       }
-
-       for (i = 0; i < opt_count; i++) {
-               uint64_t j;
-               const bt_value *opt_name_a =
-                       bt_value_array_borrow_element_by_index_const(
-                               prot_opt_names, i);
-
-               for (j = 0; j < opt_count; j++) {
-                       const bt_value *opt_name_b;
-
-                       if (i == j) {
-                               continue;
-                       }
-
-                       opt_name_b =
-                               bt_value_array_borrow_element_by_index_const(
-                                       prot_opt_names, j);
-                       if (bt_value_is_equal(opt_name_a, opt_name_b)) {
-                               /*
-                                * Variant FC option names are not
-                                * unique when protected.
-                                */
-                               fc->tag_is_before = true;
-                               goto append_to_parent;
-                       }
-               }
-       }
-
-append_to_parent:
-       append_to_parent_field_class(ctx, (void *) fc);
-
-       for (i = 0; i < opt_count; i++) {
-               const bt_field_class_variant_option *opt;
-               const bt_field_class *opt_ir_fc;
-               const bt_value *prot_opt_name_val =
-                       bt_value_array_borrow_element_by_index_const(
-                               prot_opt_names, i);
-               const char *prot_opt_name = bt_value_string_get(
-                       prot_opt_name_val);
-
-               BT_ASSERT(prot_opt_name);
-               opt = bt_field_class_variant_borrow_option_by_index_const(
-                       fc->base.ir_fc, i);
-               opt_ir_fc = bt_field_class_variant_option_borrow_field_class_const(
-                       opt);
-
-               /*
-                * We don't ask cur_path_stack_push() to protect the
-                * option name because it's already protected at this
-                * point.
-                */
-               ret = cur_path_stack_push(ctx, i, prot_opt_name, false,
-                       opt_ir_fc, (void *) fc);
-               if (ret) {
-                       BT_COMP_LOGE("Cannot translate variant field class option: "
-                               "name=\"%s\"", prot_opt_name);
-                       goto end;
-               }
-
-               ret = translate_field_class(ctx);
-               if (ret) {
-                       BT_COMP_LOGE("Cannot translate variant field class option: "
-                               "name=\"%s\"", prot_opt_name);
-                       goto end;
-               }
-
-               cur_path_stack_pop(ctx);
-       }
-
-end:
-       if (name_buf) {
-               g_string_free(name_buf, TRUE);
-       }
-
-       bt_value_put_ref(prot_opt_names);
-       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, false, elem_ir_fc,
-               (void *) fc);
-       if (ret) {
-               BT_COMP_LOGE_STR("Cannot translate static array field class element.");
-               goto end;
-       }
-
-       ret = translate_field_class(ctx);
-       if (ret) {
-               BT_COMP_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 */
-       if (bt_field_class_get_type(cur_path_stack_top(ctx)->ir_fc) ==
-                       BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY_WITH_LENGTH_FIELD) {
-               resolve_field_class(ctx,
-                       bt_field_class_array_dynamic_with_length_field_borrow_length_field_path_const(
-                               fc->base.base.ir_fc),
-                       fc->length_ref, &fc->length_is_before, NULL);
-       }
-
-       append_to_parent_field_class(ctx, (void *) fc);
-       ret = cur_path_stack_push(ctx, UINT64_C(-1), NULL, false, elem_ir_fc,
-               (void *) fc);
-       if (ret) {
-               BT_COMP_LOGE_STR("Cannot translate dynamic array field class element.");
-               goto end;
-       }
-
-       ret = translate_field_class(ctx);
-       if (ret) {
-               BT_COMP_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_bool_field_class(struct ctx *ctx)
-{
-       struct fs_sink_ctf_field_class_bool *fc =
-               fs_sink_ctf_field_class_bool_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_bit_array_field_class(struct ctx *ctx)
-{
-       struct fs_sink_ctf_field_class_bit_array *fc =
-               fs_sink_ctf_field_class_bit_array_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_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;
-       bt_field_class_type ir_fc_type =
-               bt_field_class_get_type(cur_path_stack_top(ctx)->ir_fc);
-
-       if (ir_fc_type == BT_FIELD_CLASS_TYPE_BOOL) {
-               ret = translate_bool_field_class(ctx);
-       } else if (ir_fc_type == BT_FIELD_CLASS_TYPE_BIT_ARRAY) {
-               ret = translate_bit_array_field_class(ctx);
-       } else if (bt_field_class_type_is(ir_fc_type,
-                       BT_FIELD_CLASS_TYPE_INTEGER)) {
-               ret = translate_integer_field_class(ctx);
-       } else if (bt_field_class_type_is(ir_fc_type,
-                       BT_FIELD_CLASS_TYPE_REAL)) {
-               ret = translate_real_field_class(ctx);
-       } else if (ir_fc_type == BT_FIELD_CLASS_TYPE_STRING) {
-               ret = translate_string_field_class(ctx);
-       } else if (ir_fc_type == BT_FIELD_CLASS_TYPE_STRUCTURE) {
-               ret = translate_structure_field_class(ctx);
-       } else if (ir_fc_type == BT_FIELD_CLASS_TYPE_STATIC_ARRAY) {
-               ret = translate_static_array_field_class(ctx);
-       } else if (bt_field_class_type_is(ir_fc_type,
-                       BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY)) {
-               ret = translate_dynamic_array_field_class(ctx);
-       } else if (bt_field_class_type_is(ir_fc_type,
-                       BT_FIELD_CLASS_TYPE_OPTION)) {
-               ret = translate_option_field_class(ctx);
-       } else if (bt_field_class_type_is(ir_fc_type,
-                       BT_FIELD_CLASS_TYPE_VARIANT)) {
-               ret = translate_variant_field_class(ctx);
-       } else {
-               bt_common_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_OPTION:
-       {
-               /*
-                * CTF 1.8 does not support the option field class type.
-                * To write something anyway, this component translates
-                * this type to a variant field class where the options
-                * are:
-                *
-                * * An empty structure field class.
-                * * The optional field class itself.
-                *
-                * Because the option field class becomes a CTF variant
-                * field class, we use the term "tag" too here.
-                *
-                * The "tag" is always generated/before in that case (an
-                * 8-bit unsigned enumeration field class).
-                */
-               struct fs_sink_ctf_field_class_option *opt_fc = (void *) fc;
-
-               field_ref = opt_fc->tag_ref;
-               is_before = true;
-               tgt_type = "tag";
-               break;
-       }
-       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:
-               bt_common_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 * const fc,
-               const char *fc_name, struct fs_sink_ctf_field_class *parent_fc)
-{
-       int ret = 0;
-       enum fs_sink_ctf_field_class_type fc_type;
-       BT_ASSERT(fc);
-
-       fc_type = fc->type;
-
-       switch (fc_type) {
-       case FS_SINK_CTF_FIELD_CLASS_TYPE_OPTION:
-       {
-               struct fs_sink_ctf_field_class_option *opt_fc = (void *) fc;
-
-               ret = set_field_ref(fc, fc_name, parent_fc);
-               if (ret) {
-                       goto end;
-               }
-
-               ret = set_field_refs(opt_fc->content_fc, NULL, fc);
-               if (ret) {
-                       goto end;
-               }
-
-               break;
-       }
-       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 = NULL;
-               struct fs_sink_ctf_field_class_variant *var_fc = NULL;
-               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_field_path_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, false, ir_fc, NULL);
-       if (ret) {
-               BT_COMP_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_COMP_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, struct fs_sink_comp *fs_sink)
-{
-       memset(ctx, 0, sizeof(struct ctx));
-       ctx->cur_path = g_array_new(FALSE, TRUE,
-               sizeof(struct field_path_elem));
-       BT_ASSERT(ctx->cur_path);
-       ctx->log_level = fs_sink->log_level;
-       ctx->self_comp = fs_sink->self_comp;
-}
-
-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_comp *fs_sink,
-               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, fs_sink);
-       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_FIELD_PATH_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_FIELD_PATH_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_comp *fs_sink,
-               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 (G_LIKELY(*out_ec)) {
-               goto end;
-       }
-
-       ret = translate_event_class(fs_sink, sc, ir_ec, out_ec);
-
-end:
-       return ret;
-}
-
-static
-bool default_clock_class_name_exists(struct fs_sink_ctf_trace *trace,
-               const char *name)
-{
-       bool exists = false;
-       uint64_t i;
-
-       for (i = 0; i < trace->stream_classes->len; i++) {
-               struct fs_sink_ctf_stream_class *sc =
-                       trace->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->trace, buf)) {
-               sprintf(buf, "default%u", suffix);
-               suffix++;
-       }
-
-       g_string_assign(sc->default_clock_class_name, buf);
-}
-
-static
-int translate_stream_class(struct fs_sink_comp *fs_sink,
-               struct fs_sink_ctf_trace *trace,
-               const bt_stream_class *ir_sc,
-               struct fs_sink_ctf_stream_class **out_sc)
-{
-       int ret = 0;
-       struct ctx ctx;
-
-       BT_ASSERT(trace);
-       BT_ASSERT(ir_sc);
-       ctx_init(&ctx, fs_sink);
-       *out_sc = fs_sink_ctf_stream_class_create(trace, 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 (name) {
-                       /* Try original name, protected */
-                       g_string_assign((*out_sc)->default_clock_class_name,
-                               "");
-
-                       if (must_protect_identifier(name)) {
-                               g_string_assign(
-                                       (*out_sc)->default_clock_class_name,
-                                       "_");
-                       }
-
-                       g_string_assign((*out_sc)->default_clock_class_name,
-                               name);
-                       if (!ist_valid_identifier(
-                                       (*out_sc)->default_clock_class_name->str)) {
-                               /* Invalid: create a new name */
-                               make_unique_default_clock_class_name(*out_sc);
-                       }
-               } 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_FIELD_PATH_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_FIELD_PATH_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_comp *fs_sink,
-               struct fs_sink_ctf_trace *trace,
-               const bt_stream_class *ir_sc,
-               struct fs_sink_ctf_stream_class **out_sc)
-{
-       int ret = 0;
-       uint64_t i;
-
-       BT_ASSERT(trace);
-       BT_ASSERT(ir_sc);
-
-       for (i = 0; i < trace->stream_classes->len; i++) {
-               *out_sc = trace->stream_classes->pdata[i];
-
-               if ((*out_sc)->ir_sc == ir_sc) {
-                       goto end;
-               }
-       }
-
-       ret = translate_stream_class(fs_sink, trace, ir_sc, out_sc);
-
-end:
-       return ret;
-}
-
-BT_HIDDEN
-struct fs_sink_ctf_trace *translate_trace_trace_ir_to_ctf_ir(
-               struct fs_sink_comp *fs_sink, const bt_trace *ir_trace)
-{
-       uint64_t count;
-       uint64_t i;
-       struct fs_sink_ctf_trace *trace = NULL;
-
-       /* Check that trace's environment is TSDL-compatible */
-       count = bt_trace_get_environment_entry_count(ir_trace);
-       for (i = 0; i < count; i++) {
-               const char *name;
-               const bt_value *val;
-
-               bt_trace_borrow_environment_entry_by_index_const(
-                       ir_trace, i, &name, &val);
-
-               if (!ist_valid_identifier(name)) {
-                       BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, fs_sink->log_level,
-                               fs_sink->self_comp,
-                               "Unsupported trace class's environment entry name: "
-                               "name=\"%s\"", name);
-                       goto end;
-               }
-
-               switch (bt_value_get_type(val)) {
-               case BT_VALUE_TYPE_SIGNED_INTEGER:
-               case BT_VALUE_TYPE_STRING:
-                       break;
-               default:
-                       BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, fs_sink->log_level,
-                               fs_sink->self_comp,
-                               "Unsupported trace class's environment entry value type: "
-                               "type=%s",
-                               bt_common_value_type_string(
-                                       bt_value_get_type(val)));
-                       goto end;
-               }
-       }
-
-       trace = fs_sink_ctf_trace_create(ir_trace);
-       BT_ASSERT(trace);
-
-end:
-       return trace;
-}
diff --git a/src/plugins/ctf/fs-sink/translate-trace-ir-to-ctf-ir.cpp b/src/plugins/ctf/fs-sink/translate-trace-ir-to-ctf-ir.cpp
new file mode 100644 (file)
index 0000000..a200994
--- /dev/null
@@ -0,0 +1,1887 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#define BT_COMP_LOG_SELF_COMP (ctx->self_comp)
+#define BT_LOG_OUTPUT_LEVEL (ctx->log_level)
+#define BT_LOG_TAG "PLUGIN/SINK.CTF.FS/TRANSLATE-TRACE-IR-TO-CTF-IR"
+#include "logging/comp-logging.h"
+
+#include "translate-trace-ir-to-ctf-ir.hpp"
+
+#include <babeltrace2/babeltrace.h>
+#include "common/macros.h"
+#include "common/common.h"
+#include "common/assert.h"
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <glib.h>
+
+#include "fs-sink.hpp"
+#include "fs-sink-ctf-meta.hpp"
+
+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 {
+       bt_logging_level log_level;
+       bt_self_component *self_comp;
+
+       /* Weak */
+       struct fs_sink_ctf_stream_class *cur_sc;
+
+       /* Weak */
+       struct fs_sink_ctf_event_class *cur_ec;
+
+       bt_field_path_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 const char *reserved_tsdl_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",
+};
+
+static inline
+bool ist_valid_identifier(const char *name)
+{
+       const char *at;
+       uint64_t i;
+       bool ist_valid = true;
+
+       /* Make sure the name is not a reserved keyword */
+       for (i = 0; i < sizeof(reserved_tsdl_keywords) / sizeof(*reserved_tsdl_keywords);
+                       i++) {
+               if (strcmp(name, reserved_tsdl_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((unsigned char) 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((unsigned char) *at) && *at != '_') {
+                       ist_valid = false;
+                       goto end;
+               }
+       }
+
+end:
+       return ist_valid;
+}
+
+static inline
+bool must_protect_identifier(const char *name)
+{
+       uint64_t i;
+       bool must_protect = false;
+
+       /* Protect a reserved keyword */
+       for (i = 0; i < sizeof(reserved_tsdl_keywords) / sizeof(*reserved_tsdl_keywords);
+                       i++) {
+               if (strcmp(name, reserved_tsdl_keywords[i]) == 0) {
+                       must_protect = true;
+                       goto end;
+               }
+       }
+
+       /* Protect an identifier which already starts with `_` */
+       if (name[0] == '_') {
+               must_protect = true;
+               goto end;
+       }
+
+end:
+       return must_protect;
+}
+
+static inline
+int cur_path_stack_push(struct ctx *ctx,
+               uint64_t index_in_parent, const char *name,
+               bool force_protect_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(NULL);
+
+       if (name) {
+               if (force_protect_name) {
+                       g_string_assign(field_path_elem->name, "_");
+               }
+
+               g_string_append(field_path_elem->name, name);
+
+               if (ctx->cur_scope == BT_FIELD_PATH_SCOPE_PACKET_CONTEXT) {
+                       if (is_reserved_member_name(name, "packet_size") ||
+                                       is_reserved_member_name(name, "content_size") ||
+                                       is_reserved_member_name(name, "timestamp_begin") ||
+                                       is_reserved_member_name(name, "timestamp_end") ||
+                                       is_reserved_member_name(name, "events_discarded") ||
+                                       is_reserved_member_name(name, "packet_seq_num")) {
+                               BT_COMP_LOGE("Unsupported reserved TSDL structure field class member "
+                                       "or variant field class option name: name=\"%s\"",
+                                       name);
+                               ret = -1;
+                               goto end;
+                       }
+               }
+
+               if (!ist_valid_identifier(field_path_elem->name->str)) {
+                       ret = -1;
+                       BT_COMP_LOGE("Unsupported non-TSDL structure field class member "
+                               "or variant field class option name: name=\"%s\"",
+                               field_path_elem->name->str);
+                       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);
+}
+
+/*
+ * 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,
+               struct fs_sink_ctf_field_class **user_tgt_fc)
+{
+       int ret = 0;
+       struct fs_sink_ctf_field_class *tgt_fc = NULL;
+       uint64_t i;
+       int64_t si;
+       const char *tgt_fc_name = NULL;
+       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_FIELD_PATH_SCOPE_PACKET_CONTEXT:
+               BT_ASSERT(ctx->cur_sc);
+               tgt_fc = ctx->cur_sc->packet_context_fc;
+               break;
+       case BT_FIELD_PATH_SCOPE_EVENT_COMMON_CONTEXT:
+               BT_ASSERT(ctx->cur_sc);
+               tgt_fc = ctx->cur_sc->event_common_context_fc;
+               break;
+       case BT_FIELD_PATH_SCOPE_EVENT_SPECIFIC_CONTEXT:
+               BT_ASSERT(ctx->cur_ec);
+               tgt_fc = ctx->cur_ec->spec_context_fc;
+               break;
+       case BT_FIELD_PATH_SCOPE_EVENT_PAYLOAD:
+               BT_ASSERT(ctx->cur_ec);
+               tgt_fc = ctx->cur_ec->payload_fc;
+               break;
+       default:
+               bt_common_abort();
+       }
+
+       i = 0;
+
+       while (i < bt_field_path_get_item_count(tgt_ir_field_path)) {
+               const bt_field_path_item *fp_item =
+                       bt_field_path_borrow_item_by_index_const(
+                               tgt_ir_field_path, i);
+               struct fs_sink_ctf_named_field_class *named_fc = NULL;
+
+               BT_ASSERT(tgt_fc);
+               BT_ASSERT(fp_item);
+
+               if (bt_field_path_item_get_type(fp_item) ==
+                               BT_FIELD_PATH_ITEM_TYPE_CURRENT_OPTION_CONTENT) {
+                       /* Not supported by CTF 1.8 */
+                       ret = -1;
+                       goto end;
+               }
+
+               switch (tgt_fc->type) {
+               case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT:
+                       BT_ASSERT(bt_field_path_item_get_type(fp_item) ==
+                               BT_FIELD_PATH_ITEM_TYPE_INDEX);
+                       named_fc = fs_sink_ctf_field_class_struct_borrow_member_by_index(
+                               fs_sink_ctf_field_class_as_struct(tgt_fc),
+                               bt_field_path_item_index_get_index(fp_item));
+                       break;
+               case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT:
+                       BT_ASSERT(bt_field_path_item_get_type(fp_item) ==
+                               BT_FIELD_PATH_ITEM_TYPE_INDEX);
+                       named_fc = fs_sink_ctf_field_class_variant_borrow_option_by_index(
+                               fs_sink_ctf_field_class_as_variant(tgt_fc),
+                               bt_field_path_item_index_get_index(fp_item));
+                       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 =
+                               fs_sink_ctf_field_class_as_array_base(tgt_fc);
+
+                       BT_ASSERT(bt_field_path_item_get_type(fp_item) ==
+                               BT_FIELD_PATH_ITEM_TYPE_CURRENT_ARRAY_ELEMENT);
+                       tgt_fc = array_base_fc->elem_fc;
+                       break;
+               }
+               default:
+                       bt_common_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 = NULL;
+               struct fs_sink_ctf_field_class_variant *var_fc = NULL;
+               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 = fs_sink_ctf_field_class_as_struct(fc);
+                       len = struct_fc->members->len;
+               } else {
+                       var_fc = fs_sink_ctf_field_class_as_variant(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);
+
+                                       if (user_tgt_fc) {
+                                               *user_tgt_fc = tgt_fc;
+                                       }
+                               } 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,
+               struct fs_sink_ctf_field_class **user_tgt_fc)
+{
+       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_FIELD_PATH_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_FIELD_PATH_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_FIELD_PATH_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_FIELD_PATH_SCOPE_EVENT_PAYLOAD:
+               BT_ASSERT(ctx->cur_ec);
+               fc = ctx->cur_ec->payload_fc;
+               g_string_assign(tgt_field_ref, "event.fields");
+               break;
+       default:
+               bt_common_abort();
+       }
+
+       BT_ASSERT(fc);
+
+       for (i = 0; i < bt_field_path_get_item_count(tgt_ir_field_path); i++) {
+               const bt_field_path_item *fp_item =
+                       bt_field_path_borrow_item_by_index_const(
+                               tgt_ir_field_path, i);
+               struct fs_sink_ctf_named_field_class *named_fc = NULL;
+
+               if (bt_field_path_item_get_type(fp_item) !=
+                               BT_FIELD_PATH_ITEM_TYPE_INDEX) {
+                       /* Not supported by TSDL 1.8 */
+                       ret = -1;
+                       goto end;
+               }
+
+               switch (fc->type) {
+               case FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT:
+                       BT_ASSERT(bt_field_path_item_get_type(fp_item) ==
+                               BT_FIELD_PATH_ITEM_TYPE_INDEX);
+                       named_fc = fs_sink_ctf_field_class_struct_borrow_member_by_index(
+                               fs_sink_ctf_field_class_as_struct(fc),
+                               bt_field_path_item_index_get_index(fp_item));
+                       break;
+               case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT:
+                       BT_ASSERT(bt_field_path_item_get_type(fp_item) ==
+                               BT_FIELD_PATH_ITEM_TYPE_INDEX);
+                       named_fc = fs_sink_ctf_field_class_variant_borrow_option_by_index(
+                               fs_sink_ctf_field_class_as_variant(fc),
+                               bt_field_path_item_index_get_index(fp_item));
+                       break;
+               default:
+                       bt_common_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;
+       }
+
+       if (user_tgt_fc) {
+               *user_tgt_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,
+               struct fs_sink_ctf_field_class **user_tgt_fc)
+{
+       int ret;
+       bt_field_path_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, user_tgt_fc);
+               if (ret) {
+                       ret = create_absolute_field_ref(ctx, tgt_ir_field_path,
+                               tgt_field_ref, user_tgt_fc);
+                       if (ret) {
+                               *create_before = true;
+                               goto end;
+                       }
+               }
+       } else {
+               ret = create_absolute_field_ref(ctx, tgt_ir_field_path,
+                       tgt_field_ref, user_tgt_fc);
+
+               /* 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(
+                       fs_sink_ctf_field_class_as_struct(parent_fc),
+                       cur_path_stack_top(ctx)->name->str, fc);
+               break;
+       case FS_SINK_CTF_FIELD_CLASS_TYPE_OPTION:
+       {
+               struct fs_sink_ctf_field_class_option *opt_fc =
+                       fs_sink_ctf_field_class_as_option(parent_fc);
+
+               BT_ASSERT(!opt_fc->content_fc);
+               opt_fc->content_fc = fc;
+               opt_fc->base.alignment = fc->alignment;
+               break;
+       }
+       case FS_SINK_CTF_FIELD_CLASS_TYPE_VARIANT:
+               fs_sink_ctf_field_class_variant_append_option(
+                       fs_sink_ctf_field_class_as_variant(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 =
+                       fs_sink_ctf_field_class_as_array_base(parent_fc);
+
+               BT_ASSERT(!array_base_fc->elem_fc);
+               array_base_fc->elem_fc = fc;
+               array_base_fc->base.alignment = fc->alignment;
+               break;
+       }
+       default:
+               bt_common_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(
+                       fs_sink_ctf_field_class_as_struct(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 =
+                       fs_sink_ctf_field_class_as_array_base(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, true, memb_ir_fc,
+                       &struct_fc->base);
+               if (ret) {
+                       BT_COMP_LOGE("Cannot translate structure field class member: "
+                               "name=\"%s\"", name);
+                       goto end;
+               }
+
+               ret = translate_field_class(ctx);
+               if (ret) {
+                       BT_COMP_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, &fc->base);
+       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;
+}
+
+/*
+ * This function protects a given variant FC option name (with the `_`
+ * prefix) if required. On success, `name_buf->str` contains the variant
+ * FC option name to use (original option name or protected if
+ * required).
+ *
+ * One of the goals of `sink.ctf.fs` is to write a CTF trace which is as
+ * close as possible to an original CTF trace as decoded by
+ * `src.ctf.fs`.
+ *
+ * This scenario is valid in CTF 1.8:
+ *
+ *     enum {
+ *         HELLO,
+ *         MEOW
+ *     } tag;
+ *
+ *     variant <tag> {
+ *         int HELLO;
+ *         string MEOW;
+ *     };
+ *
+ * Once in trace IR, the enumeration FC mapping names and variant FC
+ * option names are kept as is. For this reason, we don't want to
+ * protect the variant FC option names here (by prepending `_`): this
+ * would make the variant FC option name and the enumeration FC mapping
+ * name not match.
+ *
+ * This scenario is also valid in CTF 1.8:
+ *
+ *     enum {
+ *         _HELLO,
+ *         MEOW
+ *     } tag;
+ *
+ *     variant <tag> {
+ *         int _HELLO;
+ *         string MEOW;
+ *     };
+ *
+ * Once in trace IR, the enumeration FC mapping names are kept as is,
+ * but the `_HELLO` variant FC option name becomes `HELLO` (unprotected
+ * for presentation, as recommended by CTF 1.8). When going back to
+ * TSDL, we need to protect `HELLO` so that it becomes `_HELLO` to match
+ * the corresponding enumeration FC mapping name.
+ *
+ * This scenario is also valid in CTF 1.8:
+ *
+ *     enum {
+ *         __HELLO,
+ *         MEOW
+ *     } tag;
+ *
+ *     variant <tag> {
+ *         int __HELLO;
+ *         string MEOW;
+ *     };
+ *
+ * Once in trace IR, the enumeration FC mapping names are kept as is,
+ * but the `__HELLO` variant FC option name becomes `_HELLO`
+ * (unprotected). When going back to TSDL, we need to protect `_HELLO`
+ * so that it becomes `__HELLO` to match the corresponding enumeration
+ * FC mapping name.
+ *
+ * `src.ctf.fs` always uses the _same_ integer range sets for a selector
+ * FC mapping and a corresponding variant FC option. We can use that
+ * fact to find the original variant FC option names by matching variant
+ * FC options and enumeration FC mappings by range set.
+ */
+static
+int maybe_protect_variant_option_name(const bt_field_class *ir_var_fc,
+               const bt_field_class *ir_tag_fc, uint64_t opt_i,
+               GString *name_buf)
+{
+       int ret = 0;
+       uint64_t i;
+       bt_field_class_type ir_var_fc_type;
+       const bt_integer_range_set_unsigned *opt_ranges_unsigned = NULL;
+       const bt_integer_range_set_signed *opt_ranges_signed = NULL;
+       const char *mapping_label = NULL;
+       const char *ir_opt_name;
+       const bt_field_class_variant_option *base_var_opt;
+       bool force_protect = false;
+
+       ir_var_fc_type = bt_field_class_get_type(ir_var_fc);
+       base_var_opt = bt_field_class_variant_borrow_option_by_index_const(
+               ir_var_fc, opt_i);
+       BT_ASSERT(base_var_opt);
+       ir_opt_name = bt_field_class_variant_option_get_name(base_var_opt);
+       BT_ASSERT(ir_opt_name);
+
+       /*
+        * Check if the variant FC option name is required to be
+        * protected (reserved TSDL keyword or starts with `_`). In that
+        * case, the name of the selector FC mapping we find must match
+        * exactly the protected name.
+        */
+       force_protect = must_protect_identifier(ir_opt_name);
+       if (force_protect) {
+               g_string_assign(name_buf, "_");
+               g_string_append(name_buf, ir_opt_name);
+       } else {
+               g_string_assign(name_buf, ir_opt_name);
+       }
+
+       /* Borrow option's ranges */
+       if (ir_var_fc_type == BT_FIELD_CLASS_TYPE_VARIANT_WITHOUT_SELECTOR_FIELD) {
+               /* No ranges: we're done */
+               goto end;
+       } if (ir_var_fc_type == BT_FIELD_CLASS_TYPE_VARIANT_WITH_UNSIGNED_INTEGER_SELECTOR_FIELD) {
+               const bt_field_class_variant_with_selector_field_integer_unsigned_option *var_opt =
+                       bt_field_class_variant_with_selector_field_integer_unsigned_borrow_option_by_index_const(
+                               ir_var_fc, opt_i);
+               opt_ranges_unsigned =
+                       bt_field_class_variant_with_selector_field_integer_unsigned_option_borrow_ranges_const(
+                               var_opt);
+       } else {
+               const bt_field_class_variant_with_selector_field_integer_signed_option *var_opt =
+                       bt_field_class_variant_with_selector_field_integer_signed_borrow_option_by_index_const(
+                               ir_var_fc, opt_i);
+               opt_ranges_signed =
+                       bt_field_class_variant_with_selector_field_integer_signed_option_borrow_ranges_const(
+                               var_opt);
+       }
+
+       /* Find corresponding mapping by range set in selector FC */
+       for (i = 0; i < bt_field_class_enumeration_get_mapping_count(ir_tag_fc);
+                       i++) {
+               if (ir_var_fc_type == BT_FIELD_CLASS_TYPE_VARIANT_WITH_UNSIGNED_INTEGER_SELECTOR_FIELD) {
+                       const bt_field_class_enumeration_mapping *mapping_base;
+                       const bt_field_class_enumeration_unsigned_mapping *mapping;
+                       const bt_integer_range_set_unsigned *mapping_ranges;
+
+                       mapping = bt_field_class_enumeration_unsigned_borrow_mapping_by_index_const(
+                               ir_tag_fc, i);
+                       mapping_ranges = bt_field_class_enumeration_unsigned_mapping_borrow_ranges_const(
+                               mapping);
+
+                       if (bt_integer_range_set_unsigned_is_equal(opt_ranges_unsigned,
+                                       mapping_ranges)) {
+                               /* We have a winner */
+                               mapping_base =
+                                       bt_field_class_enumeration_unsigned_mapping_as_mapping_const(
+                                               mapping);
+                               mapping_label =
+                                       bt_field_class_enumeration_mapping_get_label(
+                                               mapping_base);
+                               break;
+                       }
+               } else {
+                       const bt_field_class_enumeration_mapping *mapping_base;
+                       const bt_field_class_enumeration_signed_mapping *mapping;
+                       const bt_integer_range_set_signed *mapping_ranges;
+
+                       mapping = bt_field_class_enumeration_signed_borrow_mapping_by_index_const(
+                               ir_tag_fc, i);
+                       mapping_ranges = bt_field_class_enumeration_signed_mapping_borrow_ranges_const(
+                               mapping);
+
+                       if (bt_integer_range_set_signed_is_equal(opt_ranges_signed,
+                                       mapping_ranges)) {
+                               /* We have a winner */
+                               mapping_base =
+                                       bt_field_class_enumeration_signed_mapping_as_mapping_const(
+                                               mapping);
+                               mapping_label =
+                                       bt_field_class_enumeration_mapping_get_label(
+                                               mapping_base);
+                               break;
+                       }
+               }
+       }
+
+       if (!mapping_label) {
+               /* Range set not found: invalid selector for CTF 1.8 */
+               ret = -1;
+               goto end;
+       }
+
+       /*
+        * If the enumeration FC mapping name is not the same as the
+        * variant FC option name and we didn't protect already, try
+        * protecting the option name and check again.
+        */
+       if (strcmp(mapping_label, name_buf->str) != 0) {
+               if (force_protect) {
+                       ret = -1;
+                       goto end;
+               }
+
+               if (mapping_label[0] == '\0') {
+                       ret = -1;
+                       goto end;
+               }
+
+               g_string_assign(name_buf, "_");
+               g_string_append(name_buf, ir_opt_name);
+
+               if (strcmp(mapping_label, name_buf->str) != 0) {
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+end:
+       return ret;
+}
+
+static inline
+int translate_option_field_class(struct ctx *ctx)
+{
+       struct fs_sink_ctf_field_class_option *fc =
+               fs_sink_ctf_field_class_option_create_empty(
+                       cur_path_stack_top(ctx)->ir_fc,
+                       cur_path_stack_top(ctx)->index_in_parent);
+       const bt_field_class *content_ir_fc =
+               bt_field_class_option_borrow_field_class_const(fc->base.ir_fc);
+       int ret;
+
+       BT_ASSERT(fc);
+
+       /*
+        * CTF 1.8 does not support the option field class type. To
+        * write something anyway, this component translates this type
+        * to a variant field class where the options are:
+        *
+        * * An empty structure field class.
+        * * The optional field class itself.
+        *
+        * The "tag" is always generated/before in that case (an 8-bit
+        * unsigned enumeration field class).
+        */
+       append_to_parent_field_class(ctx, &fc->base);
+       ret = cur_path_stack_push(ctx, UINT64_C(-1), NULL, false, content_ir_fc,
+               &fc->base);
+       if (ret) {
+               BT_COMP_LOGE_STR("Cannot translate option field class content.");
+               goto end;
+       }
+
+       ret = translate_field_class(ctx);
+       if (ret) {
+               BT_COMP_LOGE_STR("Cannot translate option field class content.");
+               goto end;
+       }
+
+       cur_path_stack_pop(ctx);
+       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_field_class_type ir_fc_type;
+       const bt_field_path *ir_selector_field_path = NULL;
+       struct fs_sink_ctf_field_class *tgt_fc = NULL;
+       GString *name_buf = g_string_new(NULL);
+       bt_value *prot_opt_names = bt_value_array_create();
+       uint64_t opt_count;
+
+       BT_ASSERT(fc);
+       BT_ASSERT(name_buf);
+       BT_ASSERT(prot_opt_names);
+       ir_fc_type = bt_field_class_get_type(fc->base.ir_fc);
+       opt_count = bt_field_class_variant_get_option_count(fc->base.ir_fc);
+
+       if (bt_field_class_type_is(ir_fc_type,
+                       BT_FIELD_CLASS_TYPE_VARIANT_WITH_SELECTOR_FIELD)) {
+               ir_selector_field_path = bt_field_class_variant_with_selector_field_borrow_selector_field_path_const(
+                       fc->base.ir_fc);
+               BT_ASSERT(ir_selector_field_path);
+       }
+
+       /* Resolve tag field class before appending to parent */
+       resolve_field_class(ctx, ir_selector_field_path, fc->tag_ref,
+               &fc->tag_is_before, &tgt_fc);
+
+       if (ir_selector_field_path && tgt_fc) {
+               uint64_t mapping_count;
+               uint64_t option_count;
+
+               /* CTF 1.8: selector FC must be an enumeration FC */
+               bt_field_class_type type = bt_field_class_get_type(
+                       tgt_fc->ir_fc);
+
+               if (!bt_field_class_type_is(type,
+                               BT_FIELD_CLASS_TYPE_ENUMERATION)) {
+                       fc->tag_is_before = true;
+                       goto validate_opts;
+               }
+
+               /*
+                * Call maybe_protect_variant_option_name() for each
+                * option below. In that case we also want selector FC
+                * to contain as many mappings as the variant FC has
+                * options.
+                */
+               mapping_count = bt_field_class_enumeration_get_mapping_count(
+                       tgt_fc->ir_fc);
+               option_count = bt_field_class_variant_get_option_count(
+                       fc->base.ir_fc);
+
+               if (mapping_count != option_count) {
+                       fc->tag_is_before = true;
+                       goto validate_opts;
+               }
+       } else {
+               /*
+                * No compatible selector field class for CTF 1.8:
+                * create the appropriate selector field class.
+                */
+               fc->tag_is_before = true;
+               goto validate_opts;
+       }
+
+validate_opts:
+       /*
+        * First pass: detect any option name clash with option name
+        * protection. In that case, we don't fail: just create the
+        * selector field class before the variant field class.
+        *
+        * After this, `prot_opt_names` contains the final option names,
+        * potentially protected if needed. They can still be invalid
+        * TSDL identifiers however; this will be checked by
+        * cur_path_stack_push().
+        */
+       for (i = 0; i < opt_count; i++) {
+               if (!fc->tag_is_before) {
+                       BT_ASSERT(tgt_fc->ir_fc);
+                       ret = maybe_protect_variant_option_name(fc->base.ir_fc,
+                               tgt_fc->ir_fc, i, name_buf);
+                       if (ret) {
+                               fc->tag_is_before = true;
+                       }
+               }
+
+               ret = bt_value_array_append_string_element(prot_opt_names,
+                       name_buf->str);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+       for (i = 0; i < opt_count; i++) {
+               uint64_t j;
+               const bt_value *opt_name_a =
+                       bt_value_array_borrow_element_by_index_const(
+                               prot_opt_names, i);
+
+               for (j = 0; j < opt_count; j++) {
+                       const bt_value *opt_name_b;
+
+                       if (i == j) {
+                               continue;
+                       }
+
+                       opt_name_b =
+                               bt_value_array_borrow_element_by_index_const(
+                                       prot_opt_names, j);
+                       if (bt_value_is_equal(opt_name_a, opt_name_b)) {
+                               /*
+                                * Variant FC option names are not
+                                * unique when protected.
+                                */
+                               fc->tag_is_before = true;
+                               goto append_to_parent;
+                       }
+               }
+       }
+
+append_to_parent:
+       append_to_parent_field_class(ctx, &fc->base);
+
+       for (i = 0; i < opt_count; i++) {
+               const bt_field_class_variant_option *opt;
+               const bt_field_class *opt_ir_fc;
+               const bt_value *prot_opt_name_val =
+                       bt_value_array_borrow_element_by_index_const(
+                               prot_opt_names, i);
+               const char *prot_opt_name = bt_value_string_get(
+                       prot_opt_name_val);
+
+               BT_ASSERT(prot_opt_name);
+               opt = bt_field_class_variant_borrow_option_by_index_const(
+                       fc->base.ir_fc, i);
+               opt_ir_fc = bt_field_class_variant_option_borrow_field_class_const(
+                       opt);
+
+               /*
+                * We don't ask cur_path_stack_push() to protect the
+                * option name because it's already protected at this
+                * point.
+                */
+               ret = cur_path_stack_push(ctx, i, prot_opt_name, false,
+                       opt_ir_fc, &fc->base);
+               if (ret) {
+                       BT_COMP_LOGE("Cannot translate variant field class option: "
+                               "name=\"%s\"", prot_opt_name);
+                       goto end;
+               }
+
+               ret = translate_field_class(ctx);
+               if (ret) {
+                       BT_COMP_LOGE("Cannot translate variant field class option: "
+                               "name=\"%s\"", prot_opt_name);
+                       goto end;
+               }
+
+               cur_path_stack_pop(ctx);
+       }
+
+end:
+       if (name_buf) {
+               g_string_free(name_buf, TRUE);
+       }
+
+       bt_value_put_ref(prot_opt_names);
+       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, &fc->base.base);
+       ret = cur_path_stack_push(ctx, UINT64_C(-1), NULL, false, elem_ir_fc,
+               &fc->base.base);
+       if (ret) {
+               BT_COMP_LOGE_STR("Cannot translate static array field class element.");
+               goto end;
+       }
+
+       ret = translate_field_class(ctx);
+       if (ret) {
+               BT_COMP_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 */
+       if (bt_field_class_get_type(cur_path_stack_top(ctx)->ir_fc) ==
+                       BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY_WITH_LENGTH_FIELD) {
+               resolve_field_class(ctx,
+                       bt_field_class_array_dynamic_with_length_field_borrow_length_field_path_const(
+                               fc->base.base.ir_fc),
+                       fc->length_ref, &fc->length_is_before, NULL);
+       }
+
+       append_to_parent_field_class(ctx, &fc->base.base);
+       ret = cur_path_stack_push(ctx, UINT64_C(-1), NULL, false, elem_ir_fc,
+               &fc->base.base);
+       if (ret) {
+               BT_COMP_LOGE_STR("Cannot translate dynamic array field class element.");
+               goto end;
+       }
+
+       ret = translate_field_class(ctx);
+       if (ret) {
+               BT_COMP_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_bool_field_class(struct ctx *ctx)
+{
+       struct fs_sink_ctf_field_class_bool *fc =
+               fs_sink_ctf_field_class_bool_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, &fc->base.base);
+       return 0;
+}
+
+static inline
+int translate_bit_array_field_class(struct ctx *ctx)
+{
+       struct fs_sink_ctf_field_class_bit_array *fc =
+               fs_sink_ctf_field_class_bit_array_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, &fc->base);
+       return 0;
+}
+
+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, &fc->base.base);
+       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, &fc->base.base);
+       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, &fc->base);
+       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;
+       bt_field_class_type ir_fc_type =
+               bt_field_class_get_type(cur_path_stack_top(ctx)->ir_fc);
+
+       if (ir_fc_type == BT_FIELD_CLASS_TYPE_BOOL) {
+               ret = translate_bool_field_class(ctx);
+       } else if (ir_fc_type == BT_FIELD_CLASS_TYPE_BIT_ARRAY) {
+               ret = translate_bit_array_field_class(ctx);
+       } else if (bt_field_class_type_is(ir_fc_type,
+                       BT_FIELD_CLASS_TYPE_INTEGER)) {
+               ret = translate_integer_field_class(ctx);
+       } else if (bt_field_class_type_is(ir_fc_type,
+                       BT_FIELD_CLASS_TYPE_REAL)) {
+               ret = translate_real_field_class(ctx);
+       } else if (ir_fc_type == BT_FIELD_CLASS_TYPE_STRING) {
+               ret = translate_string_field_class(ctx);
+       } else if (ir_fc_type == BT_FIELD_CLASS_TYPE_STRUCTURE) {
+               ret = translate_structure_field_class(ctx);
+       } else if (ir_fc_type == BT_FIELD_CLASS_TYPE_STATIC_ARRAY) {
+               ret = translate_static_array_field_class(ctx);
+       } else if (bt_field_class_type_is(ir_fc_type,
+                       BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY)) {
+               ret = translate_dynamic_array_field_class(ctx);
+       } else if (bt_field_class_type_is(ir_fc_type,
+                       BT_FIELD_CLASS_TYPE_OPTION)) {
+               ret = translate_option_field_class(ctx);
+       } else if (bt_field_class_type_is(ir_fc_type,
+                       BT_FIELD_CLASS_TYPE_VARIANT)) {
+               ret = translate_variant_field_class(ctx);
+       } else {
+               bt_common_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 =
+               fs_sink_ctf_field_class_as_struct(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_OPTION:
+       {
+               /*
+                * CTF 1.8 does not support the option field class type.
+                * To write something anyway, this component translates
+                * this type to a variant field class where the options
+                * are:
+                *
+                * * An empty structure field class.
+                * * The optional field class itself.
+                *
+                * Because the option field class becomes a CTF variant
+                * field class, we use the term "tag" too here.
+                *
+                * The "tag" is always generated/before in that case (an
+                * 8-bit unsigned enumeration field class).
+                */
+               struct fs_sink_ctf_field_class_option *opt_fc =
+                       fs_sink_ctf_field_class_as_option(fc);
+
+               field_ref = opt_fc->tag_ref;
+               is_before = true;
+               tgt_type = "tag";
+               break;
+       }
+       case FS_SINK_CTF_FIELD_CLASS_TYPE_SEQUENCE:
+       {
+               struct fs_sink_ctf_field_class_sequence *seq_fc =
+                       fs_sink_ctf_field_class_as_sequence(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 =
+                       fs_sink_ctf_field_class_as_variant(fc);
+
+               field_ref = var_fc->tag_ref;
+               is_before = var_fc->tag_is_before;
+               tgt_type = "tag";
+               break;
+       }
+       default:
+               bt_common_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 * const fc,
+               const char *fc_name, struct fs_sink_ctf_field_class *parent_fc)
+{
+       int ret = 0;
+       enum fs_sink_ctf_field_class_type fc_type;
+       BT_ASSERT(fc);
+
+       fc_type = fc->type;
+
+       switch (fc_type) {
+       case FS_SINK_CTF_FIELD_CLASS_TYPE_OPTION:
+       {
+               struct fs_sink_ctf_field_class_option *opt_fc =
+                       fs_sink_ctf_field_class_as_option(fc);
+
+               ret = set_field_ref(fc, fc_name, parent_fc);
+               if (ret) {
+                       goto end;
+               }
+
+               ret = set_field_refs(opt_fc->content_fc, NULL, fc);
+               if (ret) {
+                       goto end;
+               }
+
+               break;
+       }
+       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 = NULL;
+               struct fs_sink_ctf_field_class_variant *var_fc = NULL;
+               struct fs_sink_ctf_named_field_class *named_fc;
+
+               if (fc_type == FS_SINK_CTF_FIELD_CLASS_TYPE_STRUCT) {
+                       struct_fc = fs_sink_ctf_field_class_as_struct(fc);
+                       len = struct_fc->members->len;
+               } else {
+                       var_fc = fs_sink_ctf_field_class_as_variant(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 =
+                       fs_sink_ctf_field_class_as_array_base(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_field_path_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 = &fs_sink_ctf_field_class_struct_create_empty(
+               ir_fc, UINT64_C(-1))->base;
+       BT_ASSERT(*fc);
+       ctx->cur_scope = scope;
+       BT_ASSERT(ctx->cur_path->len == 0);
+       ret = cur_path_stack_push(ctx, UINT64_C(-1), NULL, false, ir_fc, NULL);
+       if (ret) {
+               BT_COMP_LOGE("Cannot translate scope structure field class: "
+                       "scope=%d", scope);
+               goto end;
+       }
+
+       ret = translate_structure_field_class_members(ctx,
+               fs_sink_ctf_field_class_as_struct(*fc), ir_fc);
+       if (ret) {
+               BT_COMP_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, struct fs_sink_comp *fs_sink)
+{
+       memset(ctx, 0, sizeof(struct ctx));
+       ctx->cur_path = g_array_new(FALSE, TRUE,
+               sizeof(struct field_path_elem));
+       BT_ASSERT(ctx->cur_path);
+       ctx->log_level = fs_sink->log_level;
+       ctx->self_comp = fs_sink->self_comp;
+}
+
+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_comp *fs_sink,
+               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, fs_sink);
+       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_FIELD_PATH_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_FIELD_PATH_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_comp *fs_sink,
+               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 = (fs_sink_ctf_event_class *) g_hash_table_lookup(
+               sc->event_classes_from_ir, ir_ec);
+       if (G_LIKELY(*out_ec)) {
+               goto end;
+       }
+
+       ret = translate_event_class(fs_sink, sc, ir_ec, out_ec);
+
+end:
+       return ret;
+}
+
+static
+bool default_clock_class_name_exists(struct fs_sink_ctf_trace *trace,
+               const char *name)
+{
+       bool exists = false;
+       uint64_t i;
+
+       for (i = 0; i < trace->stream_classes->len; i++) {
+               struct fs_sink_ctf_stream_class *sc =
+                       (fs_sink_ctf_stream_class *) trace->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->trace, buf)) {
+               sprintf(buf, "default%u", suffix);
+               suffix++;
+       }
+
+       g_string_assign(sc->default_clock_class_name, buf);
+}
+
+static
+int translate_stream_class(struct fs_sink_comp *fs_sink,
+               struct fs_sink_ctf_trace *trace,
+               const bt_stream_class *ir_sc,
+               struct fs_sink_ctf_stream_class **out_sc)
+{
+       int ret = 0;
+       struct ctx ctx;
+
+       BT_ASSERT(trace);
+       BT_ASSERT(ir_sc);
+       ctx_init(&ctx, fs_sink);
+       *out_sc = fs_sink_ctf_stream_class_create(trace, 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 (name) {
+                       /* Try original name, protected */
+                       g_string_assign((*out_sc)->default_clock_class_name,
+                               "");
+
+                       if (must_protect_identifier(name)) {
+                               g_string_assign(
+                                       (*out_sc)->default_clock_class_name,
+                                       "_");
+                       }
+
+                       g_string_assign((*out_sc)->default_clock_class_name,
+                               name);
+                       if (!ist_valid_identifier(
+                                       (*out_sc)->default_clock_class_name->str)) {
+                               /* Invalid: create a new name */
+                               make_unique_default_clock_class_name(*out_sc);
+                       }
+               } 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_FIELD_PATH_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(
+                       fs_sink_ctf_field_class_as_struct((*out_sc)->packet_context_fc), 8);
+       }
+
+       ret = translate_scope_field_class(&ctx, BT_FIELD_PATH_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_comp *fs_sink,
+               struct fs_sink_ctf_trace *trace,
+               const bt_stream_class *ir_sc,
+               struct fs_sink_ctf_stream_class **out_sc)
+{
+       int ret = 0;
+       uint64_t i;
+
+       BT_ASSERT(trace);
+       BT_ASSERT(ir_sc);
+
+       for (i = 0; i < trace->stream_classes->len; i++) {
+               *out_sc = (fs_sink_ctf_stream_class *) trace->stream_classes->pdata[i];
+
+               if ((*out_sc)->ir_sc == ir_sc) {
+                       goto end;
+               }
+       }
+
+       ret = translate_stream_class(fs_sink, trace, ir_sc, out_sc);
+
+end:
+       return ret;
+}
+
+BT_HIDDEN
+struct fs_sink_ctf_trace *translate_trace_trace_ir_to_ctf_ir(
+               struct fs_sink_comp *fs_sink, const bt_trace *ir_trace)
+{
+       uint64_t count;
+       uint64_t i;
+       struct fs_sink_ctf_trace *trace = NULL;
+
+       /* Check that trace's environment is TSDL-compatible */
+       count = bt_trace_get_environment_entry_count(ir_trace);
+       for (i = 0; i < count; i++) {
+               const char *name;
+               const bt_value *val;
+
+               bt_trace_borrow_environment_entry_by_index_const(
+                       ir_trace, i, &name, &val);
+
+               if (!ist_valid_identifier(name)) {
+                       BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, fs_sink->log_level,
+                               fs_sink->self_comp,
+                               "Unsupported trace class's environment entry name: "
+                               "name=\"%s\"", name);
+                       goto end;
+               }
+
+               switch (bt_value_get_type(val)) {
+               case BT_VALUE_TYPE_SIGNED_INTEGER:
+               case BT_VALUE_TYPE_STRING:
+                       break;
+               default:
+                       BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, fs_sink->log_level,
+                               fs_sink->self_comp,
+                               "Unsupported trace class's environment entry value type: "
+                               "type=%s",
+                               bt_common_value_type_string(
+                                       bt_value_get_type(val)));
+                       goto end;
+               }
+       }
+
+       trace = fs_sink_ctf_trace_create(ir_trace);
+       BT_ASSERT(trace);
+
+end:
+       return trace;
+}
diff --git a/src/plugins/ctf/fs-sink/translate-trace-ir-to-ctf-ir.h b/src/plugins/ctf/fs-sink/translate-trace-ir-to-ctf-ir.h
deleted file mode 100644 (file)
index 3f03873..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
- */
-
-#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
-
-#include "common/macros.h"
-#include <babeltrace2/babeltrace.h>
-
-#include "fs-sink.h"
-#include "fs-sink-ctf-meta.h"
-
-BT_HIDDEN
-int try_translate_event_class_trace_ir_to_ctf_ir(
-               struct fs_sink_comp *fs_sink,
-               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_comp *fs_sink,
-               struct fs_sink_ctf_trace *trace,
-               const bt_stream_class *ir_sc,
-               struct fs_sink_ctf_stream_class **out_sc);
-
-BT_HIDDEN
-struct fs_sink_ctf_trace *translate_trace_trace_ir_to_ctf_ir(
-               struct fs_sink_comp *fs_sink, const bt_trace *ir_trace);
-
-#endif /* BABELTRACE_PLUGIN_CTF_FS_SINK_TRANSLATE_TRACE_IR_TO_CTF_IR_H */
diff --git a/src/plugins/ctf/fs-sink/translate-trace-ir-to-ctf-ir.hpp b/src/plugins/ctf/fs-sink/translate-trace-ir-to-ctf-ir.hpp
new file mode 100644 (file)
index 0000000..76213de
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2019 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#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
+
+#include "common/macros.h"
+#include <babeltrace2/babeltrace.h>
+
+#include "fs-sink.hpp"
+#include "fs-sink-ctf-meta.hpp"
+
+BT_HIDDEN
+int try_translate_event_class_trace_ir_to_ctf_ir(
+               struct fs_sink_comp *fs_sink,
+               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_comp *fs_sink,
+               struct fs_sink_ctf_trace *trace,
+               const bt_stream_class *ir_sc,
+               struct fs_sink_ctf_stream_class **out_sc);
+
+BT_HIDDEN
+struct fs_sink_ctf_trace *translate_trace_trace_ir_to_ctf_ir(
+               struct fs_sink_comp *fs_sink, const bt_trace *ir_trace);
+
+#endif /* BABELTRACE_PLUGIN_CTF_FS_SINK_TRANSLATE_TRACE_IR_TO_CTF_IR_H */
index b59c7620e38bda6a47cda76c3ed5edb4ebd1cb85..cb990b6995b66f2788ec246ec17467854b855ab0 100644 (file)
@@ -3,14 +3,14 @@
 noinst_LTLIBRARIES = libbabeltrace2-plugin-ctf-fs-src.la
 
 libbabeltrace2_plugin_ctf_fs_src_la_SOURCES = \
-       data-stream-file.c \
-       data-stream-file.h \
-       file.c \
-       file.h \
-       fs.c \
-       fs.h \
-       lttng-index.h \
-       metadata.c \
-       metadata.h \
-       query.h \
-       query.c
+       data-stream-file.cpp \
+       data-stream-file.hpp \
+       file.cpp \
+       file.hpp \
+       fs.cpp \
+       fs.hpp \
+       lttng-index.hpp \
+       metadata.cpp \
+       metadata.hpp \
+       query.hpp \
+       query.cpp
diff --git a/src/plugins/ctf/fs-src/data-stream-file.c b/src/plugins/ctf/fs-src/data-stream-file.c
deleted file mode 100644 (file)
index 7d4ccf5..0000000
+++ /dev/null
@@ -1,1035 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2016-2017 Philippe Proulx <pproulx@efficios.com>
- * Copyright 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- * Copyright 2010-2011 EfficiOS Inc. and Linux Foundation
- */
-
-#define BT_COMP_LOG_SELF_COMP (self_comp)
-#define BT_LOG_OUTPUT_LEVEL (log_level)
-#define BT_LOG_TAG "PLUGIN/SRC.CTF.FS/DS"
-#include "logging/comp-logging.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <glib.h>
-#include <inttypes.h>
-#include "compat/mman.h"
-#include "compat/endian.h"
-#include <babeltrace2/babeltrace.h>
-#include "common/common.h"
-#include "file.h"
-#include "metadata.h"
-#include "../common/msg-iter/msg-iter.h"
-#include "common/assert.h"
-#include "data-stream-file.h"
-#include <string.h>
-
-static inline
-size_t remaining_mmap_bytes(struct ctf_fs_ds_file *ds_file)
-{
-       BT_ASSERT_DBG(ds_file->mmap_len >= ds_file->request_offset_in_mapping);
-       return ds_file->mmap_len - ds_file->request_offset_in_mapping;
-}
-
-/*
- * Return true if `offset_in_file` is in the current mapping.
- */
-
-static
-bool offset_ist_mapped(struct ctf_fs_ds_file *ds_file, off_t offset_in_file)
-{
-       return offset_in_file >= ds_file->mmap_offset_in_file &&
-               offset_in_file < (ds_file->mmap_offset_in_file + ds_file->mmap_len);
-}
-
-static
-enum ctf_msg_iter_medium_status ds_file_munmap(
-               struct ctf_fs_ds_file *ds_file)
-{
-       enum ctf_msg_iter_medium_status status;
-       bt_self_component *self_comp = ds_file->self_comp;
-       bt_logging_level log_level = ds_file->log_level;
-
-       BT_ASSERT(ds_file);
-
-       if (!ds_file->mmap_addr) {
-               status = CTF_MSG_ITER_MEDIUM_STATUS_OK;
-               goto end;
-       }
-
-       if (bt_munmap(ds_file->mmap_addr, ds_file->mmap_len)) {
-               BT_COMP_LOGE_ERRNO("Cannot memory-unmap file",
-                       ": address=%p, size=%zu, file_path=\"%s\", file=%p",
-                       ds_file->mmap_addr, ds_file->mmap_len,
-                       ds_file->file ? ds_file->file->path->str : "NULL",
-                       ds_file->file ? ds_file->file->fp : NULL);
-               status = CTF_MSG_ITER_MEDIUM_STATUS_ERROR;
-               goto end;
-       }
-
-       ds_file->mmap_addr = NULL;
-
-       status = CTF_MSG_ITER_MEDIUM_STATUS_OK;
-end:
-       return status;
-}
-
-/*
- * mmap a region of `ds_file` such that `requested_offset_in_file` is in the
- * mapping.  If the currently mmap-ed region already contains
- * `requested_offset_in_file`, the mapping is kept.
- *
- * Set `ds_file->requested_offset_in_mapping` based on `request_offset_in_file`,
- * such that the next call to `request_bytes` will return bytes starting at that
- * position.
- *
- * `requested_offset_in_file` must be a valid offset in the file.
- */
-static
-enum ctf_msg_iter_medium_status ds_file_mmap(
-               struct ctf_fs_ds_file *ds_file, off_t requested_offset_in_file)
-{
-       enum ctf_msg_iter_medium_status status;
-       bt_self_component *self_comp = ds_file->self_comp;
-       bt_logging_level log_level = ds_file->log_level;
-
-       /* Ensure the requested offset is in the file range. */
-       BT_ASSERT(requested_offset_in_file >= 0);
-       BT_ASSERT(requested_offset_in_file < ds_file->file->size);
-
-       /*
-        * If the mapping already contains the requested offset, just adjust
-        * requested_offset_in_mapping.
-        */
-       if (offset_ist_mapped(ds_file, requested_offset_in_file)) {
-               ds_file->request_offset_in_mapping =
-                       requested_offset_in_file - ds_file->mmap_offset_in_file;
-               status = CTF_MSG_ITER_MEDIUM_STATUS_OK;
-               goto end;
-       }
-
-       /* Unmap old region */
-       status = ds_file_munmap(ds_file);
-       if (status != CTF_MSG_ITER_MEDIUM_STATUS_OK) {
-               goto end;
-       }
-
-       /*
-        * Compute a mapping that has the required alignment properties and
-        * contains `requested_offset_in_file`.
-        */
-       ds_file->request_offset_in_mapping =
-               requested_offset_in_file % bt_mmap_get_offset_align_size(ds_file->log_level);
-       ds_file->mmap_offset_in_file =
-               requested_offset_in_file - ds_file->request_offset_in_mapping;
-       ds_file->mmap_len = MIN(ds_file->file->size - ds_file->mmap_offset_in_file,
-               ds_file->mmap_max_len);
-
-       BT_ASSERT(ds_file->mmap_len > 0);
-
-       ds_file->mmap_addr = bt_mmap((void *) 0, ds_file->mmap_len,
-                       PROT_READ, MAP_PRIVATE, fileno(ds_file->file->fp),
-                       ds_file->mmap_offset_in_file, ds_file->log_level);
-       if (ds_file->mmap_addr == MAP_FAILED) {
-               BT_COMP_LOGE("Cannot memory-map address (size %zu) of file \"%s\" (%p) at offset %jd: %s",
-                               ds_file->mmap_len, ds_file->file->path->str,
-                               ds_file->file->fp, (intmax_t) ds_file->mmap_offset_in_file,
-                               strerror(errno));
-               status = CTF_MSG_ITER_MEDIUM_STATUS_ERROR;
-               goto end;
-       }
-
-       status = CTF_MSG_ITER_MEDIUM_STATUS_OK;
-
-end:
-       return status;
-}
-
-/*
- * Change the mapping of the file to read the region that follows the current
- * mapping.
- *
- * If the file hasn't been mapped yet, then everything (mmap_offset_in_file,
- * mmap_len, request_offset_in_mapping) should have the value 0, which will
- * result in the beginning of the file getting mapped.
- *
- * return _EOF if the current mapping is the end of the file.
- */
-
-static
-enum ctf_msg_iter_medium_status ds_file_mmap_next(
-               struct ctf_fs_ds_file *ds_file)
-{
-       enum ctf_msg_iter_medium_status status;
-
-       /*
-        * If we're called, it's because more bytes are requested but we have
-        * given all the bytes of the current mapping.
-        */
-       BT_ASSERT(ds_file->request_offset_in_mapping == ds_file->mmap_len);
-
-       /*
-        * If the current mapping coincides with the end of the file, there is
-        * no next mapping.
-        */
-       if (ds_file->mmap_offset_in_file + ds_file->mmap_len == ds_file->file->size) {
-               status = CTF_MSG_ITER_MEDIUM_STATUS_EOF;
-               goto end;
-       }
-
-       status = ds_file_mmap(ds_file,
-               ds_file->mmap_offset_in_file + ds_file->mmap_len);
-
-end:
-       return status;
-}
-
-static
-enum ctf_msg_iter_medium_status medop_request_bytes(
-               size_t request_sz, uint8_t **buffer_addr,
-               size_t *buffer_sz, void *data)
-{
-       enum ctf_msg_iter_medium_status status =
-               CTF_MSG_ITER_MEDIUM_STATUS_OK;
-       struct ctf_fs_ds_file *ds_file = data;
-       bt_self_component *self_comp = ds_file->self_comp;
-       bt_logging_level log_level = ds_file->log_level;
-
-       BT_ASSERT(request_sz > 0);
-
-       /*
-        * Check if we have at least one memory-mapped byte left. If we don't,
-        * mmap the next file.
-        */
-       if (remaining_mmap_bytes(ds_file) == 0) {
-               /* Are we at the end of the file? */
-               if (ds_file->mmap_offset_in_file >= ds_file->file->size) {
-                       BT_COMP_LOGD("Reached end of file \"%s\" (%p)",
-                               ds_file->file->path->str, ds_file->file->fp);
-                       status = CTF_MSG_ITER_MEDIUM_STATUS_EOF;
-                       goto end;
-               }
-
-               status = ds_file_mmap_next(ds_file);
-               switch (status) {
-               case CTF_MSG_ITER_MEDIUM_STATUS_OK:
-                       break;
-               case CTF_MSG_ITER_MEDIUM_STATUS_EOF:
-                       goto end;
-               default:
-                       BT_COMP_LOGE("Cannot memory-map next region of file \"%s\" (%p)",
-                                       ds_file->file->path->str,
-                                       ds_file->file->fp);
-                       goto error;
-               }
-       }
-
-       BT_ASSERT(remaining_mmap_bytes(ds_file) > 0);
-       *buffer_sz = MIN(remaining_mmap_bytes(ds_file), request_sz);
-
-       BT_ASSERT(ds_file->mmap_addr);
-       *buffer_addr = ((uint8_t *) ds_file->mmap_addr) + ds_file->request_offset_in_mapping;
-
-       ds_file->request_offset_in_mapping += *buffer_sz;
-       goto end;
-
-error:
-       status = CTF_MSG_ITER_MEDIUM_STATUS_ERROR;
-
-end:
-       return status;
-}
-
-static
-bt_stream *medop_borrow_stream(bt_stream_class *stream_class, int64_t stream_id,
-               void *data)
-{
-       struct ctf_fs_ds_file *ds_file = data;
-       bt_stream_class *ds_file_stream_class;
-       bt_stream *stream = NULL;
-
-       ds_file_stream_class = bt_stream_borrow_class(
-               ds_file->stream);
-
-       if (stream_class != ds_file_stream_class) {
-               /*
-                * Not supported: two packets described by two different
-                * stream classes within the same data stream file.
-                */
-               goto end;
-       }
-
-       stream = ds_file->stream;
-
-end:
-       return stream;
-}
-
-static
-enum ctf_msg_iter_medium_status medop_seek(off_t offset, void *data)
-{
-       struct ctf_fs_ds_file *ds_file = data;
-
-       BT_ASSERT(offset >= 0);
-       BT_ASSERT(offset < ds_file->file->size);
-
-       return ds_file_mmap(ds_file, offset);
-}
-
-BT_HIDDEN
-struct ctf_msg_iter_medium_ops ctf_fs_ds_file_medops = {
-       .request_bytes = medop_request_bytes,
-       .borrow_stream = medop_borrow_stream,
-       .seek = medop_seek,
-};
-
-struct ctf_fs_ds_group_medops_data {
-       /* Weak, set once at creation time. */
-       struct ctf_fs_ds_file_group *ds_file_group;
-
-       /*
-        * Index (as in element rank) of the index entry of ds_file_groups'
-        * index we will read next (so, the one after the one we are reading
-        * right now).
-        */
-       guint next_index_entry_index;
-
-       /*
-        * File we are currently reading.  Changes whenever we switch to
-        * reading another data file.
-        *
-        * Owned by this.
-        */
-       struct ctf_fs_ds_file *file;
-
-       /* Weak, for context / logging / appending causes. */
-       bt_self_message_iterator *self_msg_iter;
-       bt_logging_level log_level;
-};
-
-static
-enum ctf_msg_iter_medium_status medop_group_request_bytes(
-               size_t request_sz,
-               uint8_t **buffer_addr,
-               size_t *buffer_sz,
-               void *void_data)
-{
-       struct ctf_fs_ds_group_medops_data *data = void_data;
-
-       /* Return bytes from the current file. */
-       return medop_request_bytes(request_sz, buffer_addr, buffer_sz, data->file);
-}
-
-static
-bt_stream *medop_group_borrow_stream(
-               bt_stream_class *stream_class,
-               int64_t stream_id,
-               void *void_data)
-{
-       struct ctf_fs_ds_group_medops_data *data = void_data;
-
-       return medop_borrow_stream(stream_class, stream_id, data->file);
-}
-
-/*
- * Set `data->file` to prepare it to read the packet described
- * by `index_entry`.
- */
-
-static
-enum ctf_msg_iter_medium_status ctf_fs_ds_group_medops_set_file(
-               struct ctf_fs_ds_group_medops_data *data,
-               struct ctf_fs_ds_index_entry *index_entry,
-               bt_self_message_iterator *self_msg_iter,
-               bt_logging_level log_level)
-{
-       enum ctf_msg_iter_medium_status status;
-
-       BT_ASSERT(data);
-       BT_ASSERT(index_entry);
-
-       /* Check if that file is already the one mapped. */
-       if (!data->file || strcmp(index_entry->path, data->file->file->path->str) != 0) {
-               /* Destroy the previously used file. */
-               ctf_fs_ds_file_destroy(data->file);
-
-               /* Create the new file. */
-               data->file = ctf_fs_ds_file_create(
-                       data->ds_file_group->ctf_fs_trace,
-                       self_msg_iter,
-                       data->ds_file_group->stream,
-                       index_entry->path,
-                       log_level);
-               if (!data->file) {
-                       BT_MSG_ITER_LOGE_APPEND_CAUSE(self_msg_iter,
-                               "failed to create ctf_fs_ds_file.");
-                       status = CTF_MSG_ITER_MEDIUM_STATUS_ERROR;
-                       goto end;
-               }
-       }
-
-       /*
-        * Ensure the right portion of the file will be returned on the next
-        * request_bytes call.
-        */
-       status = ds_file_mmap(data->file, index_entry->offset);
-       if (status != CTF_MSG_ITER_MEDIUM_STATUS_OK) {
-               goto end;
-       }
-
-       status = CTF_MSG_ITER_MEDIUM_STATUS_OK;
-
-end:
-       return status;
-}
-
-static
-enum ctf_msg_iter_medium_status medop_group_switch_packet(void *void_data)
-{
-       struct ctf_fs_ds_group_medops_data *data = void_data;
-       struct ctf_fs_ds_index_entry *index_entry;
-       enum ctf_msg_iter_medium_status status;
-
-       /* If we have gone through all index entries, we are done. */
-       if (data->next_index_entry_index >=
-               data->ds_file_group->index->entries->len) {
-               status = CTF_MSG_ITER_MEDIUM_STATUS_EOF;
-               goto end;
-       }
-
-       /*
-        * Otherwise, look up the next index entry / packet and prepare it
-        *  for reading.
-        */
-       index_entry = g_ptr_array_index(
-               data->ds_file_group->index->entries,
-               data->next_index_entry_index);
-
-       status = ctf_fs_ds_group_medops_set_file(
-               data, index_entry, data->self_msg_iter, data->log_level);
-       if (status != CTF_MSG_ITER_MEDIUM_STATUS_OK) {
-               goto end;
-       }
-
-       data->next_index_entry_index++;
-
-       status = CTF_MSG_ITER_MEDIUM_STATUS_OK;
-end:
-       return status;
-}
-
-BT_HIDDEN
-void ctf_fs_ds_group_medops_data_destroy(
-               struct ctf_fs_ds_group_medops_data *data)
-{
-       if (!data) {
-               goto end;
-       }
-
-       ctf_fs_ds_file_destroy(data->file);
-
-       g_free(data);
-
-end:
-       return;
-}
-
-enum ctf_msg_iter_medium_status ctf_fs_ds_group_medops_data_create(
-               struct ctf_fs_ds_file_group *ds_file_group,
-               bt_self_message_iterator *self_msg_iter,
-               bt_logging_level log_level,
-               struct ctf_fs_ds_group_medops_data **out)
-{
-       struct ctf_fs_ds_group_medops_data *data;
-       enum ctf_msg_iter_medium_status status;
-
-       BT_ASSERT(self_msg_iter);
-       BT_ASSERT(ds_file_group);
-       BT_ASSERT(ds_file_group->index);
-       BT_ASSERT(ds_file_group->index->entries->len > 0);
-
-       data = g_new0(struct ctf_fs_ds_group_medops_data, 1);
-       if (!data) {
-               BT_MSG_ITER_LOGE_APPEND_CAUSE(self_msg_iter,
-                       "Failed to allocate a struct ctf_fs_ds_group_medops_data");
-               status = CTF_MSG_ITER_MEDIUM_STATUS_MEMORY_ERROR;
-               goto error;
-       }
-
-       data->ds_file_group = ds_file_group;
-       data->self_msg_iter = self_msg_iter;
-       data->log_level = log_level;
-
-       /*
-        * No need to prepare the first file.  ctf_msg_iter will call
-        * switch_packet before reading the first packet, it will be
-        * done then.
-        */
-
-       *out = data;
-       status = CTF_MSG_ITER_MEDIUM_STATUS_OK;
-       goto end;
-
-error:
-       ctf_fs_ds_group_medops_data_destroy(data);
-
-end:
-       return status;
-}
-
-void ctf_fs_ds_group_medops_data_reset(struct ctf_fs_ds_group_medops_data *data)
-{
-       data->next_index_entry_index = 0;
-}
-
-struct ctf_msg_iter_medium_ops ctf_fs_ds_group_medops = {
-       .request_bytes = medop_group_request_bytes,
-       .borrow_stream = medop_group_borrow_stream,
-       .switch_packet = medop_group_switch_packet,
-
-       /*
-        * We don't support seeking using this medops.  It would probably be
-        * possible, but it's not needed at the moment.
-        */
-       .seek = NULL,
-};
-
-static
-struct ctf_fs_ds_index_entry *ctf_fs_ds_index_entry_create(
-               bt_self_component *self_comp, bt_logging_level log_level)
-{
-       struct ctf_fs_ds_index_entry *entry;
-
-       entry = g_new0(struct ctf_fs_ds_index_entry, 1);
-       if (!entry) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Failed to allocate a ctf_fs_ds_index_entry.");
-               goto end;
-       }
-
-       entry->packet_seq_num = UINT64_MAX;
-
-end:
-       return entry;
-}
-
-static
-int convert_cycles_to_ns(struct ctf_clock_class *clock_class,
-               uint64_t cycles, int64_t *ns)
-{
-       return bt_util_clock_cycles_to_ns_from_origin(cycles,
-                       clock_class->frequency, clock_class->offset_seconds,
-                       clock_class->offset_cycles, ns);
-}
-
-static
-struct ctf_fs_ds_index *build_index_from_idx_file(
-               struct ctf_fs_ds_file *ds_file,
-               struct ctf_fs_ds_file_info *file_info,
-               struct ctf_msg_iter *msg_iter)
-{
-       int ret;
-       gchar *directory = NULL;
-       gchar *basename = NULL;
-       GString *index_basename = NULL;
-       gchar *index_file_path = NULL;
-       GMappedFile *mapped_file = NULL;
-       gsize filesize;
-       const char *mmap_begin = NULL, *file_pos = NULL;
-       const struct ctf_packet_index_file_hdr *header = NULL;
-       struct ctf_fs_ds_index *index = NULL;
-       struct ctf_fs_ds_index_entry *index_entry = NULL, *prev_index_entry = NULL;
-       uint64_t total_packets_size = 0;
-       size_t file_index_entry_size;
-       size_t file_entry_count;
-       size_t i;
-       struct ctf_stream_class *sc;
-       struct ctf_msg_iter_packet_properties props;
-       uint32_t version_major, version_minor;
-       bt_self_component *self_comp = ds_file->self_comp;
-       bt_logging_level log_level = ds_file->log_level;
-
-       BT_COMP_LOGI("Building index from .idx file of stream file %s",
-                       ds_file->file->path->str);
-       ret = ctf_msg_iter_get_packet_properties(msg_iter, &props);
-       if (ret) {
-               BT_COMP_LOGI_STR("Cannot read first packet's header and context fields.");
-               goto error;
-       }
-
-       sc = ctf_trace_class_borrow_stream_class_by_id(ds_file->metadata->tc,
-               props.stream_class_id);
-       BT_ASSERT(sc);
-       if (!sc->default_clock_class) {
-               BT_COMP_LOGI_STR("Cannot find stream class's default clock class.");
-               goto error;
-       }
-
-       /* Look for index file in relative path index/name.idx. */
-       basename = g_path_get_basename(ds_file->file->path->str);
-       if (!basename) {
-               BT_COMP_LOGE("Cannot get the basename of datastream file %s",
-                               ds_file->file->path->str);
-               goto error;
-       }
-
-       directory = g_path_get_dirname(ds_file->file->path->str);
-       if (!directory) {
-               BT_COMP_LOGE("Cannot get dirname of datastream file %s",
-                               ds_file->file->path->str);
-               goto error;
-       }
-
-       index_basename = g_string_new(basename);
-       if (!index_basename) {
-               BT_COMP_LOGE_STR("Cannot allocate index file basename string");
-               goto error;
-       }
-
-       g_string_append(index_basename, ".idx");
-       index_file_path = g_build_filename(directory, "index",
-                       index_basename->str, NULL);
-       mapped_file = g_mapped_file_new(index_file_path, FALSE, NULL);
-       if (!mapped_file) {
-               BT_COMP_LOGD("Cannot create new mapped file %s",
-                               index_file_path);
-               goto error;
-       }
-
-       /*
-        * The g_mapped_file API limits us to 4GB files on 32-bit.
-        * Traces with such large indexes have never been seen in the wild,
-        * but this would need to be adjusted to support them.
-        */
-       filesize = g_mapped_file_get_length(mapped_file);
-       if (filesize < sizeof(*header)) {
-               BT_COMP_LOGW("Invalid LTTng trace index file: "
-                       "file size (%zu bytes) < header size (%zu bytes)",
-                       filesize, sizeof(*header));
-               goto error;
-       }
-
-       mmap_begin = g_mapped_file_get_contents(mapped_file);
-       header = (struct ctf_packet_index_file_hdr *) mmap_begin;
-
-       file_pos = g_mapped_file_get_contents(mapped_file) + sizeof(*header);
-       if (be32toh(header->magic) != CTF_INDEX_MAGIC) {
-               BT_COMP_LOGW_STR("Invalid LTTng trace index: \"magic\" field validation failed");
-               goto error;
-       }
-
-       version_major = be32toh(header->index_major);
-       version_minor = be32toh(header->index_minor);
-       if (version_major != 1) {
-               BT_COMP_LOGW(
-                       "Unknown LTTng trace index version: "
-                       "major=%" PRIu32 ", minor=%" PRIu32,
-                       version_major, version_minor);
-               goto error;
-       }
-
-       file_index_entry_size = be32toh(header->packet_index_len);
-       if (file_index_entry_size < CTF_INDEX_1_0_SIZE) {
-               BT_COMP_LOGW("Invalid `packet_index_len` in LTTng trace index file (`packet_index_len` < CTF index 1.0 index entry size): "
-                       "packet_index_len=%zu, CTF_INDEX_1_0_SIZE=%zu",
-                       file_index_entry_size, CTF_INDEX_1_0_SIZE);
-               goto error;
-       }
-
-       file_entry_count = (filesize - sizeof(*header)) / file_index_entry_size;
-       if ((filesize - sizeof(*header)) % file_index_entry_size) {
-               BT_COMP_LOGW("Invalid LTTng trace index: the index's size after the header "
-                       "(%zu bytes) is not a multiple of the index entry size "
-                       "(%zu bytes)", (filesize - sizeof(*header)),
-                       sizeof(*header));
-               goto error;
-       }
-
-       index = ctf_fs_ds_index_create(ds_file->log_level, ds_file->self_comp);
-       if (!index) {
-               goto error;
-       }
-
-       for (i = 0; i < file_entry_count; i++) {
-               struct ctf_packet_index *file_index =
-                               (struct ctf_packet_index *) file_pos;
-               uint64_t packet_size = be64toh(file_index->packet_size);
-
-               if (packet_size % CHAR_BIT) {
-                       BT_COMP_LOGW("Invalid packet size encountered in LTTng trace index file");
-                       goto error;
-               }
-
-               index_entry = ctf_fs_ds_index_entry_create(
-                       ds_file->self_comp, ds_file->log_level);
-               if (!index_entry) {
-                       BT_COMP_LOGE_APPEND_CAUSE(ds_file->self_comp,
-                               "Failed to create a ctf_fs_ds_index_entry.");
-                       goto error;
-               }
-
-               /* Set path to stream file. */
-               index_entry->path = file_info->path->str;
-
-               /* Convert size in bits to bytes. */
-               packet_size /= CHAR_BIT;
-               index_entry->packet_size = packet_size;
-
-               index_entry->offset = be64toh(file_index->offset);
-               if (i != 0 && index_entry->offset < prev_index_entry->offset) {
-                       BT_COMP_LOGW("Invalid, non-monotonic, packet offset encountered in LTTng trace index file: "
-                               "previous offset=%" PRIu64 ", current offset=%" PRIu64,
-                               prev_index_entry->offset, index_entry->offset);
-                       goto error;
-               }
-
-               index_entry->timestamp_begin = be64toh(file_index->timestamp_begin);
-               index_entry->timestamp_end = be64toh(file_index->timestamp_end);
-               if (index_entry->timestamp_end < index_entry->timestamp_begin) {
-                       BT_COMP_LOGW("Invalid packet time bounds encountered in LTTng trace index file (begin > end): "
-                               "timestamp_begin=%" PRIu64 "timestamp_end=%" PRIu64,
-                               index_entry->timestamp_begin,
-                               index_entry->timestamp_end);
-                       goto error;
-               }
-
-               /* Convert the packet's bound to nanoseconds since Epoch. */
-               ret = convert_cycles_to_ns(sc->default_clock_class,
-                               index_entry->timestamp_begin,
-                               &index_entry->timestamp_begin_ns);
-               if (ret) {
-                       BT_COMP_LOGI_STR("Failed to convert raw timestamp to nanoseconds since Epoch during index parsing");
-                       goto error;
-               }
-               ret = convert_cycles_to_ns(sc->default_clock_class,
-                               index_entry->timestamp_end,
-                               &index_entry->timestamp_end_ns);
-               if (ret) {
-                       BT_COMP_LOGI_STR("Failed to convert raw timestamp to nanoseconds since Epoch during LTTng trace index parsing");
-                       goto error;
-               }
-
-               if (version_minor >= 1) {
-                       index_entry->packet_seq_num = be64toh(file_index->packet_seq_num);
-               }
-
-               total_packets_size += packet_size;
-               file_pos += file_index_entry_size;
-
-               prev_index_entry = index_entry;
-
-               /* Give ownership of `index_entry` to `index->entries`. */
-               g_ptr_array_add(index->entries, index_entry);
-               index_entry = NULL;
-       }
-
-       /* Validate that the index addresses the complete stream. */
-       if (ds_file->file->size != total_packets_size) {
-               BT_COMP_LOGW("Invalid LTTng trace index file; indexed size != stream file size: "
-                       "file-size=%" PRIu64 ", total-packets-size=%" PRIu64,
-                       ds_file->file->size, total_packets_size);
-               goto error;
-       }
-end:
-       g_free(directory);
-       g_free(basename);
-       g_free(index_file_path);
-       if (index_basename) {
-               g_string_free(index_basename, TRUE);
-       }
-       if (mapped_file) {
-               g_mapped_file_unref(mapped_file);
-       }
-       return index;
-error:
-       ctf_fs_ds_index_destroy(index);
-       g_free(index_entry);
-       index = NULL;
-       goto end;
-}
-
-static
-int init_index_entry(struct ctf_fs_ds_index_entry *entry,
-               struct ctf_fs_ds_file *ds_file,
-               struct ctf_msg_iter_packet_properties *props,
-               off_t packet_size, off_t packet_offset)
-{
-       int ret = 0;
-       struct ctf_stream_class *sc;
-       bt_self_component *self_comp = ds_file->self_comp;
-       bt_logging_level log_level = ds_file->log_level;
-
-       sc = ctf_trace_class_borrow_stream_class_by_id(ds_file->metadata->tc,
-               props->stream_class_id);
-       BT_ASSERT(sc);
-       BT_ASSERT(packet_offset >= 0);
-       entry->offset = packet_offset;
-       BT_ASSERT(packet_size >= 0);
-       entry->packet_size = packet_size;
-
-       if (props->snapshots.beginning_clock != UINT64_C(-1)) {
-               entry->timestamp_begin = props->snapshots.beginning_clock;
-
-               /* Convert the packet's bound to nanoseconds since Epoch. */
-               ret = convert_cycles_to_ns(sc->default_clock_class,
-                                          props->snapshots.beginning_clock,
-                                          &entry->timestamp_begin_ns);
-               if (ret) {
-                       BT_COMP_LOGI_STR("Failed to convert raw timestamp to nanoseconds since Epoch.");
-                       goto end;
-               }
-       } else {
-               entry->timestamp_begin = UINT64_C(-1);
-               entry->timestamp_begin_ns = UINT64_C(-1);
-       }
-
-       if (props->snapshots.end_clock != UINT64_C(-1)) {
-               entry->timestamp_end = props->snapshots.end_clock;
-
-               /* Convert the packet's bound to nanoseconds since Epoch. */
-               ret = convert_cycles_to_ns(sc->default_clock_class,
-                                          props->snapshots.end_clock,
-                                          &entry->timestamp_end_ns);
-               if (ret) {
-                       BT_COMP_LOGI_STR("Failed to convert raw timestamp to nanoseconds since Epoch.");
-                       goto end;
-               }
-       } else {
-               entry->timestamp_end = UINT64_C(-1);
-               entry->timestamp_end_ns = UINT64_C(-1);
-       }
-
-end:
-       return ret;
-}
-
-static
-struct ctf_fs_ds_index *build_index_from_stream_file(
-               struct ctf_fs_ds_file *ds_file,
-               struct ctf_fs_ds_file_info *file_info,
-               struct ctf_msg_iter *msg_iter)
-{
-       int ret;
-       struct ctf_fs_ds_index *index = NULL;
-       enum ctf_msg_iter_status iter_status = CTF_MSG_ITER_STATUS_OK;
-       off_t current_packet_offset_bytes = 0;
-       bt_self_component *self_comp = ds_file->self_comp;
-       bt_logging_level log_level = ds_file->log_level;
-
-       BT_COMP_LOGI("Indexing stream file %s", ds_file->file->path->str);
-
-       index = ctf_fs_ds_index_create(ds_file->log_level, ds_file->self_comp);
-       if (!index) {
-               goto error;
-       }
-
-       while (true) {
-               off_t current_packet_size_bytes;
-               struct ctf_fs_ds_index_entry *index_entry;
-               struct ctf_msg_iter_packet_properties props;
-
-               if (current_packet_offset_bytes < 0) {
-                       BT_COMP_LOGE_STR("Cannot get the current packet's offset.");
-                       goto error;
-               } else if (current_packet_offset_bytes > ds_file->file->size) {
-                       BT_COMP_LOGE_STR("Unexpected current packet's offset (larger than file).");
-                       goto error;
-               } else if (current_packet_offset_bytes == ds_file->file->size) {
-                       /* No more data */
-                       break;
-               }
-
-               iter_status = ctf_msg_iter_seek(msg_iter,
-                               current_packet_offset_bytes);
-               if (iter_status != CTF_MSG_ITER_STATUS_OK) {
-                       goto error;
-               }
-
-               iter_status = ctf_msg_iter_get_packet_properties(
-                       msg_iter, &props);
-               if (iter_status != CTF_MSG_ITER_STATUS_OK) {
-                       goto error;
-               }
-
-               if (props.exp_packet_total_size >= 0) {
-                       current_packet_size_bytes =
-                               (uint64_t) props.exp_packet_total_size / 8;
-               } else {
-                       current_packet_size_bytes = ds_file->file->size;
-               }
-
-               if (current_packet_offset_bytes + current_packet_size_bytes >
-                               ds_file->file->size) {
-                       BT_COMP_LOGW("Invalid packet size reported in file: stream=\"%s\", "
-                                       "packet-offset=%jd, packet-size-bytes=%jd, "
-                                       "file-size=%jd",
-                                       ds_file->file->path->str,
-                                       (intmax_t) current_packet_offset_bytes,
-                                       (intmax_t) current_packet_size_bytes,
-                                       (intmax_t) ds_file->file->size);
-                       goto error;
-               }
-
-               index_entry = ctf_fs_ds_index_entry_create(
-                       ds_file->self_comp, ds_file->log_level);
-               if (!index_entry) {
-                       BT_COMP_LOGE_APPEND_CAUSE(ds_file->self_comp,
-                               "Failed to create a ctf_fs_ds_index_entry.");
-                       goto error;
-               }
-
-               /* Set path to stream file. */
-               index_entry->path = file_info->path->str;
-
-               ret = init_index_entry(index_entry, ds_file, &props,
-                       current_packet_size_bytes, current_packet_offset_bytes);
-               if (ret) {
-                       g_free(index_entry);
-                       goto error;
-               }
-
-               g_ptr_array_add(index->entries, index_entry);
-
-               current_packet_offset_bytes += current_packet_size_bytes;
-               BT_COMP_LOGD("Seeking to next packet: current-packet-offset=%jd, "
-                       "next-packet-offset=%jd",
-                       (intmax_t) (current_packet_offset_bytes - current_packet_size_bytes),
-                       (intmax_t) current_packet_offset_bytes);
-       }
-
-end:
-       return index;
-
-error:
-       ctf_fs_ds_index_destroy(index);
-       index = NULL;
-       goto end;
-}
-
-BT_HIDDEN
-struct ctf_fs_ds_file *ctf_fs_ds_file_create(
-               struct ctf_fs_trace *ctf_fs_trace,
-               bt_self_message_iterator *self_msg_iter,
-               bt_stream *stream, const char *path,
-               bt_logging_level log_level)
-{
-       int ret;
-       const size_t offset_align = bt_mmap_get_offset_align_size(log_level);
-       struct ctf_fs_ds_file *ds_file = g_new0(struct ctf_fs_ds_file, 1);
-
-       if (!ds_file) {
-               goto error;
-       }
-
-       ds_file->log_level = log_level;
-       ds_file->self_comp = ctf_fs_trace->self_comp;
-       ds_file->self_msg_iter = self_msg_iter;
-       ds_file->file = ctf_fs_file_create(log_level, ds_file->self_comp);
-       if (!ds_file->file) {
-               goto error;
-       }
-
-       ds_file->stream = stream;
-       bt_stream_get_ref(ds_file->stream);
-       ds_file->metadata = ctf_fs_trace->metadata;
-       g_string_assign(ds_file->file->path, path);
-       ret = ctf_fs_file_open(ds_file->file, "rb");
-       if (ret) {
-               goto error;
-       }
-
-       ds_file->mmap_max_len = offset_align * 2048;
-
-       goto end;
-
-error:
-       /* Do not touch "borrowed" file. */
-       ctf_fs_ds_file_destroy(ds_file);
-       ds_file = NULL;
-
-end:
-       return ds_file;
-}
-
-BT_HIDDEN
-struct ctf_fs_ds_index *ctf_fs_ds_file_build_index(
-               struct ctf_fs_ds_file *ds_file,
-               struct ctf_fs_ds_file_info *file_info,
-               struct ctf_msg_iter *msg_iter)
-{
-       struct ctf_fs_ds_index *index;
-       bt_self_component *self_comp = ds_file->self_comp;
-       bt_logging_level log_level = ds_file->log_level;
-
-       index = build_index_from_idx_file(ds_file, file_info, msg_iter);
-       if (index) {
-               goto end;
-       }
-
-       BT_COMP_LOGI("Failed to build index from .index file; "
-               "falling back to stream indexing.");
-       index = build_index_from_stream_file(ds_file, file_info, msg_iter);
-end:
-       return index;
-}
-
-BT_HIDDEN
-struct ctf_fs_ds_index *ctf_fs_ds_index_create(bt_logging_level log_level,
-               bt_self_component *self_comp)
-{
-       struct ctf_fs_ds_index *index = g_new0(struct ctf_fs_ds_index, 1);
-
-       if (!index) {
-               BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, log_level, self_comp,
-                       "Failed to allocate index");
-               goto error;
-       }
-
-       index->entries = g_ptr_array_new_with_free_func((GDestroyNotify) g_free);
-       if (!index->entries) {
-               BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, log_level, self_comp,
-                       "Failed to allocate index entries.");
-               goto error;
-       }
-
-       goto end;
-
-error:
-       ctf_fs_ds_index_destroy(index);
-       index = NULL;
-end:
-       return index;
-}
-
-BT_HIDDEN
-void ctf_fs_ds_file_destroy(struct ctf_fs_ds_file *ds_file)
-{
-       if (!ds_file) {
-               return;
-       }
-
-       bt_stream_put_ref(ds_file->stream);
-       (void) ds_file_munmap(ds_file);
-
-       if (ds_file->file) {
-               ctf_fs_file_destroy(ds_file->file);
-       }
-
-       g_free(ds_file);
-}
-
-BT_HIDDEN
-void ctf_fs_ds_index_destroy(struct ctf_fs_ds_index *index)
-{
-       if (!index) {
-               return;
-       }
-
-       if (index->entries) {
-               g_ptr_array_free(index->entries, TRUE);
-       }
-       g_free(index);
-}
diff --git a/src/plugins/ctf/fs-src/data-stream-file.cpp b/src/plugins/ctf/fs-src/data-stream-file.cpp
new file mode 100644 (file)
index 0000000..8608ff7
--- /dev/null
@@ -0,0 +1,1037 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2016-2017 Philippe Proulx <pproulx@efficios.com>
+ * Copyright 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ * Copyright 2010-2011 EfficiOS Inc. and Linux Foundation
+ */
+
+#define BT_COMP_LOG_SELF_COMP (self_comp)
+#define BT_LOG_OUTPUT_LEVEL (log_level)
+#define BT_LOG_TAG "PLUGIN/SRC.CTF.FS/DS"
+#include "logging/comp-logging.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <inttypes.h>
+#include "compat/mman.h"
+#include "compat/endian.h"
+#include <babeltrace2/babeltrace.h>
+#include "common/common.h"
+#include "file.hpp"
+#include "metadata.hpp"
+#include "../common/msg-iter/msg-iter.hpp"
+#include "common/assert.h"
+#include "data-stream-file.hpp"
+#include <string.h>
+
+static inline
+size_t remaining_mmap_bytes(struct ctf_fs_ds_file *ds_file)
+{
+       BT_ASSERT_DBG(ds_file->mmap_len >= ds_file->request_offset_in_mapping);
+       return ds_file->mmap_len - ds_file->request_offset_in_mapping;
+}
+
+/*
+ * Return true if `offset_in_file` is in the current mapping.
+ */
+
+static
+bool offset_ist_mapped(struct ctf_fs_ds_file *ds_file, off_t offset_in_file)
+{
+       return offset_in_file >= ds_file->mmap_offset_in_file &&
+               offset_in_file < (ds_file->mmap_offset_in_file + ds_file->mmap_len);
+}
+
+static
+enum ctf_msg_iter_medium_status ds_file_munmap(
+               struct ctf_fs_ds_file *ds_file)
+{
+       enum ctf_msg_iter_medium_status status;
+       bt_self_component *self_comp = ds_file->self_comp;
+       bt_logging_level log_level = ds_file->log_level;
+
+       BT_ASSERT(ds_file);
+
+       if (!ds_file->mmap_addr) {
+               status = CTF_MSG_ITER_MEDIUM_STATUS_OK;
+               goto end;
+       }
+
+       if (bt_munmap(ds_file->mmap_addr, ds_file->mmap_len)) {
+               BT_COMP_LOGE_ERRNO("Cannot memory-unmap file",
+                       ": address=%p, size=%zu, file_path=\"%s\", file=%p",
+                       ds_file->mmap_addr, ds_file->mmap_len,
+                       ds_file->file ? ds_file->file->path->str : "NULL",
+                       ds_file->file ? ds_file->file->fp : NULL);
+               status = CTF_MSG_ITER_MEDIUM_STATUS_ERROR;
+               goto end;
+       }
+
+       ds_file->mmap_addr = NULL;
+
+       status = CTF_MSG_ITER_MEDIUM_STATUS_OK;
+end:
+       return status;
+}
+
+/*
+ * mmap a region of `ds_file` such that `requested_offset_in_file` is in the
+ * mapping.  If the currently mmap-ed region already contains
+ * `requested_offset_in_file`, the mapping is kept.
+ *
+ * Set `ds_file->requested_offset_in_mapping` based on `request_offset_in_file`,
+ * such that the next call to `request_bytes` will return bytes starting at that
+ * position.
+ *
+ * `requested_offset_in_file` must be a valid offset in the file.
+ */
+static
+enum ctf_msg_iter_medium_status ds_file_mmap(
+               struct ctf_fs_ds_file *ds_file, off_t requested_offset_in_file)
+{
+       enum ctf_msg_iter_medium_status status;
+       bt_self_component *self_comp = ds_file->self_comp;
+       bt_logging_level log_level = ds_file->log_level;
+
+       /* Ensure the requested offset is in the file range. */
+       BT_ASSERT(requested_offset_in_file >= 0);
+       BT_ASSERT(requested_offset_in_file < ds_file->file->size);
+
+       /*
+        * If the mapping already contains the requested offset, just adjust
+        * requested_offset_in_mapping.
+        */
+       if (offset_ist_mapped(ds_file, requested_offset_in_file)) {
+               ds_file->request_offset_in_mapping =
+                       requested_offset_in_file - ds_file->mmap_offset_in_file;
+               status = CTF_MSG_ITER_MEDIUM_STATUS_OK;
+               goto end;
+       }
+
+       /* Unmap old region */
+       status = ds_file_munmap(ds_file);
+       if (status != CTF_MSG_ITER_MEDIUM_STATUS_OK) {
+               goto end;
+       }
+
+       /*
+        * Compute a mapping that has the required alignment properties and
+        * contains `requested_offset_in_file`.
+        */
+       ds_file->request_offset_in_mapping =
+               requested_offset_in_file % bt_mmap_get_offset_align_size(ds_file->log_level);
+       ds_file->mmap_offset_in_file =
+               requested_offset_in_file - ds_file->request_offset_in_mapping;
+       ds_file->mmap_len = MIN(ds_file->file->size - ds_file->mmap_offset_in_file,
+               ds_file->mmap_max_len);
+
+       BT_ASSERT(ds_file->mmap_len > 0);
+
+       ds_file->mmap_addr = bt_mmap((void *) 0, ds_file->mmap_len,
+                       PROT_READ, MAP_PRIVATE, fileno(ds_file->file->fp),
+                       ds_file->mmap_offset_in_file, ds_file->log_level);
+       if (ds_file->mmap_addr == MAP_FAILED) {
+               BT_COMP_LOGE("Cannot memory-map address (size %zu) of file \"%s\" (%p) at offset %jd: %s",
+                               ds_file->mmap_len, ds_file->file->path->str,
+                               ds_file->file->fp, (intmax_t) ds_file->mmap_offset_in_file,
+                               strerror(errno));
+               status = CTF_MSG_ITER_MEDIUM_STATUS_ERROR;
+               goto end;
+       }
+
+       status = CTF_MSG_ITER_MEDIUM_STATUS_OK;
+
+end:
+       return status;
+}
+
+/*
+ * Change the mapping of the file to read the region that follows the current
+ * mapping.
+ *
+ * If the file hasn't been mapped yet, then everything (mmap_offset_in_file,
+ * mmap_len, request_offset_in_mapping) should have the value 0, which will
+ * result in the beginning of the file getting mapped.
+ *
+ * return _EOF if the current mapping is the end of the file.
+ */
+
+static
+enum ctf_msg_iter_medium_status ds_file_mmap_next(
+               struct ctf_fs_ds_file *ds_file)
+{
+       enum ctf_msg_iter_medium_status status;
+
+       /*
+        * If we're called, it's because more bytes are requested but we have
+        * given all the bytes of the current mapping.
+        */
+       BT_ASSERT(ds_file->request_offset_in_mapping == ds_file->mmap_len);
+
+       /*
+        * If the current mapping coincides with the end of the file, there is
+        * no next mapping.
+        */
+       if (ds_file->mmap_offset_in_file + ds_file->mmap_len == ds_file->file->size) {
+               status = CTF_MSG_ITER_MEDIUM_STATUS_EOF;
+               goto end;
+       }
+
+       status = ds_file_mmap(ds_file,
+               ds_file->mmap_offset_in_file + ds_file->mmap_len);
+
+end:
+       return status;
+}
+
+static
+enum ctf_msg_iter_medium_status medop_request_bytes(
+               size_t request_sz, uint8_t **buffer_addr,
+               size_t *buffer_sz, void *data)
+{
+       enum ctf_msg_iter_medium_status status =
+               CTF_MSG_ITER_MEDIUM_STATUS_OK;
+       struct ctf_fs_ds_file *ds_file = (struct ctf_fs_ds_file *) data;
+       bt_self_component *self_comp = ds_file->self_comp;
+       bt_logging_level log_level = ds_file->log_level;
+
+       BT_ASSERT(request_sz > 0);
+
+       /*
+        * Check if we have at least one memory-mapped byte left. If we don't,
+        * mmap the next file.
+        */
+       if (remaining_mmap_bytes(ds_file) == 0) {
+               /* Are we at the end of the file? */
+               if (ds_file->mmap_offset_in_file >= ds_file->file->size) {
+                       BT_COMP_LOGD("Reached end of file \"%s\" (%p)",
+                               ds_file->file->path->str, ds_file->file->fp);
+                       status = CTF_MSG_ITER_MEDIUM_STATUS_EOF;
+                       goto end;
+               }
+
+               status = ds_file_mmap_next(ds_file);
+               switch (status) {
+               case CTF_MSG_ITER_MEDIUM_STATUS_OK:
+                       break;
+               case CTF_MSG_ITER_MEDIUM_STATUS_EOF:
+                       goto end;
+               default:
+                       BT_COMP_LOGE("Cannot memory-map next region of file \"%s\" (%p)",
+                                       ds_file->file->path->str,
+                                       ds_file->file->fp);
+                       goto error;
+               }
+       }
+
+       BT_ASSERT(remaining_mmap_bytes(ds_file) > 0);
+       *buffer_sz = MIN(remaining_mmap_bytes(ds_file), request_sz);
+
+       BT_ASSERT(ds_file->mmap_addr);
+       *buffer_addr = ((uint8_t *) ds_file->mmap_addr) + ds_file->request_offset_in_mapping;
+
+       ds_file->request_offset_in_mapping += *buffer_sz;
+       goto end;
+
+error:
+       status = CTF_MSG_ITER_MEDIUM_STATUS_ERROR;
+
+end:
+       return status;
+}
+
+static
+bt_stream *medop_borrow_stream(bt_stream_class *stream_class, int64_t stream_id,
+               void *data)
+{
+       struct ctf_fs_ds_file *ds_file = (struct ctf_fs_ds_file *) data;
+       bt_stream_class *ds_file_stream_class;
+       bt_stream *stream = NULL;
+
+       ds_file_stream_class = bt_stream_borrow_class(
+               ds_file->stream);
+
+       if (stream_class != ds_file_stream_class) {
+               /*
+                * Not supported: two packets described by two different
+                * stream classes within the same data stream file.
+                */
+               goto end;
+       }
+
+       stream = ds_file->stream;
+
+end:
+       return stream;
+}
+
+static
+enum ctf_msg_iter_medium_status medop_seek(off_t offset, void *data)
+{
+       struct ctf_fs_ds_file *ds_file = (struct ctf_fs_ds_file *) data;
+
+       BT_ASSERT(offset >= 0);
+       BT_ASSERT(offset < ds_file->file->size);
+
+       return ds_file_mmap(ds_file, offset);
+}
+
+BT_HIDDEN
+struct ctf_msg_iter_medium_ops ctf_fs_ds_file_medops = {
+       medop_request_bytes,
+       medop_seek,
+       nullptr,
+       medop_borrow_stream,
+};
+
+struct ctf_fs_ds_group_medops_data {
+       /* Weak, set once at creation time. */
+       struct ctf_fs_ds_file_group *ds_file_group;
+
+       /*
+        * Index (as in element rank) of the index entry of ds_file_groups'
+        * index we will read next (so, the one after the one we are reading
+        * right now).
+        */
+       guint next_index_entry_index;
+
+       /*
+        * File we are currently reading.  Changes whenever we switch to
+        * reading another data file.
+        *
+        * Owned by this.
+        */
+       struct ctf_fs_ds_file *file;
+
+       /* Weak, for context / logging / appending causes. */
+       bt_self_message_iterator *self_msg_iter;
+       bt_logging_level log_level;
+};
+
+static
+enum ctf_msg_iter_medium_status medop_group_request_bytes(
+               size_t request_sz,
+               uint8_t **buffer_addr,
+               size_t *buffer_sz,
+               void *void_data)
+{
+       struct ctf_fs_ds_group_medops_data *data = (struct ctf_fs_ds_group_medops_data *)  void_data;
+
+       /* Return bytes from the current file. */
+       return medop_request_bytes(request_sz, buffer_addr, buffer_sz, data->file);
+}
+
+static
+bt_stream *medop_group_borrow_stream(
+               bt_stream_class *stream_class,
+               int64_t stream_id,
+               void *void_data)
+{
+       struct ctf_fs_ds_group_medops_data *data = (struct ctf_fs_ds_group_medops_data *) void_data;
+
+       return medop_borrow_stream(stream_class, stream_id, data->file);
+}
+
+/*
+ * Set `data->file` to prepare it to read the packet described
+ * by `index_entry`.
+ */
+
+static
+enum ctf_msg_iter_medium_status ctf_fs_ds_group_medops_set_file(
+               struct ctf_fs_ds_group_medops_data *data,
+               struct ctf_fs_ds_index_entry *index_entry,
+               bt_self_message_iterator *self_msg_iter,
+               bt_logging_level log_level)
+{
+       enum ctf_msg_iter_medium_status status;
+
+       BT_ASSERT(data);
+       BT_ASSERT(index_entry);
+
+       /* Check if that file is already the one mapped. */
+       if (!data->file || strcmp(index_entry->path, data->file->file->path->str) != 0) {
+               /* Destroy the previously used file. */
+               ctf_fs_ds_file_destroy(data->file);
+
+               /* Create the new file. */
+               data->file = ctf_fs_ds_file_create(
+                       data->ds_file_group->ctf_fs_trace,
+                       self_msg_iter,
+                       data->ds_file_group->stream,
+                       index_entry->path,
+                       log_level);
+               if (!data->file) {
+                       BT_MSG_ITER_LOGE_APPEND_CAUSE(self_msg_iter,
+                               "failed to create ctf_fs_ds_file.");
+                       status = CTF_MSG_ITER_MEDIUM_STATUS_ERROR;
+                       goto end;
+               }
+       }
+
+       /*
+        * Ensure the right portion of the file will be returned on the next
+        * request_bytes call.
+        */
+       status = ds_file_mmap(data->file, index_entry->offset);
+       if (status != CTF_MSG_ITER_MEDIUM_STATUS_OK) {
+               goto end;
+       }
+
+       status = CTF_MSG_ITER_MEDIUM_STATUS_OK;
+
+end:
+       return status;
+}
+
+static
+enum ctf_msg_iter_medium_status medop_group_switch_packet(void *void_data)
+{
+       struct ctf_fs_ds_group_medops_data *data = (struct ctf_fs_ds_group_medops_data *) void_data;
+       struct ctf_fs_ds_index_entry *index_entry;
+       enum ctf_msg_iter_medium_status status;
+
+       /* If we have gone through all index entries, we are done. */
+       if (data->next_index_entry_index >=
+               data->ds_file_group->index->entries->len) {
+               status = CTF_MSG_ITER_MEDIUM_STATUS_EOF;
+               goto end;
+       }
+
+       /*
+        * Otherwise, look up the next index entry / packet and prepare it
+        *  for reading.
+        */
+       index_entry = (struct ctf_fs_ds_index_entry *) g_ptr_array_index(
+               data->ds_file_group->index->entries,
+               data->next_index_entry_index);
+
+       status = ctf_fs_ds_group_medops_set_file(
+               data, index_entry, data->self_msg_iter, data->log_level);
+       if (status != CTF_MSG_ITER_MEDIUM_STATUS_OK) {
+               goto end;
+       }
+
+       data->next_index_entry_index++;
+
+       status = CTF_MSG_ITER_MEDIUM_STATUS_OK;
+end:
+       return status;
+}
+
+BT_HIDDEN
+void ctf_fs_ds_group_medops_data_destroy(
+               struct ctf_fs_ds_group_medops_data *data)
+{
+       if (!data) {
+               goto end;
+       }
+
+       ctf_fs_ds_file_destroy(data->file);
+
+       g_free(data);
+
+end:
+       return;
+}
+
+enum ctf_msg_iter_medium_status ctf_fs_ds_group_medops_data_create(
+               struct ctf_fs_ds_file_group *ds_file_group,
+               bt_self_message_iterator *self_msg_iter,
+               bt_logging_level log_level,
+               struct ctf_fs_ds_group_medops_data **out)
+{
+       struct ctf_fs_ds_group_medops_data *data;
+       enum ctf_msg_iter_medium_status status;
+
+       BT_ASSERT(self_msg_iter);
+       BT_ASSERT(ds_file_group);
+       BT_ASSERT(ds_file_group->index);
+       BT_ASSERT(ds_file_group->index->entries->len > 0);
+
+       data = g_new0(struct ctf_fs_ds_group_medops_data, 1);
+       if (!data) {
+               BT_MSG_ITER_LOGE_APPEND_CAUSE(self_msg_iter,
+                       "Failed to allocate a struct ctf_fs_ds_group_medops_data");
+               status = CTF_MSG_ITER_MEDIUM_STATUS_MEMORY_ERROR;
+               goto error;
+       }
+
+       data->ds_file_group = ds_file_group;
+       data->self_msg_iter = self_msg_iter;
+       data->log_level = log_level;
+
+       /*
+        * No need to prepare the first file.  ctf_msg_iter will call
+        * switch_packet before reading the first packet, it will be
+        * done then.
+        */
+
+       *out = data;
+       status = CTF_MSG_ITER_MEDIUM_STATUS_OK;
+       goto end;
+
+error:
+       ctf_fs_ds_group_medops_data_destroy(data);
+
+end:
+       return status;
+}
+
+void ctf_fs_ds_group_medops_data_reset(struct ctf_fs_ds_group_medops_data *data)
+{
+       data->next_index_entry_index = 0;
+}
+
+struct ctf_msg_iter_medium_ops ctf_fs_ds_group_medops = {
+       .request_bytes = medop_group_request_bytes,
+
+       /*
+        * We don't support seeking using this medops.  It would probably be
+        * possible, but it's not needed at the moment.
+        */
+       .seek = NULL,
+
+       .switch_packet = medop_group_switch_packet,
+       .borrow_stream = medop_group_borrow_stream,
+};
+
+static
+struct ctf_fs_ds_index_entry *ctf_fs_ds_index_entry_create(
+               bt_self_component *self_comp, bt_logging_level log_level)
+{
+       struct ctf_fs_ds_index_entry *entry;
+
+       entry = g_new0(struct ctf_fs_ds_index_entry, 1);
+       if (!entry) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Failed to allocate a ctf_fs_ds_index_entry.");
+               goto end;
+       }
+
+       entry->packet_seq_num = UINT64_MAX;
+
+end:
+       return entry;
+}
+
+static
+int convert_cycles_to_ns(struct ctf_clock_class *clock_class,
+               uint64_t cycles, int64_t *ns)
+{
+       return bt_util_clock_cycles_to_ns_from_origin(cycles,
+                       clock_class->frequency, clock_class->offset_seconds,
+                       clock_class->offset_cycles, ns);
+}
+
+static
+struct ctf_fs_ds_index *build_index_from_idx_file(
+               struct ctf_fs_ds_file *ds_file,
+               struct ctf_fs_ds_file_info *file_info,
+               struct ctf_msg_iter *msg_iter)
+{
+       int ret;
+       gchar *directory = NULL;
+       gchar *basename = NULL;
+       GString *index_basename = NULL;
+       gchar *index_file_path = NULL;
+       GMappedFile *mapped_file = NULL;
+       gsize filesize;
+       const char *mmap_begin = NULL, *file_pos = NULL;
+       const struct ctf_packet_index_file_hdr *header = NULL;
+       struct ctf_fs_ds_index *index = NULL;
+       struct ctf_fs_ds_index_entry *index_entry = NULL, *prev_index_entry = NULL;
+       uint64_t total_packets_size = 0;
+       size_t file_index_entry_size;
+       size_t file_entry_count;
+       size_t i;
+       struct ctf_stream_class *sc;
+       struct ctf_msg_iter_packet_properties props;
+       uint32_t version_major, version_minor;
+       bt_self_component *self_comp = ds_file->self_comp;
+       bt_logging_level log_level = ds_file->log_level;
+
+       BT_COMP_LOGI("Building index from .idx file of stream file %s",
+                       ds_file->file->path->str);
+       ret = ctf_msg_iter_get_packet_properties(msg_iter, &props);
+       if (ret) {
+               BT_COMP_LOGI_STR("Cannot read first packet's header and context fields.");
+               goto error;
+       }
+
+       sc = ctf_trace_class_borrow_stream_class_by_id(ds_file->metadata->tc,
+               props.stream_class_id);
+       BT_ASSERT(sc);
+       if (!sc->default_clock_class) {
+               BT_COMP_LOGI_STR("Cannot find stream class's default clock class.");
+               goto error;
+       }
+
+       /* Look for index file in relative path index/name.idx. */
+       basename = g_path_get_basename(ds_file->file->path->str);
+       if (!basename) {
+               BT_COMP_LOGE("Cannot get the basename of datastream file %s",
+                               ds_file->file->path->str);
+               goto error;
+       }
+
+       directory = g_path_get_dirname(ds_file->file->path->str);
+       if (!directory) {
+               BT_COMP_LOGE("Cannot get dirname of datastream file %s",
+                               ds_file->file->path->str);
+               goto error;
+       }
+
+       index_basename = g_string_new(basename);
+       if (!index_basename) {
+               BT_COMP_LOGE_STR("Cannot allocate index file basename string");
+               goto error;
+       }
+
+       g_string_append(index_basename, ".idx");
+       index_file_path = g_build_filename(directory, "index",
+                       index_basename->str, NULL);
+       mapped_file = g_mapped_file_new(index_file_path, FALSE, NULL);
+       if (!mapped_file) {
+               BT_COMP_LOGD("Cannot create new mapped file %s",
+                               index_file_path);
+               goto error;
+       }
+
+       /*
+        * The g_mapped_file API limits us to 4GB files on 32-bit.
+        * Traces with such large indexes have never been seen in the wild,
+        * but this would need to be adjusted to support them.
+        */
+       filesize = g_mapped_file_get_length(mapped_file);
+       if (filesize < sizeof(*header)) {
+               BT_COMP_LOGW("Invalid LTTng trace index file: "
+                       "file size (%zu bytes) < header size (%zu bytes)",
+                       filesize, sizeof(*header));
+               goto error;
+       }
+
+       mmap_begin = g_mapped_file_get_contents(mapped_file);
+       header = (struct ctf_packet_index_file_hdr *) mmap_begin;
+
+       file_pos = g_mapped_file_get_contents(mapped_file) + sizeof(*header);
+       if (be32toh(header->magic) != CTF_INDEX_MAGIC) {
+               BT_COMP_LOGW_STR("Invalid LTTng trace index: \"magic\" field validation failed");
+               goto error;
+       }
+
+       version_major = be32toh(header->index_major);
+       version_minor = be32toh(header->index_minor);
+       if (version_major != 1) {
+               BT_COMP_LOGW(
+                       "Unknown LTTng trace index version: "
+                       "major=%" PRIu32 ", minor=%" PRIu32,
+                       version_major, version_minor);
+               goto error;
+       }
+
+       file_index_entry_size = be32toh(header->packet_index_len);
+       if (file_index_entry_size < CTF_INDEX_1_0_SIZE) {
+               BT_COMP_LOGW("Invalid `packet_index_len` in LTTng trace index file (`packet_index_len` < CTF index 1.0 index entry size): "
+                       "packet_index_len=%zu, CTF_INDEX_1_0_SIZE=%zu",
+                       file_index_entry_size, CTF_INDEX_1_0_SIZE);
+               goto error;
+       }
+
+       file_entry_count = (filesize - sizeof(*header)) / file_index_entry_size;
+       if ((filesize - sizeof(*header)) % file_index_entry_size) {
+               BT_COMP_LOGW("Invalid LTTng trace index: the index's size after the header "
+                       "(%zu bytes) is not a multiple of the index entry size "
+                       "(%zu bytes)", (filesize - sizeof(*header)),
+                       sizeof(*header));
+               goto error;
+       }
+
+       index = ctf_fs_ds_index_create(ds_file->log_level, ds_file->self_comp);
+       if (!index) {
+               goto error;
+       }
+
+       for (i = 0; i < file_entry_count; i++) {
+               struct ctf_packet_index *file_index =
+                               (struct ctf_packet_index *) file_pos;
+               uint64_t packet_size = be64toh(file_index->packet_size);
+
+               if (packet_size % CHAR_BIT) {
+                       BT_COMP_LOGW("Invalid packet size encountered in LTTng trace index file");
+                       goto error;
+               }
+
+               index_entry = ctf_fs_ds_index_entry_create(
+                       ds_file->self_comp, ds_file->log_level);
+               if (!index_entry) {
+                       BT_COMP_LOGE_APPEND_CAUSE(ds_file->self_comp,
+                               "Failed to create a ctf_fs_ds_index_entry.");
+                       goto error;
+               }
+
+               /* Set path to stream file. */
+               index_entry->path = file_info->path->str;
+
+               /* Convert size in bits to bytes. */
+               packet_size /= CHAR_BIT;
+               index_entry->packet_size = packet_size;
+
+               index_entry->offset = be64toh(file_index->offset);
+               if (i != 0 && index_entry->offset < prev_index_entry->offset) {
+                       BT_COMP_LOGW("Invalid, non-monotonic, packet offset encountered in LTTng trace index file: "
+                               "previous offset=%" PRIu64 ", current offset=%" PRIu64,
+                               prev_index_entry->offset, index_entry->offset);
+                       goto error;
+               }
+
+               index_entry->timestamp_begin = be64toh(file_index->timestamp_begin);
+               index_entry->timestamp_end = be64toh(file_index->timestamp_end);
+               if (index_entry->timestamp_end < index_entry->timestamp_begin) {
+                       BT_COMP_LOGW("Invalid packet time bounds encountered in LTTng trace index file (begin > end): "
+                               "timestamp_begin=%" PRIu64 "timestamp_end=%" PRIu64,
+                               index_entry->timestamp_begin,
+                               index_entry->timestamp_end);
+                       goto error;
+               }
+
+               /* Convert the packet's bound to nanoseconds since Epoch. */
+               ret = convert_cycles_to_ns(sc->default_clock_class,
+                               index_entry->timestamp_begin,
+                               &index_entry->timestamp_begin_ns);
+               if (ret) {
+                       BT_COMP_LOGI_STR("Failed to convert raw timestamp to nanoseconds since Epoch during index parsing");
+                       goto error;
+               }
+               ret = convert_cycles_to_ns(sc->default_clock_class,
+                               index_entry->timestamp_end,
+                               &index_entry->timestamp_end_ns);
+               if (ret) {
+                       BT_COMP_LOGI_STR("Failed to convert raw timestamp to nanoseconds since Epoch during LTTng trace index parsing");
+                       goto error;
+               }
+
+               if (version_minor >= 1) {
+                       index_entry->packet_seq_num = be64toh(file_index->packet_seq_num);
+               }
+
+               total_packets_size += packet_size;
+               file_pos += file_index_entry_size;
+
+               prev_index_entry = index_entry;
+
+               /* Give ownership of `index_entry` to `index->entries`. */
+               g_ptr_array_add(index->entries, index_entry);
+               index_entry = NULL;
+       }
+
+       /* Validate that the index addresses the complete stream. */
+       if (ds_file->file->size != total_packets_size) {
+               BT_COMP_LOGW("Invalid LTTng trace index file; indexed size != stream file size: "
+                       "file-size=%" PRIu64 ", total-packets-size=%" PRIu64,
+                       ds_file->file->size, total_packets_size);
+               goto error;
+       }
+end:
+       g_free(directory);
+       g_free(basename);
+       g_free(index_file_path);
+       if (index_basename) {
+               g_string_free(index_basename, TRUE);
+       }
+       if (mapped_file) {
+               g_mapped_file_unref(mapped_file);
+       }
+       return index;
+error:
+       ctf_fs_ds_index_destroy(index);
+       g_free(index_entry);
+       index = NULL;
+       goto end;
+}
+
+static
+int init_index_entry(struct ctf_fs_ds_index_entry *entry,
+               struct ctf_fs_ds_file *ds_file,
+               struct ctf_msg_iter_packet_properties *props,
+               off_t packet_size, off_t packet_offset)
+{
+       int ret = 0;
+       struct ctf_stream_class *sc;
+       bt_self_component *self_comp = ds_file->self_comp;
+       bt_logging_level log_level = ds_file->log_level;
+
+       sc = ctf_trace_class_borrow_stream_class_by_id(ds_file->metadata->tc,
+               props->stream_class_id);
+       BT_ASSERT(sc);
+       BT_ASSERT(packet_offset >= 0);
+       entry->offset = packet_offset;
+       BT_ASSERT(packet_size >= 0);
+       entry->packet_size = packet_size;
+
+       if (props->snapshots.beginning_clock != UINT64_C(-1)) {
+               entry->timestamp_begin = props->snapshots.beginning_clock;
+
+               /* Convert the packet's bound to nanoseconds since Epoch. */
+               ret = convert_cycles_to_ns(sc->default_clock_class,
+                                          props->snapshots.beginning_clock,
+                                          &entry->timestamp_begin_ns);
+               if (ret) {
+                       BT_COMP_LOGI_STR("Failed to convert raw timestamp to nanoseconds since Epoch.");
+                       goto end;
+               }
+       } else {
+               entry->timestamp_begin = UINT64_C(-1);
+               entry->timestamp_begin_ns = UINT64_C(-1);
+       }
+
+       if (props->snapshots.end_clock != UINT64_C(-1)) {
+               entry->timestamp_end = props->snapshots.end_clock;
+
+               /* Convert the packet's bound to nanoseconds since Epoch. */
+               ret = convert_cycles_to_ns(sc->default_clock_class,
+                                          props->snapshots.end_clock,
+                                          &entry->timestamp_end_ns);
+               if (ret) {
+                       BT_COMP_LOGI_STR("Failed to convert raw timestamp to nanoseconds since Epoch.");
+                       goto end;
+               }
+       } else {
+               entry->timestamp_end = UINT64_C(-1);
+               entry->timestamp_end_ns = UINT64_C(-1);
+       }
+
+end:
+       return ret;
+}
+
+static
+struct ctf_fs_ds_index *build_index_from_stream_file(
+               struct ctf_fs_ds_file *ds_file,
+               struct ctf_fs_ds_file_info *file_info,
+               struct ctf_msg_iter *msg_iter)
+{
+       int ret;
+       struct ctf_fs_ds_index *index = NULL;
+       enum ctf_msg_iter_status iter_status = CTF_MSG_ITER_STATUS_OK;
+       off_t current_packet_offset_bytes = 0;
+       bt_self_component *self_comp = ds_file->self_comp;
+       bt_logging_level log_level = ds_file->log_level;
+
+       BT_COMP_LOGI("Indexing stream file %s", ds_file->file->path->str);
+
+       index = ctf_fs_ds_index_create(ds_file->log_level, ds_file->self_comp);
+       if (!index) {
+               goto error;
+       }
+
+       while (true) {
+               off_t current_packet_size_bytes;
+               struct ctf_fs_ds_index_entry *index_entry;
+               struct ctf_msg_iter_packet_properties props;
+
+               if (current_packet_offset_bytes < 0) {
+                       BT_COMP_LOGE_STR("Cannot get the current packet's offset.");
+                       goto error;
+               } else if (current_packet_offset_bytes > ds_file->file->size) {
+                       BT_COMP_LOGE_STR("Unexpected current packet's offset (larger than file).");
+                       goto error;
+               } else if (current_packet_offset_bytes == ds_file->file->size) {
+                       /* No more data */
+                       break;
+               }
+
+               iter_status = ctf_msg_iter_seek(msg_iter,
+                               current_packet_offset_bytes);
+               if (iter_status != CTF_MSG_ITER_STATUS_OK) {
+                       goto error;
+               }
+
+               iter_status = ctf_msg_iter_get_packet_properties(
+                       msg_iter, &props);
+               if (iter_status != CTF_MSG_ITER_STATUS_OK) {
+                       goto error;
+               }
+
+               if (props.exp_packet_total_size >= 0) {
+                       current_packet_size_bytes =
+                               (uint64_t) props.exp_packet_total_size / 8;
+               } else {
+                       current_packet_size_bytes = ds_file->file->size;
+               }
+
+               if (current_packet_offset_bytes + current_packet_size_bytes >
+                               ds_file->file->size) {
+                       BT_COMP_LOGW("Invalid packet size reported in file: stream=\"%s\", "
+                                       "packet-offset=%jd, packet-size-bytes=%jd, "
+                                       "file-size=%jd",
+                                       ds_file->file->path->str,
+                                       (intmax_t) current_packet_offset_bytes,
+                                       (intmax_t) current_packet_size_bytes,
+                                       (intmax_t) ds_file->file->size);
+                       goto error;
+               }
+
+               index_entry = ctf_fs_ds_index_entry_create(
+                       ds_file->self_comp, ds_file->log_level);
+               if (!index_entry) {
+                       BT_COMP_LOGE_APPEND_CAUSE(ds_file->self_comp,
+                               "Failed to create a ctf_fs_ds_index_entry.");
+                       goto error;
+               }
+
+               /* Set path to stream file. */
+               index_entry->path = file_info->path->str;
+
+               ret = init_index_entry(index_entry, ds_file, &props,
+                       current_packet_size_bytes, current_packet_offset_bytes);
+               if (ret) {
+                       g_free(index_entry);
+                       goto error;
+               }
+
+               g_ptr_array_add(index->entries, index_entry);
+
+               current_packet_offset_bytes += current_packet_size_bytes;
+               BT_COMP_LOGD("Seeking to next packet: current-packet-offset=%jd, "
+                       "next-packet-offset=%jd",
+                       (intmax_t) (current_packet_offset_bytes - current_packet_size_bytes),
+                       (intmax_t) current_packet_offset_bytes);
+       }
+
+end:
+       return index;
+
+error:
+       ctf_fs_ds_index_destroy(index);
+       index = NULL;
+       goto end;
+}
+
+BT_HIDDEN
+struct ctf_fs_ds_file *ctf_fs_ds_file_create(
+               struct ctf_fs_trace *ctf_fs_trace,
+               bt_self_message_iterator *self_msg_iter,
+               bt_stream *stream, const char *path,
+               bt_logging_level log_level)
+{
+       int ret;
+       const size_t offset_align = bt_mmap_get_offset_align_size(log_level);
+       struct ctf_fs_ds_file *ds_file = g_new0(struct ctf_fs_ds_file, 1);
+
+       if (!ds_file) {
+               goto error;
+       }
+
+       ds_file->log_level = log_level;
+       ds_file->self_comp = ctf_fs_trace->self_comp;
+       ds_file->self_msg_iter = self_msg_iter;
+       ds_file->file = ctf_fs_file_create(log_level, ds_file->self_comp);
+       if (!ds_file->file) {
+               goto error;
+       }
+
+       ds_file->stream = stream;
+       bt_stream_get_ref(ds_file->stream);
+       ds_file->metadata = ctf_fs_trace->metadata;
+       g_string_assign(ds_file->file->path, path);
+       ret = ctf_fs_file_open(ds_file->file, "rb");
+       if (ret) {
+               goto error;
+       }
+
+       ds_file->mmap_max_len = offset_align * 2048;
+
+       goto end;
+
+error:
+       /* Do not touch "borrowed" file. */
+       ctf_fs_ds_file_destroy(ds_file);
+       ds_file = NULL;
+
+end:
+       return ds_file;
+}
+
+BT_HIDDEN
+struct ctf_fs_ds_index *ctf_fs_ds_file_build_index(
+               struct ctf_fs_ds_file *ds_file,
+               struct ctf_fs_ds_file_info *file_info,
+               struct ctf_msg_iter *msg_iter)
+{
+       struct ctf_fs_ds_index *index;
+       bt_self_component *self_comp = ds_file->self_comp;
+       bt_logging_level log_level = ds_file->log_level;
+
+       index = build_index_from_idx_file(ds_file, file_info, msg_iter);
+       if (index) {
+               goto end;
+       }
+
+       BT_COMP_LOGI("Failed to build index from .index file; "
+               "falling back to stream indexing.");
+       index = build_index_from_stream_file(ds_file, file_info, msg_iter);
+end:
+       return index;
+}
+
+BT_HIDDEN
+struct ctf_fs_ds_index *ctf_fs_ds_index_create(bt_logging_level log_level,
+               bt_self_component *self_comp)
+{
+       struct ctf_fs_ds_index *index = g_new0(struct ctf_fs_ds_index, 1);
+
+       if (!index) {
+               BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, log_level, self_comp,
+                       "Failed to allocate index");
+               goto error;
+       }
+
+       index->entries = g_ptr_array_new_with_free_func((GDestroyNotify) g_free);
+       if (!index->entries) {
+               BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, log_level, self_comp,
+                       "Failed to allocate index entries.");
+               goto error;
+       }
+
+       goto end;
+
+error:
+       ctf_fs_ds_index_destroy(index);
+       index = NULL;
+end:
+       return index;
+}
+
+BT_HIDDEN
+void ctf_fs_ds_file_destroy(struct ctf_fs_ds_file *ds_file)
+{
+       if (!ds_file) {
+               return;
+       }
+
+       bt_stream_put_ref(ds_file->stream);
+       (void) ds_file_munmap(ds_file);
+
+       if (ds_file->file) {
+               ctf_fs_file_destroy(ds_file->file);
+       }
+
+       g_free(ds_file);
+}
+
+BT_HIDDEN
+void ctf_fs_ds_index_destroy(struct ctf_fs_ds_index *index)
+{
+       if (!index) {
+               return;
+       }
+
+       if (index->entries) {
+               g_ptr_array_free(index->entries, TRUE);
+       }
+       g_free(index);
+}
diff --git a/src/plugins/ctf/fs-src/data-stream-file.h b/src/plugins/ctf/fs-src/data-stream-file.h
deleted file mode 100644 (file)
index ec28e0d..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright (C) 2016 Philippe Proulx <pproulx@efficios.com>
- */
-
-#ifndef CTF_FS_DS_FILE_H
-#define CTF_FS_DS_FILE_H
-
-#include <stdio.h>
-#include <stdbool.h>
-#include <glib.h>
-#include "common/macros.h"
-#include <babeltrace2/babeltrace.h>
-
-#include "../common/msg-iter/msg-iter.h"
-#include "lttng-index.h"
-
-struct ctf_fs_component;
-struct ctf_fs_file;
-struct ctf_fs_trace;
-struct ctf_fs_ds_file;
-struct ctf_fs_ds_file_group;
-struct ctf_fs_ds_group_medops_data;
-
-struct ctf_fs_ds_file_info {
-       /* Owned by this. */
-       GString *path;
-
-       /* Guaranteed to be set, as opposed to the index. */
-       int64_t begin_ns;
-};
-
-struct ctf_fs_metadata;
-
-struct ctf_fs_ds_file {
-       bt_logging_level log_level;
-
-       /* Weak */
-       bt_self_component *self_comp;
-
-       /* Weak */
-       bt_self_message_iterator *self_msg_iter;
-
-       /* Weak */
-       struct ctf_fs_metadata *metadata;
-
-       /* Owned by this */
-       struct ctf_fs_file *file;
-
-       /* Owned by this */
-       bt_stream *stream;
-
-       void *mmap_addr;
-
-       /*
-        * Max length of chunk to mmap() when updating the current mapping.
-        * This value must be page-aligned.
-        */
-       size_t mmap_max_len;
-
-       /* Length of the current mapping. Never exceeds the file's length. */
-       size_t mmap_len;
-
-       /* Offset in the file where the current mapping starts. */
-       off_t mmap_offset_in_file;
-
-       /*
-        * Offset, in the current mapping, of the address to return on the next
-        * request.
-        */
-       off_t request_offset_in_mapping;
-};
-
-BT_HIDDEN
-struct ctf_fs_ds_file *ctf_fs_ds_file_create(
-               struct ctf_fs_trace *ctf_fs_trace,
-               bt_self_message_iterator *self_msg_iter,
-               bt_stream *stream, const char *path,
-               bt_logging_level log_level);
-
-BT_HIDDEN
-void ctf_fs_ds_file_destroy(struct ctf_fs_ds_file *stream);
-
-BT_HIDDEN
-struct ctf_fs_ds_index *ctf_fs_ds_file_build_index(
-               struct ctf_fs_ds_file *ds_file,
-               struct ctf_fs_ds_file_info *ds_file_info,
-               struct ctf_msg_iter *msg_iter);
-
-BT_HIDDEN
-struct ctf_fs_ds_index *ctf_fs_ds_index_create(bt_logging_level log_level,
-               bt_self_component *self_comp);
-
-BT_HIDDEN
-void ctf_fs_ds_index_destroy(struct ctf_fs_ds_index *index);
-
-/*
- * Medium operations to iterate on a single ctf_fs_ds_file.
- *
- * The data pointer when using this must be a pointer to the ctf_fs_ds_file.
- */
-extern struct ctf_msg_iter_medium_ops ctf_fs_ds_file_medops;
-
-/*
- * Medium operations to iterate on the packet of a ctf_fs_ds_group.
- *
- * The iteration is done based on the index of the group.
- *
- * The data pointer when using these medops must be a pointer to a ctf_fs_ds
- * group_medops_data structure.
- */
-BT_HIDDEN
-extern struct ctf_msg_iter_medium_ops ctf_fs_ds_group_medops;
-
-BT_HIDDEN
-enum ctf_msg_iter_medium_status ctf_fs_ds_group_medops_data_create(
-               struct ctf_fs_ds_file_group *ds_file_group,
-               bt_self_message_iterator *self_msg_iter,
-               bt_logging_level log_level,
-               struct ctf_fs_ds_group_medops_data **out);
-
-BT_HIDDEN
-void ctf_fs_ds_group_medops_data_reset(struct ctf_fs_ds_group_medops_data *data);
-
-BT_HIDDEN
-void ctf_fs_ds_group_medops_data_destroy(
-               struct ctf_fs_ds_group_medops_data *data);
-
-#endif /* CTF_FS_DS_FILE_H */
diff --git a/src/plugins/ctf/fs-src/data-stream-file.hpp b/src/plugins/ctf/fs-src/data-stream-file.hpp
new file mode 100644 (file)
index 0000000..9bd1e50
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2016 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#ifndef CTF_FS_DS_FILE_H
+#define CTF_FS_DS_FILE_H
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <glib.h>
+#include "common/macros.h"
+#include <babeltrace2/babeltrace.h>
+
+#include "../common/msg-iter/msg-iter.hpp"
+#include "lttng-index.hpp"
+
+struct ctf_fs_component;
+struct ctf_fs_file;
+struct ctf_fs_trace;
+struct ctf_fs_ds_file;
+struct ctf_fs_ds_file_group;
+struct ctf_fs_ds_group_medops_data;
+
+struct ctf_fs_ds_file_info {
+       /* Owned by this. */
+       GString *path;
+
+       /* Guaranteed to be set, as opposed to the index. */
+       int64_t begin_ns;
+};
+
+struct ctf_fs_metadata;
+
+struct ctf_fs_ds_file {
+       bt_logging_level log_level;
+
+       /* Weak */
+       bt_self_component *self_comp;
+
+       /* Weak */
+       bt_self_message_iterator *self_msg_iter;
+
+       /* Weak */
+       struct ctf_fs_metadata *metadata;
+
+       /* Owned by this */
+       struct ctf_fs_file *file;
+
+       /* Owned by this */
+       bt_stream *stream;
+
+       void *mmap_addr;
+
+       /*
+        * Max length of chunk to mmap() when updating the current mapping.
+        * This value must be page-aligned.
+        */
+       size_t mmap_max_len;
+
+       /* Length of the current mapping. Never exceeds the file's length. */
+       size_t mmap_len;
+
+       /* Offset in the file where the current mapping starts. */
+       off_t mmap_offset_in_file;
+
+       /*
+        * Offset, in the current mapping, of the address to return on the next
+        * request.
+        */
+       off_t request_offset_in_mapping;
+};
+
+BT_HIDDEN
+struct ctf_fs_ds_file *ctf_fs_ds_file_create(
+               struct ctf_fs_trace *ctf_fs_trace,
+               bt_self_message_iterator *self_msg_iter,
+               bt_stream *stream, const char *path,
+               bt_logging_level log_level);
+
+BT_HIDDEN
+void ctf_fs_ds_file_destroy(struct ctf_fs_ds_file *stream);
+
+BT_HIDDEN
+struct ctf_fs_ds_index *ctf_fs_ds_file_build_index(
+               struct ctf_fs_ds_file *ds_file,
+               struct ctf_fs_ds_file_info *ds_file_info,
+               struct ctf_msg_iter *msg_iter);
+
+BT_HIDDEN
+struct ctf_fs_ds_index *ctf_fs_ds_index_create(bt_logging_level log_level,
+               bt_self_component *self_comp);
+
+BT_HIDDEN
+void ctf_fs_ds_index_destroy(struct ctf_fs_ds_index *index);
+
+/*
+ * Medium operations to iterate on a single ctf_fs_ds_file.
+ *
+ * The data pointer when using this must be a pointer to the ctf_fs_ds_file.
+ */
+extern struct ctf_msg_iter_medium_ops ctf_fs_ds_file_medops;
+
+/*
+ * Medium operations to iterate on the packet of a ctf_fs_ds_group.
+ *
+ * The iteration is done based on the index of the group.
+ *
+ * The data pointer when using these medops must be a pointer to a ctf_fs_ds
+ * group_medops_data structure.
+ */
+BT_HIDDEN
+extern struct ctf_msg_iter_medium_ops ctf_fs_ds_group_medops;
+
+BT_HIDDEN
+enum ctf_msg_iter_medium_status ctf_fs_ds_group_medops_data_create(
+               struct ctf_fs_ds_file_group *ds_file_group,
+               bt_self_message_iterator *self_msg_iter,
+               bt_logging_level log_level,
+               struct ctf_fs_ds_group_medops_data **out);
+
+BT_HIDDEN
+void ctf_fs_ds_group_medops_data_reset(struct ctf_fs_ds_group_medops_data *data);
+
+BT_HIDDEN
+void ctf_fs_ds_group_medops_data_destroy(
+               struct ctf_fs_ds_group_medops_data *data);
+
+#endif /* CTF_FS_DS_FILE_H */
diff --git a/src/plugins/ctf/fs-src/file.c b/src/plugins/ctf/fs-src/file.c
deleted file mode 100644 (file)
index b705ca4..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2016 Philippe Proulx <pproulx@efficios.com>
- */
-
-#define BT_COMP_LOG_SELF_COMP (file->self_comp)
-#define BT_LOG_OUTPUT_LEVEL (file->log_level)
-#define BT_LOG_TAG "PLUGIN/SRC.CTF.FS/FILE"
-#include "logging/comp-logging.h"
-
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <glib.h>
-#include "file.h"
-
-BT_HIDDEN
-void ctf_fs_file_destroy(struct ctf_fs_file *file)
-{
-       if (!file) {
-               return;
-       }
-
-       if (file->fp) {
-               BT_COMP_LOGD("Closing file \"%s\" (%p)",
-                               file->path ? file->path->str : NULL, file->fp);
-
-               if (fclose(file->fp)) {
-                       BT_COMP_LOGE("Cannot close file \"%s\": %s",
-                                       file->path ? file->path->str : "NULL",
-                                       strerror(errno));
-               }
-       }
-
-       if (file->path) {
-               g_string_free(file->path, TRUE);
-       }
-
-       g_free(file);
-}
-
-BT_HIDDEN
-struct ctf_fs_file *ctf_fs_file_create(bt_logging_level log_level,
-               bt_self_component *self_comp)
-{
-       struct ctf_fs_file *file = g_new0(struct ctf_fs_file, 1);
-
-       if (!file) {
-               goto error;
-       }
-
-       file->log_level = log_level;
-       file->self_comp = self_comp;
-       file->path = g_string_new(NULL);
-       if (!file->path) {
-               goto error;
-       }
-
-       goto end;
-
-error:
-       ctf_fs_file_destroy(file);
-       file = NULL;
-
-end:
-       return file;
-}
-
-BT_HIDDEN
-int ctf_fs_file_open(struct ctf_fs_file *file, const char *mode)
-{
-       int ret = 0;
-       struct stat stat;
-
-       BT_COMP_LOGI("Opening file \"%s\" with mode \"%s\"", file->path->str, mode);
-       file->fp = fopen(file->path->str, mode);
-       if (!file->fp) {
-               BT_COMP_LOGE_APPEND_CAUSE_ERRNO(file->self_comp,
-                       "Cannot open file", ": path=%s, mode=%s",
-                       file->path->str, mode);
-               goto error;
-       }
-
-       BT_COMP_LOGI("Opened file: %p", file->fp);
-
-       if (fstat(fileno(file->fp), &stat)) {
-               BT_COMP_LOGE_APPEND_CAUSE_ERRNO(file->self_comp,
-                       "Cannot get file information",
-                       ": path=%s", file->path->str);
-               goto error;
-       }
-
-       file->size = stat.st_size;
-       BT_COMP_LOGI("File is %jd bytes", (intmax_t) file->size);
-       goto end;
-
-error:
-       ret = -1;
-
-       if (file->fp) {
-               if (fclose(file->fp)) {
-                       BT_COMP_LOGE("Cannot close file \"%s\": %s", file->path->str,
-                               strerror(errno));
-               }
-       }
-
-end:
-       return ret;
-}
diff --git a/src/plugins/ctf/fs-src/file.cpp b/src/plugins/ctf/fs-src/file.cpp
new file mode 100644 (file)
index 0000000..42b865a
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2016 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#define BT_COMP_LOG_SELF_COMP (file->self_comp)
+#define BT_LOG_OUTPUT_LEVEL (file->log_level)
+#define BT_LOG_TAG "PLUGIN/SRC.CTF.FS/FILE"
+#include "logging/comp-logging.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <glib.h>
+#include "file.hpp"
+
+BT_HIDDEN
+void ctf_fs_file_destroy(struct ctf_fs_file *file)
+{
+       if (!file) {
+               return;
+       }
+
+       if (file->fp) {
+               BT_COMP_LOGD("Closing file \"%s\" (%p)",
+                               file->path ? file->path->str : NULL, file->fp);
+
+               if (fclose(file->fp)) {
+                       BT_COMP_LOGE("Cannot close file \"%s\": %s",
+                                       file->path ? file->path->str : "NULL",
+                                       strerror(errno));
+               }
+       }
+
+       if (file->path) {
+               g_string_free(file->path, TRUE);
+       }
+
+       g_free(file);
+}
+
+BT_HIDDEN
+struct ctf_fs_file *ctf_fs_file_create(bt_logging_level log_level,
+               bt_self_component *self_comp)
+{
+       struct ctf_fs_file *file = g_new0(struct ctf_fs_file, 1);
+
+       if (!file) {
+               goto error;
+       }
+
+       file->log_level = log_level;
+       file->self_comp = self_comp;
+       file->path = g_string_new(NULL);
+       if (!file->path) {
+               goto error;
+       }
+
+       goto end;
+
+error:
+       ctf_fs_file_destroy(file);
+       file = NULL;
+
+end:
+       return file;
+}
+
+BT_HIDDEN
+int ctf_fs_file_open(struct ctf_fs_file *file, const char *mode)
+{
+       int ret = 0;
+       struct stat stat;
+
+       BT_COMP_LOGI("Opening file \"%s\" with mode \"%s\"", file->path->str, mode);
+       file->fp = fopen(file->path->str, mode);
+       if (!file->fp) {
+               BT_COMP_LOGE_APPEND_CAUSE_ERRNO(file->self_comp,
+                       "Cannot open file", ": path=%s, mode=%s",
+                       file->path->str, mode);
+               goto error;
+       }
+
+       BT_COMP_LOGI("Opened file: %p", file->fp);
+
+       if (fstat(fileno(file->fp), &stat)) {
+               BT_COMP_LOGE_APPEND_CAUSE_ERRNO(file->self_comp,
+                       "Cannot get file information",
+                       ": path=%s", file->path->str);
+               goto error;
+       }
+
+       file->size = stat.st_size;
+       BT_COMP_LOGI("File is %jd bytes", (intmax_t) file->size);
+       goto end;
+
+error:
+       ret = -1;
+
+       if (file->fp) {
+               if (fclose(file->fp)) {
+                       BT_COMP_LOGE("Cannot close file \"%s\": %s", file->path->str,
+                               strerror(errno));
+               }
+       }
+
+end:
+       return ret;
+}
diff --git a/src/plugins/ctf/fs-src/file.h b/src/plugins/ctf/fs-src/file.h
deleted file mode 100644 (file)
index c127e6c..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright (C) 2016 Philippe Proulx <pproulx@efficios.com>
- */
-
-#ifndef CTF_FS_FILE_H
-#define CTF_FS_FILE_H
-
-#include <stdio.h>
-#include <glib.h>
-#include "common/macros.h"
-#include "fs.h"
-
-BT_HIDDEN
-void ctf_fs_file_destroy(struct ctf_fs_file *file);
-
-BT_HIDDEN
-struct ctf_fs_file *ctf_fs_file_create(bt_logging_level log_level,
-               bt_self_component *self_comp);
-
-BT_HIDDEN
-int ctf_fs_file_open(struct ctf_fs_file *file, const char *mode);
-
-#endif /* CTF_FS_FILE_H */
diff --git a/src/plugins/ctf/fs-src/file.hpp b/src/plugins/ctf/fs-src/file.hpp
new file mode 100644 (file)
index 0000000..e828423
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2016 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#ifndef CTF_FS_FILE_H
+#define CTF_FS_FILE_H
+
+#include <stdio.h>
+#include <glib.h>
+#include "common/macros.h"
+#include "fs.hpp"
+
+BT_HIDDEN
+void ctf_fs_file_destroy(struct ctf_fs_file *file);
+
+BT_HIDDEN
+struct ctf_fs_file *ctf_fs_file_create(bt_logging_level log_level,
+               bt_self_component *self_comp);
+
+BT_HIDDEN
+int ctf_fs_file_open(struct ctf_fs_file *file, const char *mode);
+
+#endif /* CTF_FS_FILE_H */
diff --git a/src/plugins/ctf/fs-src/fs.c b/src/plugins/ctf/fs-src/fs.c
deleted file mode 100644 (file)
index f3104b8..0000000
+++ /dev/null
@@ -1,2487 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2015-2017 Philippe Proulx <pproulx@efficios.com>
- * Copyright 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * Babeltrace CTF file system Reader Component
- */
-
-#define BT_COMP_LOG_SELF_COMP self_comp
-#define BT_LOG_OUTPUT_LEVEL log_level
-#define BT_LOG_TAG "PLUGIN/SRC.CTF.FS"
-#include "logging/comp-logging.h"
-
-#include "common/common.h"
-#include <babeltrace2/babeltrace.h>
-#include "common/uuid.h"
-#include <glib.h>
-#include "common/assert.h"
-#include <inttypes.h>
-#include <stdbool.h>
-#include "fs.h"
-#include "metadata.h"
-#include "data-stream-file.h"
-#include "file.h"
-#include "../common/metadata/decoder.h"
-#include "../common/metadata/ctf-meta-configure-ir-trace.h"
-#include "../common/msg-iter/msg-iter.h"
-#include "query.h"
-#include "plugins/common/param-validation/param-validation.h"
-
-struct tracer_info {
-       const char *name;
-       int64_t major;
-       int64_t minor;
-       int64_t patch;
-};
-
-static
-void ctf_fs_msg_iter_data_destroy(
-               struct ctf_fs_msg_iter_data *msg_iter_data)
-{
-       if (!msg_iter_data) {
-               return;
-       }
-
-       if (msg_iter_data->msg_iter) {
-               ctf_msg_iter_destroy(msg_iter_data->msg_iter);
-       }
-
-       if (msg_iter_data->msg_iter_medops_data) {
-               ctf_fs_ds_group_medops_data_destroy(
-                       msg_iter_data->msg_iter_medops_data);
-       }
-
-       g_free(msg_iter_data);
-}
-
-static
-bt_message_iterator_class_next_method_status ctf_fs_iterator_next_one(
-               struct ctf_fs_msg_iter_data *msg_iter_data,
-               const bt_message **out_msg)
-{
-       bt_message_iterator_class_next_method_status status;
-       enum ctf_msg_iter_status msg_iter_status;
-       bt_logging_level log_level = msg_iter_data->log_level;
-
-       msg_iter_status = ctf_msg_iter_get_next_message(
-               msg_iter_data->msg_iter, out_msg);
-
-       switch (msg_iter_status) {
-       case CTF_MSG_ITER_STATUS_OK:
-               /* Cool, message has been written to *out_msg. */
-               status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK;
-               break;
-
-       case CTF_MSG_ITER_STATUS_EOF:
-               status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_END;
-               break;
-
-       case CTF_MSG_ITER_STATUS_AGAIN:
-               /*
-                * Should not make it this far as this is
-                * medium-specific; there is nothing for the user to do
-                * and it should have been handled upstream.
-                */
-               bt_common_abort();
-
-       case CTF_MSG_ITER_STATUS_ERROR:
-               BT_MSG_ITER_LOGE_APPEND_CAUSE(msg_iter_data->self_msg_iter,
-                       "Failed to get next message from CTF message iterator.");
-               status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR;
-               break;
-
-       case CTF_MSG_ITER_STATUS_MEMORY_ERROR:
-               BT_MSG_ITER_LOGE_APPEND_CAUSE(msg_iter_data->self_msg_iter,
-                       "Failed to get next message from CTF message iterator.");
-               status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_MEMORY_ERROR;
-               break;
-
-       default:
-               bt_common_abort();
-       }
-
-       return status;
-}
-
-BT_HIDDEN
-bt_message_iterator_class_next_method_status ctf_fs_iterator_next(
-               bt_self_message_iterator *iterator,
-               bt_message_array_const msgs, uint64_t capacity,
-               uint64_t *count)
-{
-       bt_message_iterator_class_next_method_status status;
-       struct ctf_fs_msg_iter_data *msg_iter_data =
-               bt_self_message_iterator_get_data(iterator);
-       uint64_t i = 0;
-
-       if (G_UNLIKELY(msg_iter_data->next_saved_error)) {
-               /*
-                * Last time we were called, we hit an error but had some
-                * messages to deliver, so we stashed the error here.  Return
-                * it now.
-                */
-               BT_CURRENT_THREAD_MOVE_ERROR_AND_RESET(msg_iter_data->next_saved_error);
-               status = msg_iter_data->next_saved_status;
-               goto end;
-       }
-
-       do {
-               status = ctf_fs_iterator_next_one(msg_iter_data, &msgs[i]);
-               if (status == BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK) {
-                       i++;
-               }
-       } while (i < capacity &&
-                       status == BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK);
-
-       if (i > 0) {
-               /*
-                * Even if ctf_fs_iterator_next_one() returned something
-                * else than BT_MESSAGE_ITERATOR_NEXT_METHOD_STATUS_OK, we
-                * accumulated message objects in the output
-                * message array, so we need to return
-                * BT_MESSAGE_ITERATOR_NEXT_METHOD_STATUS_OK so that they are
-                * transfered to downstream. This other status occurs
-                * again the next time muxer_msg_iter_do_next() is
-                * called, possibly without any accumulated
-                * message, in which case we'll return it.
-                */
-               if (status < 0) {
-                       /*
-                        * Save this error for the next _next call.  Assume that
-                        * this component always appends error causes when
-                        * returning an error status code, which will cause the
-                        * current thread error to be non-NULL.
-                        */
-                       msg_iter_data->next_saved_error = bt_current_thread_take_error();
-                       BT_ASSERT(msg_iter_data->next_saved_error);
-                       msg_iter_data->next_saved_status = status;
-               }
-
-               *count = i;
-               status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK;
-       }
-
-end:
-       return status;
-}
-
-BT_HIDDEN
-bt_message_iterator_class_seek_beginning_method_status
-ctf_fs_iterator_seek_beginning(bt_self_message_iterator *it)
-{
-       struct ctf_fs_msg_iter_data *msg_iter_data =
-               bt_self_message_iterator_get_data(it);
-
-       BT_ASSERT(msg_iter_data);
-
-       ctf_msg_iter_reset(msg_iter_data->msg_iter);
-       ctf_fs_ds_group_medops_data_reset(msg_iter_data->msg_iter_medops_data);
-
-       return BT_MESSAGE_ITERATOR_CLASS_SEEK_BEGINNING_METHOD_STATUS_OK;
-}
-
-BT_HIDDEN
-void ctf_fs_iterator_finalize(bt_self_message_iterator *it)
-{
-       ctf_fs_msg_iter_data_destroy(
-               bt_self_message_iterator_get_data(it));
-}
-
-static
-bt_message_iterator_class_initialize_method_status
-ctf_msg_iter_medium_status_to_msg_iter_initialize_status
-       (enum ctf_msg_iter_medium_status status)
-{
-       switch (status) {
-       case CTF_MSG_ITER_MEDIUM_STATUS_EOF:
-       case CTF_MSG_ITER_MEDIUM_STATUS_AGAIN:
-       case CTF_MSG_ITER_MEDIUM_STATUS_ERROR:
-               return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
-       case CTF_MSG_ITER_MEDIUM_STATUS_MEMORY_ERROR:
-               return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
-       case CTF_MSG_ITER_MEDIUM_STATUS_OK:
-               return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_OK;
-       }
-
-       bt_common_abort();
-}
-
-BT_HIDDEN
-bt_message_iterator_class_initialize_method_status ctf_fs_iterator_init(
-               bt_self_message_iterator *self_msg_iter,
-               bt_self_message_iterator_configuration *config,
-               bt_self_component_port_output *self_port)
-{
-       struct ctf_fs_port_data *port_data;
-       struct ctf_fs_msg_iter_data *msg_iter_data = NULL;
-       bt_message_iterator_class_initialize_method_status status;
-       bt_logging_level log_level;
-       enum ctf_msg_iter_medium_status medium_status;
-       bt_self_component *self_comp =
-               bt_self_message_iterator_borrow_component(self_msg_iter);
-
-       port_data = bt_self_component_port_get_data(
-               bt_self_component_port_output_as_self_component_port(
-                       self_port));
-       BT_ASSERT(port_data);
-       log_level = port_data->ctf_fs->log_level;
-       msg_iter_data = g_new0(struct ctf_fs_msg_iter_data, 1);
-       if (!msg_iter_data) {
-               status = BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
-               goto error;
-       }
-
-       msg_iter_data->log_level = log_level;
-       msg_iter_data->self_comp = self_comp;
-       msg_iter_data->self_msg_iter = self_msg_iter;
-       msg_iter_data->ds_file_group = port_data->ds_file_group;
-
-       medium_status = ctf_fs_ds_group_medops_data_create(
-               msg_iter_data->ds_file_group, self_msg_iter, log_level,
-               &msg_iter_data->msg_iter_medops_data);
-       BT_ASSERT(
-               medium_status == CTF_MSG_ITER_MEDIUM_STATUS_OK ||
-               medium_status == CTF_MSG_ITER_MEDIUM_STATUS_ERROR ||
-               medium_status == CTF_MSG_ITER_MEDIUM_STATUS_MEMORY_ERROR);
-       if (medium_status != CTF_MSG_ITER_MEDIUM_STATUS_OK) {
-               BT_MSG_ITER_LOGE_APPEND_CAUSE(self_msg_iter,
-                       "Failed to create ctf_fs_ds_group_medops");
-               status = ctf_msg_iter_medium_status_to_msg_iter_initialize_status(medium_status);
-               goto error;
-       }
-
-       msg_iter_data->msg_iter = ctf_msg_iter_create(
-               msg_iter_data->ds_file_group->ctf_fs_trace->metadata->tc,
-               bt_common_get_page_size(msg_iter_data->log_level) * 8,
-               ctf_fs_ds_group_medops,
-               msg_iter_data->msg_iter_medops_data,
-               msg_iter_data->log_level,
-               self_comp, self_msg_iter);
-       if (!msg_iter_data->msg_iter) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Cannot create a CTF message iterator.");
-               status = BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
-               goto error;
-       }
-
-       /*
-        * This iterator can seek forward if its stream class has a default
-        * clock class.
-        */
-       if (msg_iter_data->ds_file_group->sc->default_clock_class) {
-               bt_self_message_iterator_configuration_set_can_seek_forward(
-                       config, true);
-       }
-
-       bt_self_message_iterator_set_data(self_msg_iter,
-               msg_iter_data);
-       msg_iter_data = NULL;
-
-       status = BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_OK;
-       goto end;
-
-error:
-       bt_self_message_iterator_set_data(self_msg_iter, NULL);
-
-end:
-       ctf_fs_msg_iter_data_destroy(msg_iter_data);
-       return status;
-}
-
-static
-void ctf_fs_trace_destroy(struct ctf_fs_trace *ctf_fs_trace)
-{
-       if (!ctf_fs_trace) {
-               return;
-       }
-
-       if (ctf_fs_trace->ds_file_groups) {
-               g_ptr_array_free(ctf_fs_trace->ds_file_groups, TRUE);
-       }
-
-       BT_TRACE_PUT_REF_AND_RESET(ctf_fs_trace->trace);
-
-       if (ctf_fs_trace->path) {
-               g_string_free(ctf_fs_trace->path, TRUE);
-       }
-
-       if (ctf_fs_trace->metadata) {
-               ctf_fs_metadata_fini(ctf_fs_trace->metadata);
-               g_free(ctf_fs_trace->metadata);
-       }
-
-       g_free(ctf_fs_trace);
-}
-
-BT_HIDDEN
-void ctf_fs_destroy(struct ctf_fs_component *ctf_fs)
-{
-       if (!ctf_fs) {
-               return;
-       }
-
-       ctf_fs_trace_destroy(ctf_fs->trace);
-
-       if (ctf_fs->port_data) {
-               g_ptr_array_free(ctf_fs->port_data, TRUE);
-       }
-
-       g_free(ctf_fs);
-}
-
-static
-void port_data_destroy(struct ctf_fs_port_data *port_data)
-{
-       if (!port_data) {
-               return;
-       }
-
-       g_free(port_data);
-}
-
-static
-void port_data_destroy_notifier(void *data) {
-       port_data_destroy(data);
-}
-
-static
-void ctf_fs_trace_destroy_notifier(void *data)
-{
-       struct ctf_fs_trace *trace = data;
-       ctf_fs_trace_destroy(trace);
-}
-
-struct ctf_fs_component *ctf_fs_component_create(bt_logging_level log_level,
-               bt_self_component *self_comp)
-{
-       struct ctf_fs_component *ctf_fs;
-
-       ctf_fs = g_new0(struct ctf_fs_component, 1);
-       if (!ctf_fs) {
-               goto error;
-       }
-
-       ctf_fs->log_level = log_level;
-       ctf_fs->port_data =
-               g_ptr_array_new_with_free_func(port_data_destroy_notifier);
-       if (!ctf_fs->port_data) {
-               goto error;
-       }
-
-       goto end;
-
-error:
-       ctf_fs_destroy(ctf_fs);
-       ctf_fs = NULL;
-
-end:
-       return ctf_fs;
-}
-
-void ctf_fs_finalize(bt_self_component_source *component)
-{
-       ctf_fs_destroy(bt_self_component_get_data(
-               bt_self_component_source_as_self_component(component)));
-}
-
-gchar *ctf_fs_make_port_name(struct ctf_fs_ds_file_group *ds_file_group)
-{
-       GString *name = g_string_new(NULL);
-
-       /*
-        * The unique port name is generated by concatenating unique identifiers
-        * for:
-        *
-        *   - the trace
-        *   - the stream class
-        *   - the stream
-        */
-
-       /* For the trace, use the uuid if present, else the path. */
-       if (ds_file_group->ctf_fs_trace->metadata->tc->is_uuid_set) {
-               char uuid_str[BT_UUID_STR_LEN + 1];
-
-               bt_uuid_to_str(ds_file_group->ctf_fs_trace->metadata->tc->uuid, uuid_str);
-               g_string_assign(name, uuid_str);
-       } else {
-               g_string_assign(name, ds_file_group->ctf_fs_trace->path->str);
-       }
-
-       /*
-        * For the stream class, use the id if present.  We can omit this field
-        * otherwise, as there will only be a single stream class.
-        */
-       if (ds_file_group->sc->id != UINT64_C(-1)) {
-               g_string_append_printf(name, " | %" PRIu64, ds_file_group->sc->id);
-       }
-
-       /* For the stream, use the id if present, else, use the path. */
-       if (ds_file_group->stream_id != UINT64_C(-1)) {
-               g_string_append_printf(name, " | %" PRIu64, ds_file_group->stream_id);
-       } else {
-               BT_ASSERT(ds_file_group->ds_file_infos->len == 1);
-               struct ctf_fs_ds_file_info *ds_file_info =
-                       g_ptr_array_index(ds_file_group->ds_file_infos, 0);
-               g_string_append_printf(name, " | %s", ds_file_info->path->str);
-       }
-
-       return g_string_free(name, FALSE);
-}
-
-static
-int create_one_port_for_trace(struct ctf_fs_component *ctf_fs,
-               struct ctf_fs_trace *ctf_fs_trace,
-               struct ctf_fs_ds_file_group *ds_file_group,
-               bt_self_component_source *self_comp_src)
-{
-       int ret = 0;
-       struct ctf_fs_port_data *port_data = NULL;
-       gchar *port_name;
-       bt_logging_level log_level = ctf_fs->log_level;
-       bt_self_component *self_comp =
-               bt_self_component_source_as_self_component(self_comp_src);
-
-       port_name = ctf_fs_make_port_name(ds_file_group);
-       if (!port_name) {
-               goto error;
-       }
-
-       BT_COMP_LOGI("Creating one port named `%s`", port_name);
-
-       /* Create output port for this file */
-       port_data = g_new0(struct ctf_fs_port_data, 1);
-       if (!port_data) {
-               goto error;
-       }
-
-       port_data->ctf_fs = ctf_fs;
-       port_data->ds_file_group = ds_file_group;
-       ret = bt_self_component_source_add_output_port(
-               self_comp_src, port_name, port_data, NULL);
-       if (ret) {
-               goto error;
-       }
-
-       g_ptr_array_add(ctf_fs->port_data, port_data);
-       port_data = NULL;
-       goto end;
-
-error:
-       ret = -1;
-
-end:
-       g_free(port_name);
-
-       port_data_destroy(port_data);
-       return ret;
-}
-
-static
-int create_ports_for_trace(struct ctf_fs_component *ctf_fs,
-               struct ctf_fs_trace *ctf_fs_trace,
-               bt_self_component_source *self_comp_src)
-{
-       int ret = 0;
-       size_t i;
-       bt_logging_level log_level = ctf_fs_trace->log_level;
-       bt_self_component *self_comp =
-               bt_self_component_source_as_self_component(self_comp_src);
-
-       /* Create one output port for each stream file group */
-       for (i = 0; i < ctf_fs_trace->ds_file_groups->len; i++) {
-               struct ctf_fs_ds_file_group *ds_file_group =
-                       g_ptr_array_index(ctf_fs_trace->ds_file_groups, i);
-
-               ret = create_one_port_for_trace(ctf_fs, ctf_fs_trace,
-                       ds_file_group, self_comp_src);
-               if (ret) {
-                       BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Cannot create output port.");
-                       goto end;
-               }
-       }
-
-end:
-       return ret;
-}
-
-static
-void ctf_fs_ds_file_info_destroy(struct ctf_fs_ds_file_info *ds_file_info)
-{
-       if (!ds_file_info) {
-               return;
-       }
-
-       if (ds_file_info->path) {
-               g_string_free(ds_file_info->path, TRUE);
-       }
-
-       g_free(ds_file_info);
-}
-
-static
-struct ctf_fs_ds_file_info *ctf_fs_ds_file_info_create(const char *path,
-               int64_t begin_ns)
-{
-       struct ctf_fs_ds_file_info *ds_file_info;
-
-       ds_file_info = g_new0(struct ctf_fs_ds_file_info, 1);
-       if (!ds_file_info) {
-               goto end;
-       }
-
-       ds_file_info->path = g_string_new(path);
-       if (!ds_file_info->path) {
-               ctf_fs_ds_file_info_destroy(ds_file_info);
-               ds_file_info = NULL;
-               goto end;
-       }
-
-       ds_file_info->begin_ns = begin_ns;
-
-end:
-       return ds_file_info;
-}
-
-static
-void ctf_fs_ds_file_group_destroy(struct ctf_fs_ds_file_group *ds_file_group)
-{
-       if (!ds_file_group) {
-               return;
-       }
-
-       if (ds_file_group->ds_file_infos) {
-               g_ptr_array_free(ds_file_group->ds_file_infos, TRUE);
-       }
-
-       ctf_fs_ds_index_destroy(ds_file_group->index);
-
-       bt_stream_put_ref(ds_file_group->stream);
-       g_free(ds_file_group);
-}
-
-static
-struct ctf_fs_ds_file_group *ctf_fs_ds_file_group_create(
-               struct ctf_fs_trace *ctf_fs_trace,
-               struct ctf_stream_class *sc,
-               uint64_t stream_instance_id,
-               struct ctf_fs_ds_index *index)
-{
-       struct ctf_fs_ds_file_group *ds_file_group;
-
-       ds_file_group = g_new0(struct ctf_fs_ds_file_group, 1);
-       if (!ds_file_group) {
-               goto error;
-       }
-
-       ds_file_group->ds_file_infos = g_ptr_array_new_with_free_func(
-               (GDestroyNotify) ctf_fs_ds_file_info_destroy);
-       if (!ds_file_group->ds_file_infos) {
-               goto error;
-       }
-
-       ds_file_group->index = index;
-
-       ds_file_group->stream_id = stream_instance_id;
-       BT_ASSERT(sc);
-       ds_file_group->sc = sc;
-       ds_file_group->ctf_fs_trace = ctf_fs_trace;
-       goto end;
-
-error:
-       ctf_fs_ds_file_group_destroy(ds_file_group);
-       ctf_fs_ds_index_destroy(index);
-       ds_file_group = NULL;
-
-end:
-       return ds_file_group;
-}
-
-/* Replace by g_ptr_array_insert when we depend on glib >= 2.40. */
-static
-void array_insert(GPtrArray *array, gpointer element, size_t pos)
-{
-       size_t original_array_len = array->len;
-
-       /* Allocate an unused element at the end of the array. */
-       g_ptr_array_add(array, NULL);
-
-       /* If we are not inserting at the end, move the elements by one. */
-       if (pos < original_array_len) {
-               memmove(&(array->pdata[pos + 1]),
-                       &(array->pdata[pos]),
-                       (original_array_len - pos) * sizeof(gpointer));
-       }
-
-       /* Insert the value. */
-       array->pdata[pos] = element;
-}
-
-/*
- * Insert ds_file_info in ds_file_group's list of ds_file_infos at the right
- * place to keep it sorted.
- */
-
-static
-void ds_file_group_insert_ds_file_info_sorted(
-               struct ctf_fs_ds_file_group *ds_file_group,
-               struct ctf_fs_ds_file_info *ds_file_info)
-{
-       guint i;
-
-       /* Find the spot where to insert this ds_file_info. */
-       for (i = 0; i < ds_file_group->ds_file_infos->len; i++) {
-               struct ctf_fs_ds_file_info *other_ds_file_info =
-                       g_ptr_array_index(ds_file_group->ds_file_infos, i);
-
-               if (ds_file_info->begin_ns < other_ds_file_info->begin_ns) {
-                       break;
-               }
-       }
-
-       array_insert(ds_file_group->ds_file_infos, ds_file_info, i);
-}
-
-static
-bool ds_index_entries_equal(
-       const struct ctf_fs_ds_index_entry *left,
-       const struct ctf_fs_ds_index_entry *right)
-{
-       if (left->packet_size != right->packet_size) {
-               return false;
-       }
-
-       if (left->timestamp_begin != right->timestamp_begin) {
-               return false;
-       }
-
-       if (left->timestamp_end != right->timestamp_end) {
-               return false;
-       }
-
-       if (left->packet_seq_num != right->packet_seq_num) {
-               return false;
-       }
-
-       return true;
-}
-
-/*
- * Insert `entry` into `index`, without duplication.
- *
- * The entry is inserted only if there isn't an identical entry already.
- *
- * In any case, the ownership of `entry` is transferred to this function.  So if
- * the entry is not inserted, it is freed.
- */
-
-static
-void ds_index_insert_ds_index_entry_sorted(
-       struct ctf_fs_ds_index *index,
-       struct ctf_fs_ds_index_entry *entry)
-{
-       guint i;
-       struct ctf_fs_ds_index_entry *other_entry = NULL;
-
-       /* Find the spot where to insert this index entry. */
-       for (i = 0; i < index->entries->len; i++) {
-               other_entry = g_ptr_array_index(index->entries, i);
-
-               if (entry->timestamp_begin_ns <= other_entry->timestamp_begin_ns) {
-                       break;
-               }
-       }
-
-       /*
-        * Insert the entry only if a duplicate doesn't already exist.
-        *
-        * There can be duplicate packets if reading multiple overlapping
-        * snapshots of the same trace.  We then want the index to contain
-        * a reference to only one copy of that packet.
-        */
-       if (i == index->entries->len ||
-                       !ds_index_entries_equal(entry, other_entry)) {
-               array_insert(index->entries, entry, i);
-       } else {
-               g_free(entry);
-       }
-}
-
-static
-void merge_ctf_fs_ds_indexes(struct ctf_fs_ds_index *dest, struct ctf_fs_ds_index *src)
-{
-       guint i;
-
-       for (i = 0; i < src->entries->len; i++) {
-               struct ctf_fs_ds_index_entry *entry =
-                       g_ptr_array_index(src->entries, i);
-
-               /*
-               * Ownership of the ctf_fs_ds_index_entry is transferred to
-               * ds_index_insert_ds_index_entry_sorted.
-               */
-               g_ptr_array_index(src->entries, i) = NULL;
-               ds_index_insert_ds_index_entry_sorted(dest, entry);
-       }
-}
-
-static
-int add_ds_file_to_ds_file_group(struct ctf_fs_trace *ctf_fs_trace,
-               const char *path)
-{
-       int64_t stream_instance_id = -1;
-       int64_t begin_ns = -1;
-       struct ctf_fs_ds_file_group *ds_file_group = NULL;
-       bool add_group = false;
-       int ret;
-       size_t i;
-       struct ctf_fs_ds_file *ds_file = NULL;
-       struct ctf_fs_ds_file_info *ds_file_info = NULL;
-       struct ctf_fs_ds_index *index = NULL;
-       struct ctf_msg_iter *msg_iter = NULL;
-       struct ctf_stream_class *sc = NULL;
-       struct ctf_msg_iter_packet_properties props;
-       bt_logging_level log_level = ctf_fs_trace->log_level;
-       bt_self_component *self_comp = ctf_fs_trace->self_comp;
-       bt_self_component_class *self_comp_class = ctf_fs_trace->self_comp_class;
-
-       /*
-        * Create a temporary ds_file to read some properties about the data
-        * stream file.
-        */
-       ds_file = ctf_fs_ds_file_create(ctf_fs_trace, NULL, NULL, path,
-               log_level);
-       if (!ds_file) {
-               goto error;
-       }
-
-       /* Create a temporary iterator to read the ds_file. */
-       msg_iter = ctf_msg_iter_create(ctf_fs_trace->metadata->tc,
-               bt_common_get_page_size(log_level) * 8,
-               ctf_fs_ds_file_medops, ds_file, log_level, self_comp, NULL);
-       if (!msg_iter) {
-               BT_COMP_LOGE_STR("Cannot create a CTF message iterator.");
-               goto error;
-       }
-
-       ctf_msg_iter_set_dry_run(msg_iter, true);
-
-       ret = ctf_msg_iter_get_packet_properties(msg_iter, &props);
-       if (ret) {
-               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                       "Cannot get stream file's first packet's header and context fields (`%s`).",
-                       path);
-               goto error;
-       }
-
-       sc = ctf_trace_class_borrow_stream_class_by_id(ds_file->metadata->tc,
-               props.stream_class_id);
-       BT_ASSERT(sc);
-       stream_instance_id = props.data_stream_id;
-
-       if (props.snapshots.beginning_clock != UINT64_C(-1)) {
-               BT_ASSERT(sc->default_clock_class);
-               ret = bt_util_clock_cycles_to_ns_from_origin(
-                       props.snapshots.beginning_clock,
-                       sc->default_clock_class->frequency,
-                       sc->default_clock_class->offset_seconds,
-                       sc->default_clock_class->offset_cycles, &begin_ns);
-               if (ret) {
-                       BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                               "Cannot convert clock cycles to nanoseconds from origin (`%s`).",
-                               path);
-                       goto error;
-               }
-       }
-
-       ds_file_info = ctf_fs_ds_file_info_create(path, begin_ns);
-       if (!ds_file_info) {
-               goto error;
-       }
-
-       index = ctf_fs_ds_file_build_index(ds_file, ds_file_info, msg_iter);
-       if (!index) {
-               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                       self_comp, self_comp_class,
-                       "Failed to index CTF stream file \'%s\'",
-                       ds_file->file->path->str);
-               goto error;
-       }
-
-       if (begin_ns == -1) {
-               /*
-                * No beginning timestamp to sort the stream files
-                * within a stream file group, so consider that this
-                * file must be the only one within its group.
-                */
-               stream_instance_id = -1;
-       }
-
-       if (stream_instance_id == -1) {
-               /*
-                * No stream instance ID or no beginning timestamp:
-                * create a unique stream file group for this stream
-                * file because, even if there's a stream instance ID,
-                * there's no timestamp to order the file within its
-                * group.
-                */
-               ds_file_group = ctf_fs_ds_file_group_create(ctf_fs_trace,
-                       sc, UINT64_C(-1), index);
-               /* Ownership of index is transferred. */
-               index = NULL;
-
-               if (!ds_file_group) {
-                       goto error;
-               }
-
-               ds_file_group_insert_ds_file_info_sorted(ds_file_group,
-                       BT_MOVE_REF(ds_file_info));
-
-               add_group = true;
-               goto end;
-       }
-
-       BT_ASSERT(stream_instance_id != -1);
-       BT_ASSERT(begin_ns != -1);
-
-       /* Find an existing stream file group with this ID */
-       for (i = 0; i < ctf_fs_trace->ds_file_groups->len; i++) {
-               ds_file_group = g_ptr_array_index(
-                       ctf_fs_trace->ds_file_groups, i);
-
-               if (ds_file_group->sc == sc &&
-                               ds_file_group->stream_id ==
-                               stream_instance_id) {
-                       break;
-               }
-
-               ds_file_group = NULL;
-       }
-
-       if (!ds_file_group) {
-               ds_file_group = ctf_fs_ds_file_group_create(ctf_fs_trace,
-                       sc, stream_instance_id, index);
-               /* Ownership of index is transferred. */
-               index = NULL;
-               if (!ds_file_group) {
-                       goto error;
-               }
-
-               add_group = true;
-       } else {
-               merge_ctf_fs_ds_indexes(ds_file_group->index, index);
-       }
-
-       ds_file_group_insert_ds_file_info_sorted(ds_file_group,
-               BT_MOVE_REF(ds_file_info));
-
-       goto end;
-
-error:
-       ctf_fs_ds_file_group_destroy(ds_file_group);
-       ds_file_group = NULL;
-       ret = -1;
-
-end:
-       if (add_group && ds_file_group) {
-               g_ptr_array_add(ctf_fs_trace->ds_file_groups, ds_file_group);
-       }
-
-       ctf_fs_ds_file_destroy(ds_file);
-       ctf_fs_ds_file_info_destroy(ds_file_info);
-
-       if (msg_iter) {
-               ctf_msg_iter_destroy(msg_iter);
-       }
-
-       ctf_fs_ds_index_destroy(index);
-       return ret;
-}
-
-static
-int create_ds_file_groups(struct ctf_fs_trace *ctf_fs_trace)
-{
-       int ret = 0;
-       const char *basename;
-       GError *error = NULL;
-       GDir *dir = NULL;
-       bt_logging_level log_level = ctf_fs_trace->log_level;
-       bt_self_component *self_comp = ctf_fs_trace->self_comp;
-       bt_self_component_class *self_comp_class = ctf_fs_trace->self_comp_class;
-
-       /* Check each file in the path directory, except specific ones */
-       dir = g_dir_open(ctf_fs_trace->path->str, 0, &error);
-       if (!dir) {
-               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                       "Cannot open directory `%s`: %s (code %d)",
-                       ctf_fs_trace->path->str, error->message,
-                       error->code);
-               goto error;
-       }
-
-       while ((basename = g_dir_read_name(dir))) {
-               struct ctf_fs_file *file;
-
-               if (strcmp(basename, CTF_FS_METADATA_FILENAME) == 0) {
-                       /* Ignore the metadata stream. */
-                       BT_COMP_LOGI("Ignoring metadata file `%s" G_DIR_SEPARATOR_S "%s`",
-                               ctf_fs_trace->path->str, basename);
-                       continue;
-               }
-
-               if (basename[0] == '.') {
-                       BT_COMP_LOGI("Ignoring hidden file `%s" G_DIR_SEPARATOR_S "%s`",
-                               ctf_fs_trace->path->str, basename);
-                       continue;
-               }
-
-               /* Create the file. */
-               file = ctf_fs_file_create(log_level, self_comp);
-               if (!file) {
-                       BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                               "Cannot create stream file object for file `%s" G_DIR_SEPARATOR_S "%s`",
-                               ctf_fs_trace->path->str, basename);
-                       goto error;
-               }
-
-               /* Create full path string. */
-               g_string_append_printf(file->path, "%s" G_DIR_SEPARATOR_S "%s",
-                               ctf_fs_trace->path->str, basename);
-               if (!g_file_test(file->path->str, G_FILE_TEST_IS_REGULAR)) {
-                       BT_COMP_LOGI("Ignoring non-regular file `%s`",
-                               file->path->str);
-                       ctf_fs_file_destroy(file);
-                       file = NULL;
-                       continue;
-               }
-
-               ret = ctf_fs_file_open(file, "rb");
-               if (ret) {
-                       BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                               "Cannot open stream file `%s`",
-                               file->path->str);
-                       goto error;
-               }
-
-               if (file->size == 0) {
-                       /* Skip empty stream. */
-                       BT_COMP_LOGI("Ignoring empty file `%s`", file->path->str);
-                       ctf_fs_file_destroy(file);
-                       continue;
-               }
-
-               ret = add_ds_file_to_ds_file_group(ctf_fs_trace,
-                       file->path->str);
-               if (ret) {
-                       BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                               "Cannot add stream file `%s` to stream file group",
-                               file->path->str);
-                       ctf_fs_file_destroy(file);
-                       goto error;
-               }
-
-               ctf_fs_file_destroy(file);
-       }
-
-       goto end;
-
-error:
-       ret = -1;
-
-end:
-       if (dir) {
-               g_dir_close(dir);
-               dir = NULL;
-       }
-
-       if (error) {
-               g_error_free(error);
-       }
-
-       return ret;
-}
-
-static
-int set_trace_name(bt_trace *trace, const char *name_suffix,
-               bt_logging_level log_level, bt_self_component *self_comp)
-{
-       int ret = 0;
-       const bt_value *val;
-       GString *name;
-
-       name = g_string_new(NULL);
-       if (!name) {
-               BT_COMP_LOGE_STR("Failed to allocate a GString.");
-               ret = -1;
-               goto end;
-       }
-
-       /*
-        * Check if we have a trace environment string value named `hostname`.
-        * If so, use it as the trace name's prefix.
-        */
-       val = bt_trace_borrow_environment_entry_value_by_name_const(
-               trace, "hostname");
-       if (val && bt_value_is_string(val)) {
-               g_string_append(name, bt_value_string_get(val));
-
-               if (name_suffix) {
-                       g_string_append_c(name, G_DIR_SEPARATOR);
-               }
-       }
-
-       if (name_suffix) {
-               g_string_append(name, name_suffix);
-       }
-
-       ret = bt_trace_set_name(trace, name->str);
-       if (ret) {
-               goto end;
-       }
-
-       goto end;
-
-end:
-       if (name) {
-               g_string_free(name, TRUE);
-       }
-
-       return ret;
-}
-
-static
-struct ctf_fs_trace *ctf_fs_trace_create(
-               bt_self_component *self_comp,
-               bt_self_component_class *self_comp_class,
-               const char *path, const char *name,
-               struct ctf_fs_metadata_config *metadata_config,
-               bt_logging_level log_level)
-{
-       struct ctf_fs_trace *ctf_fs_trace;
-       int ret;
-
-       /* Only one of them must be set. */
-       BT_ASSERT(!self_comp != !self_comp_class);
-
-       ctf_fs_trace = g_new0(struct ctf_fs_trace, 1);
-       if (!ctf_fs_trace) {
-               goto end;
-       }
-
-       ctf_fs_trace->log_level = log_level;
-       ctf_fs_trace->self_comp = self_comp;
-       ctf_fs_trace->self_comp_class = self_comp_class;
-       ctf_fs_trace->path = g_string_new(path);
-       if (!ctf_fs_trace->path) {
-               goto error;
-       }
-
-       ctf_fs_trace->metadata = g_new0(struct ctf_fs_metadata, 1);
-       if (!ctf_fs_trace->metadata) {
-               goto error;
-       }
-
-       ctf_fs_metadata_init(ctf_fs_trace->metadata);
-       ctf_fs_trace->ds_file_groups = g_ptr_array_new_with_free_func(
-               (GDestroyNotify) ctf_fs_ds_file_group_destroy);
-       if (!ctf_fs_trace->ds_file_groups) {
-               goto error;
-       }
-
-       ret = ctf_fs_metadata_set_trace_class(self_comp, ctf_fs_trace,
-               metadata_config);
-       if (ret) {
-               goto error;
-       }
-
-       if (ctf_fs_trace->metadata->trace_class) {
-               ctf_fs_trace->trace =
-                       bt_trace_create(ctf_fs_trace->metadata->trace_class);
-               if (!ctf_fs_trace->trace) {
-                       goto error;
-               }
-       }
-
-       if (ctf_fs_trace->trace) {
-               ret = ctf_trace_class_configure_ir_trace(
-                       ctf_fs_trace->metadata->tc, ctf_fs_trace->trace);
-               if (ret) {
-                       goto error;
-               }
-
-               ret = set_trace_name(ctf_fs_trace->trace, name, log_level,
-                       self_comp);
-               if (ret) {
-                       goto error;
-               }
-       }
-
-       ret = create_ds_file_groups(ctf_fs_trace);
-       if (ret) {
-               goto error;
-       }
-
-       goto end;
-
-error:
-       ctf_fs_trace_destroy(ctf_fs_trace);
-       ctf_fs_trace = NULL;
-
-end:
-       return ctf_fs_trace;
-}
-
-static
-int path_is_ctf_trace(const char *path)
-{
-       GString *metadata_path = g_string_new(NULL);
-       int ret = 0;
-
-       if (!metadata_path) {
-               ret = -1;
-               goto end;
-       }
-
-       g_string_printf(metadata_path, "%s" G_DIR_SEPARATOR_S "%s", path, CTF_FS_METADATA_FILENAME);
-
-       if (g_file_test(metadata_path->str, G_FILE_TEST_IS_REGULAR)) {
-               ret = 1;
-               goto end;
-       }
-
-end:
-       g_string_free(metadata_path, TRUE);
-       return ret;
-}
-
-/* Helper for ctf_fs_component_create_ctf_fs_trace, to handle a single path. */
-
-static
-int ctf_fs_component_create_ctf_fs_trace_one_path(
-               struct ctf_fs_component *ctf_fs,
-               const char *path_param,
-               const char *trace_name,
-               GPtrArray *traces,
-               bt_self_component *self_comp,
-               bt_self_component_class *self_comp_class)
-{
-       struct ctf_fs_trace *ctf_fs_trace;
-       int ret;
-       GString *norm_path;
-       bt_logging_level log_level = ctf_fs->log_level;
-
-       norm_path = bt_common_normalize_path(path_param, NULL);
-       if (!norm_path) {
-               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                       "Failed to normalize path: `%s`.",
-                       path_param);
-               goto error;
-       }
-
-       ret = path_is_ctf_trace(norm_path->str);
-       if (ret < 0) {
-               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                       "Failed to check if path is a CTF trace: path=%s",
-                       norm_path->str);
-               goto error;
-       } else if (ret == 0) {
-               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                       "Path is not a CTF trace (does not contain a metadata file): `%s`.",
-                       norm_path->str);
-               goto error;
-       }
-
-       // FIXME: Remove or ifdef for __MINGW32__
-       if (strcmp(norm_path->str, "/") == 0) {
-               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                       "Opening a trace in `/` is not supported.");
-               ret = -1;
-               goto end;
-       }
-
-       ctf_fs_trace = ctf_fs_trace_create(self_comp, self_comp_class, norm_path->str,
-               trace_name, &ctf_fs->metadata_config, log_level);
-       if (!ctf_fs_trace) {
-               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                       "Cannot create trace for `%s`.",
-                       norm_path->str);
-               goto error;
-       }
-
-       g_ptr_array_add(traces, ctf_fs_trace);
-       ctf_fs_trace = NULL;
-
-       ret = 0;
-       goto end;
-
-error:
-       ret = -1;
-
-end:
-       if (norm_path) {
-               g_string_free(norm_path, TRUE);
-       }
-
-       return ret;
-}
-
-/*
- * Count the number of stream and event classes defined by this trace's metadata.
- *
- * This is used to determine which metadata is the "latest", out of multiple
- * traces sharing the same UUID.  It is assumed that amongst all these metadatas,
- * a bigger metadata is a superset of a smaller metadata.  Therefore, it is
- * enough to just count the classes.
- */
-
-static
-unsigned int metadata_count_stream_and_event_classes(struct ctf_fs_trace *trace)
-{
-       unsigned int num = trace->metadata->tc->stream_classes->len;
-       guint i;
-
-       for (i = 0; i < trace->metadata->tc->stream_classes->len; i++) {
-               struct ctf_stream_class *sc = trace->metadata->tc->stream_classes->pdata[i];
-               num += sc->event_classes->len;
-       }
-
-       return num;
-}
-
-/*
- * Merge the src ds_file_group into dest.  This consists of merging their
- * ds_file_infos, making sure to keep the result sorted.
- */
-
-static
-void merge_ctf_fs_ds_file_groups(struct ctf_fs_ds_file_group *dest, struct ctf_fs_ds_file_group *src)
-{
-       guint i;
-
-       for (i = 0; i < src->ds_file_infos->len; i++) {
-               struct ctf_fs_ds_file_info *ds_file_info =
-                       g_ptr_array_index(src->ds_file_infos, i);
-
-               /* Ownership of the ds_file_info is transferred to dest. */
-               g_ptr_array_index(src->ds_file_infos, i) = NULL;
-
-               ds_file_group_insert_ds_file_info_sorted(dest, ds_file_info);
-       }
-
-       /* Merge both indexes. */
-       merge_ctf_fs_ds_indexes(dest->index, src->index);
-}
-/* Merge src_trace's data stream file groups into dest_trace's. */
-
-static
-int merge_matching_ctf_fs_ds_file_groups(
-               struct ctf_fs_trace *dest_trace,
-               struct ctf_fs_trace *src_trace)
-{
-
-       GPtrArray *dest = dest_trace->ds_file_groups;
-       GPtrArray *src = src_trace->ds_file_groups;
-       guint s_i;
-       int ret = 0;
-
-       /*
-        * Save the initial length of dest: we only want to check against the
-        * original elements in the inner loop.
-        */
-       const guint dest_len = dest->len;
-
-       for (s_i = 0; s_i < src->len; s_i++) {
-               struct ctf_fs_ds_file_group *src_group = g_ptr_array_index(src, s_i);
-               struct ctf_fs_ds_file_group *dest_group = NULL;
-
-               /* A stream instance without ID can't match a stream in the other trace.  */
-               if (src_group->stream_id != -1) {
-                       guint d_i;
-
-                       /* Let's search for a matching ds_file_group in the destination.  */
-                       for (d_i = 0; d_i < dest_len; d_i++) {
-                               struct ctf_fs_ds_file_group *candidate_dest = g_ptr_array_index(dest, d_i);
-
-                               /* Can't match a stream instance without ID.  */
-                               if (candidate_dest->stream_id == -1) {
-                                       continue;
-                               }
-
-                               /*
-                                * If the two groups have the same stream instance id
-                                * and belong to the same stream class (stream instance
-                                * ids are per-stream class), they represent the same
-                                * stream instance.
-                                */
-                               if (candidate_dest->stream_id != src_group->stream_id ||
-                                               candidate_dest->sc->id != src_group->sc->id) {
-                                       continue;
-                               }
-
-                               dest_group = candidate_dest;
-                               break;
-                       }
-               }
-
-               /*
-                * Didn't find a friend in dest to merge our src_group into?
-                * Create a new empty one. This can happen if a stream was
-                * active in the source trace chunk but not in the destination
-                * trace chunk.
-                */
-               if (!dest_group) {
-                       struct ctf_stream_class *sc;
-                       struct ctf_fs_ds_index *index;
-
-                       sc = ctf_trace_class_borrow_stream_class_by_id(
-                               dest_trace->metadata->tc, src_group->sc->id);
-                       BT_ASSERT(sc);
-
-                       index = ctf_fs_ds_index_create(dest_trace->log_level,
-                               dest_trace->self_comp);
-                       if (!index) {
-                               ret = -1;
-                               goto end;
-                       }
-
-                       dest_group = ctf_fs_ds_file_group_create(dest_trace, sc,
-                               src_group->stream_id, index);
-                       /* Ownership of index is transferred. */
-                       index = NULL;
-                       if (!dest_group) {
-                               ret = -1;
-                               goto end;
-                       }
-
-                       g_ptr_array_add(dest_trace->ds_file_groups, dest_group);
-               }
-
-               BT_ASSERT(dest_group);
-               merge_ctf_fs_ds_file_groups(dest_group, src_group);
-       }
-
-end:
-       return ret;
-}
-
-/*
- * Collapse the given traces, which must all share the same UUID, in a single
- * one.
- *
- * The trace with the most expansive metadata is chosen and all other traces
- * are merged into that one.  The array slots of all the traces that get merged
- * in the chosen one are set to NULL, so only the slot of the chosen trace
- * remains non-NULL.
- */
-
-static
-int merge_ctf_fs_traces(struct ctf_fs_trace **traces, unsigned int num_traces,
-               struct ctf_fs_trace **out_trace)
-{
-       unsigned int winner_count;
-       struct ctf_fs_trace *winner;
-       guint i, winner_i;
-       int ret = 0;
-
-       BT_ASSERT(num_traces >= 2);
-
-       winner_count = metadata_count_stream_and_event_classes(traces[0]);
-       winner = traces[0];
-       winner_i = 0;
-
-       /* Find the trace with the largest metadata. */
-       for (i = 1; i < num_traces; i++) {
-               struct ctf_fs_trace *candidate;
-               unsigned int candidate_count;
-
-               candidate = traces[i];
-
-               /* A bit of sanity check. */
-               BT_ASSERT(bt_uuid_compare(winner->metadata->tc->uuid, candidate->metadata->tc->uuid) == 0);
-
-               candidate_count = metadata_count_stream_and_event_classes(candidate);
-
-               if (candidate_count > winner_count) {
-                       winner_count = candidate_count;
-                       winner = candidate;
-                       winner_i = i;
-               }
-       }
-
-       /* Merge all the other traces in the winning trace. */
-       for (i = 0; i < num_traces; i++) {
-               struct ctf_fs_trace *trace = traces[i];
-
-               /* Don't merge the winner into itself. */
-               if (trace == winner) {
-                       continue;
-               }
-
-               /* Merge trace's data stream file groups into winner's. */
-               ret = merge_matching_ctf_fs_ds_file_groups(winner, trace);
-               if (ret) {
-                       goto end;
-               }
-       }
-
-       /*
-        * Move the winner out of the array, into `*out_trace`.
-        */
-       *out_trace = winner;
-       traces[winner_i] = NULL;
-
-end:
-       return ret;
-}
-
-enum target_event {
-       FIRST_EVENT,
-       LAST_EVENT,
-};
-
-static
-int decode_clock_snapshot_after_event(struct ctf_fs_trace *ctf_fs_trace,
-       struct ctf_clock_class *default_cc,
-       struct ctf_fs_ds_index_entry *index_entry,
-       enum target_event target_event, uint64_t *cs, int64_t *ts_ns)
-{
-       enum ctf_msg_iter_status iter_status = CTF_MSG_ITER_STATUS_OK;
-       struct ctf_fs_ds_file *ds_file = NULL;
-       struct ctf_msg_iter *msg_iter = NULL;
-       bt_logging_level log_level = ctf_fs_trace->log_level;
-       bt_self_component *self_comp = ctf_fs_trace->self_comp;
-       int ret = 0;
-
-       BT_ASSERT(ctf_fs_trace);
-       BT_ASSERT(index_entry);
-       BT_ASSERT(index_entry->path);
-
-       ds_file = ctf_fs_ds_file_create(ctf_fs_trace, NULL,
-               NULL, index_entry->path, log_level);
-       if (!ds_file) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Failed to create a ctf_fs_ds_file");
-               ret = -1;
-               goto end;
-       }
-
-       BT_ASSERT(ctf_fs_trace->metadata);
-       BT_ASSERT(ctf_fs_trace->metadata->tc);
-
-       msg_iter = ctf_msg_iter_create(ctf_fs_trace->metadata->tc,
-               bt_common_get_page_size(log_level) * 8, ctf_fs_ds_file_medops,
-               ds_file, log_level, self_comp, NULL);
-       if (!msg_iter) {
-               /* ctf_msg_iter_create() logs errors. */
-               ret = -1;
-               goto end;
-       }
-
-       /*
-        * Turn on dry run mode to prevent the creation and usage of Babeltrace
-        * library objects (bt_field, bt_message_*, etc.).
-        */
-       ctf_msg_iter_set_dry_run(msg_iter, true);
-
-       /* Seek to the beginning of the target packet. */
-       iter_status = ctf_msg_iter_seek(msg_iter, index_entry->offset);
-       if (iter_status) {
-               /* ctf_msg_iter_seek() logs errors. */
-               ret = -1;
-               goto end;
-       }
-
-       switch (target_event) {
-       case FIRST_EVENT:
-               /*
-                * Start to decode the packet until we reach the end of
-                * the first event. To extract the first event's clock
-                * snapshot.
-                */
-               iter_status = ctf_msg_iter_curr_packet_first_event_clock_snapshot(
-                       msg_iter, cs);
-               break;
-       case LAST_EVENT:
-               /* Decode the packet to extract the last event's clock snapshot. */
-               iter_status = ctf_msg_iter_curr_packet_last_event_clock_snapshot(
-                       msg_iter, cs);
-               break;
-       default:
-               bt_common_abort();
-       }
-       if (iter_status) {
-               ret = -1;
-               goto end;
-       }
-
-       /* Convert clock snapshot to timestamp. */
-       ret = bt_util_clock_cycles_to_ns_from_origin(*cs,
-               default_cc->frequency, default_cc->offset_seconds,
-               default_cc->offset_cycles, ts_ns);
-       if (ret) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Failed to convert clock snapshot to timestamp");
-               goto end;
-       }
-
-end:
-       if (ds_file) {
-               ctf_fs_ds_file_destroy(ds_file);
-       }
-       if (msg_iter) {
-               ctf_msg_iter_destroy(msg_iter);
-       }
-
-       return ret;
-}
-
-static
-int decode_packet_first_event_timestamp(struct ctf_fs_trace *ctf_fs_trace,
-       struct ctf_clock_class *default_cc,
-       struct ctf_fs_ds_index_entry *index_entry, uint64_t *cs, int64_t *ts_ns)
-{
-       return decode_clock_snapshot_after_event(ctf_fs_trace, default_cc,
-               index_entry, FIRST_EVENT, cs, ts_ns);
-}
-
-static
-int decode_packet_last_event_timestamp(struct ctf_fs_trace *ctf_fs_trace,
-       struct ctf_clock_class *default_cc,
-       struct ctf_fs_ds_index_entry *index_entry, uint64_t *cs, int64_t *ts_ns)
-{
-       return decode_clock_snapshot_after_event(ctf_fs_trace, default_cc,
-               index_entry, LAST_EVENT, cs, ts_ns);
-}
-
-/*
- * Fix up packet index entries for lttng's "event-after-packet" bug.
- * Some buggy lttng tracer versions may emit events with a timestamp that is
- * larger (after) than the timestamp_end of the their packets.
- *
- * To fix up this erroneous data we do the following:
- *  1. If it's not the stream file's last packet: set the packet index entry's
- *     end time to the next packet's beginning time.
- *  2. If it's the stream file's last packet, set the packet index entry's end
- *     time to the packet's last event's time, if any, or to the packet's
- *     beginning time otherwise.
- *
- * Known buggy tracer versions:
- *  - before lttng-ust 2.11.0
- *  - before lttng-module 2.11.0
- *  - before lttng-module 2.10.10
- *  - before lttng-module 2.9.13
- */
-static
-int fix_index_lttng_event_after_packet_bug(struct ctf_fs_trace *trace)
-{
-       int ret = 0;
-       guint ds_file_group_i;
-       GPtrArray *ds_file_groups = trace->ds_file_groups;
-       bt_logging_level log_level = trace->log_level;
-
-       for (ds_file_group_i = 0; ds_file_group_i < ds_file_groups->len;
-                       ds_file_group_i++) {
-               guint entry_i;
-               struct ctf_clock_class *default_cc;
-               struct ctf_fs_ds_index_entry *last_entry;
-               struct ctf_fs_ds_index *index;
-
-               struct ctf_fs_ds_file_group *ds_file_group =
-                       g_ptr_array_index(ds_file_groups, ds_file_group_i);
-
-               BT_ASSERT(ds_file_group);
-               index = ds_file_group->index;
-
-               BT_ASSERT(index);
-               BT_ASSERT(index->entries);
-               BT_ASSERT(index->entries->len > 0);
-
-               /*
-                * Iterate over all entries but the last one. The last one is
-                * fixed differently after.
-                */
-               for (entry_i = 0; entry_i < index->entries->len - 1;
-                               entry_i++) {
-                       struct ctf_fs_ds_index_entry *curr_entry, *next_entry;
-
-                       curr_entry = g_ptr_array_index(index->entries, entry_i);
-                       next_entry = g_ptr_array_index(index->entries, entry_i + 1);
-
-                       /*
-                        * 1. Set the current index entry `end` timestamp to
-                        * the next index entry `begin` timestamp.
-                        */
-                       curr_entry->timestamp_end = next_entry->timestamp_begin;
-                       curr_entry->timestamp_end_ns = next_entry->timestamp_begin_ns;
-               }
-
-               /*
-                * 2. Fix the last entry by decoding the last event of the last
-                * packet.
-                */
-               last_entry = g_ptr_array_index(index->entries,
-                       index->entries->len - 1);
-               BT_ASSERT(last_entry);
-
-               BT_ASSERT(ds_file_group->sc->default_clock_class);
-               default_cc = ds_file_group->sc->default_clock_class;
-
-               /*
-                * Decode packet to read the timestamp of the last event of the
-                * entry.
-                */
-               ret = decode_packet_last_event_timestamp(trace, default_cc,
-                       last_entry, &last_entry->timestamp_end,
-                       &last_entry->timestamp_end_ns);
-               if (ret) {
-                       BT_COMP_LOGE_APPEND_CAUSE(trace->self_comp,
-                               "Failed to decode stream's last packet to get its last event's clock snapshot.");
-                       goto end;
-               }
-       }
-
-end:
-       return ret;
-}
-
-/*
- * Fix up packet index entries for barectf's "event-before-packet" bug.
- * Some buggy barectf tracer versions may emit events with a timestamp that is
- * less than the timestamp_begin of the their packets.
- *
- * To fix up this erroneous data we do the following:
- *  1. Starting at the second index entry, set the timestamp_begin of the
- *     current entry to the timestamp of the first event of the packet.
- *  2. Set the previous entry's timestamp_end to the timestamp_begin of the
- *     current packet.
- *
- * Known buggy tracer versions:
- *  - before barectf 2.3.1
- */
-static
-int fix_index_barectf_event_before_packet_bug(struct ctf_fs_trace *trace)
-{
-       int ret = 0;
-       guint ds_file_group_i;
-       GPtrArray *ds_file_groups = trace->ds_file_groups;
-       bt_logging_level log_level = trace->log_level;
-
-       for (ds_file_group_i = 0; ds_file_group_i < ds_file_groups->len;
-                       ds_file_group_i++) {
-               guint entry_i;
-               struct ctf_clock_class *default_cc;
-               struct ctf_fs_ds_file_group *ds_file_group =
-                       g_ptr_array_index(ds_file_groups, ds_file_group_i);
-
-               struct ctf_fs_ds_index *index = ds_file_group->index;
-
-               BT_ASSERT(index);
-               BT_ASSERT(index->entries);
-               BT_ASSERT(index->entries->len > 0);
-
-               BT_ASSERT(ds_file_group->sc->default_clock_class);
-               default_cc = ds_file_group->sc->default_clock_class;
-
-               /*
-                * 1. Iterate over the index, starting from the second entry
-                * (index = 1).
-                */
-               for (entry_i = 1; entry_i < index->entries->len;
-                               entry_i++) {
-                       struct ctf_fs_ds_index_entry *curr_entry, *prev_entry;
-                       prev_entry = g_ptr_array_index(index->entries, entry_i - 1);
-                       curr_entry = g_ptr_array_index(index->entries, entry_i);
-                       /*
-                        * 2. Set the current entry `begin` timestamp to the
-                        * timestamp of the first event of the current packet.
-                        */
-                       ret = decode_packet_first_event_timestamp(trace, default_cc,
-                               curr_entry, &curr_entry->timestamp_begin,
-                               &curr_entry->timestamp_begin_ns);
-                       if (ret) {
-                               BT_COMP_LOGE_APPEND_CAUSE(trace->self_comp,
-                                       "Failed to decode first event's clock snapshot");
-                               goto end;
-                       }
-
-                       /*
-                        * 3. Set the previous entry `end` timestamp to the
-                        * timestamp of the first event of the current packet.
-                        */
-                       prev_entry->timestamp_end = curr_entry->timestamp_begin;
-                       prev_entry->timestamp_end_ns = curr_entry->timestamp_begin_ns;
-               }
-       }
-end:
-       return ret;
-}
-
-/*
- * When using the lttng-crash feature it's likely that the last packets of each
- * stream have their timestamp_end set to zero. This is caused by the fact that
- * the tracer crashed and was not able to properly close the packets.
- *
- * To fix up this erroneous data we do the following:
- * For each index entry, if the entry's timestamp_end is 0 and the
- * timestamp_begin is not 0:
- *  - If it's the stream file's last packet: set the packet index entry's end
- *    time to the packet's last event's time, if any, or to the packet's
- *    beginning time otherwise.
- *  - If it's not the stream file's last packet: set the packet index
- *    entry's end time to the next packet's beginning time.
- *
- * Affected versions:
- * - All current and future lttng-ust and lttng-modules versions.
- */
-static
-int fix_index_lttng_crash_quirk(struct ctf_fs_trace *trace)
-{
-       int ret = 0;
-       guint ds_file_group_idx;
-       GPtrArray *ds_file_groups = trace->ds_file_groups;
-       bt_logging_level log_level = trace->log_level;
-
-       for (ds_file_group_idx = 0; ds_file_group_idx < ds_file_groups->len;
-                       ds_file_group_idx++) {
-               guint entry_idx;
-               struct ctf_clock_class *default_cc;
-               struct ctf_fs_ds_index_entry *last_entry;
-               struct ctf_fs_ds_index *index;
-
-               struct ctf_fs_ds_file_group *ds_file_group =
-                       g_ptr_array_index(ds_file_groups, ds_file_group_idx);
-
-               BT_ASSERT(ds_file_group);
-               index = ds_file_group->index;
-
-               BT_ASSERT(ds_file_group->sc->default_clock_class);
-               default_cc = ds_file_group->sc->default_clock_class;
-
-               BT_ASSERT(index);
-               BT_ASSERT(index->entries);
-               BT_ASSERT(index->entries->len > 0);
-
-               last_entry = g_ptr_array_index(index->entries,
-                       index->entries->len - 1);
-               BT_ASSERT(last_entry);
-
-
-               /* 1. Fix the last entry first. */
-               if (last_entry->timestamp_end == 0 &&
-                               last_entry->timestamp_begin != 0) {
-                       /*
-                        * Decode packet to read the timestamp of the
-                        * last event of the stream file.
-                        */
-                       ret = decode_packet_last_event_timestamp(trace,
-                               default_cc, last_entry,
-                               &last_entry->timestamp_end,
-                               &last_entry->timestamp_end_ns);
-                       if (ret) {
-                               BT_COMP_LOGE_APPEND_CAUSE(trace->self_comp,
-                                       "Failed to decode last event's clock snapshot");
-                               goto end;
-                       }
-               }
-
-               /* Iterate over all entries but the last one. */
-               for (entry_idx = 0; entry_idx < index->entries->len - 1;
-                               entry_idx++) {
-                       struct ctf_fs_ds_index_entry *curr_entry, *next_entry;
-                       curr_entry = g_ptr_array_index(index->entries, entry_idx);
-                       next_entry = g_ptr_array_index(index->entries, entry_idx + 1);
-
-                       if (curr_entry->timestamp_end == 0 &&
-                                       curr_entry->timestamp_begin != 0) {
-                               /*
-                                * 2. Set the current index entry `end` timestamp to
-                                * the next index entry `begin` timestamp.
-                                */
-                               curr_entry->timestamp_end = next_entry->timestamp_begin;
-                               curr_entry->timestamp_end_ns = next_entry->timestamp_begin_ns;
-                       }
-               }
-       }
-
-end:
-       return ret;
-}
-
-/*
- * Extract the tracer information necessary to compare versions.
- * Returns 0 on success, and -1 if the extraction is not successful because the
- * necessary fields are absents in the trace metadata.
- */
-static
-int extract_tracer_info(struct ctf_fs_trace *trace,
-               struct tracer_info *current_tracer_info)
-{
-       int ret = 0;
-       struct ctf_trace_class_env_entry *entry;
-
-       /* Clear the current_tracer_info struct */
-       memset(current_tracer_info, 0, sizeof(*current_tracer_info));
-
-       /*
-        * To compare 2 tracer versions, at least the tracer name and it's
-        * major version are needed. If one of these is missing, consider it an
-        * extraction failure.
-        */
-       entry = ctf_trace_class_borrow_env_entry_by_name(
-               trace->metadata->tc, "tracer_name");
-       if (!entry || entry->type != CTF_TRACE_CLASS_ENV_ENTRY_TYPE_STR) {
-               goto missing_bare_minimum;
-       }
-
-       /* Set tracer name. */
-       current_tracer_info->name = entry->value.str->str;
-
-       entry = ctf_trace_class_borrow_env_entry_by_name(
-               trace->metadata->tc, "tracer_major");
-       if (!entry || entry->type != CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT) {
-               goto missing_bare_minimum;
-       }
-
-       /* Set major version number. */
-       current_tracer_info->major = entry->value.i;
-
-       entry = ctf_trace_class_borrow_env_entry_by_name(
-               trace->metadata->tc, "tracer_minor");
-       if (!entry || entry->type != CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT) {
-               goto end;
-       }
-
-       /* Set minor version number. */
-       current_tracer_info->minor = entry->value.i;
-
-       entry = ctf_trace_class_borrow_env_entry_by_name(
-               trace->metadata->tc, "tracer_patch");
-       if (!entry) {
-               /*
-                * If `tracer_patch` doesn't exist `tracer_patchlevel` might.
-                * For example, `lttng-modules` uses entry name
-                * `tracer_patchlevel`.
-                */
-               entry = ctf_trace_class_borrow_env_entry_by_name(
-                       trace->metadata->tc, "tracer_patchlevel");
-       }
-
-       if (!entry || entry->type != CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT) {
-               goto end;
-       }
-
-       /* Set patch version number. */
-       current_tracer_info->patch = entry->value.i;
-
-       goto end;
-
-missing_bare_minimum:
-       ret = -1;
-end:
-       return ret;
-}
-
-static
-bool is_tracer_affected_by_lttng_event_after_packet_bug(
-               struct tracer_info *curr_tracer_info)
-{
-       bool is_affected = false;
-
-       if (strcmp(curr_tracer_info->name, "lttng-ust") == 0) {
-               if (curr_tracer_info->major < 2) {
-                       is_affected = true;
-               } else if (curr_tracer_info->major == 2) {
-                       /* fixed in lttng-ust 2.11.0 */
-                       if (curr_tracer_info->minor < 11) {
-                               is_affected = true;
-                       }
-               }
-       } else if (strcmp(curr_tracer_info->name, "lttng-modules") == 0) {
-               if (curr_tracer_info->major < 2) {
-                       is_affected = true;
-               } else if (curr_tracer_info->major == 2) {
-                       /* fixed in lttng-modules 2.11.0 */
-                       if (curr_tracer_info->minor == 10) {
-                               /* fixed in lttng-modules 2.10.10 */
-                               if (curr_tracer_info->patch < 10) {
-                                       is_affected = true;
-                               }
-                       } else if (curr_tracer_info->minor == 9) {
-                               /* fixed in lttng-modules 2.9.13 */
-                               if (curr_tracer_info->patch < 13) {
-                                       is_affected = true;
-                               }
-                       } else if (curr_tracer_info->minor < 9) {
-                               is_affected = true;
-                       }
-               }
-       }
-
-       return is_affected;
-}
-
-static
-bool is_tracer_affected_by_barectf_event_before_packet_bug(
-               struct tracer_info *curr_tracer_info)
-{
-       bool is_affected = false;
-
-       if (strcmp(curr_tracer_info->name, "barectf") == 0) {
-               if (curr_tracer_info->major < 2) {
-                       is_affected = true;
-               } else if (curr_tracer_info->major == 2) {
-                       if (curr_tracer_info->minor < 3) {
-                               is_affected = true;
-                       } else if (curr_tracer_info->minor == 3) {
-                               /* fixed in barectf 2.3.1 */
-                               if (curr_tracer_info->patch < 1) {
-                                       is_affected = true;
-                               }
-                       }
-               }
-       }
-
-       return is_affected;
-}
-
-static
-bool is_tracer_affected_by_lttng_crash_quirk(
-               struct tracer_info *curr_tracer_info)
-{
-       bool is_affected = false;
-
-       /* All LTTng tracer may be affected by this lttng crash quirk. */
-       if (strcmp(curr_tracer_info->name, "lttng-ust") == 0) {
-               is_affected = true;
-       } else if (strcmp(curr_tracer_info->name, "lttng-modules") == 0) {
-               is_affected = true;
-       }
-
-       return is_affected;
-}
-
-/*
- * Looks for trace produced by known buggy tracers and fix up the index
- * produced earlier.
- */
-static
-int fix_packet_index_tracer_bugs(struct ctf_fs_component *ctf_fs,
-               bt_self_component *self_comp,
-               bt_self_component_class *self_comp_class)
-{
-       int ret = 0;
-       struct tracer_info current_tracer_info;
-       bt_logging_level log_level = ctf_fs->log_level;
-
-       ret = extract_tracer_info(ctf_fs->trace, &current_tracer_info);
-       if (ret) {
-               /*
-                * A trace may not have all the necessary environment
-                * entries to do the tracer version comparison.
-                * At least, the tracer name and major version number
-                * are needed. Failing to extract these entries is not
-                * an error.
-                */
-               ret = 0;
-               BT_LOGI_STR("Cannot extract tracer information necessary to compare with buggy versions.");
-               goto end;;
-       }
-
-       /* Check if the trace may be affected by old tracer bugs. */
-       if (is_tracer_affected_by_lttng_event_after_packet_bug(
-                       &current_tracer_info)) {
-               BT_LOGI_STR("Trace may be affected by LTTng tracer packet timestamp bug. Fixing up.");
-               ret = fix_index_lttng_event_after_packet_bug(ctf_fs->trace);
-               if (ret) {
-                       BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                               self_comp, self_comp_class,
-                               "Failed to fix LTTng event-after-packet bug.");
-                       goto end;
-               }
-               ctf_fs->trace->metadata->tc->quirks.lttng_event_after_packet = true;
-       }
-
-       if (is_tracer_affected_by_barectf_event_before_packet_bug(
-                       &current_tracer_info)) {
-               BT_LOGI_STR("Trace may be affected by barectf tracer packet timestamp bug. Fixing up.");
-               ret = fix_index_barectf_event_before_packet_bug(ctf_fs->trace);
-               if (ret) {
-                       BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                               self_comp, self_comp_class,
-                               "Failed to fix barectf event-before-packet bug.");
-                       goto end;
-               }
-               ctf_fs->trace->metadata->tc->quirks.barectf_event_before_packet = true;
-       }
-
-       if (is_tracer_affected_by_lttng_crash_quirk(
-                       &current_tracer_info)) {
-               ret = fix_index_lttng_crash_quirk(ctf_fs->trace);
-               if (ret) {
-                       BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                               self_comp, self_comp_class,
-                               "Failed to fix lttng-crash timestamp quirks.");
-                       goto end;
-               }
-               ctf_fs->trace->metadata->tc->quirks.lttng_crash = true;
-       }
-
-end:
-       return ret;
-}
-
-static
-gint compare_ds_file_groups_by_first_path(gconstpointer a, gconstpointer b)
-{
-       struct ctf_fs_ds_file_group * const *ds_file_group_a = a;
-       struct ctf_fs_ds_file_group * const *ds_file_group_b = b;
-       const struct ctf_fs_ds_file_info *first_ds_file_info_a;
-       const struct ctf_fs_ds_file_info *first_ds_file_info_b;
-
-       BT_ASSERT((*ds_file_group_a)->ds_file_infos->len > 0);
-       BT_ASSERT((*ds_file_group_b)->ds_file_infos->len > 0);
-       first_ds_file_info_a = (*ds_file_group_a)->ds_file_infos->pdata[0];
-       first_ds_file_info_b = (*ds_file_group_b)->ds_file_infos->pdata[0];
-       return strcmp(first_ds_file_info_a->path->str,
-               first_ds_file_info_b->path->str);
-}
-
-static
-gint compare_strings(gconstpointer p_a, gconstpointer p_b)
-{
-       const char *a = *((const char **) p_a);
-       const char *b = *((const char **) p_b);
-
-       return strcmp(a, b);
-}
-
-int ctf_fs_component_create_ctf_fs_trace(
-               struct ctf_fs_component *ctf_fs,
-               const bt_value *paths_value,
-               const bt_value *trace_name_value,
-               bt_self_component *self_comp,
-               bt_self_component_class *self_comp_class)
-{
-       int ret = 0;
-       uint64_t i;
-       bt_logging_level log_level = ctf_fs->log_level;
-       GPtrArray *paths = NULL;
-       GPtrArray *traces;
-       const char *trace_name;
-
-       BT_ASSERT(bt_value_get_type(paths_value) == BT_VALUE_TYPE_ARRAY);
-       BT_ASSERT(!bt_value_array_is_empty(paths_value));
-
-       traces = g_ptr_array_new_with_free_func(ctf_fs_trace_destroy_notifier);
-       if (!traces) {
-               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                       "Failed to allocate a GPtrArray.");
-               goto error;
-       }
-
-       paths = g_ptr_array_new_with_free_func(g_free);
-       if (!paths) {
-               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                       "Failed to allocate a GPtrArray.");
-               goto error;
-       }
-
-       trace_name = trace_name_value ? bt_value_string_get(trace_name_value) : NULL;
-
-       /*
-        * Create a sorted array of the paths, to make the execution of this
-        * component deterministic.
-        */
-       for (i = 0; i < bt_value_array_get_length(paths_value); i++) {
-               const bt_value *path_value =
-                       bt_value_array_borrow_element_by_index_const(paths_value, i);
-               const char *input = bt_value_string_get(path_value);
-               gchar *input_copy;
-
-               input_copy = g_strdup(input);
-               if (!input_copy) {
-                       BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                               "Failed to copy a string.");
-                       goto error;
-               }
-
-               g_ptr_array_add(paths, input_copy);
-       }
-
-       g_ptr_array_sort(paths, compare_strings);
-
-       /* Create a separate ctf_fs_trace object for each path. */
-       for (i = 0; i < paths->len; i++) {
-               const char *path = g_ptr_array_index(paths, i);
-
-               ret = ctf_fs_component_create_ctf_fs_trace_one_path(ctf_fs,
-                       path, trace_name, traces, self_comp, self_comp_class);
-               if (ret) {
-                       goto end;
-               }
-       }
-
-       if (traces->len > 1) {
-               struct ctf_fs_trace *first_trace = (struct ctf_fs_trace *) traces->pdata[0];
-               const uint8_t *first_trace_uuid = first_trace->metadata->tc->uuid;
-               struct ctf_fs_trace *trace;
-
-               /*
-                * We have more than one trace, they must all share the same
-                * UUID, verify that.
-                */
-               for (i = 0; i < traces->len; i++) {
-                       struct ctf_fs_trace *this_trace =
-                               (struct ctf_fs_trace *) traces->pdata[i];
-                       const uint8_t *this_trace_uuid = this_trace->metadata->tc->uuid;
-
-                       if (!this_trace->metadata->tc->is_uuid_set) {
-                               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                                       "Multiple traces given, but a trace does not have a UUID: path=%s",
-                                       this_trace->path->str);
-                               goto error;
-                       }
-
-                       if (bt_uuid_compare(first_trace_uuid, this_trace_uuid) != 0) {
-                               char first_trace_uuid_str[BT_UUID_STR_LEN + 1];
-                               char this_trace_uuid_str[BT_UUID_STR_LEN + 1];
-
-                               bt_uuid_to_str(first_trace_uuid, first_trace_uuid_str);
-                               bt_uuid_to_str(this_trace_uuid, this_trace_uuid_str);
-
-                               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                                       "Multiple traces given, but UUIDs don't match: "
-                                       "first-trace-uuid=%s, first-trace-path=%s, "
-                                       "trace-uuid=%s, trace-path=%s",
-                                       first_trace_uuid_str, first_trace->path->str,
-                                       this_trace_uuid_str, this_trace->path->str);
-                               goto error;
-                       }
-               }
-
-               ret = merge_ctf_fs_traces((struct ctf_fs_trace **) traces->pdata,
-                       traces->len, &trace);
-               if (ret) {
-                       BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                               "Failed to merge traces with the same UUID.");
-                       goto error;
-               }
-
-               ctf_fs->trace = trace;
-       } else {
-               /* Just one trace, it may or may not have a UUID, both are fine. */
-               ctf_fs->trace = traces->pdata[0];
-               traces->pdata[0] = NULL;
-       }
-
-       ret = fix_packet_index_tracer_bugs(ctf_fs, self_comp, self_comp_class);
-       if (ret) {
-               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                       "Failed to fix packet index tracer bugs.");
-       }
-
-       /*
-        * Sort data stream file groups by first data stream file info
-        * path to get a deterministic order. This order influences the
-        * order of the output ports. It also influences the order of
-        * the automatic stream IDs if the trace's packet headers do not
-        * contain a `stream_instance_id` field, in which case the data
-        * stream file to stream ID association is always the same,
-        * whatever the build and the system.
-        *
-        * Having a deterministic order here can help debugging and
-        * testing.
-        */
-       g_ptr_array_sort(ctf_fs->trace->ds_file_groups,
-               compare_ds_file_groups_by_first_path);
-       goto end;
-error:
-       ret = -1;
-
-end:
-       if (traces) {
-               g_ptr_array_free(traces, TRUE);
-       }
-
-       if (paths) {
-               g_ptr_array_free(paths, TRUE);
-       }
-
-       return ret;
-}
-
-static
-GString *get_stream_instance_unique_name(
-               struct ctf_fs_ds_file_group *ds_file_group)
-{
-       GString *name;
-       struct ctf_fs_ds_file_info *ds_file_info;
-
-       name = g_string_new(NULL);
-       if (!name) {
-               goto end;
-       }
-
-       /*
-        * If there's more than one stream file in the stream file
-        * group, the first (earliest) stream file's path is used as
-        * the stream's unique name.
-        */
-       BT_ASSERT(ds_file_group->ds_file_infos->len > 0);
-       ds_file_info = g_ptr_array_index(ds_file_group->ds_file_infos, 0);
-       g_string_assign(name, ds_file_info->path->str);
-
-end:
-       return name;
-}
-
-/* Create the IR stream objects for ctf_fs_trace. */
-
-static
-int create_streams_for_trace(struct ctf_fs_trace *ctf_fs_trace)
-{
-       int ret;
-       GString *name = NULL;
-       guint i;
-       bt_logging_level log_level = ctf_fs_trace->log_level;
-       bt_self_component *self_comp = ctf_fs_trace->self_comp;
-
-       for (i = 0; i < ctf_fs_trace->ds_file_groups->len; i++) {
-               struct ctf_fs_ds_file_group *ds_file_group =
-                       g_ptr_array_index(ctf_fs_trace->ds_file_groups, i);
-               name = get_stream_instance_unique_name(ds_file_group);
-
-               if (!name) {
-                       goto error;
-               }
-
-               if (ds_file_group->sc->ir_sc) {
-                       BT_ASSERT(ctf_fs_trace->trace);
-
-                       if (ds_file_group->stream_id == UINT64_C(-1)) {
-                               /* No stream ID: use 0 */
-                               ds_file_group->stream = bt_stream_create_with_id(
-                                       ds_file_group->sc->ir_sc,
-                                       ctf_fs_trace->trace,
-                                       ctf_fs_trace->next_stream_id);
-                               ctf_fs_trace->next_stream_id++;
-                       } else {
-                               /* Specific stream ID */
-                               ds_file_group->stream = bt_stream_create_with_id(
-                                       ds_file_group->sc->ir_sc,
-                                       ctf_fs_trace->trace,
-                                       (uint64_t) ds_file_group->stream_id);
-                       }
-               } else {
-                       ds_file_group->stream = NULL;
-               }
-
-               if (!ds_file_group->stream) {
-                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                               "Cannot create stream for DS file group: "
-                               "addr=%p, stream-name=\"%s\"",
-                               ds_file_group, name->str);
-                       goto error;
-               }
-
-               ret = bt_stream_set_name(ds_file_group->stream,
-                       name->str);
-               if (ret) {
-                       BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Cannot set stream's name: "
-                               "addr=%p, stream-name=\"%s\"",
-                               ds_file_group->stream, name->str);
-                       goto error;
-               }
-
-               g_string_free(name, TRUE);
-               name = NULL;
-       }
-
-       ret = 0;
-       goto end;
-
-error:
-       ret = -1;
-
-end:
-
-       if (name) {
-               g_string_free(name, TRUE);
-       }
-       return ret;
-}
-
-static const struct bt_param_validation_value_descr inputs_elem_descr = {
-       .type = BT_VALUE_TYPE_STRING,
-};
-
-static const struct bt_param_validation_map_value_entry_descr fs_params_entries_descr[] = {
-       { "inputs", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, {
-               BT_VALUE_TYPE_ARRAY,
-               .array = {
-                       .min_length = 1,
-                       .max_length = BT_PARAM_VALIDATION_INFINITE,
-                       .element_type = &inputs_elem_descr,
-               }
-       }},
-       { "trace-name", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_STRING } },
-       { "clock-class-offset-s", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_SIGNED_INTEGER } },
-       { "clock-class-offset-ns", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_SIGNED_INTEGER } },
-       { "force-clock-class-origin-unix-epoch", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { .type = BT_VALUE_TYPE_BOOL } },
-       BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
-};
-
-
-bool read_src_fs_parameters(const bt_value *params,
-               const bt_value **inputs,
-               const bt_value **trace_name,
-               struct ctf_fs_component *ctf_fs,
-               bt_self_component *self_comp,
-               bt_self_component_class *self_comp_class) {
-       bool ret;
-       const bt_value *value;
-       bt_logging_level log_level = ctf_fs->log_level;
-       enum bt_param_validation_status validate_value_status;
-       gchar *error = NULL;
-
-       validate_value_status = bt_param_validation_validate(params,
-               fs_params_entries_descr, &error);
-       if (validate_value_status != BT_PARAM_VALIDATION_STATUS_OK) {
-               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
-                       "%s", error);
-               ret = false;
-               goto end;
-       }
-
-       /* inputs parameter */
-       *inputs = bt_value_map_borrow_entry_value_const(params, "inputs");
-
-       /* clock-class-offset-s parameter */
-       value = bt_value_map_borrow_entry_value_const(params,
-               "clock-class-offset-s");
-       if (value) {
-               ctf_fs->metadata_config.clock_class_offset_s =
-                       bt_value_integer_signed_get(value);
-       }
-
-       /* clock-class-offset-ns parameter */
-       value = bt_value_map_borrow_entry_value_const(params,
-               "clock-class-offset-ns");
-       if (value) {
-               ctf_fs->metadata_config.clock_class_offset_ns =
-                       bt_value_integer_signed_get(value);
-       }
-
-       /* force-clock-class-origin-unix-epoch parameter */
-       value = bt_value_map_borrow_entry_value_const(params,
-               "force-clock-class-origin-unix-epoch");
-       if (value) {
-               ctf_fs->metadata_config.force_clock_class_origin_unix_epoch =
-                       bt_value_bool_get(value);
-       }
-
-       /* trace-name parameter */
-       *trace_name = bt_value_map_borrow_entry_value_const(params, "trace-name");
-
-       ret = true;
-
-end:
-       g_free(error);
-       return ret;
-}
-
-static
-struct ctf_fs_component *ctf_fs_create(
-       const bt_value *params,
-       bt_self_component_source *self_comp_src)
-{
-       struct ctf_fs_component *ctf_fs = NULL;
-       const bt_value *inputs_value;
-       const bt_value *trace_name_value;
-       bt_self_component *self_comp =
-               bt_self_component_source_as_self_component(self_comp_src);
-
-       ctf_fs = ctf_fs_component_create(bt_component_get_logging_level(
-               bt_self_component_as_component(self_comp)), self_comp);
-       if (!ctf_fs) {
-               goto error;
-       }
-
-       if (!read_src_fs_parameters(params, &inputs_value, &trace_name_value,
-                       ctf_fs, self_comp, NULL)) {
-               goto error;
-       }
-
-       bt_self_component_set_data(self_comp, ctf_fs);
-
-       if (ctf_fs_component_create_ctf_fs_trace(ctf_fs, inputs_value,
-                       trace_name_value, self_comp, NULL)) {
-               goto error;
-       }
-
-       if (create_streams_for_trace(ctf_fs->trace)) {
-               goto error;
-       }
-
-       if (create_ports_for_trace(ctf_fs, ctf_fs->trace, self_comp_src)) {
-               goto error;
-       }
-
-       goto end;
-
-error:
-       ctf_fs_destroy(ctf_fs);
-       ctf_fs = NULL;
-       bt_self_component_set_data(self_comp, NULL);
-
-end:
-       return ctf_fs;
-}
-
-BT_HIDDEN
-bt_component_class_initialize_method_status ctf_fs_init(
-               bt_self_component_source *self_comp_src,
-               bt_self_component_source_configuration *config,
-               const bt_value *params, __attribute__((unused)) void *init_method_data)
-{
-       struct ctf_fs_component *ctf_fs;
-       bt_component_class_initialize_method_status ret =
-               BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
-
-       ctf_fs = ctf_fs_create(params, self_comp_src);
-       if (!ctf_fs) {
-               ret = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
-       }
-
-       return ret;
-}
-
-BT_HIDDEN
-bt_component_class_query_method_status ctf_fs_query(
-               bt_self_component_class_source *comp_class,
-               bt_private_query_executor *priv_query_exec,
-               const char *object, const bt_value *params,
-               __attribute__((unused)) void *method_data,
-               const bt_value **result)
-{
-       bt_component_class_query_method_status status =
-               BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_OK;
-       bt_logging_level log_level = bt_query_executor_get_logging_level(
-               bt_private_query_executor_as_query_executor_const(
-                       priv_query_exec));
-
-       if (strcmp(object, "metadata-info") == 0) {
-               status = metadata_info_query(comp_class, params, log_level,
-                       result);
-       } else if (strcmp(object, "babeltrace.trace-infos") == 0) {
-               status = trace_infos_query(comp_class, params, log_level,
-                       result);
-       } else if (!strcmp(object, "babeltrace.support-info")) {
-               status = support_info_query(comp_class, params, log_level, result);
-       } else {
-               BT_LOGE("Unknown query object `%s`", object);
-               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_UNKNOWN_OBJECT;
-               goto end;
-       }
-end:
-       return status;
-}
diff --git a/src/plugins/ctf/fs-src/fs.cpp b/src/plugins/ctf/fs-src/fs.cpp
new file mode 100644 (file)
index 0000000..0e6e96e
--- /dev/null
@@ -0,0 +1,2496 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2015-2017 Philippe Proulx <pproulx@efficios.com>
+ * Copyright 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * Babeltrace CTF file system Reader Component
+ */
+
+#define BT_COMP_LOG_SELF_COMP self_comp
+#define BT_LOG_OUTPUT_LEVEL log_level
+#define BT_LOG_TAG "PLUGIN/SRC.CTF.FS"
+#include "logging/comp-logging.h"
+
+#include "common/common.h"
+#include <babeltrace2/babeltrace.h>
+#include "common/uuid.h"
+#include <glib.h>
+#include "common/assert.h"
+#include <inttypes.h>
+#include <stdbool.h>
+#include "fs.hpp"
+#include "metadata.hpp"
+#include "data-stream-file.hpp"
+#include "file.hpp"
+#include "../common/metadata/decoder.hpp"
+#include "../common/metadata/ctf-meta-configure-ir-trace.hpp"
+#include "../common/msg-iter/msg-iter.hpp"
+#include "query.hpp"
+#include "plugins/common/param-validation/param-validation.h"
+
+struct tracer_info {
+       const char *name;
+       int64_t major;
+       int64_t minor;
+       int64_t patch;
+};
+
+static
+void ctf_fs_msg_iter_data_destroy(
+               struct ctf_fs_msg_iter_data *msg_iter_data)
+{
+       if (!msg_iter_data) {
+               return;
+       }
+
+       if (msg_iter_data->msg_iter) {
+               ctf_msg_iter_destroy(msg_iter_data->msg_iter);
+       }
+
+       if (msg_iter_data->msg_iter_medops_data) {
+               ctf_fs_ds_group_medops_data_destroy(
+                       msg_iter_data->msg_iter_medops_data);
+       }
+
+       g_free(msg_iter_data);
+}
+
+static
+bt_message_iterator_class_next_method_status ctf_fs_iterator_next_one(
+               struct ctf_fs_msg_iter_data *msg_iter_data,
+               const bt_message **out_msg)
+{
+       bt_message_iterator_class_next_method_status status;
+       enum ctf_msg_iter_status msg_iter_status;
+       bt_logging_level log_level = msg_iter_data->log_level;
+
+       msg_iter_status = ctf_msg_iter_get_next_message(
+               msg_iter_data->msg_iter, out_msg);
+
+       switch (msg_iter_status) {
+       case CTF_MSG_ITER_STATUS_OK:
+               /* Cool, message has been written to *out_msg. */
+               status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK;
+               break;
+
+       case CTF_MSG_ITER_STATUS_EOF:
+               status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_END;
+               break;
+
+       case CTF_MSG_ITER_STATUS_AGAIN:
+               /*
+                * Should not make it this far as this is
+                * medium-specific; there is nothing for the user to do
+                * and it should have been handled upstream.
+                */
+               bt_common_abort();
+
+       case CTF_MSG_ITER_STATUS_ERROR:
+               BT_MSG_ITER_LOGE_APPEND_CAUSE(msg_iter_data->self_msg_iter,
+                       "Failed to get next message from CTF message iterator.");
+               status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR;
+               break;
+
+       case CTF_MSG_ITER_STATUS_MEMORY_ERROR:
+               BT_MSG_ITER_LOGE_APPEND_CAUSE(msg_iter_data->self_msg_iter,
+                       "Failed to get next message from CTF message iterator.");
+               status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_MEMORY_ERROR;
+               break;
+
+       default:
+               bt_common_abort();
+       }
+
+       return status;
+}
+
+BT_HIDDEN
+bt_message_iterator_class_next_method_status ctf_fs_iterator_next(
+               bt_self_message_iterator *iterator,
+               bt_message_array_const msgs, uint64_t capacity,
+               uint64_t *count)
+{
+       bt_message_iterator_class_next_method_status status;
+       struct ctf_fs_msg_iter_data *msg_iter_data =
+               (struct ctf_fs_msg_iter_data *) bt_self_message_iterator_get_data(iterator);
+       uint64_t i = 0;
+
+       if (G_UNLIKELY(msg_iter_data->next_saved_error)) {
+               /*
+                * Last time we were called, we hit an error but had some
+                * messages to deliver, so we stashed the error here.  Return
+                * it now.
+                */
+               BT_CURRENT_THREAD_MOVE_ERROR_AND_RESET(msg_iter_data->next_saved_error);
+               status = msg_iter_data->next_saved_status;
+               goto end;
+       }
+
+       do {
+               status = ctf_fs_iterator_next_one(msg_iter_data, &msgs[i]);
+               if (status == BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK) {
+                       i++;
+               }
+       } while (i < capacity &&
+                       status == BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK);
+
+       if (i > 0) {
+               /*
+                * Even if ctf_fs_iterator_next_one() returned something
+                * else than BT_MESSAGE_ITERATOR_NEXT_METHOD_STATUS_OK, we
+                * accumulated message objects in the output
+                * message array, so we need to return
+                * BT_MESSAGE_ITERATOR_NEXT_METHOD_STATUS_OK so that they are
+                * transfered to downstream. This other status occurs
+                * again the next time muxer_msg_iter_do_next() is
+                * called, possibly without any accumulated
+                * message, in which case we'll return it.
+                */
+               if (status < 0) {
+                       /*
+                        * Save this error for the next _next call.  Assume that
+                        * this component always appends error causes when
+                        * returning an error status code, which will cause the
+                        * current thread error to be non-NULL.
+                        */
+                       msg_iter_data->next_saved_error = bt_current_thread_take_error();
+                       BT_ASSERT(msg_iter_data->next_saved_error);
+                       msg_iter_data->next_saved_status = status;
+               }
+
+               *count = i;
+               status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK;
+       }
+
+end:
+       return status;
+}
+
+BT_HIDDEN
+bt_message_iterator_class_seek_beginning_method_status
+ctf_fs_iterator_seek_beginning(bt_self_message_iterator *it)
+{
+       struct ctf_fs_msg_iter_data *msg_iter_data =
+               (struct ctf_fs_msg_iter_data *) bt_self_message_iterator_get_data(it);
+
+       BT_ASSERT(msg_iter_data);
+
+       ctf_msg_iter_reset(msg_iter_data->msg_iter);
+       ctf_fs_ds_group_medops_data_reset(msg_iter_data->msg_iter_medops_data);
+
+       return BT_MESSAGE_ITERATOR_CLASS_SEEK_BEGINNING_METHOD_STATUS_OK;
+}
+
+BT_HIDDEN
+void ctf_fs_iterator_finalize(bt_self_message_iterator *it)
+{
+       ctf_fs_msg_iter_data_destroy(
+               (struct ctf_fs_msg_iter_data *) bt_self_message_iterator_get_data(it));
+}
+
+static
+bt_message_iterator_class_initialize_method_status
+ctf_msg_iter_medium_status_to_msg_iter_initialize_status
+       (enum ctf_msg_iter_medium_status status)
+{
+       switch (status) {
+       case CTF_MSG_ITER_MEDIUM_STATUS_EOF:
+       case CTF_MSG_ITER_MEDIUM_STATUS_AGAIN:
+       case CTF_MSG_ITER_MEDIUM_STATUS_ERROR:
+               return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
+       case CTF_MSG_ITER_MEDIUM_STATUS_MEMORY_ERROR:
+               return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
+       case CTF_MSG_ITER_MEDIUM_STATUS_OK:
+               return BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_OK;
+       }
+
+       bt_common_abort();
+}
+
+BT_HIDDEN
+bt_message_iterator_class_initialize_method_status ctf_fs_iterator_init(
+               bt_self_message_iterator *self_msg_iter,
+               bt_self_message_iterator_configuration *config,
+               bt_self_component_port_output *self_port)
+{
+       struct ctf_fs_port_data *port_data;
+       struct ctf_fs_msg_iter_data *msg_iter_data = NULL;
+       bt_message_iterator_class_initialize_method_status status;
+       bt_logging_level log_level;
+       enum ctf_msg_iter_medium_status medium_status;
+       bt_self_component *self_comp =
+               bt_self_message_iterator_borrow_component(self_msg_iter);
+
+       port_data = (struct ctf_fs_port_data *) bt_self_component_port_get_data(
+               bt_self_component_port_output_as_self_component_port(
+                       self_port));
+       BT_ASSERT(port_data);
+       log_level = port_data->ctf_fs->log_level;
+       msg_iter_data = g_new0(struct ctf_fs_msg_iter_data, 1);
+       if (!msg_iter_data) {
+               status = BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
+               goto error;
+       }
+
+       msg_iter_data->log_level = log_level;
+       msg_iter_data->self_comp = self_comp;
+       msg_iter_data->self_msg_iter = self_msg_iter;
+       msg_iter_data->ds_file_group = port_data->ds_file_group;
+
+       medium_status = ctf_fs_ds_group_medops_data_create(
+               msg_iter_data->ds_file_group, self_msg_iter, log_level,
+               &msg_iter_data->msg_iter_medops_data);
+       BT_ASSERT(
+               medium_status == CTF_MSG_ITER_MEDIUM_STATUS_OK ||
+               medium_status == CTF_MSG_ITER_MEDIUM_STATUS_ERROR ||
+               medium_status == CTF_MSG_ITER_MEDIUM_STATUS_MEMORY_ERROR);
+       if (medium_status != CTF_MSG_ITER_MEDIUM_STATUS_OK) {
+               BT_MSG_ITER_LOGE_APPEND_CAUSE(self_msg_iter,
+                       "Failed to create ctf_fs_ds_group_medops");
+               status = ctf_msg_iter_medium_status_to_msg_iter_initialize_status(medium_status);
+               goto error;
+       }
+
+       msg_iter_data->msg_iter = ctf_msg_iter_create(
+               msg_iter_data->ds_file_group->ctf_fs_trace->metadata->tc,
+               bt_common_get_page_size(msg_iter_data->log_level) * 8,
+               ctf_fs_ds_group_medops,
+               msg_iter_data->msg_iter_medops_data,
+               msg_iter_data->log_level,
+               self_comp, self_msg_iter);
+       if (!msg_iter_data->msg_iter) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Cannot create a CTF message iterator.");
+               status = BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
+               goto error;
+       }
+
+       /*
+        * This iterator can seek forward if its stream class has a default
+        * clock class.
+        */
+       if (msg_iter_data->ds_file_group->sc->default_clock_class) {
+               bt_self_message_iterator_configuration_set_can_seek_forward(
+                       config, true);
+       }
+
+       bt_self_message_iterator_set_data(self_msg_iter,
+               msg_iter_data);
+       msg_iter_data = NULL;
+
+       status = BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_OK;
+       goto end;
+
+error:
+       bt_self_message_iterator_set_data(self_msg_iter, NULL);
+
+end:
+       ctf_fs_msg_iter_data_destroy(msg_iter_data);
+       return status;
+}
+
+static
+void ctf_fs_trace_destroy(struct ctf_fs_trace *ctf_fs_trace)
+{
+       if (!ctf_fs_trace) {
+               return;
+       }
+
+       if (ctf_fs_trace->ds_file_groups) {
+               g_ptr_array_free(ctf_fs_trace->ds_file_groups, TRUE);
+       }
+
+       BT_TRACE_PUT_REF_AND_RESET(ctf_fs_trace->trace);
+
+       if (ctf_fs_trace->path) {
+               g_string_free(ctf_fs_trace->path, TRUE);
+       }
+
+       if (ctf_fs_trace->metadata) {
+               ctf_fs_metadata_fini(ctf_fs_trace->metadata);
+               g_free(ctf_fs_trace->metadata);
+       }
+
+       g_free(ctf_fs_trace);
+}
+
+BT_HIDDEN
+void ctf_fs_destroy(struct ctf_fs_component *ctf_fs)
+{
+       if (!ctf_fs) {
+               return;
+       }
+
+       ctf_fs_trace_destroy(ctf_fs->trace);
+
+       if (ctf_fs->port_data) {
+               g_ptr_array_free(ctf_fs->port_data, TRUE);
+       }
+
+       g_free(ctf_fs);
+}
+
+static
+void port_data_destroy(struct ctf_fs_port_data *port_data)
+{
+       if (!port_data) {
+               return;
+       }
+
+       g_free(port_data);
+}
+
+static
+void port_data_destroy_notifier(void *data) {
+       port_data_destroy((struct ctf_fs_port_data *) data);
+}
+
+static
+void ctf_fs_trace_destroy_notifier(void *data)
+{
+       struct ctf_fs_trace *trace = (struct ctf_fs_trace *) data;
+       ctf_fs_trace_destroy(trace);
+}
+
+struct ctf_fs_component *ctf_fs_component_create(bt_logging_level log_level,
+               bt_self_component *self_comp)
+{
+       struct ctf_fs_component *ctf_fs;
+
+       ctf_fs = g_new0(struct ctf_fs_component, 1);
+       if (!ctf_fs) {
+               goto error;
+       }
+
+       ctf_fs->log_level = log_level;
+       ctf_fs->port_data =
+               g_ptr_array_new_with_free_func(port_data_destroy_notifier);
+       if (!ctf_fs->port_data) {
+               goto error;
+       }
+
+       goto end;
+
+error:
+       ctf_fs_destroy(ctf_fs);
+       ctf_fs = NULL;
+
+end:
+       return ctf_fs;
+}
+
+void ctf_fs_finalize(bt_self_component_source *component)
+{
+       ctf_fs_destroy((struct ctf_fs_component *) bt_self_component_get_data(
+               bt_self_component_source_as_self_component(component)));
+}
+
+gchar *ctf_fs_make_port_name(struct ctf_fs_ds_file_group *ds_file_group)
+{
+       GString *name = g_string_new(NULL);
+
+       /*
+        * The unique port name is generated by concatenating unique identifiers
+        * for:
+        *
+        *   - the trace
+        *   - the stream class
+        *   - the stream
+        */
+
+       /* For the trace, use the uuid if present, else the path. */
+       if (ds_file_group->ctf_fs_trace->metadata->tc->is_uuid_set) {
+               char uuid_str[BT_UUID_STR_LEN + 1];
+
+               bt_uuid_to_str(ds_file_group->ctf_fs_trace->metadata->tc->uuid, uuid_str);
+               g_string_assign(name, uuid_str);
+       } else {
+               g_string_assign(name, ds_file_group->ctf_fs_trace->path->str);
+       }
+
+       /*
+        * For the stream class, use the id if present.  We can omit this field
+        * otherwise, as there will only be a single stream class.
+        */
+       if (ds_file_group->sc->id != UINT64_C(-1)) {
+               g_string_append_printf(name, " | %" PRIu64, ds_file_group->sc->id);
+       }
+
+       /* For the stream, use the id if present, else, use the path. */
+       if (ds_file_group->stream_id != UINT64_C(-1)) {
+               g_string_append_printf(name, " | %" PRIu64, ds_file_group->stream_id);
+       } else {
+               BT_ASSERT(ds_file_group->ds_file_infos->len == 1);
+               struct ctf_fs_ds_file_info *ds_file_info =
+                       (struct ctf_fs_ds_file_info *) g_ptr_array_index(ds_file_group->ds_file_infos, 0);
+               g_string_append_printf(name, " | %s", ds_file_info->path->str);
+       }
+
+       return g_string_free(name, FALSE);
+}
+
+static
+int create_one_port_for_trace(struct ctf_fs_component *ctf_fs,
+               struct ctf_fs_trace *ctf_fs_trace,
+               struct ctf_fs_ds_file_group *ds_file_group,
+               bt_self_component_source *self_comp_src)
+{
+       int ret = 0;
+       struct ctf_fs_port_data *port_data = NULL;
+       gchar *port_name;
+       bt_logging_level log_level = ctf_fs->log_level;
+       bt_self_component *self_comp =
+               bt_self_component_source_as_self_component(self_comp_src);
+
+       port_name = ctf_fs_make_port_name(ds_file_group);
+       if (!port_name) {
+               goto error;
+       }
+
+       BT_COMP_LOGI("Creating one port named `%s`", port_name);
+
+       /* Create output port for this file */
+       port_data = g_new0(struct ctf_fs_port_data, 1);
+       if (!port_data) {
+               goto error;
+       }
+
+       port_data->ctf_fs = ctf_fs;
+       port_data->ds_file_group = ds_file_group;
+       ret = bt_self_component_source_add_output_port(
+               self_comp_src, port_name, port_data, NULL);
+       if (ret) {
+               goto error;
+       }
+
+       g_ptr_array_add(ctf_fs->port_data, port_data);
+       port_data = NULL;
+       goto end;
+
+error:
+       ret = -1;
+
+end:
+       g_free(port_name);
+
+       port_data_destroy(port_data);
+       return ret;
+}
+
+static
+int create_ports_for_trace(struct ctf_fs_component *ctf_fs,
+               struct ctf_fs_trace *ctf_fs_trace,
+               bt_self_component_source *self_comp_src)
+{
+       int ret = 0;
+       size_t i;
+       bt_logging_level log_level = ctf_fs_trace->log_level;
+       bt_self_component *self_comp =
+               bt_self_component_source_as_self_component(self_comp_src);
+
+       /* Create one output port for each stream file group */
+       for (i = 0; i < ctf_fs_trace->ds_file_groups->len; i++) {
+               struct ctf_fs_ds_file_group *ds_file_group =
+                       (struct ctf_fs_ds_file_group *) g_ptr_array_index(ctf_fs_trace->ds_file_groups, i);
+
+               ret = create_one_port_for_trace(ctf_fs, ctf_fs_trace,
+                       ds_file_group, self_comp_src);
+               if (ret) {
+                       BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Cannot create output port.");
+                       goto end;
+               }
+       }
+
+end:
+       return ret;
+}
+
+static
+void ctf_fs_ds_file_info_destroy(struct ctf_fs_ds_file_info *ds_file_info)
+{
+       if (!ds_file_info) {
+               return;
+       }
+
+       if (ds_file_info->path) {
+               g_string_free(ds_file_info->path, TRUE);
+       }
+
+       g_free(ds_file_info);
+}
+
+static
+struct ctf_fs_ds_file_info *ctf_fs_ds_file_info_create(const char *path,
+               int64_t begin_ns)
+{
+       struct ctf_fs_ds_file_info *ds_file_info;
+
+       ds_file_info = g_new0(struct ctf_fs_ds_file_info, 1);
+       if (!ds_file_info) {
+               goto end;
+       }
+
+       ds_file_info->path = g_string_new(path);
+       if (!ds_file_info->path) {
+               ctf_fs_ds_file_info_destroy(ds_file_info);
+               ds_file_info = NULL;
+               goto end;
+       }
+
+       ds_file_info->begin_ns = begin_ns;
+
+end:
+       return ds_file_info;
+}
+
+static
+void ctf_fs_ds_file_group_destroy(struct ctf_fs_ds_file_group *ds_file_group)
+{
+       if (!ds_file_group) {
+               return;
+       }
+
+       if (ds_file_group->ds_file_infos) {
+               g_ptr_array_free(ds_file_group->ds_file_infos, TRUE);
+       }
+
+       ctf_fs_ds_index_destroy(ds_file_group->index);
+
+       bt_stream_put_ref(ds_file_group->stream);
+       g_free(ds_file_group);
+}
+
+static
+struct ctf_fs_ds_file_group *ctf_fs_ds_file_group_create(
+               struct ctf_fs_trace *ctf_fs_trace,
+               struct ctf_stream_class *sc,
+               uint64_t stream_instance_id,
+               struct ctf_fs_ds_index *index)
+{
+       struct ctf_fs_ds_file_group *ds_file_group;
+
+       ds_file_group = g_new0(struct ctf_fs_ds_file_group, 1);
+       if (!ds_file_group) {
+               goto error;
+       }
+
+       ds_file_group->ds_file_infos = g_ptr_array_new_with_free_func(
+               (GDestroyNotify) ctf_fs_ds_file_info_destroy);
+       if (!ds_file_group->ds_file_infos) {
+               goto error;
+       }
+
+       ds_file_group->index = index;
+
+       ds_file_group->stream_id = stream_instance_id;
+       BT_ASSERT(sc);
+       ds_file_group->sc = sc;
+       ds_file_group->ctf_fs_trace = ctf_fs_trace;
+       goto end;
+
+error:
+       ctf_fs_ds_file_group_destroy(ds_file_group);
+       ctf_fs_ds_index_destroy(index);
+       ds_file_group = NULL;
+
+end:
+       return ds_file_group;
+}
+
+/* Replace by g_ptr_array_insert when we depend on glib >= 2.40. */
+static
+void array_insert(GPtrArray *array, gpointer element, size_t pos)
+{
+       size_t original_array_len = array->len;
+
+       /* Allocate an unused element at the end of the array. */
+       g_ptr_array_add(array, NULL);
+
+       /* If we are not inserting at the end, move the elements by one. */
+       if (pos < original_array_len) {
+               memmove(&(array->pdata[pos + 1]),
+                       &(array->pdata[pos]),
+                       (original_array_len - pos) * sizeof(gpointer));
+       }
+
+       /* Insert the value. */
+       array->pdata[pos] = element;
+}
+
+/*
+ * Insert ds_file_info in ds_file_group's list of ds_file_infos at the right
+ * place to keep it sorted.
+ */
+
+static
+void ds_file_group_insert_ds_file_info_sorted(
+               struct ctf_fs_ds_file_group *ds_file_group,
+               struct ctf_fs_ds_file_info *ds_file_info)
+{
+       guint i;
+
+       /* Find the spot where to insert this ds_file_info. */
+       for (i = 0; i < ds_file_group->ds_file_infos->len; i++) {
+               struct ctf_fs_ds_file_info *other_ds_file_info =
+                       (struct ctf_fs_ds_file_info *) g_ptr_array_index(ds_file_group->ds_file_infos, i);
+
+               if (ds_file_info->begin_ns < other_ds_file_info->begin_ns) {
+                       break;
+               }
+       }
+
+       array_insert(ds_file_group->ds_file_infos, ds_file_info, i);
+}
+
+static
+bool ds_index_entries_equal(
+       const struct ctf_fs_ds_index_entry *left,
+       const struct ctf_fs_ds_index_entry *right)
+{
+       if (left->packet_size != right->packet_size) {
+               return false;
+       }
+
+       if (left->timestamp_begin != right->timestamp_begin) {
+               return false;
+       }
+
+       if (left->timestamp_end != right->timestamp_end) {
+               return false;
+       }
+
+       if (left->packet_seq_num != right->packet_seq_num) {
+               return false;
+       }
+
+       return true;
+}
+
+/*
+ * Insert `entry` into `index`, without duplication.
+ *
+ * The entry is inserted only if there isn't an identical entry already.
+ *
+ * In any case, the ownership of `entry` is transferred to this function.  So if
+ * the entry is not inserted, it is freed.
+ */
+
+static
+void ds_index_insert_ds_index_entry_sorted(
+       struct ctf_fs_ds_index *index,
+       struct ctf_fs_ds_index_entry *entry)
+{
+       guint i;
+       struct ctf_fs_ds_index_entry *other_entry = NULL;
+
+       /* Find the spot where to insert this index entry. */
+       for (i = 0; i < index->entries->len; i++) {
+               other_entry = (struct ctf_fs_ds_index_entry *) g_ptr_array_index(index->entries, i);
+
+               if (entry->timestamp_begin_ns <= other_entry->timestamp_begin_ns) {
+                       break;
+               }
+       }
+
+       /*
+        * Insert the entry only if a duplicate doesn't already exist.
+        *
+        * There can be duplicate packets if reading multiple overlapping
+        * snapshots of the same trace.  We then want the index to contain
+        * a reference to only one copy of that packet.
+        */
+       if (i == index->entries->len ||
+                       !ds_index_entries_equal(entry, other_entry)) {
+               array_insert(index->entries, entry, i);
+       } else {
+               g_free(entry);
+       }
+}
+
+static
+void merge_ctf_fs_ds_indexes(struct ctf_fs_ds_index *dest, struct ctf_fs_ds_index *src)
+{
+       guint i;
+
+       for (i = 0; i < src->entries->len; i++) {
+               struct ctf_fs_ds_index_entry *entry =
+                       (struct ctf_fs_ds_index_entry *) g_ptr_array_index(src->entries, i);
+
+               /*
+               * Ownership of the ctf_fs_ds_index_entry is transferred to
+               * ds_index_insert_ds_index_entry_sorted.
+               */
+               g_ptr_array_index(src->entries, i) = NULL;
+               ds_index_insert_ds_index_entry_sorted(dest, entry);
+       }
+}
+
+static
+int add_ds_file_to_ds_file_group(struct ctf_fs_trace *ctf_fs_trace,
+               const char *path)
+{
+       int64_t stream_instance_id = -1;
+       int64_t begin_ns = -1;
+       struct ctf_fs_ds_file_group *ds_file_group = NULL;
+       bool add_group = false;
+       int ret;
+       size_t i;
+       struct ctf_fs_ds_file *ds_file = NULL;
+       struct ctf_fs_ds_file_info *ds_file_info = NULL;
+       struct ctf_fs_ds_index *index = NULL;
+       struct ctf_msg_iter *msg_iter = NULL;
+       struct ctf_stream_class *sc = NULL;
+       struct ctf_msg_iter_packet_properties props;
+       bt_logging_level log_level = ctf_fs_trace->log_level;
+       bt_self_component *self_comp = ctf_fs_trace->self_comp;
+       bt_self_component_class *self_comp_class = ctf_fs_trace->self_comp_class;
+
+       /*
+        * Create a temporary ds_file to read some properties about the data
+        * stream file.
+        */
+       ds_file = ctf_fs_ds_file_create(ctf_fs_trace, NULL, NULL, path,
+               log_level);
+       if (!ds_file) {
+               goto error;
+       }
+
+       /* Create a temporary iterator to read the ds_file. */
+       msg_iter = ctf_msg_iter_create(ctf_fs_trace->metadata->tc,
+               bt_common_get_page_size(log_level) * 8,
+               ctf_fs_ds_file_medops, ds_file, log_level, self_comp, NULL);
+       if (!msg_iter) {
+               BT_COMP_LOGE_STR("Cannot create a CTF message iterator.");
+               goto error;
+       }
+
+       ctf_msg_iter_set_dry_run(msg_iter, true);
+
+       ret = ctf_msg_iter_get_packet_properties(msg_iter, &props);
+       if (ret) {
+               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
+                       "Cannot get stream file's first packet's header and context fields (`%s`).",
+                       path);
+               goto error;
+       }
+
+       sc = ctf_trace_class_borrow_stream_class_by_id(ds_file->metadata->tc,
+               props.stream_class_id);
+       BT_ASSERT(sc);
+       stream_instance_id = props.data_stream_id;
+
+       if (props.snapshots.beginning_clock != UINT64_C(-1)) {
+               BT_ASSERT(sc->default_clock_class);
+               ret = bt_util_clock_cycles_to_ns_from_origin(
+                       props.snapshots.beginning_clock,
+                       sc->default_clock_class->frequency,
+                       sc->default_clock_class->offset_seconds,
+                       sc->default_clock_class->offset_cycles, &begin_ns);
+               if (ret) {
+                       BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
+                               "Cannot convert clock cycles to nanoseconds from origin (`%s`).",
+                               path);
+                       goto error;
+               }
+       }
+
+       ds_file_info = ctf_fs_ds_file_info_create(path, begin_ns);
+       if (!ds_file_info) {
+               goto error;
+       }
+
+       index = ctf_fs_ds_file_build_index(ds_file, ds_file_info, msg_iter);
+       if (!index) {
+               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
+                       self_comp, self_comp_class,
+                       "Failed to index CTF stream file \'%s\'",
+                       ds_file->file->path->str);
+               goto error;
+       }
+
+       if (begin_ns == -1) {
+               /*
+                * No beginning timestamp to sort the stream files
+                * within a stream file group, so consider that this
+                * file must be the only one within its group.
+                */
+               stream_instance_id = -1;
+       }
+
+       if (stream_instance_id == -1) {
+               /*
+                * No stream instance ID or no beginning timestamp:
+                * create a unique stream file group for this stream
+                * file because, even if there's a stream instance ID,
+                * there's no timestamp to order the file within its
+                * group.
+                */
+               ds_file_group = ctf_fs_ds_file_group_create(ctf_fs_trace,
+                       sc, UINT64_C(-1), index);
+               /* Ownership of index is transferred. */
+               index = NULL;
+
+               if (!ds_file_group) {
+                       goto error;
+               }
+
+               ds_file_group_insert_ds_file_info_sorted(ds_file_group,
+                       BT_MOVE_REF(ds_file_info));
+
+               add_group = true;
+               goto end;
+       }
+
+       BT_ASSERT(stream_instance_id != -1);
+       BT_ASSERT(begin_ns != -1);
+
+       /* Find an existing stream file group with this ID */
+       for (i = 0; i < ctf_fs_trace->ds_file_groups->len; i++) {
+               ds_file_group = (struct ctf_fs_ds_file_group *) g_ptr_array_index(
+                       ctf_fs_trace->ds_file_groups, i);
+
+               if (ds_file_group->sc == sc &&
+                               ds_file_group->stream_id ==
+                               stream_instance_id) {
+                       break;
+               }
+
+               ds_file_group = NULL;
+       }
+
+       if (!ds_file_group) {
+               ds_file_group = ctf_fs_ds_file_group_create(ctf_fs_trace,
+                       sc, stream_instance_id, index);
+               /* Ownership of index is transferred. */
+               index = NULL;
+               if (!ds_file_group) {
+                       goto error;
+               }
+
+               add_group = true;
+       } else {
+               merge_ctf_fs_ds_indexes(ds_file_group->index, index);
+       }
+
+       ds_file_group_insert_ds_file_info_sorted(ds_file_group,
+               BT_MOVE_REF(ds_file_info));
+
+       goto end;
+
+error:
+       ctf_fs_ds_file_group_destroy(ds_file_group);
+       ds_file_group = NULL;
+       ret = -1;
+
+end:
+       if (add_group && ds_file_group) {
+               g_ptr_array_add(ctf_fs_trace->ds_file_groups, ds_file_group);
+       }
+
+       ctf_fs_ds_file_destroy(ds_file);
+       ctf_fs_ds_file_info_destroy(ds_file_info);
+
+       if (msg_iter) {
+               ctf_msg_iter_destroy(msg_iter);
+       }
+
+       ctf_fs_ds_index_destroy(index);
+       return ret;
+}
+
+static
+int create_ds_file_groups(struct ctf_fs_trace *ctf_fs_trace)
+{
+       int ret = 0;
+       const char *basename;
+       GError *error = NULL;
+       GDir *dir = NULL;
+       bt_logging_level log_level = ctf_fs_trace->log_level;
+       bt_self_component *self_comp = ctf_fs_trace->self_comp;
+       bt_self_component_class *self_comp_class = ctf_fs_trace->self_comp_class;
+
+       /* Check each file in the path directory, except specific ones */
+       dir = g_dir_open(ctf_fs_trace->path->str, 0, &error);
+       if (!dir) {
+               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
+                       "Cannot open directory `%s`: %s (code %d)",
+                       ctf_fs_trace->path->str, error->message,
+                       error->code);
+               goto error;
+       }
+
+       while ((basename = g_dir_read_name(dir))) {
+               struct ctf_fs_file *file;
+
+               if (strcmp(basename, CTF_FS_METADATA_FILENAME) == 0) {
+                       /* Ignore the metadata stream. */
+                       BT_COMP_LOGI("Ignoring metadata file `%s" G_DIR_SEPARATOR_S "%s`",
+                               ctf_fs_trace->path->str, basename);
+                       continue;
+               }
+
+               if (basename[0] == '.') {
+                       BT_COMP_LOGI("Ignoring hidden file `%s" G_DIR_SEPARATOR_S "%s`",
+                               ctf_fs_trace->path->str, basename);
+                       continue;
+               }
+
+               /* Create the file. */
+               file = ctf_fs_file_create(log_level, self_comp);
+               if (!file) {
+                       BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
+                               "Cannot create stream file object for file `%s" G_DIR_SEPARATOR_S "%s`",
+                               ctf_fs_trace->path->str, basename);
+                       goto error;
+               }
+
+               /* Create full path string. */
+               g_string_append_printf(file->path, "%s" G_DIR_SEPARATOR_S "%s",
+                               ctf_fs_trace->path->str, basename);
+               if (!g_file_test(file->path->str, G_FILE_TEST_IS_REGULAR)) {
+                       BT_COMP_LOGI("Ignoring non-regular file `%s`",
+                               file->path->str);
+                       ctf_fs_file_destroy(file);
+                       file = NULL;
+                       continue;
+               }
+
+               ret = ctf_fs_file_open(file, "rb");
+               if (ret) {
+                       BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
+                               "Cannot open stream file `%s`",
+                               file->path->str);
+                       goto error;
+               }
+
+               if (file->size == 0) {
+                       /* Skip empty stream. */
+                       BT_COMP_LOGI("Ignoring empty file `%s`", file->path->str);
+                       ctf_fs_file_destroy(file);
+                       continue;
+               }
+
+               ret = add_ds_file_to_ds_file_group(ctf_fs_trace,
+                       file->path->str);
+               if (ret) {
+                       BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
+                               "Cannot add stream file `%s` to stream file group",
+                               file->path->str);
+                       ctf_fs_file_destroy(file);
+                       goto error;
+               }
+
+               ctf_fs_file_destroy(file);
+       }
+
+       goto end;
+
+error:
+       ret = -1;
+
+end:
+       if (dir) {
+               g_dir_close(dir);
+               dir = NULL;
+       }
+
+       if (error) {
+               g_error_free(error);
+       }
+
+       return ret;
+}
+
+static
+int set_trace_name(bt_trace *trace, const char *name_suffix,
+               bt_logging_level log_level, bt_self_component *self_comp)
+{
+       int ret = 0;
+       const bt_value *val;
+       GString *name;
+
+       name = g_string_new(NULL);
+       if (!name) {
+               BT_COMP_LOGE_STR("Failed to allocate a GString.");
+               ret = -1;
+               goto end;
+       }
+
+       /*
+        * Check if we have a trace environment string value named `hostname`.
+        * If so, use it as the trace name's prefix.
+        */
+       val = bt_trace_borrow_environment_entry_value_by_name_const(
+               trace, "hostname");
+       if (val && bt_value_is_string(val)) {
+               g_string_append(name, bt_value_string_get(val));
+
+               if (name_suffix) {
+                       g_string_append_c(name, G_DIR_SEPARATOR);
+               }
+       }
+
+       if (name_suffix) {
+               g_string_append(name, name_suffix);
+       }
+
+       ret = bt_trace_set_name(trace, name->str);
+       if (ret) {
+               goto end;
+       }
+
+       goto end;
+
+end:
+       if (name) {
+               g_string_free(name, TRUE);
+       }
+
+       return ret;
+}
+
+static
+struct ctf_fs_trace *ctf_fs_trace_create(
+               bt_self_component *self_comp,
+               bt_self_component_class *self_comp_class,
+               const char *path, const char *name,
+               struct ctf_fs_metadata_config *metadata_config,
+               bt_logging_level log_level)
+{
+       struct ctf_fs_trace *ctf_fs_trace;
+       int ret;
+
+       /* Only one of them must be set. */
+       BT_ASSERT(!self_comp != !self_comp_class);
+
+       ctf_fs_trace = g_new0(struct ctf_fs_trace, 1);
+       if (!ctf_fs_trace) {
+               goto end;
+       }
+
+       ctf_fs_trace->log_level = log_level;
+       ctf_fs_trace->self_comp = self_comp;
+       ctf_fs_trace->self_comp_class = self_comp_class;
+       ctf_fs_trace->path = g_string_new(path);
+       if (!ctf_fs_trace->path) {
+               goto error;
+       }
+
+       ctf_fs_trace->metadata = g_new0(struct ctf_fs_metadata, 1);
+       if (!ctf_fs_trace->metadata) {
+               goto error;
+       }
+
+       ctf_fs_metadata_init(ctf_fs_trace->metadata);
+       ctf_fs_trace->ds_file_groups = g_ptr_array_new_with_free_func(
+               (GDestroyNotify) ctf_fs_ds_file_group_destroy);
+       if (!ctf_fs_trace->ds_file_groups) {
+               goto error;
+       }
+
+       ret = ctf_fs_metadata_set_trace_class(self_comp, ctf_fs_trace,
+               metadata_config);
+       if (ret) {
+               goto error;
+       }
+
+       if (ctf_fs_trace->metadata->trace_class) {
+               ctf_fs_trace->trace =
+                       bt_trace_create(ctf_fs_trace->metadata->trace_class);
+               if (!ctf_fs_trace->trace) {
+                       goto error;
+               }
+       }
+
+       if (ctf_fs_trace->trace) {
+               ret = ctf_trace_class_configure_ir_trace(
+                       ctf_fs_trace->metadata->tc, ctf_fs_trace->trace);
+               if (ret) {
+                       goto error;
+               }
+
+               ret = set_trace_name(ctf_fs_trace->trace, name, log_level,
+                       self_comp);
+               if (ret) {
+                       goto error;
+               }
+       }
+
+       ret = create_ds_file_groups(ctf_fs_trace);
+       if (ret) {
+               goto error;
+       }
+
+       goto end;
+
+error:
+       ctf_fs_trace_destroy(ctf_fs_trace);
+       ctf_fs_trace = NULL;
+
+end:
+       return ctf_fs_trace;
+}
+
+static
+int path_is_ctf_trace(const char *path)
+{
+       GString *metadata_path = g_string_new(NULL);
+       int ret = 0;
+
+       if (!metadata_path) {
+               ret = -1;
+               goto end;
+       }
+
+       g_string_printf(metadata_path, "%s" G_DIR_SEPARATOR_S "%s", path, CTF_FS_METADATA_FILENAME);
+
+       if (g_file_test(metadata_path->str, G_FILE_TEST_IS_REGULAR)) {
+               ret = 1;
+               goto end;
+       }
+
+end:
+       g_string_free(metadata_path, TRUE);
+       return ret;
+}
+
+/* Helper for ctf_fs_component_create_ctf_fs_trace, to handle a single path. */
+
+static
+int ctf_fs_component_create_ctf_fs_trace_one_path(
+               struct ctf_fs_component *ctf_fs,
+               const char *path_param,
+               const char *trace_name,
+               GPtrArray *traces,
+               bt_self_component *self_comp,
+               bt_self_component_class *self_comp_class)
+{
+       struct ctf_fs_trace *ctf_fs_trace;
+       int ret;
+       GString *norm_path;
+       bt_logging_level log_level = ctf_fs->log_level;
+
+       norm_path = bt_common_normalize_path(path_param, NULL);
+       if (!norm_path) {
+               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
+                       "Failed to normalize path: `%s`.",
+                       path_param);
+               goto error;
+       }
+
+       ret = path_is_ctf_trace(norm_path->str);
+       if (ret < 0) {
+               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
+                       "Failed to check if path is a CTF trace: path=%s",
+                       norm_path->str);
+               goto error;
+       } else if (ret == 0) {
+               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
+                       "Path is not a CTF trace (does not contain a metadata file): `%s`.",
+                       norm_path->str);
+               goto error;
+       }
+
+       // FIXME: Remove or ifdef for __MINGW32__
+       if (strcmp(norm_path->str, "/") == 0) {
+               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
+                       "Opening a trace in `/` is not supported.");
+               ret = -1;
+               goto end;
+       }
+
+       ctf_fs_trace = ctf_fs_trace_create(self_comp, self_comp_class, norm_path->str,
+               trace_name, &ctf_fs->metadata_config, log_level);
+       if (!ctf_fs_trace) {
+               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
+                       "Cannot create trace for `%s`.",
+                       norm_path->str);
+               goto error;
+       }
+
+       g_ptr_array_add(traces, ctf_fs_trace);
+       ctf_fs_trace = NULL;
+
+       ret = 0;
+       goto end;
+
+error:
+       ret = -1;
+
+end:
+       if (norm_path) {
+               g_string_free(norm_path, TRUE);
+       }
+
+       return ret;
+}
+
+/*
+ * Count the number of stream and event classes defined by this trace's metadata.
+ *
+ * This is used to determine which metadata is the "latest", out of multiple
+ * traces sharing the same UUID.  It is assumed that amongst all these metadatas,
+ * a bigger metadata is a superset of a smaller metadata.  Therefore, it is
+ * enough to just count the classes.
+ */
+
+static
+unsigned int metadata_count_stream_and_event_classes(struct ctf_fs_trace *trace)
+{
+       unsigned int num = trace->metadata->tc->stream_classes->len;
+       guint i;
+
+       for (i = 0; i < trace->metadata->tc->stream_classes->len; i++) {
+               struct ctf_stream_class *sc =
+                       (struct ctf_stream_class *) trace->metadata->tc->stream_classes->pdata[i];
+               num += sc->event_classes->len;
+       }
+
+       return num;
+}
+
+/*
+ * Merge the src ds_file_group into dest.  This consists of merging their
+ * ds_file_infos, making sure to keep the result sorted.
+ */
+
+static
+void merge_ctf_fs_ds_file_groups(struct ctf_fs_ds_file_group *dest, struct ctf_fs_ds_file_group *src)
+{
+       guint i;
+
+       for (i = 0; i < src->ds_file_infos->len; i++) {
+               struct ctf_fs_ds_file_info *ds_file_info =
+                       (struct ctf_fs_ds_file_info *) g_ptr_array_index(src->ds_file_infos, i);
+
+               /* Ownership of the ds_file_info is transferred to dest. */
+               g_ptr_array_index(src->ds_file_infos, i) = NULL;
+
+               ds_file_group_insert_ds_file_info_sorted(dest, ds_file_info);
+       }
+
+       /* Merge both indexes. */
+       merge_ctf_fs_ds_indexes(dest->index, src->index);
+}
+/* Merge src_trace's data stream file groups into dest_trace's. */
+
+static
+int merge_matching_ctf_fs_ds_file_groups(
+               struct ctf_fs_trace *dest_trace,
+               struct ctf_fs_trace *src_trace)
+{
+
+       GPtrArray *dest = dest_trace->ds_file_groups;
+       GPtrArray *src = src_trace->ds_file_groups;
+       guint s_i;
+       int ret = 0;
+
+       /*
+        * Save the initial length of dest: we only want to check against the
+        * original elements in the inner loop.
+        */
+       const guint dest_len = dest->len;
+
+       for (s_i = 0; s_i < src->len; s_i++) {
+               struct ctf_fs_ds_file_group *src_group =
+                       (struct ctf_fs_ds_file_group *) g_ptr_array_index(src, s_i);
+               struct ctf_fs_ds_file_group *dest_group = NULL;
+
+               /* A stream instance without ID can't match a stream in the other trace.  */
+               if (src_group->stream_id != -1) {
+                       guint d_i;
+
+                       /* Let's search for a matching ds_file_group in the destination.  */
+                       for (d_i = 0; d_i < dest_len; d_i++) {
+                               struct ctf_fs_ds_file_group *candidate_dest =
+                                       (struct ctf_fs_ds_file_group *) g_ptr_array_index(dest, d_i);
+
+                               /* Can't match a stream instance without ID.  */
+                               if (candidate_dest->stream_id == -1) {
+                                       continue;
+                               }
+
+                               /*
+                                * If the two groups have the same stream instance id
+                                * and belong to the same stream class (stream instance
+                                * ids are per-stream class), they represent the same
+                                * stream instance.
+                                */
+                               if (candidate_dest->stream_id != src_group->stream_id ||
+                                               candidate_dest->sc->id != src_group->sc->id) {
+                                       continue;
+                               }
+
+                               dest_group = candidate_dest;
+                               break;
+                       }
+               }
+
+               /*
+                * Didn't find a friend in dest to merge our src_group into?
+                * Create a new empty one. This can happen if a stream was
+                * active in the source trace chunk but not in the destination
+                * trace chunk.
+                */
+               if (!dest_group) {
+                       struct ctf_stream_class *sc;
+                       struct ctf_fs_ds_index *index;
+
+                       sc = ctf_trace_class_borrow_stream_class_by_id(
+                               dest_trace->metadata->tc, src_group->sc->id);
+                       BT_ASSERT(sc);
+
+                       index = ctf_fs_ds_index_create(dest_trace->log_level,
+                               dest_trace->self_comp);
+                       if (!index) {
+                               ret = -1;
+                               goto end;
+                       }
+
+                       dest_group = ctf_fs_ds_file_group_create(dest_trace, sc,
+                               src_group->stream_id, index);
+                       /* Ownership of index is transferred. */
+                       index = NULL;
+                       if (!dest_group) {
+                               ret = -1;
+                               goto end;
+                       }
+
+                       g_ptr_array_add(dest_trace->ds_file_groups, dest_group);
+               }
+
+               BT_ASSERT(dest_group);
+               merge_ctf_fs_ds_file_groups(dest_group, src_group);
+       }
+
+end:
+       return ret;
+}
+
+/*
+ * Collapse the given traces, which must all share the same UUID, in a single
+ * one.
+ *
+ * The trace with the most expansive metadata is chosen and all other traces
+ * are merged into that one.  The array slots of all the traces that get merged
+ * in the chosen one are set to NULL, so only the slot of the chosen trace
+ * remains non-NULL.
+ */
+
+static
+int merge_ctf_fs_traces(struct ctf_fs_trace **traces, unsigned int num_traces,
+               struct ctf_fs_trace **out_trace)
+{
+       unsigned int winner_count;
+       struct ctf_fs_trace *winner;
+       guint i, winner_i;
+       int ret = 0;
+
+       BT_ASSERT(num_traces >= 2);
+
+       winner_count = metadata_count_stream_and_event_classes(traces[0]);
+       winner = traces[0];
+       winner_i = 0;
+
+       /* Find the trace with the largest metadata. */
+       for (i = 1; i < num_traces; i++) {
+               struct ctf_fs_trace *candidate;
+               unsigned int candidate_count;
+
+               candidate = traces[i];
+
+               /* A bit of sanity check. */
+               BT_ASSERT(bt_uuid_compare(winner->metadata->tc->uuid, candidate->metadata->tc->uuid) == 0);
+
+               candidate_count = metadata_count_stream_and_event_classes(candidate);
+
+               if (candidate_count > winner_count) {
+                       winner_count = candidate_count;
+                       winner = candidate;
+                       winner_i = i;
+               }
+       }
+
+       /* Merge all the other traces in the winning trace. */
+       for (i = 0; i < num_traces; i++) {
+               struct ctf_fs_trace *trace = traces[i];
+
+               /* Don't merge the winner into itself. */
+               if (trace == winner) {
+                       continue;
+               }
+
+               /* Merge trace's data stream file groups into winner's. */
+               ret = merge_matching_ctf_fs_ds_file_groups(winner, trace);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+       /*
+        * Move the winner out of the array, into `*out_trace`.
+        */
+       *out_trace = winner;
+       traces[winner_i] = NULL;
+
+end:
+       return ret;
+}
+
+enum target_event {
+       FIRST_EVENT,
+       LAST_EVENT,
+};
+
+static
+int decode_clock_snapshot_after_event(struct ctf_fs_trace *ctf_fs_trace,
+       struct ctf_clock_class *default_cc,
+       struct ctf_fs_ds_index_entry *index_entry,
+       enum target_event target_event, uint64_t *cs, int64_t *ts_ns)
+{
+       enum ctf_msg_iter_status iter_status = CTF_MSG_ITER_STATUS_OK;
+       struct ctf_fs_ds_file *ds_file = NULL;
+       struct ctf_msg_iter *msg_iter = NULL;
+       bt_logging_level log_level = ctf_fs_trace->log_level;
+       bt_self_component *self_comp = ctf_fs_trace->self_comp;
+       int ret = 0;
+
+       BT_ASSERT(ctf_fs_trace);
+       BT_ASSERT(index_entry);
+       BT_ASSERT(index_entry->path);
+
+       ds_file = ctf_fs_ds_file_create(ctf_fs_trace, NULL,
+               NULL, index_entry->path, log_level);
+       if (!ds_file) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Failed to create a ctf_fs_ds_file");
+               ret = -1;
+               goto end;
+       }
+
+       BT_ASSERT(ctf_fs_trace->metadata);
+       BT_ASSERT(ctf_fs_trace->metadata->tc);
+
+       msg_iter = ctf_msg_iter_create(ctf_fs_trace->metadata->tc,
+               bt_common_get_page_size(log_level) * 8, ctf_fs_ds_file_medops,
+               ds_file, log_level, self_comp, NULL);
+       if (!msg_iter) {
+               /* ctf_msg_iter_create() logs errors. */
+               ret = -1;
+               goto end;
+       }
+
+       /*
+        * Turn on dry run mode to prevent the creation and usage of Babeltrace
+        * library objects (bt_field, bt_message_*, etc.).
+        */
+       ctf_msg_iter_set_dry_run(msg_iter, true);
+
+       /* Seek to the beginning of the target packet. */
+       iter_status = ctf_msg_iter_seek(msg_iter, index_entry->offset);
+       if (iter_status) {
+               /* ctf_msg_iter_seek() logs errors. */
+               ret = -1;
+               goto end;
+       }
+
+       switch (target_event) {
+       case FIRST_EVENT:
+               /*
+                * Start to decode the packet until we reach the end of
+                * the first event. To extract the first event's clock
+                * snapshot.
+                */
+               iter_status = ctf_msg_iter_curr_packet_first_event_clock_snapshot(
+                       msg_iter, cs);
+               break;
+       case LAST_EVENT:
+               /* Decode the packet to extract the last event's clock snapshot. */
+               iter_status = ctf_msg_iter_curr_packet_last_event_clock_snapshot(
+                       msg_iter, cs);
+               break;
+       default:
+               bt_common_abort();
+       }
+       if (iter_status) {
+               ret = -1;
+               goto end;
+       }
+
+       /* Convert clock snapshot to timestamp. */
+       ret = bt_util_clock_cycles_to_ns_from_origin(*cs,
+               default_cc->frequency, default_cc->offset_seconds,
+               default_cc->offset_cycles, ts_ns);
+       if (ret) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Failed to convert clock snapshot to timestamp");
+               goto end;
+       }
+
+end:
+       if (ds_file) {
+               ctf_fs_ds_file_destroy(ds_file);
+       }
+       if (msg_iter) {
+               ctf_msg_iter_destroy(msg_iter);
+       }
+
+       return ret;
+}
+
+static
+int decode_packet_first_event_timestamp(struct ctf_fs_trace *ctf_fs_trace,
+       struct ctf_clock_class *default_cc,
+       struct ctf_fs_ds_index_entry *index_entry, uint64_t *cs, int64_t *ts_ns)
+{
+       return decode_clock_snapshot_after_event(ctf_fs_trace, default_cc,
+               index_entry, FIRST_EVENT, cs, ts_ns);
+}
+
+static
+int decode_packet_last_event_timestamp(struct ctf_fs_trace *ctf_fs_trace,
+       struct ctf_clock_class *default_cc,
+       struct ctf_fs_ds_index_entry *index_entry, uint64_t *cs, int64_t *ts_ns)
+{
+       return decode_clock_snapshot_after_event(ctf_fs_trace, default_cc,
+               index_entry, LAST_EVENT, cs, ts_ns);
+}
+
+/*
+ * Fix up packet index entries for lttng's "event-after-packet" bug.
+ * Some buggy lttng tracer versions may emit events with a timestamp that is
+ * larger (after) than the timestamp_end of the their packets.
+ *
+ * To fix up this erroneous data we do the following:
+ *  1. If it's not the stream file's last packet: set the packet index entry's
+ *     end time to the next packet's beginning time.
+ *  2. If it's the stream file's last packet, set the packet index entry's end
+ *     time to the packet's last event's time, if any, or to the packet's
+ *     beginning time otherwise.
+ *
+ * Known buggy tracer versions:
+ *  - before lttng-ust 2.11.0
+ *  - before lttng-module 2.11.0
+ *  - before lttng-module 2.10.10
+ *  - before lttng-module 2.9.13
+ */
+static
+int fix_index_lttng_event_after_packet_bug(struct ctf_fs_trace *trace)
+{
+       int ret = 0;
+       guint ds_file_group_i;
+       GPtrArray *ds_file_groups = trace->ds_file_groups;
+       bt_logging_level log_level = trace->log_level;
+
+       for (ds_file_group_i = 0; ds_file_group_i < ds_file_groups->len;
+                       ds_file_group_i++) {
+               guint entry_i;
+               struct ctf_clock_class *default_cc;
+               struct ctf_fs_ds_index_entry *last_entry;
+               struct ctf_fs_ds_index *index;
+
+               struct ctf_fs_ds_file_group *ds_file_group =
+                       (struct ctf_fs_ds_file_group *) g_ptr_array_index(ds_file_groups, ds_file_group_i);
+
+               BT_ASSERT(ds_file_group);
+               index = ds_file_group->index;
+
+               BT_ASSERT(index);
+               BT_ASSERT(index->entries);
+               BT_ASSERT(index->entries->len > 0);
+
+               /*
+                * Iterate over all entries but the last one. The last one is
+                * fixed differently after.
+                */
+               for (entry_i = 0; entry_i < index->entries->len - 1;
+                               entry_i++) {
+                       struct ctf_fs_ds_index_entry *curr_entry, *next_entry;
+
+                       curr_entry = (ctf_fs_ds_index_entry *) g_ptr_array_index(index->entries, entry_i);
+                       next_entry = (ctf_fs_ds_index_entry *) g_ptr_array_index(index->entries, entry_i + 1);
+
+                       /*
+                        * 1. Set the current index entry `end` timestamp to
+                        * the next index entry `begin` timestamp.
+                        */
+                       curr_entry->timestamp_end = next_entry->timestamp_begin;
+                       curr_entry->timestamp_end_ns = next_entry->timestamp_begin_ns;
+               }
+
+               /*
+                * 2. Fix the last entry by decoding the last event of the last
+                * packet.
+                */
+               last_entry = (ctf_fs_ds_index_entry *) g_ptr_array_index(index->entries,
+                       index->entries->len - 1);
+               BT_ASSERT(last_entry);
+
+               BT_ASSERT(ds_file_group->sc->default_clock_class);
+               default_cc = ds_file_group->sc->default_clock_class;
+
+               /*
+                * Decode packet to read the timestamp of the last event of the
+                * entry.
+                */
+               ret = decode_packet_last_event_timestamp(trace, default_cc,
+                       last_entry, &last_entry->timestamp_end,
+                       &last_entry->timestamp_end_ns);
+               if (ret) {
+                       BT_COMP_LOGE_APPEND_CAUSE(trace->self_comp,
+                               "Failed to decode stream's last packet to get its last event's clock snapshot.");
+                       goto end;
+               }
+       }
+
+end:
+       return ret;
+}
+
+/*
+ * Fix up packet index entries for barectf's "event-before-packet" bug.
+ * Some buggy barectf tracer versions may emit events with a timestamp that is
+ * less than the timestamp_begin of the their packets.
+ *
+ * To fix up this erroneous data we do the following:
+ *  1. Starting at the second index entry, set the timestamp_begin of the
+ *     current entry to the timestamp of the first event of the packet.
+ *  2. Set the previous entry's timestamp_end to the timestamp_begin of the
+ *     current packet.
+ *
+ * Known buggy tracer versions:
+ *  - before barectf 2.3.1
+ */
+static
+int fix_index_barectf_event_before_packet_bug(struct ctf_fs_trace *trace)
+{
+       int ret = 0;
+       guint ds_file_group_i;
+       GPtrArray *ds_file_groups = trace->ds_file_groups;
+       bt_logging_level log_level = trace->log_level;
+
+       for (ds_file_group_i = 0; ds_file_group_i < ds_file_groups->len;
+                       ds_file_group_i++) {
+               guint entry_i;
+               struct ctf_clock_class *default_cc;
+               ctf_fs_ds_file_group *ds_file_group =
+                       (ctf_fs_ds_file_group *) g_ptr_array_index(ds_file_groups, ds_file_group_i);
+
+               struct ctf_fs_ds_index *index = ds_file_group->index;
+
+               BT_ASSERT(index);
+               BT_ASSERT(index->entries);
+               BT_ASSERT(index->entries->len > 0);
+
+               BT_ASSERT(ds_file_group->sc->default_clock_class);
+               default_cc = ds_file_group->sc->default_clock_class;
+
+               /*
+                * 1. Iterate over the index, starting from the second entry
+                * (index = 1).
+                */
+               for (entry_i = 1; entry_i < index->entries->len;
+                               entry_i++) {
+                       ctf_fs_ds_index_entry *prev_entry =
+                               (ctf_fs_ds_index_entry *) g_ptr_array_index(index->entries, entry_i - 1);
+                       ctf_fs_ds_index_entry *curr_entry =
+                               (ctf_fs_ds_index_entry *) g_ptr_array_index(index->entries, entry_i);
+                       /*
+                        * 2. Set the current entry `begin` timestamp to the
+                        * timestamp of the first event of the current packet.
+                        */
+                       ret = decode_packet_first_event_timestamp(trace, default_cc,
+                               curr_entry, &curr_entry->timestamp_begin,
+                               &curr_entry->timestamp_begin_ns);
+                       if (ret) {
+                               BT_COMP_LOGE_APPEND_CAUSE(trace->self_comp,
+                                       "Failed to decode first event's clock snapshot");
+                               goto end;
+                       }
+
+                       /*
+                        * 3. Set the previous entry `end` timestamp to the
+                        * timestamp of the first event of the current packet.
+                        */
+                       prev_entry->timestamp_end = curr_entry->timestamp_begin;
+                       prev_entry->timestamp_end_ns = curr_entry->timestamp_begin_ns;
+               }
+       }
+end:
+       return ret;
+}
+
+/*
+ * When using the lttng-crash feature it's likely that the last packets of each
+ * stream have their timestamp_end set to zero. This is caused by the fact that
+ * the tracer crashed and was not able to properly close the packets.
+ *
+ * To fix up this erroneous data we do the following:
+ * For each index entry, if the entry's timestamp_end is 0 and the
+ * timestamp_begin is not 0:
+ *  - If it's the stream file's last packet: set the packet index entry's end
+ *    time to the packet's last event's time, if any, or to the packet's
+ *    beginning time otherwise.
+ *  - If it's not the stream file's last packet: set the packet index
+ *    entry's end time to the next packet's beginning time.
+ *
+ * Affected versions:
+ * - All current and future lttng-ust and lttng-modules versions.
+ */
+static
+int fix_index_lttng_crash_quirk(struct ctf_fs_trace *trace)
+{
+       int ret = 0;
+       guint ds_file_group_idx;
+       GPtrArray *ds_file_groups = trace->ds_file_groups;
+       bt_logging_level log_level = trace->log_level;
+
+       for (ds_file_group_idx = 0; ds_file_group_idx < ds_file_groups->len;
+                       ds_file_group_idx++) {
+               guint entry_idx;
+               struct ctf_clock_class *default_cc;
+               struct ctf_fs_ds_index *index;
+
+               ctf_fs_ds_file_group *ds_file_group =
+                       (ctf_fs_ds_file_group *) g_ptr_array_index(ds_file_groups, ds_file_group_idx);
+
+               BT_ASSERT(ds_file_group);
+               index = ds_file_group->index;
+
+               BT_ASSERT(ds_file_group->sc->default_clock_class);
+               default_cc = ds_file_group->sc->default_clock_class;
+
+               BT_ASSERT(index);
+               BT_ASSERT(index->entries);
+               BT_ASSERT(index->entries->len > 0);
+
+               ctf_fs_ds_index_entry *last_entry =
+                       (ctf_fs_ds_index_entry *) g_ptr_array_index(index->entries,
+                               index->entries->len - 1);
+               BT_ASSERT(last_entry);
+
+
+               /* 1. Fix the last entry first. */
+               if (last_entry->timestamp_end == 0 &&
+                               last_entry->timestamp_begin != 0) {
+                       /*
+                        * Decode packet to read the timestamp of the
+                        * last event of the stream file.
+                        */
+                       ret = decode_packet_last_event_timestamp(trace,
+                               default_cc, last_entry,
+                               &last_entry->timestamp_end,
+                               &last_entry->timestamp_end_ns);
+                       if (ret) {
+                               BT_COMP_LOGE_APPEND_CAUSE(trace->self_comp,
+                                       "Failed to decode last event's clock snapshot");
+                               goto end;
+                       }
+               }
+
+               /* Iterate over all entries but the last one. */
+               for (entry_idx = 0; entry_idx < index->entries->len - 1;
+                               entry_idx++) {
+                       ctf_fs_ds_index_entry *curr_entry =
+                               (ctf_fs_ds_index_entry *) g_ptr_array_index(index->entries, entry_idx);
+                       ctf_fs_ds_index_entry *next_entry =
+                               (ctf_fs_ds_index_entry *) g_ptr_array_index(index->entries, entry_idx + 1);
+
+                       if (curr_entry->timestamp_end == 0 &&
+                                       curr_entry->timestamp_begin != 0) {
+                               /*
+                                * 2. Set the current index entry `end` timestamp to
+                                * the next index entry `begin` timestamp.
+                                */
+                               curr_entry->timestamp_end = next_entry->timestamp_begin;
+                               curr_entry->timestamp_end_ns = next_entry->timestamp_begin_ns;
+                       }
+               }
+       }
+
+end:
+       return ret;
+}
+
+/*
+ * Extract the tracer information necessary to compare versions.
+ * Returns 0 on success, and -1 if the extraction is not successful because the
+ * necessary fields are absents in the trace metadata.
+ */
+static
+int extract_tracer_info(struct ctf_fs_trace *trace,
+               struct tracer_info *current_tracer_info)
+{
+       int ret = 0;
+       struct ctf_trace_class_env_entry *entry;
+
+       /* Clear the current_tracer_info struct */
+       memset(current_tracer_info, 0, sizeof(*current_tracer_info));
+
+       /*
+        * To compare 2 tracer versions, at least the tracer name and it's
+        * major version are needed. If one of these is missing, consider it an
+        * extraction failure.
+        */
+       entry = ctf_trace_class_borrow_env_entry_by_name(
+               trace->metadata->tc, "tracer_name");
+       if (!entry || entry->type != CTF_TRACE_CLASS_ENV_ENTRY_TYPE_STR) {
+               goto missing_bare_minimum;
+       }
+
+       /* Set tracer name. */
+       current_tracer_info->name = entry->value.str->str;
+
+       entry = ctf_trace_class_borrow_env_entry_by_name(
+               trace->metadata->tc, "tracer_major");
+       if (!entry || entry->type != CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT) {
+               goto missing_bare_minimum;
+       }
+
+       /* Set major version number. */
+       current_tracer_info->major = entry->value.i;
+
+       entry = ctf_trace_class_borrow_env_entry_by_name(
+               trace->metadata->tc, "tracer_minor");
+       if (!entry || entry->type != CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT) {
+               goto end;
+       }
+
+       /* Set minor version number. */
+       current_tracer_info->minor = entry->value.i;
+
+       entry = ctf_trace_class_borrow_env_entry_by_name(
+               trace->metadata->tc, "tracer_patch");
+       if (!entry) {
+               /*
+                * If `tracer_patch` doesn't exist `tracer_patchlevel` might.
+                * For example, `lttng-modules` uses entry name
+                * `tracer_patchlevel`.
+                */
+               entry = ctf_trace_class_borrow_env_entry_by_name(
+                       trace->metadata->tc, "tracer_patchlevel");
+       }
+
+       if (!entry || entry->type != CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT) {
+               goto end;
+       }
+
+       /* Set patch version number. */
+       current_tracer_info->patch = entry->value.i;
+
+       goto end;
+
+missing_bare_minimum:
+       ret = -1;
+end:
+       return ret;
+}
+
+static
+bool is_tracer_affected_by_lttng_event_after_packet_bug(
+               struct tracer_info *curr_tracer_info)
+{
+       bool is_affected = false;
+
+       if (strcmp(curr_tracer_info->name, "lttng-ust") == 0) {
+               if (curr_tracer_info->major < 2) {
+                       is_affected = true;
+               } else if (curr_tracer_info->major == 2) {
+                       /* fixed in lttng-ust 2.11.0 */
+                       if (curr_tracer_info->minor < 11) {
+                               is_affected = true;
+                       }
+               }
+       } else if (strcmp(curr_tracer_info->name, "lttng-modules") == 0) {
+               if (curr_tracer_info->major < 2) {
+                       is_affected = true;
+               } else if (curr_tracer_info->major == 2) {
+                       /* fixed in lttng-modules 2.11.0 */
+                       if (curr_tracer_info->minor == 10) {
+                               /* fixed in lttng-modules 2.10.10 */
+                               if (curr_tracer_info->patch < 10) {
+                                       is_affected = true;
+                               }
+                       } else if (curr_tracer_info->minor == 9) {
+                               /* fixed in lttng-modules 2.9.13 */
+                               if (curr_tracer_info->patch < 13) {
+                                       is_affected = true;
+                               }
+                       } else if (curr_tracer_info->minor < 9) {
+                               is_affected = true;
+                       }
+               }
+       }
+
+       return is_affected;
+}
+
+static
+bool is_tracer_affected_by_barectf_event_before_packet_bug(
+               struct tracer_info *curr_tracer_info)
+{
+       bool is_affected = false;
+
+       if (strcmp(curr_tracer_info->name, "barectf") == 0) {
+               if (curr_tracer_info->major < 2) {
+                       is_affected = true;
+               } else if (curr_tracer_info->major == 2) {
+                       if (curr_tracer_info->minor < 3) {
+                               is_affected = true;
+                       } else if (curr_tracer_info->minor == 3) {
+                               /* fixed in barectf 2.3.1 */
+                               if (curr_tracer_info->patch < 1) {
+                                       is_affected = true;
+                               }
+                       }
+               }
+       }
+
+       return is_affected;
+}
+
+static
+bool is_tracer_affected_by_lttng_crash_quirk(
+               struct tracer_info *curr_tracer_info)
+{
+       bool is_affected = false;
+
+       /* All LTTng tracer may be affected by this lttng crash quirk. */
+       if (strcmp(curr_tracer_info->name, "lttng-ust") == 0) {
+               is_affected = true;
+       } else if (strcmp(curr_tracer_info->name, "lttng-modules") == 0) {
+               is_affected = true;
+       }
+
+       return is_affected;
+}
+
+/*
+ * Looks for trace produced by known buggy tracers and fix up the index
+ * produced earlier.
+ */
+static
+int fix_packet_index_tracer_bugs(struct ctf_fs_component *ctf_fs,
+               bt_self_component *self_comp,
+               bt_self_component_class *self_comp_class)
+{
+       int ret = 0;
+       struct tracer_info current_tracer_info;
+       bt_logging_level log_level = ctf_fs->log_level;
+
+       ret = extract_tracer_info(ctf_fs->trace, &current_tracer_info);
+       if (ret) {
+               /*
+                * A trace may not have all the necessary environment
+                * entries to do the tracer version comparison.
+                * At least, the tracer name and major version number
+                * are needed. Failing to extract these entries is not
+                * an error.
+                */
+               ret = 0;
+               BT_LOGI_STR("Cannot extract tracer information necessary to compare with buggy versions.");
+               goto end;;
+       }
+
+       /* Check if the trace may be affected by old tracer bugs. */
+       if (is_tracer_affected_by_lttng_event_after_packet_bug(
+                       &current_tracer_info)) {
+               BT_LOGI_STR("Trace may be affected by LTTng tracer packet timestamp bug. Fixing up.");
+               ret = fix_index_lttng_event_after_packet_bug(ctf_fs->trace);
+               if (ret) {
+                       BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
+                               self_comp, self_comp_class,
+                               "Failed to fix LTTng event-after-packet bug.");
+                       goto end;
+               }
+               ctf_fs->trace->metadata->tc->quirks.lttng_event_after_packet = true;
+       }
+
+       if (is_tracer_affected_by_barectf_event_before_packet_bug(
+                       &current_tracer_info)) {
+               BT_LOGI_STR("Trace may be affected by barectf tracer packet timestamp bug. Fixing up.");
+               ret = fix_index_barectf_event_before_packet_bug(ctf_fs->trace);
+               if (ret) {
+                       BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
+                               self_comp, self_comp_class,
+                               "Failed to fix barectf event-before-packet bug.");
+                       goto end;
+               }
+               ctf_fs->trace->metadata->tc->quirks.barectf_event_before_packet = true;
+       }
+
+       if (is_tracer_affected_by_lttng_crash_quirk(
+                       &current_tracer_info)) {
+               ret = fix_index_lttng_crash_quirk(ctf_fs->trace);
+               if (ret) {
+                       BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
+                               self_comp, self_comp_class,
+                               "Failed to fix lttng-crash timestamp quirks.");
+                       goto end;
+               }
+               ctf_fs->trace->metadata->tc->quirks.lttng_crash = true;
+       }
+
+end:
+       return ret;
+}
+
+static
+gint compare_ds_file_groups_by_first_path(gconstpointer a, gconstpointer b)
+{
+       ctf_fs_ds_file_group * const *ds_file_group_a =
+               (ctf_fs_ds_file_group **) a;
+       ctf_fs_ds_file_group * const *ds_file_group_b =
+               (ctf_fs_ds_file_group **) b;
+
+       BT_ASSERT((*ds_file_group_a)->ds_file_infos->len > 0);
+       BT_ASSERT((*ds_file_group_b)->ds_file_infos->len > 0);
+
+       const ctf_fs_ds_file_info *first_ds_file_info_a =
+               (const ctf_fs_ds_file_info *) (*ds_file_group_a)->ds_file_infos->pdata[0];
+       const ctf_fs_ds_file_info *first_ds_file_info_b =
+               (const ctf_fs_ds_file_info *) (*ds_file_group_b)->ds_file_infos->pdata[0];
+
+       return strcmp(first_ds_file_info_a->path->str,
+               first_ds_file_info_b->path->str);
+}
+
+static
+gint compare_strings(gconstpointer p_a, gconstpointer p_b)
+{
+       const char *a = *((const char **) p_a);
+       const char *b = *((const char **) p_b);
+
+       return strcmp(a, b);
+}
+
+int ctf_fs_component_create_ctf_fs_trace(
+               struct ctf_fs_component *ctf_fs,
+               const bt_value *paths_value,
+               const bt_value *trace_name_value,
+               bt_self_component *self_comp,
+               bt_self_component_class *self_comp_class)
+{
+       int ret = 0;
+       uint64_t i;
+       bt_logging_level log_level = ctf_fs->log_level;
+       GPtrArray *paths = NULL;
+       GPtrArray *traces;
+       const char *trace_name;
+
+       BT_ASSERT(bt_value_get_type(paths_value) == BT_VALUE_TYPE_ARRAY);
+       BT_ASSERT(!bt_value_array_is_empty(paths_value));
+
+       traces = g_ptr_array_new_with_free_func(ctf_fs_trace_destroy_notifier);
+       if (!traces) {
+               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
+                       "Failed to allocate a GPtrArray.");
+               goto error;
+       }
+
+       paths = g_ptr_array_new_with_free_func(g_free);
+       if (!paths) {
+               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
+                       "Failed to allocate a GPtrArray.");
+               goto error;
+       }
+
+       trace_name = trace_name_value ? bt_value_string_get(trace_name_value) : NULL;
+
+       /*
+        * Create a sorted array of the paths, to make the execution of this
+        * component deterministic.
+        */
+       for (i = 0; i < bt_value_array_get_length(paths_value); i++) {
+               const bt_value *path_value =
+                       bt_value_array_borrow_element_by_index_const(paths_value, i);
+               const char *input = bt_value_string_get(path_value);
+               gchar *input_copy;
+
+               input_copy = g_strdup(input);
+               if (!input_copy) {
+                       BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
+                               "Failed to copy a string.");
+                       goto error;
+               }
+
+               g_ptr_array_add(paths, input_copy);
+       }
+
+       g_ptr_array_sort(paths, compare_strings);
+
+       /* Create a separate ctf_fs_trace object for each path. */
+       for (i = 0; i < paths->len; i++) {
+               const char *path = (const char *) g_ptr_array_index(paths, i);
+
+               ret = ctf_fs_component_create_ctf_fs_trace_one_path(ctf_fs,
+                       path, trace_name, traces, self_comp, self_comp_class);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+       if (traces->len > 1) {
+               struct ctf_fs_trace *first_trace = (struct ctf_fs_trace *) traces->pdata[0];
+               const uint8_t *first_trace_uuid = first_trace->metadata->tc->uuid;
+               struct ctf_fs_trace *trace;
+
+               /*
+                * We have more than one trace, they must all share the same
+                * UUID, verify that.
+                */
+               for (i = 0; i < traces->len; i++) {
+                       struct ctf_fs_trace *this_trace =
+                               (struct ctf_fs_trace *) traces->pdata[i];
+                       const uint8_t *this_trace_uuid = this_trace->metadata->tc->uuid;
+
+                       if (!this_trace->metadata->tc->is_uuid_set) {
+                               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
+                                       "Multiple traces given, but a trace does not have a UUID: path=%s",
+                                       this_trace->path->str);
+                               goto error;
+                       }
+
+                       if (bt_uuid_compare(first_trace_uuid, this_trace_uuid) != 0) {
+                               char first_trace_uuid_str[BT_UUID_STR_LEN + 1];
+                               char this_trace_uuid_str[BT_UUID_STR_LEN + 1];
+
+                               bt_uuid_to_str(first_trace_uuid, first_trace_uuid_str);
+                               bt_uuid_to_str(this_trace_uuid, this_trace_uuid_str);
+
+                               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
+                                       "Multiple traces given, but UUIDs don't match: "
+                                       "first-trace-uuid=%s, first-trace-path=%s, "
+                                       "trace-uuid=%s, trace-path=%s",
+                                       first_trace_uuid_str, first_trace->path->str,
+                                       this_trace_uuid_str, this_trace->path->str);
+                               goto error;
+                       }
+               }
+
+               ret = merge_ctf_fs_traces((struct ctf_fs_trace **) traces->pdata,
+                       traces->len, &trace);
+               if (ret) {
+                       BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
+                               "Failed to merge traces with the same UUID.");
+                       goto error;
+               }
+
+               ctf_fs->trace = trace;
+       } else {
+               /* Just one trace, it may or may not have a UUID, both are fine. */
+               ctf_fs->trace = (ctf_fs_trace *) traces->pdata[0];
+               traces->pdata[0] = NULL;
+       }
+
+       ret = fix_packet_index_tracer_bugs(ctf_fs, self_comp, self_comp_class);
+       if (ret) {
+               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
+                       "Failed to fix packet index tracer bugs.");
+       }
+
+       /*
+        * Sort data stream file groups by first data stream file info
+        * path to get a deterministic order. This order influences the
+        * order of the output ports. It also influences the order of
+        * the automatic stream IDs if the trace's packet headers do not
+        * contain a `stream_instance_id` field, in which case the data
+        * stream file to stream ID association is always the same,
+        * whatever the build and the system.
+        *
+        * Having a deterministic order here can help debugging and
+        * testing.
+        */
+       g_ptr_array_sort(ctf_fs->trace->ds_file_groups,
+               compare_ds_file_groups_by_first_path);
+       goto end;
+error:
+       ret = -1;
+
+end:
+       if (traces) {
+               g_ptr_array_free(traces, TRUE);
+       }
+
+       if (paths) {
+               g_ptr_array_free(paths, TRUE);
+       }
+
+       return ret;
+}
+
+static
+GString *get_stream_instance_unique_name(
+               struct ctf_fs_ds_file_group *ds_file_group)
+{
+       GString *name;
+       struct ctf_fs_ds_file_info *ds_file_info;
+
+       name = g_string_new(NULL);
+       if (!name) {
+               goto end;
+       }
+
+       /*
+        * If there's more than one stream file in the stream file
+        * group, the first (earliest) stream file's path is used as
+        * the stream's unique name.
+        */
+       BT_ASSERT(ds_file_group->ds_file_infos->len > 0);
+       ds_file_info = (ctf_fs_ds_file_info *) g_ptr_array_index(ds_file_group->ds_file_infos, 0);
+       g_string_assign(name, ds_file_info->path->str);
+
+end:
+       return name;
+}
+
+/* Create the IR stream objects for ctf_fs_trace. */
+
+static
+int create_streams_for_trace(struct ctf_fs_trace *ctf_fs_trace)
+{
+       int ret;
+       GString *name = NULL;
+       guint i;
+       bt_logging_level log_level = ctf_fs_trace->log_level;
+       bt_self_component *self_comp = ctf_fs_trace->self_comp;
+
+       for (i = 0; i < ctf_fs_trace->ds_file_groups->len; i++) {
+               ctf_fs_ds_file_group *ds_file_group =
+                       (ctf_fs_ds_file_group *) g_ptr_array_index(ctf_fs_trace->ds_file_groups, i);
+               name = get_stream_instance_unique_name(ds_file_group);
+
+               if (!name) {
+                       goto error;
+               }
+
+               if (ds_file_group->sc->ir_sc) {
+                       BT_ASSERT(ctf_fs_trace->trace);
+
+                       if (ds_file_group->stream_id == UINT64_C(-1)) {
+                               /* No stream ID: use 0 */
+                               ds_file_group->stream = bt_stream_create_with_id(
+                                       ds_file_group->sc->ir_sc,
+                                       ctf_fs_trace->trace,
+                                       ctf_fs_trace->next_stream_id);
+                               ctf_fs_trace->next_stream_id++;
+                       } else {
+                               /* Specific stream ID */
+                               ds_file_group->stream = bt_stream_create_with_id(
+                                       ds_file_group->sc->ir_sc,
+                                       ctf_fs_trace->trace,
+                                       (uint64_t) ds_file_group->stream_id);
+                       }
+               } else {
+                       ds_file_group->stream = NULL;
+               }
+
+               if (!ds_file_group->stream) {
+                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                               "Cannot create stream for DS file group: "
+                               "addr=%p, stream-name=\"%s\"",
+                               ds_file_group, name->str);
+                       goto error;
+               }
+
+               ret = bt_stream_set_name(ds_file_group->stream,
+                       name->str);
+               if (ret) {
+                       BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Cannot set stream's name: "
+                               "addr=%p, stream-name=\"%s\"",
+                               ds_file_group->stream, name->str);
+                       goto error;
+               }
+
+               g_string_free(name, TRUE);
+               name = NULL;
+       }
+
+       ret = 0;
+       goto end;
+
+error:
+       ret = -1;
+
+end:
+
+       if (name) {
+               g_string_free(name, TRUE);
+       }
+       return ret;
+}
+
+static const bt_param_validation_value_descr inputs_elem_descr{
+  bt_param_validation_value_descr::string_t
+};
+
+
+static bt_param_validation_map_value_entry_descr fs_params_entries_descr[] = {
+       {"inputs", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY,
+               {bt_param_validation_value_descr::array_t, 1,
+                       BT_PARAM_VALIDATION_INFINITE, inputs_elem_descr}},
+       {"trace-name", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL,
+               {bt_param_validation_value_descr::string_t}},
+       { "clock-class-offset-s", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL,
+               { bt_param_validation_value_descr::signed_integer_t }},
+       { "clock-class-offset-ns", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL,
+               { bt_param_validation_value_descr::signed_integer_t }},
+       { "force-clock-class-origin-unix-epoch", BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL,
+               { bt_param_validation_value_descr::bool_t }},
+       BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
+};
+
+
+bool read_src_fs_parameters(const bt_value *params,
+               const bt_value **inputs,
+               const bt_value **trace_name,
+               struct ctf_fs_component *ctf_fs,
+               bt_self_component *self_comp,
+               bt_self_component_class *self_comp_class) {
+       bool ret;
+       const bt_value *value;
+       bt_logging_level log_level = ctf_fs->log_level;
+       enum bt_param_validation_status validate_value_status;
+       gchar *error = NULL;
+
+       validate_value_status = bt_param_validation_validate(params,
+               fs_params_entries_descr, &error);
+       if (validate_value_status != BT_PARAM_VALIDATION_STATUS_OK) {
+               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp, self_comp_class,
+                       "%s", error);
+               ret = false;
+               goto end;
+       }
+
+       /* inputs parameter */
+       *inputs = bt_value_map_borrow_entry_value_const(params, "inputs");
+
+       /* clock-class-offset-s parameter */
+       value = bt_value_map_borrow_entry_value_const(params,
+               "clock-class-offset-s");
+       if (value) {
+               ctf_fs->metadata_config.clock_class_offset_s =
+                       bt_value_integer_signed_get(value);
+       }
+
+       /* clock-class-offset-ns parameter */
+       value = bt_value_map_borrow_entry_value_const(params,
+               "clock-class-offset-ns");
+       if (value) {
+               ctf_fs->metadata_config.clock_class_offset_ns =
+                       bt_value_integer_signed_get(value);
+       }
+
+       /* force-clock-class-origin-unix-epoch parameter */
+       value = bt_value_map_borrow_entry_value_const(params,
+               "force-clock-class-origin-unix-epoch");
+       if (value) {
+               ctf_fs->metadata_config.force_clock_class_origin_unix_epoch =
+                       bt_value_bool_get(value);
+       }
+
+       /* trace-name parameter */
+       *trace_name = bt_value_map_borrow_entry_value_const(params, "trace-name");
+
+       ret = true;
+
+end:
+       g_free(error);
+       return ret;
+}
+
+static
+struct ctf_fs_component *ctf_fs_create(
+       const bt_value *params,
+       bt_self_component_source *self_comp_src)
+{
+       struct ctf_fs_component *ctf_fs = NULL;
+       const bt_value *inputs_value;
+       const bt_value *trace_name_value;
+       bt_self_component *self_comp =
+               bt_self_component_source_as_self_component(self_comp_src);
+
+       ctf_fs = ctf_fs_component_create(bt_component_get_logging_level(
+               bt_self_component_as_component(self_comp)), self_comp);
+       if (!ctf_fs) {
+               goto error;
+       }
+
+       if (!read_src_fs_parameters(params, &inputs_value, &trace_name_value,
+                       ctf_fs, self_comp, NULL)) {
+               goto error;
+       }
+
+       bt_self_component_set_data(self_comp, ctf_fs);
+
+       if (ctf_fs_component_create_ctf_fs_trace(ctf_fs, inputs_value,
+                       trace_name_value, self_comp, NULL)) {
+               goto error;
+       }
+
+       if (create_streams_for_trace(ctf_fs->trace)) {
+               goto error;
+       }
+
+       if (create_ports_for_trace(ctf_fs, ctf_fs->trace, self_comp_src)) {
+               goto error;
+       }
+
+       goto end;
+
+error:
+       ctf_fs_destroy(ctf_fs);
+       ctf_fs = NULL;
+       bt_self_component_set_data(self_comp, NULL);
+
+end:
+       return ctf_fs;
+}
+
+BT_HIDDEN
+bt_component_class_initialize_method_status ctf_fs_init(
+               bt_self_component_source *self_comp_src,
+               bt_self_component_source_configuration *config,
+               const bt_value *params, __attribute__((unused)) void *init_method_data)
+{
+       struct ctf_fs_component *ctf_fs;
+       bt_component_class_initialize_method_status ret =
+               BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
+
+       ctf_fs = ctf_fs_create(params, self_comp_src);
+       if (!ctf_fs) {
+               ret = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
+       }
+
+       return ret;
+}
+
+BT_HIDDEN
+bt_component_class_query_method_status ctf_fs_query(
+               bt_self_component_class_source *comp_class,
+               bt_private_query_executor *priv_query_exec,
+               const char *object, const bt_value *params,
+               __attribute__((unused)) void *method_data,
+               const bt_value **result)
+{
+       bt_component_class_query_method_status status =
+               BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_OK;
+       bt_logging_level log_level = bt_query_executor_get_logging_level(
+               bt_private_query_executor_as_query_executor_const(
+                       priv_query_exec));
+
+       if (strcmp(object, "metadata-info") == 0) {
+               status = metadata_info_query(comp_class, params, log_level,
+                       result);
+       } else if (strcmp(object, "babeltrace.trace-infos") == 0) {
+               status = trace_infos_query(comp_class, params, log_level,
+                       result);
+       } else if (!strcmp(object, "babeltrace.support-info")) {
+               status = support_info_query(comp_class, params, log_level, result);
+       } else {
+               BT_LOGE("Unknown query object `%s`", object);
+               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_UNKNOWN_OBJECT;
+               goto end;
+       }
+end:
+       return status;
+}
diff --git a/src/plugins/ctf/fs-src/fs.h b/src/plugins/ctf/fs-src/fs.h
deleted file mode 100644 (file)
index e7cffe5..0000000
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- * Copyright 2016 Philippe Proulx <pproulx@efficios.com>
- *
- * BabelTrace - CTF on File System Component
- */
-
-#ifndef BABELTRACE_PLUGIN_CTF_FS_H
-#define BABELTRACE_PLUGIN_CTF_FS_H
-
-#include <stdbool.h>
-#include "common/macros.h"
-#include <babeltrace2/babeltrace.h>
-#include "data-stream-file.h"
-#include "metadata.h"
-#include "../common/metadata/decoder.h"
-
-BT_HIDDEN
-extern bool ctf_fs_debug;
-
-struct ctf_fs_file {
-       bt_logging_level log_level;
-
-       /* Weak */
-       bt_self_component *self_comp;
-
-       /* Owned by this */
-       GString *path;
-
-       /* Owned by this */
-       FILE *fp;
-
-       off_t size;
-};
-
-struct ctf_fs_metadata {
-       /* Owned by this */
-       struct ctf_metadata_decoder *decoder;
-
-       /* Owned by this */
-       bt_trace_class *trace_class;
-
-       /* Weak (owned by `decoder` above) */
-       struct ctf_trace_class *tc;
-
-       /* Owned by this */
-       char *text;
-
-       int bo;
-};
-
-struct ctf_fs_component {
-       bt_logging_level log_level;
-
-       /* Array of struct ctf_fs_port_data *, owned by this */
-       GPtrArray *port_data;
-
-       /* Owned by this */
-       struct ctf_fs_trace *trace;
-
-       struct ctf_fs_metadata_config metadata_config;
-};
-
-struct ctf_fs_trace {
-       bt_logging_level log_level;
-
-       /*
-        * Weak. These are mostly used to generate log messages or to append
-        * error causes. They are mutually exclusive, only one of them must be
-        * set.
-        */
-       bt_self_component *self_comp;
-       bt_self_component_class *self_comp_class;
-
-       /* Owned by this */
-       struct ctf_fs_metadata *metadata;
-
-       /* Owned by this */
-       bt_trace *trace;
-
-       /* Array of struct ctf_fs_ds_file_group *, owned by this */
-       GPtrArray *ds_file_groups;
-
-       /* Owned by this */
-       GString *path;
-
-       /* Next automatic stream ID when not provided by packet header */
-       uint64_t next_stream_id;
-};
-
-struct ctf_fs_ds_index_entry {
-       /* Weak, belongs to ctf_fs_ds_file_info. */
-       const char *path;
-
-       /* Position, in bytes, of the packet from the beginning of the file. */
-       uint64_t offset;
-
-       /* Size of the packet, in bytes. */
-       uint64_t packet_size;
-
-       /*
-        * Extracted from the packet context, relative to the respective fields'
-        * mapped clock classes (in cycles).
-        */
-       uint64_t timestamp_begin, timestamp_end;
-
-       /*
-        * Converted from the packet context, relative to the trace's EPOCH
-        * (in ns since EPOCH).
-        */
-       int64_t timestamp_begin_ns, timestamp_end_ns;
-
-       /*
-        * Packet sequence number, or UINT64_MAX if not present in the index.
-        */
-       uint64_t packet_seq_num;
-};
-
-struct ctf_fs_ds_index {
-       /* Array of pointer to struct ctf_fs_ds_index_entry. */
-       GPtrArray *entries;
-};
-
-struct ctf_fs_ds_file_group {
-       /*
-        * Array of struct ctf_fs_ds_file_info, owned by this.
-        *
-        * This is an _ordered_ array of data stream file infos which
-        * belong to this group (a single stream instance).
-        *
-        * You can call ctf_fs_ds_file_create() with one of those paths
-        * and the trace IR stream below.
-        */
-       GPtrArray *ds_file_infos;
-
-       /* Owned by this */
-       struct ctf_stream_class *sc;
-
-       /* Owned by this */
-       bt_stream *stream;
-
-       /* Stream (instance) ID; -1ULL means none */
-       uint64_t stream_id;
-
-       /* Weak, belongs to component */
-       struct ctf_fs_trace *ctf_fs_trace;
-
-       /*
-        * Owned by this.
-        */
-       struct ctf_fs_ds_index *index;
-};
-
-struct ctf_fs_port_data {
-       /* Weak, belongs to ctf_fs_trace */
-       struct ctf_fs_ds_file_group *ds_file_group;
-
-       /* Weak */
-       struct ctf_fs_component *ctf_fs;
-};
-
-struct ctf_fs_msg_iter_data {
-       bt_logging_level log_level;
-
-       /* Weak */
-       bt_self_component *self_comp;
-
-       /* Weak */
-       bt_self_message_iterator *self_msg_iter;
-
-       /* Weak, belongs to ctf_fs_trace */
-       struct ctf_fs_ds_file_group *ds_file_group;
-
-       /* Owned by this */
-       struct ctf_msg_iter *msg_iter;
-
-       /*
-        * Saved error.  If we hit an error in the _next method, but have some
-        * messages ready to return, we save the error here and return it on
-        * the next _next call.
-        */
-       bt_message_iterator_class_next_method_status next_saved_status;
-       const struct bt_error *next_saved_error;
-
-       struct ctf_fs_ds_group_medops_data *msg_iter_medops_data;
-};
-
-BT_HIDDEN
-bt_component_class_initialize_method_status ctf_fs_init(
-               bt_self_component_source *source,
-               bt_self_component_source_configuration *config,
-               const bt_value *params, void *init_method_data);
-
-BT_HIDDEN
-void ctf_fs_finalize(bt_self_component_source *component);
-
-BT_HIDDEN
-bt_component_class_query_method_status ctf_fs_query(
-               bt_self_component_class_source *comp_class,
-               bt_private_query_executor *priv_query_exec,
-               const char *object, const bt_value *params,
-               void *method_data, const bt_value **result);
-
-BT_HIDDEN
-bt_message_iterator_class_initialize_method_status ctf_fs_iterator_init(
-               bt_self_message_iterator *self_msg_iter,
-               bt_self_message_iterator_configuration *config,
-               bt_self_component_port_output *self_port);
-
-BT_HIDDEN
-void ctf_fs_iterator_finalize(bt_self_message_iterator *it);
-
-BT_HIDDEN
-bt_message_iterator_class_next_method_status ctf_fs_iterator_next(
-               bt_self_message_iterator *iterator,
-               bt_message_array_const msgs, uint64_t capacity,
-               uint64_t *count);
-
-BT_HIDDEN
-bt_message_iterator_class_seek_beginning_method_status ctf_fs_iterator_seek_beginning(
-               bt_self_message_iterator *message_iterator);
-
-/* Create and initialize a new, empty ctf_fs_component. */
-
-BT_HIDDEN
-struct ctf_fs_component *ctf_fs_component_create(bt_logging_level log_level,
-               bt_self_component *self_comp);
-
-/*
- * Create one `struct ctf_fs_trace` from one trace, or multiple traces sharing
- * the same UUID.
- *
- * `paths_value` must be an array of strings,
- *
- * The created `struct ctf_fs_trace` is assigned to `ctf_fs->trace`.
- *
- * `self_comp` and `self_comp_class` are used for logging, only one of them
- * should be set.
- */
-
-BT_HIDDEN
-int ctf_fs_component_create_ctf_fs_trace(
-               struct ctf_fs_component *ctf_fs,
-               const bt_value *paths_value,
-               const bt_value *trace_name_value,
-               bt_self_component *self_comp,
-               bt_self_component_class *self_comp_class);
-
-/* Free `ctf_fs` and everything it owns. */
-
-BT_HIDDEN
-void ctf_fs_destroy(struct ctf_fs_component *ctf_fs);
-
-/*
- * Read and validate parameters taken by the src.ctf.fs plugin.
- *
- *  - The mandatory `paths` parameter is returned in `*paths`.
- *  - The optional `clock-class-offset-s` and `clock-class-offset-ns`, if
- *    present, are recorded in the `ctf_fs` structure.
- *  - The optional `trace-name` parameter is returned in `*trace_name` if
- *    present, else `*trace_name` is set to NULL.
- *
- * `self_comp` and `self_comp_class` are used for logging, only one of them
- * should be set.
- *
- * Return true on success, false if any parameter didn't pass validation.
- */
-
-BT_HIDDEN
-bool read_src_fs_parameters(const bt_value *params,
-               const bt_value **paths,
-               const bt_value **trace_name,
-               struct ctf_fs_component *ctf_fs,
-               bt_self_component *self_comp,
-               bt_self_component_class *self_comp_class);
-
-/*
- * Generate the port name to be used for a given data stream file group.
- *
- * The result must be freed using g_free by the caller.
- */
-
-BT_HIDDEN
-gchar *ctf_fs_make_port_name(struct ctf_fs_ds_file_group *ds_file_group);
-
-#endif /* BABELTRACE_PLUGIN_CTF_FS_H */
diff --git a/src/plugins/ctf/fs-src/fs.hpp b/src/plugins/ctf/fs-src/fs.hpp
new file mode 100644 (file)
index 0000000..da8a3cb
--- /dev/null
@@ -0,0 +1,288 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ * Copyright 2016 Philippe Proulx <pproulx@efficios.com>
+ *
+ * BabelTrace - CTF on File System Component
+ */
+
+#ifndef BABELTRACE_PLUGIN_CTF_FS_H
+#define BABELTRACE_PLUGIN_CTF_FS_H
+
+#include <stdbool.h>
+#include "common/macros.h"
+#include <babeltrace2/babeltrace.h>
+#include "data-stream-file.hpp"
+#include "metadata.hpp"
+#include "../common/metadata/decoder.hpp"
+
+BT_HIDDEN
+extern bool ctf_fs_debug;
+
+struct ctf_fs_file {
+       bt_logging_level log_level;
+
+       /* Weak */
+       bt_self_component *self_comp;
+
+       /* Owned by this */
+       GString *path;
+
+       /* Owned by this */
+       FILE *fp;
+
+       off_t size;
+};
+
+struct ctf_fs_metadata {
+       /* Owned by this */
+       struct ctf_metadata_decoder *decoder;
+
+       /* Owned by this */
+       bt_trace_class *trace_class;
+
+       /* Weak (owned by `decoder` above) */
+       struct ctf_trace_class *tc;
+
+       /* Owned by this */
+       char *text;
+
+       int bo;
+};
+
+struct ctf_fs_component {
+       bt_logging_level log_level;
+
+       /* Array of struct ctf_fs_port_data *, owned by this */
+       GPtrArray *port_data;
+
+       /* Owned by this */
+       struct ctf_fs_trace *trace;
+
+       struct ctf_fs_metadata_config metadata_config;
+};
+
+struct ctf_fs_trace {
+       bt_logging_level log_level;
+
+       /*
+        * Weak. These are mostly used to generate log messages or to append
+        * error causes. They are mutually exclusive, only one of them must be
+        * set.
+        */
+       bt_self_component *self_comp;
+       bt_self_component_class *self_comp_class;
+
+       /* Owned by this */
+       struct ctf_fs_metadata *metadata;
+
+       /* Owned by this */
+       bt_trace *trace;
+
+       /* Array of struct ctf_fs_ds_file_group *, owned by this */
+       GPtrArray *ds_file_groups;
+
+       /* Owned by this */
+       GString *path;
+
+       /* Next automatic stream ID when not provided by packet header */
+       uint64_t next_stream_id;
+};
+
+struct ctf_fs_ds_index_entry {
+       /* Weak, belongs to ctf_fs_ds_file_info. */
+       const char *path;
+
+       /* Position, in bytes, of the packet from the beginning of the file. */
+       uint64_t offset;
+
+       /* Size of the packet, in bytes. */
+       uint64_t packet_size;
+
+       /*
+        * Extracted from the packet context, relative to the respective fields'
+        * mapped clock classes (in cycles).
+        */
+       uint64_t timestamp_begin, timestamp_end;
+
+       /*
+        * Converted from the packet context, relative to the trace's EPOCH
+        * (in ns since EPOCH).
+        */
+       int64_t timestamp_begin_ns, timestamp_end_ns;
+
+       /*
+        * Packet sequence number, or UINT64_MAX if not present in the index.
+        */
+       uint64_t packet_seq_num;
+};
+
+struct ctf_fs_ds_index {
+       /* Array of pointer to struct ctf_fs_ds_index_entry. */
+       GPtrArray *entries;
+};
+
+struct ctf_fs_ds_file_group {
+       /*
+        * Array of struct ctf_fs_ds_file_info, owned by this.
+        *
+        * This is an _ordered_ array of data stream file infos which
+        * belong to this group (a single stream instance).
+        *
+        * You can call ctf_fs_ds_file_create() with one of those paths
+        * and the trace IR stream below.
+        */
+       GPtrArray *ds_file_infos;
+
+       /* Owned by this */
+       struct ctf_stream_class *sc;
+
+       /* Owned by this */
+       bt_stream *stream;
+
+       /* Stream (instance) ID; -1ULL means none */
+       uint64_t stream_id;
+
+       /* Weak, belongs to component */
+       struct ctf_fs_trace *ctf_fs_trace;
+
+       /*
+        * Owned by this.
+        */
+       struct ctf_fs_ds_index *index;
+};
+
+struct ctf_fs_port_data {
+       /* Weak, belongs to ctf_fs_trace */
+       struct ctf_fs_ds_file_group *ds_file_group;
+
+       /* Weak */
+       struct ctf_fs_component *ctf_fs;
+};
+
+struct ctf_fs_msg_iter_data {
+       bt_logging_level log_level;
+
+       /* Weak */
+       bt_self_component *self_comp;
+
+       /* Weak */
+       bt_self_message_iterator *self_msg_iter;
+
+       /* Weak, belongs to ctf_fs_trace */
+       struct ctf_fs_ds_file_group *ds_file_group;
+
+       /* Owned by this */
+       struct ctf_msg_iter *msg_iter;
+
+       /*
+        * Saved error.  If we hit an error in the _next method, but have some
+        * messages ready to return, we save the error here and return it on
+        * the next _next call.
+        */
+       bt_message_iterator_class_next_method_status next_saved_status;
+       const struct bt_error *next_saved_error;
+
+       struct ctf_fs_ds_group_medops_data *msg_iter_medops_data;
+};
+
+BT_HIDDEN
+bt_component_class_initialize_method_status ctf_fs_init(
+               bt_self_component_source *source,
+               bt_self_component_source_configuration *config,
+               const bt_value *params, void *init_method_data);
+
+BT_HIDDEN
+void ctf_fs_finalize(bt_self_component_source *component);
+
+BT_HIDDEN
+bt_component_class_query_method_status ctf_fs_query(
+               bt_self_component_class_source *comp_class,
+               bt_private_query_executor *priv_query_exec,
+               const char *object, const bt_value *params,
+               void *method_data, const bt_value **result);
+
+BT_HIDDEN
+bt_message_iterator_class_initialize_method_status ctf_fs_iterator_init(
+               bt_self_message_iterator *self_msg_iter,
+               bt_self_message_iterator_configuration *config,
+               bt_self_component_port_output *self_port);
+
+BT_HIDDEN
+void ctf_fs_iterator_finalize(bt_self_message_iterator *it);
+
+BT_HIDDEN
+bt_message_iterator_class_next_method_status ctf_fs_iterator_next(
+               bt_self_message_iterator *iterator,
+               bt_message_array_const msgs, uint64_t capacity,
+               uint64_t *count);
+
+BT_HIDDEN
+bt_message_iterator_class_seek_beginning_method_status ctf_fs_iterator_seek_beginning(
+               bt_self_message_iterator *message_iterator);
+
+/* Create and initialize a new, empty ctf_fs_component. */
+
+BT_HIDDEN
+struct ctf_fs_component *ctf_fs_component_create(bt_logging_level log_level,
+               bt_self_component *self_comp);
+
+/*
+ * Create one `struct ctf_fs_trace` from one trace, or multiple traces sharing
+ * the same UUID.
+ *
+ * `paths_value` must be an array of strings,
+ *
+ * The created `struct ctf_fs_trace` is assigned to `ctf_fs->trace`.
+ *
+ * `self_comp` and `self_comp_class` are used for logging, only one of them
+ * should be set.
+ */
+
+BT_HIDDEN
+int ctf_fs_component_create_ctf_fs_trace(
+               struct ctf_fs_component *ctf_fs,
+               const bt_value *paths_value,
+               const bt_value *trace_name_value,
+               bt_self_component *self_comp,
+               bt_self_component_class *self_comp_class);
+
+/* Free `ctf_fs` and everything it owns. */
+
+BT_HIDDEN
+void ctf_fs_destroy(struct ctf_fs_component *ctf_fs);
+
+/*
+ * Read and validate parameters taken by the src.ctf.fs plugin.
+ *
+ *  - The mandatory `paths` parameter is returned in `*paths`.
+ *  - The optional `clock-class-offset-s` and `clock-class-offset-ns`, if
+ *    present, are recorded in the `ctf_fs` structure.
+ *  - The optional `trace-name` parameter is returned in `*trace_name` if
+ *    present, else `*trace_name` is set to NULL.
+ *
+ * `self_comp` and `self_comp_class` are used for logging, only one of them
+ * should be set.
+ *
+ * Return true on success, false if any parameter didn't pass validation.
+ */
+
+BT_HIDDEN
+bool read_src_fs_parameters(const bt_value *params,
+               const bt_value **paths,
+               const bt_value **trace_name,
+               struct ctf_fs_component *ctf_fs,
+               bt_self_component *self_comp,
+               bt_self_component_class *self_comp_class);
+
+/*
+ * Generate the port name to be used for a given data stream file group.
+ *
+ * The result must be freed using g_free by the caller.
+ */
+
+BT_HIDDEN
+gchar *ctf_fs_make_port_name(struct ctf_fs_ds_file_group *ds_file_group);
+
+#endif /* BABELTRACE_PLUGIN_CTF_FS_H */
diff --git a/src/plugins/ctf/fs-src/lttng-index.h b/src/plugins/ctf/fs-src/lttng-index.h
deleted file mode 100644 (file)
index 46eafa6..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright (C) 2013 Julien Desfossez <jdesfossez@efficios.com>
- * Copyright (C) 2013 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
- */
-
-#ifndef LTTNG_INDEX_H
-#define LTTNG_INDEX_H
-
-#include <stddef.h>
-#include "compat/limits.h"
-
-#define CTF_INDEX_MAGIC 0xC1F1DCC1
-#define CTF_INDEX_MAJOR 1
-#define CTF_INDEX_MINOR 1
-#define CTF_INDEX_1_0_SIZE offsetof(struct ctf_packet_index, stream_instance_id)
-
-/*
- * Header at the beginning of each index file.
- * All integer fields are stored in big endian.
- */
-struct ctf_packet_index_file_hdr {
-       uint32_t magic;
-       uint32_t index_major;
-       uint32_t index_minor;
-       /* size of struct ctf_packet_index, in bytes. */
-       uint32_t packet_index_len;
-} __attribute__((__packed__));
-
-/*
- * Packet index generated for each trace packet store in a trace file.
- * All integer fields are stored in big endian.
- */
-struct ctf_packet_index {
-       uint64_t offset;                /* offset of the packet in the file, in bytes */
-       uint64_t packet_size;           /* packet size, in bits */
-       uint64_t content_size;          /* content size, in bits */
-       uint64_t timestamp_begin;
-       uint64_t timestamp_end;
-       uint64_t events_discarded;
-       uint64_t stream_id;
-       /* CTF_INDEX 1.0 limit */
-       uint64_t stream_instance_id;    /* ID of the channel instance */
-       uint64_t packet_seq_num;        /* packet sequence number */
-} __attribute__((__packed__));
-
-#endif /* LTTNG_INDEX_H */
diff --git a/src/plugins/ctf/fs-src/lttng-index.hpp b/src/plugins/ctf/fs-src/lttng-index.hpp
new file mode 100644 (file)
index 0000000..46eafa6
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2013 Julien Desfossez <jdesfossez@efficios.com>
+ * Copyright (C) 2013 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
+ */
+
+#ifndef LTTNG_INDEX_H
+#define LTTNG_INDEX_H
+
+#include <stddef.h>
+#include "compat/limits.h"
+
+#define CTF_INDEX_MAGIC 0xC1F1DCC1
+#define CTF_INDEX_MAJOR 1
+#define CTF_INDEX_MINOR 1
+#define CTF_INDEX_1_0_SIZE offsetof(struct ctf_packet_index, stream_instance_id)
+
+/*
+ * Header at the beginning of each index file.
+ * All integer fields are stored in big endian.
+ */
+struct ctf_packet_index_file_hdr {
+       uint32_t magic;
+       uint32_t index_major;
+       uint32_t index_minor;
+       /* size of struct ctf_packet_index, in bytes. */
+       uint32_t packet_index_len;
+} __attribute__((__packed__));
+
+/*
+ * Packet index generated for each trace packet store in a trace file.
+ * All integer fields are stored in big endian.
+ */
+struct ctf_packet_index {
+       uint64_t offset;                /* offset of the packet in the file, in bytes */
+       uint64_t packet_size;           /* packet size, in bits */
+       uint64_t content_size;          /* content size, in bits */
+       uint64_t timestamp_begin;
+       uint64_t timestamp_end;
+       uint64_t events_discarded;
+       uint64_t stream_id;
+       /* CTF_INDEX 1.0 limit */
+       uint64_t stream_instance_id;    /* ID of the channel instance */
+       uint64_t packet_seq_num;        /* packet sequence number */
+} __attribute__((__packed__));
+
+#endif /* LTTNG_INDEX_H */
diff --git a/src/plugins/ctf/fs-src/metadata.c b/src/plugins/ctf/fs-src/metadata.c
deleted file mode 100644 (file)
index e517ff8..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2016 Philippe Proulx <pproulx@efficios.com>
- * Copyright 2010-2011 EfficiOS Inc. and Linux Foundation
- */
-
-#define BT_COMP_LOG_SELF_COMP self_comp
-#define BT_LOG_OUTPUT_LEVEL log_level
-#define BT_LOG_TAG "PLUGIN/SRC.CTF.FS/META"
-#include "logging/comp-logging.h"
-
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include "common/assert.h"
-#include <glib.h>
-#include "common/uuid.h"
-#include "compat/memstream.h"
-#include <babeltrace2/babeltrace.h>
-
-#include "fs.h"
-#include "file.h"
-#include "metadata.h"
-#include "../common/metadata/decoder.h"
-
-BT_HIDDEN
-FILE *ctf_fs_metadata_open_file(const char *trace_path)
-{
-       GString *metadata_path;
-       FILE *fp = NULL;
-
-       metadata_path = g_string_new(trace_path);
-       if (!metadata_path) {
-               goto end;
-       }
-
-       g_string_append(metadata_path, G_DIR_SEPARATOR_S CTF_FS_METADATA_FILENAME);
-       fp = fopen(metadata_path->str, "rb");
-       g_string_free(metadata_path, TRUE);
-end:
-       return fp;
-}
-
-static struct ctf_fs_file *get_file(const char *trace_path,
-               bt_logging_level log_level, bt_self_component *self_comp)
-{
-       struct ctf_fs_file *file = ctf_fs_file_create(log_level, self_comp);
-
-       if (!file) {
-               goto error;
-       }
-
-       g_string_append(file->path, trace_path);
-       g_string_append(file->path, G_DIR_SEPARATOR_S CTF_FS_METADATA_FILENAME);
-
-       if (ctf_fs_file_open(file, "rb")) {
-               goto error;
-       }
-
-       goto end;
-
-error:
-       if (file) {
-               ctf_fs_file_destroy(file);
-               file = NULL;
-       }
-
-end:
-       return file;
-}
-
-BT_HIDDEN
-int ctf_fs_metadata_set_trace_class(
-               bt_self_component *self_comp,
-               struct ctf_fs_trace *ctf_fs_trace,
-               struct ctf_fs_metadata_config *config)
-{
-       int ret = 0;
-       struct ctf_fs_file *file = NULL;
-       struct ctf_metadata_decoder_config decoder_config = {
-               .log_level = ctf_fs_trace->log_level,
-               .self_comp = self_comp,
-               .clock_class_offset_s = config ? config->clock_class_offset_s : 0,
-               .clock_class_offset_ns = config ? config->clock_class_offset_ns : 0,
-               .force_clock_class_origin_unix_epoch =
-                       config ? config->force_clock_class_origin_unix_epoch : false,
-               .create_trace_class = true,
-       };
-       bt_logging_level log_level = ctf_fs_trace->log_level;
-
-       file = get_file(ctf_fs_trace->path->str, log_level, self_comp);
-       if (!file) {
-               BT_COMP_LOGE("Cannot create metadata file object.");
-               ret = -1;
-               goto end;
-       }
-
-       ctf_fs_trace->metadata->decoder = ctf_metadata_decoder_create(
-               &decoder_config);
-       if (!ctf_fs_trace->metadata->decoder) {
-               BT_COMP_LOGE("Cannot create metadata decoder object.");
-               ret = -1;
-               goto end;
-       }
-
-       ret = ctf_metadata_decoder_append_content(
-               ctf_fs_trace->metadata->decoder, file->fp);
-       if (ret) {
-               BT_COMP_LOGE("Cannot update metadata decoder's content.");
-               goto end;
-       }
-
-       ctf_fs_trace->metadata->trace_class =
-               ctf_metadata_decoder_get_ir_trace_class(
-                       ctf_fs_trace->metadata->decoder);
-       BT_ASSERT(!self_comp || ctf_fs_trace->metadata->trace_class);
-       ctf_fs_trace->metadata->tc =
-               ctf_metadata_decoder_borrow_ctf_trace_class(
-                       ctf_fs_trace->metadata->decoder);
-       BT_ASSERT(ctf_fs_trace->metadata->tc);
-
-end:
-       ctf_fs_file_destroy(file);
-       return ret;
-}
-
-BT_HIDDEN
-int ctf_fs_metadata_init(struct ctf_fs_metadata *metadata)
-{
-       /* Nothing to initialize for the moment. */
-       return 0;
-}
-
-BT_HIDDEN
-void ctf_fs_metadata_fini(struct ctf_fs_metadata *metadata)
-{
-       free(metadata->text);
-
-       if (metadata->trace_class) {
-               BT_TRACE_CLASS_PUT_REF_AND_RESET(metadata->trace_class);
-       }
-
-       if (metadata->decoder) {
-               ctf_metadata_decoder_destroy(metadata->decoder);
-       }
-}
diff --git a/src/plugins/ctf/fs-src/metadata.cpp b/src/plugins/ctf/fs-src/metadata.cpp
new file mode 100644 (file)
index 0000000..51bd5b9
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2016 Philippe Proulx <pproulx@efficios.com>
+ * Copyright 2010-2011 EfficiOS Inc. and Linux Foundation
+ */
+
+#define BT_COMP_LOG_SELF_COMP self_comp
+#define BT_LOG_OUTPUT_LEVEL log_level
+#define BT_LOG_TAG "PLUGIN/SRC.CTF.FS/META"
+#include "logging/comp-logging.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include "common/assert.h"
+#include <glib.h>
+#include "common/uuid.h"
+#include "compat/memstream.h"
+#include <babeltrace2/babeltrace.h>
+
+#include "fs.hpp"
+#include "file.hpp"
+#include "metadata.hpp"
+#include "../common/metadata/decoder.hpp"
+
+BT_HIDDEN
+FILE *ctf_fs_metadata_open_file(const char *trace_path)
+{
+       GString *metadata_path;
+       FILE *fp = NULL;
+
+       metadata_path = g_string_new(trace_path);
+       if (!metadata_path) {
+               goto end;
+       }
+
+       g_string_append(metadata_path, G_DIR_SEPARATOR_S CTF_FS_METADATA_FILENAME);
+       fp = fopen(metadata_path->str, "rb");
+       g_string_free(metadata_path, TRUE);
+end:
+       return fp;
+}
+
+static struct ctf_fs_file *get_file(const char *trace_path,
+               bt_logging_level log_level, bt_self_component *self_comp)
+{
+       struct ctf_fs_file *file = ctf_fs_file_create(log_level, self_comp);
+
+       if (!file) {
+               goto error;
+       }
+
+       g_string_append(file->path, trace_path);
+       g_string_append(file->path, G_DIR_SEPARATOR_S CTF_FS_METADATA_FILENAME);
+
+       if (ctf_fs_file_open(file, "rb")) {
+               goto error;
+       }
+
+       goto end;
+
+error:
+       if (file) {
+               ctf_fs_file_destroy(file);
+               file = NULL;
+       }
+
+end:
+       return file;
+}
+
+BT_HIDDEN
+int ctf_fs_metadata_set_trace_class(
+               bt_self_component *self_comp,
+               struct ctf_fs_trace *ctf_fs_trace,
+               struct ctf_fs_metadata_config *config)
+{
+       int ret = 0;
+       struct ctf_fs_file *file = NULL;
+       bt_logging_level log_level = ctf_fs_trace->log_level;
+
+       ctf_metadata_decoder_config decoder_config{};
+       decoder_config.log_level = ctf_fs_trace->log_level,
+       decoder_config.self_comp = self_comp,
+       decoder_config.clock_class_offset_s = config ? config->clock_class_offset_s : 0,
+       decoder_config.clock_class_offset_ns = config ? config->clock_class_offset_ns : 0,
+       decoder_config.force_clock_class_origin_unix_epoch =
+               config ? config->force_clock_class_origin_unix_epoch : false,
+       decoder_config.create_trace_class = true,
+
+       file = get_file(ctf_fs_trace->path->str, log_level, self_comp);
+       if (!file) {
+               BT_COMP_LOGE("Cannot create metadata file object.");
+               ret = -1;
+               goto end;
+       }
+
+       ctf_fs_trace->metadata->decoder = ctf_metadata_decoder_create(
+               &decoder_config);
+       if (!ctf_fs_trace->metadata->decoder) {
+               BT_COMP_LOGE("Cannot create metadata decoder object.");
+               ret = -1;
+               goto end;
+       }
+
+       ret = ctf_metadata_decoder_append_content(
+               ctf_fs_trace->metadata->decoder, file->fp);
+       if (ret) {
+               BT_COMP_LOGE("Cannot update metadata decoder's content.");
+               goto end;
+       }
+
+       ctf_fs_trace->metadata->trace_class =
+               ctf_metadata_decoder_get_ir_trace_class(
+                       ctf_fs_trace->metadata->decoder);
+       BT_ASSERT(!self_comp || ctf_fs_trace->metadata->trace_class);
+       ctf_fs_trace->metadata->tc =
+               ctf_metadata_decoder_borrow_ctf_trace_class(
+                       ctf_fs_trace->metadata->decoder);
+       BT_ASSERT(ctf_fs_trace->metadata->tc);
+
+end:
+       ctf_fs_file_destroy(file);
+       return ret;
+}
+
+BT_HIDDEN
+int ctf_fs_metadata_init(struct ctf_fs_metadata *metadata)
+{
+       /* Nothing to initialize for the moment. */
+       return 0;
+}
+
+BT_HIDDEN
+void ctf_fs_metadata_fini(struct ctf_fs_metadata *metadata)
+{
+       free(metadata->text);
+
+       if (metadata->trace_class) {
+               BT_TRACE_CLASS_PUT_REF_AND_RESET(metadata->trace_class);
+       }
+
+       if (metadata->decoder) {
+               ctf_metadata_decoder_destroy(metadata->decoder);
+       }
+}
diff --git a/src/plugins/ctf/fs-src/metadata.h b/src/plugins/ctf/fs-src/metadata.h
deleted file mode 100644 (file)
index 63dc305..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2016 Philippe Proulx <pproulx@efficios.com>
- */
-
-#ifndef CTF_FS_METADATA_H
-#define CTF_FS_METADATA_H
-
-#include <stdbool.h>
-#include <stdio.h>
-#include <glib.h>
-#include "common/macros.h"
-#include <babeltrace2/babeltrace.h>
-
-#define CTF_FS_METADATA_FILENAME       "metadata"
-
-struct ctf_fs_trace;
-struct ctf_fs_metadata;
-
-struct ctf_fs_metadata_config {
-       bool force_clock_class_origin_unix_epoch;
-       int64_t clock_class_offset_s;
-       int64_t clock_class_offset_ns;
-};
-
-BT_HIDDEN
-int ctf_fs_metadata_init(struct ctf_fs_metadata *metadata);
-
-BT_HIDDEN
-void ctf_fs_metadata_fini(struct ctf_fs_metadata *metadata);
-
-BT_HIDDEN
-int ctf_fs_metadata_set_trace_class(bt_self_component *self_comp,
-               struct ctf_fs_trace *ctf_fs_trace,
-               struct ctf_fs_metadata_config *config);
-
-BT_HIDDEN
-FILE *ctf_fs_metadata_open_file(const char *trace_path);
-
-BT_HIDDEN
-bool ctf_metadata_is_packetized(FILE *fp, int *byte_order);
-
-#endif /* CTF_FS_METADATA_H */
diff --git a/src/plugins/ctf/fs-src/metadata.hpp b/src/plugins/ctf/fs-src/metadata.hpp
new file mode 100644 (file)
index 0000000..63dc305
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2016 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#ifndef CTF_FS_METADATA_H
+#define CTF_FS_METADATA_H
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <glib.h>
+#include "common/macros.h"
+#include <babeltrace2/babeltrace.h>
+
+#define CTF_FS_METADATA_FILENAME       "metadata"
+
+struct ctf_fs_trace;
+struct ctf_fs_metadata;
+
+struct ctf_fs_metadata_config {
+       bool force_clock_class_origin_unix_epoch;
+       int64_t clock_class_offset_s;
+       int64_t clock_class_offset_ns;
+};
+
+BT_HIDDEN
+int ctf_fs_metadata_init(struct ctf_fs_metadata *metadata);
+
+BT_HIDDEN
+void ctf_fs_metadata_fini(struct ctf_fs_metadata *metadata);
+
+BT_HIDDEN
+int ctf_fs_metadata_set_trace_class(bt_self_component *self_comp,
+               struct ctf_fs_trace *ctf_fs_trace,
+               struct ctf_fs_metadata_config *config);
+
+BT_HIDDEN
+FILE *ctf_fs_metadata_open_file(const char *trace_path);
+
+BT_HIDDEN
+bool ctf_metadata_is_packetized(FILE *fp, int *byte_order);
+
+#endif /* CTF_FS_METADATA_H */
diff --git a/src/plugins/ctf/fs-src/query.c b/src/plugins/ctf/fs-src/query.c
deleted file mode 100644 (file)
index adc8f4d..0000000
+++ /dev/null
@@ -1,515 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * Babeltrace CTF file system Reader Component queries
- */
-
-#define BT_LOG_OUTPUT_LEVEL log_level
-#define BT_LOG_TAG "PLUGIN/SRC.CTF.FS/QUERY"
-#include "logging/log.h"
-
-#include "query.h"
-#include <stdbool.h>
-#include <glib.h>
-#include <glib/gstdio.h>
-#include <fcntl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include "common/assert.h"
-#include "metadata.h"
-#include "../common/metadata/decoder.h"
-#include "common/common.h"
-#include "common/macros.h"
-#include <babeltrace2/babeltrace.h>
-#include "fs.h"
-#include "logging/comp-logging.h"
-
-#define METADATA_TEXT_SIG      "/* CTF 1.8"
-
-struct range {
-       int64_t begin_ns;
-       int64_t end_ns;
-       bool set;
-};
-
-BT_HIDDEN
-bt_component_class_query_method_status metadata_info_query(
-               bt_self_component_class_source *self_comp_class_src,
-               const bt_value *params, bt_logging_level log_level,
-               const bt_value **user_result)
-{
-       bt_component_class_query_method_status status =
-               BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_OK;
-       bt_self_component_class *self_comp_class =
-               bt_self_component_class_source_as_self_component_class(self_comp_class_src);
-       bt_value *result = NULL;
-       const bt_value *path_value = NULL;
-       FILE *metadata_fp = NULL;
-       int ret;
-       int bo;
-       const char *path;
-       bool is_packetized;
-       struct ctf_metadata_decoder *decoder = NULL;
-       struct ctf_metadata_decoder_config decoder_cfg = { 0 };
-       enum ctf_metadata_decoder_status decoder_status;
-
-       result = bt_value_map_create();
-       if (!result) {
-               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_MEMORY_ERROR;
-               goto error;
-       }
-
-       BT_ASSERT(params);
-
-       if (!bt_value_is_map(params)) {
-               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                       "Query parameters is not a map value object.");
-               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
-               goto error;
-       }
-
-       path_value = bt_value_map_borrow_entry_value_const(params, "path");
-       if (!path_value) {
-               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                       "Mandatory `path` parameter missing");
-               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
-               goto error;
-       }
-
-       if (!bt_value_is_string(path_value)) {
-               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                       "`path` parameter is required to be a string value");
-               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
-               goto error;
-       }
-
-       path = bt_value_string_get(path_value);
-
-       BT_ASSERT(path);
-       metadata_fp = ctf_fs_metadata_open_file(path);
-       if (!metadata_fp) {
-               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                       "Cannot open trace metadata: path=\"%s\".", path);
-               goto error;
-       }
-
-       ret = ctf_metadata_decoder_is_packetized(metadata_fp, &is_packetized,
-               &bo, log_level, NULL);
-       if (ret) {
-               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                       "Cannot check whether or not the metadata stream is packetized: path=\"%s\".",
-                       path);
-               goto error;
-       }
-
-       decoder_cfg.log_level = log_level;
-       decoder_cfg.self_comp_class = self_comp_class;
-       decoder_cfg.keep_plain_text = true;
-       decoder = ctf_metadata_decoder_create(&decoder_cfg);
-       if (!decoder) {
-               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                       "Cannot create metadata decoder: path=\"%s\".", path);
-               goto error;
-       }
-
-       rewind(metadata_fp);
-       decoder_status = ctf_metadata_decoder_append_content(decoder,
-               metadata_fp);
-       if (decoder_status) {
-               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                       "Cannot update metadata decoder's content: path=\"%s\".",
-                       path);
-               goto error;
-       }
-
-       ret = bt_value_map_insert_string_entry(result, "text",
-               ctf_metadata_decoder_get_text(decoder));
-       if (ret) {
-               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                       "Cannot insert metadata text into query result.");
-               goto error;
-       }
-
-       ret = bt_value_map_insert_bool_entry(result, "is-packetized",
-               is_packetized);
-       if (ret) {
-               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                       "Cannot insert \"is-packetized\" attribute into query result.");
-               goto error;
-       }
-
-       goto end;
-
-error:
-       BT_VALUE_PUT_REF_AND_RESET(result);
-       result = NULL;
-
-       if (status >= 0) {
-               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
-       }
-
-end:
-       ctf_metadata_decoder_destroy(decoder);
-
-       if (metadata_fp) {
-               ret = fclose(metadata_fp);
-               if (ret) {
-                       BT_LOGE_ERRNO("Cannot close metatada file stream",
-                               ": path=\"%s\"", path);
-               }
-       }
-
-       *user_result = result;
-       return status;
-}
-
-static
-int add_range(bt_value *info, struct range *range,
-               const char *range_name)
-{
-       int ret = 0;
-       bt_value_map_insert_entry_status status;
-       bt_value *range_map;
-
-       if (!range->set) {
-               /* Not an error. */
-               goto end;
-       }
-
-       status = bt_value_map_insert_empty_map_entry(info, range_name,
-               &range_map);
-       if (status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
-               ret = -1;
-               goto end;
-       }
-
-       status = bt_value_map_insert_signed_integer_entry(range_map, "begin",
-                       range->begin_ns);
-       if (status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
-               ret = -1;
-               goto end;
-       }
-
-       status = bt_value_map_insert_signed_integer_entry(range_map, "end",
-                       range->end_ns);
-       if (status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
-               ret = -1;
-               goto end;
-       }
-
-end:
-       return ret;
-}
-
-static
-int populate_stream_info(struct ctf_fs_ds_file_group *group,
-               bt_value *group_info, struct range *stream_range)
-{
-       int ret = 0;
-       bt_value_map_insert_entry_status insert_status;
-       struct ctf_fs_ds_index_entry *first_ds_index_entry, *last_ds_index_entry;
-       gchar *port_name = NULL;
-
-       /*
-        * Since each `struct ctf_fs_ds_file_group` has a sorted array of
-        * `struct ctf_fs_ds_index_entry`, we can compute the stream range from
-        * the timestamp_begin of the first index entry and the timestamp_end
-        * of the last index entry.
-        */
-       BT_ASSERT(group->index);
-       BT_ASSERT(group->index->entries);
-       BT_ASSERT(group->index->entries->len > 0);
-
-       /* First entry. */
-       first_ds_index_entry = (struct ctf_fs_ds_index_entry *) g_ptr_array_index(
-               group->index->entries, 0);
-
-       /* Last entry. */
-       last_ds_index_entry = (struct ctf_fs_ds_index_entry *) g_ptr_array_index(
-               group->index->entries, group->index->entries->len - 1);
-
-       stream_range->begin_ns = first_ds_index_entry->timestamp_begin_ns;
-       stream_range->end_ns = last_ds_index_entry->timestamp_end_ns;
-
-       /*
-        * If any of the begin and end timestamps is not set it means that
-        * packets don't include `timestamp_begin` _and_ `timestamp_end` fields
-        * in their packet context so we can't set the range.
-        */
-       stream_range->set = stream_range->begin_ns != UINT64_C(-1) &&
-               stream_range->end_ns != UINT64_C(-1);
-
-       ret = add_range(group_info, stream_range, "range-ns");
-       if (ret) {
-               goto end;
-       }
-
-       port_name = ctf_fs_make_port_name(group);
-       if (!port_name) {
-               ret = -1;
-               goto end;
-       }
-
-       insert_status = bt_value_map_insert_string_entry(group_info,
-               "port-name", port_name);
-       if (insert_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
-               ret = -1;
-               goto end;
-       }
-
-end:
-       g_free(port_name);
-       return ret;
-}
-
-static
-int populate_trace_info(const struct ctf_fs_trace *trace, bt_value *trace_info,
-               bt_logging_level log_level,
-               bt_self_component_class *self_comp_class)
-{
-       int ret = 0;
-       size_t group_idx;
-       bt_value_map_insert_entry_status insert_status;
-       bt_value_array_append_element_status append_status;
-       bt_value *file_groups = NULL;
-
-       BT_ASSERT(trace->ds_file_groups);
-       /* Add trace range info only if it contains streams. */
-       if (trace->ds_file_groups->len == 0) {
-               ret = -1;
-               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                       "Trace has no streams: trace-path=%s", trace->path->str);
-               goto end;
-       }
-
-       insert_status = bt_value_map_insert_empty_array_entry(trace_info,
-               "stream-infos", &file_groups);
-       if (insert_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
-               ret = -1;
-               goto end;
-       }
-
-       /* Find range of all stream groups, and of the trace. */
-       for (group_idx = 0; group_idx < trace->ds_file_groups->len;
-                       group_idx++) {
-               bt_value *group_info;
-               struct range group_range = { .set = false };
-               struct ctf_fs_ds_file_group *group = g_ptr_array_index(
-                               trace->ds_file_groups, group_idx);
-
-               append_status = bt_value_array_append_empty_map_element(
-                       file_groups, &group_info);
-               if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
-                       ret = -1;
-                       goto end;
-               }
-
-               ret = populate_stream_info(group, group_info, &group_range);
-               if (ret) {
-                       goto end;
-               }
-       }
-
-end:
-       return ret;
-}
-
-BT_HIDDEN
-bt_component_class_query_method_status trace_infos_query(
-               bt_self_component_class_source *self_comp_class_src,
-               const bt_value *params, bt_logging_level log_level,
-               const bt_value **user_result)
-{
-       struct ctf_fs_component *ctf_fs = NULL;
-       bt_component_class_query_method_status status =
-               BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_OK;
-       bt_self_component_class *self_comp_class =
-               bt_self_component_class_source_as_self_component_class(
-                       self_comp_class_src);
-       bt_value *result = NULL;
-       const bt_value *inputs_value = NULL;
-       const bt_value *trace_name_value;
-       int ret = 0;
-       bt_value *trace_info = NULL;
-       bt_value_array_append_element_status append_status;
-
-       BT_ASSERT(params);
-
-       if (!bt_value_is_map(params)) {
-               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                       "Query parameters is not a map value object.");
-               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
-               goto error;
-       }
-
-       ctf_fs = ctf_fs_component_create(log_level, NULL);
-       if (!ctf_fs) {
-               goto error;
-       }
-
-       if (!read_src_fs_parameters(params, &inputs_value, &trace_name_value,
-                       ctf_fs, NULL, self_comp_class)) {
-               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
-               goto error;
-       }
-
-       if (ctf_fs_component_create_ctf_fs_trace(ctf_fs, inputs_value,
-                       trace_name_value, NULL, self_comp_class)) {
-               goto error;
-       }
-
-       result = bt_value_array_create();
-       if (!result) {
-               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_MEMORY_ERROR;
-               goto error;
-       }
-
-       append_status = bt_value_array_append_empty_map_element(result,
-               &trace_info);
-       if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
-               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                       "Failed to create trace info map.");
-               goto error;
-       }
-
-       ret = populate_trace_info(ctf_fs->trace, trace_info, log_level,
-               self_comp_class);
-       if (ret) {
-               goto error;
-       }
-
-       goto end;
-
-error:
-       BT_VALUE_PUT_REF_AND_RESET(result);
-
-       if (status >= 0) {
-               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
-       }
-
-end:
-       if (ctf_fs) {
-               ctf_fs_destroy(ctf_fs);
-               ctf_fs = NULL;
-       }
-
-       *user_result = result;
-       return status;
-}
-
-BT_HIDDEN
-bt_component_class_query_method_status support_info_query(
-               bt_self_component_class_source *comp_class,
-               const bt_value *params, bt_logging_level log_level,
-               const bt_value **user_result)
-{
-       const bt_value *input_type_value;
-       const char *input_type;
-       bt_component_class_query_method_status status;
-       bt_value_map_insert_entry_status insert_entry_status;
-       double weight = 0;
-       gchar *metadata_path = NULL;
-       bt_value *result = NULL;
-       struct ctf_metadata_decoder *metadata_decoder = NULL;
-       FILE *metadata_file = NULL;
-       char uuid_str[BT_UUID_STR_LEN + 1];
-       bool has_uuid = false;
-       const bt_value *input_value;
-       const char *input;
-
-       input_type_value = bt_value_map_borrow_entry_value_const(params, "type");
-       BT_ASSERT(input_type_value);
-       BT_ASSERT(bt_value_get_type(input_type_value) == BT_VALUE_TYPE_STRING);
-       input_type = bt_value_string_get(input_type_value);
-
-       if (strcmp(input_type, "directory") != 0) {
-               goto create_result;
-       }
-
-       input_value = bt_value_map_borrow_entry_value_const(params, "input");
-       BT_ASSERT(input_value);
-       BT_ASSERT(bt_value_get_type(input_value) == BT_VALUE_TYPE_STRING);
-       input = bt_value_string_get(input_value);
-
-       metadata_path = g_build_filename(input, CTF_FS_METADATA_FILENAME, NULL);
-       if (!metadata_path) {
-               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_MEMORY_ERROR;
-               goto end;
-       }
-
-       metadata_file = g_fopen(metadata_path, "rb");
-       if (metadata_file) {
-               struct ctf_metadata_decoder_config metadata_decoder_config = { 0 };
-               enum ctf_metadata_decoder_status decoder_status;
-               bt_uuid_t uuid;
-
-               metadata_decoder_config.log_level = log_level;
-               metadata_decoder_config.self_comp_class =
-                       bt_self_component_class_source_as_self_component_class(comp_class);
-
-               metadata_decoder = ctf_metadata_decoder_create(
-                       &metadata_decoder_config);
-               if (!metadata_decoder) {
-                       status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
-                       goto end;
-               }
-
-               decoder_status = ctf_metadata_decoder_append_content(
-                       metadata_decoder, metadata_file);
-               if (decoder_status != CTF_METADATA_DECODER_STATUS_OK) {
-                       BT_LOGW("cannot append metadata content: metadata-decoder-status=%d",
-                               decoder_status);
-                       status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
-                       goto end;
-               }
-
-               /*
-                * We were able to parse the metadata file, so we are
-                * confident it's a CTF trace.
-                */
-               weight = 0.75;
-
-               /* If the trace has a UUID, return the stringified UUID as the group. */
-               if (ctf_metadata_decoder_get_trace_class_uuid(metadata_decoder, uuid) == 0) {
-                       bt_uuid_to_str(uuid, uuid_str);
-                       has_uuid = true;
-               }
-       }
-
-create_result:
-       result = bt_value_map_create();
-       if (!result) {
-               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_MEMORY_ERROR;
-               goto end;
-       }
-
-       insert_entry_status = bt_value_map_insert_real_entry(result, "weight", weight);
-       if (insert_entry_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
-               status = (int) insert_entry_status;
-               goto end;
-       }
-
-       /* We are not supposed to have weight == 0 and a UUID. */
-       BT_ASSERT(weight > 0 || !has_uuid);
-
-       if (weight > 0 && has_uuid) {
-               insert_entry_status = bt_value_map_insert_string_entry(result, "group", uuid_str);
-               if (insert_entry_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
-                       status = (int) insert_entry_status;
-                       goto end;
-               }
-       }
-
-       *user_result = result;
-       result = NULL;
-       status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_OK;
-
-end:
-       g_free(metadata_path);
-       bt_value_put_ref(result);
-       ctf_metadata_decoder_destroy(metadata_decoder);
-
-       return status;
-}
diff --git a/src/plugins/ctf/fs-src/query.cpp b/src/plugins/ctf/fs-src/query.cpp
new file mode 100644 (file)
index 0000000..04d5aab
--- /dev/null
@@ -0,0 +1,515 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * Babeltrace CTF file system Reader Component queries
+ */
+
+#define BT_LOG_OUTPUT_LEVEL log_level
+#define BT_LOG_TAG "PLUGIN/SRC.CTF.FS/QUERY"
+#include "logging/log.h"
+
+#include "query.hpp"
+#include <stdbool.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "common/assert.h"
+#include "metadata.hpp"
+#include "../common/metadata/decoder.hpp"
+#include "common/common.h"
+#include "common/macros.h"
+#include <babeltrace2/babeltrace.h>
+#include "fs.hpp"
+#include "logging/comp-logging.h"
+
+#define METADATA_TEXT_SIG      "/* CTF 1.8"
+
+struct range {
+       int64_t begin_ns = 0;
+       int64_t end_ns = 0;
+       bool set = false;
+};
+
+BT_HIDDEN
+bt_component_class_query_method_status metadata_info_query(
+               bt_self_component_class_source *self_comp_class_src,
+               const bt_value *params, bt_logging_level log_level,
+               const bt_value **user_result)
+{
+       bt_component_class_query_method_status status =
+               BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_OK;
+       bt_self_component_class *self_comp_class =
+               bt_self_component_class_source_as_self_component_class(self_comp_class_src);
+       bt_value *result = NULL;
+       const bt_value *path_value = NULL;
+       FILE *metadata_fp = NULL;
+       int ret;
+       int bo;
+       const char *path;
+       bool is_packetized;
+       struct ctf_metadata_decoder *decoder = NULL;
+       ctf_metadata_decoder_config decoder_cfg{};
+       enum ctf_metadata_decoder_status decoder_status;
+
+       result = bt_value_map_create();
+       if (!result) {
+               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_MEMORY_ERROR;
+               goto error;
+       }
+
+       BT_ASSERT(params);
+
+       if (!bt_value_is_map(params)) {
+               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
+                       "Query parameters is not a map value object.");
+               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
+               goto error;
+       }
+
+       path_value = bt_value_map_borrow_entry_value_const(params, "path");
+       if (!path_value) {
+               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
+                       "Mandatory `path` parameter missing");
+               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
+               goto error;
+       }
+
+       if (!bt_value_is_string(path_value)) {
+               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
+                       "`path` parameter is required to be a string value");
+               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
+               goto error;
+       }
+
+       path = bt_value_string_get(path_value);
+
+       BT_ASSERT(path);
+       metadata_fp = ctf_fs_metadata_open_file(path);
+       if (!metadata_fp) {
+               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
+                       "Cannot open trace metadata: path=\"%s\".", path);
+               goto error;
+       }
+
+       ret = ctf_metadata_decoder_is_packetized(metadata_fp, &is_packetized,
+               &bo, log_level, NULL);
+       if (ret) {
+               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
+                       "Cannot check whether or not the metadata stream is packetized: path=\"%s\".",
+                       path);
+               goto error;
+       }
+
+       decoder_cfg.log_level = log_level;
+       decoder_cfg.self_comp_class = self_comp_class;
+       decoder_cfg.keep_plain_text = true;
+       decoder = ctf_metadata_decoder_create(&decoder_cfg);
+       if (!decoder) {
+               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
+                       "Cannot create metadata decoder: path=\"%s\".", path);
+               goto error;
+       }
+
+       rewind(metadata_fp);
+       decoder_status = ctf_metadata_decoder_append_content(decoder,
+               metadata_fp);
+       if (decoder_status) {
+               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
+                       "Cannot update metadata decoder's content: path=\"%s\".",
+                       path);
+               goto error;
+       }
+
+       ret = bt_value_map_insert_string_entry(result, "text",
+               ctf_metadata_decoder_get_text(decoder));
+       if (ret) {
+               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
+                       "Cannot insert metadata text into query result.");
+               goto error;
+       }
+
+       ret = bt_value_map_insert_bool_entry(result, "is-packetized",
+               is_packetized);
+       if (ret) {
+               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
+                       "Cannot insert \"is-packetized\" attribute into query result.");
+               goto error;
+       }
+
+       goto end;
+
+error:
+       BT_VALUE_PUT_REF_AND_RESET(result);
+       result = NULL;
+
+       if (status >= 0) {
+               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
+       }
+
+end:
+       ctf_metadata_decoder_destroy(decoder);
+
+       if (metadata_fp) {
+               ret = fclose(metadata_fp);
+               if (ret) {
+                       BT_LOGE_ERRNO("Cannot close metatada file stream",
+                               ": path=\"%s\"", path);
+               }
+       }
+
+       *user_result = result;
+       return status;
+}
+
+static
+int add_range(bt_value *info, struct range *range,
+               const char *range_name)
+{
+       int ret = 0;
+       bt_value_map_insert_entry_status status;
+       bt_value *range_map;
+
+       if (!range->set) {
+               /* Not an error. */
+               goto end;
+       }
+
+       status = bt_value_map_insert_empty_map_entry(info, range_name,
+               &range_map);
+       if (status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
+               ret = -1;
+               goto end;
+       }
+
+       status = bt_value_map_insert_signed_integer_entry(range_map, "begin",
+                       range->begin_ns);
+       if (status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
+               ret = -1;
+               goto end;
+       }
+
+       status = bt_value_map_insert_signed_integer_entry(range_map, "end",
+                       range->end_ns);
+       if (status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
+               ret = -1;
+               goto end;
+       }
+
+end:
+       return ret;
+}
+
+static
+int populate_stream_info(struct ctf_fs_ds_file_group *group,
+               bt_value *group_info, struct range *stream_range)
+{
+       int ret = 0;
+       bt_value_map_insert_entry_status insert_status;
+       struct ctf_fs_ds_index_entry *first_ds_index_entry, *last_ds_index_entry;
+       gchar *port_name = NULL;
+
+       /*
+        * Since each `struct ctf_fs_ds_file_group` has a sorted array of
+        * `struct ctf_fs_ds_index_entry`, we can compute the stream range from
+        * the timestamp_begin of the first index entry and the timestamp_end
+        * of the last index entry.
+        */
+       BT_ASSERT(group->index);
+       BT_ASSERT(group->index->entries);
+       BT_ASSERT(group->index->entries->len > 0);
+
+       /* First entry. */
+       first_ds_index_entry = (struct ctf_fs_ds_index_entry *) g_ptr_array_index(
+               group->index->entries, 0);
+
+       /* Last entry. */
+       last_ds_index_entry = (struct ctf_fs_ds_index_entry *) g_ptr_array_index(
+               group->index->entries, group->index->entries->len - 1);
+
+       stream_range->begin_ns = first_ds_index_entry->timestamp_begin_ns;
+       stream_range->end_ns = last_ds_index_entry->timestamp_end_ns;
+
+       /*
+        * If any of the begin and end timestamps is not set it means that
+        * packets don't include `timestamp_begin` _and_ `timestamp_end` fields
+        * in their packet context so we can't set the range.
+        */
+       stream_range->set = stream_range->begin_ns != UINT64_C(-1) &&
+               stream_range->end_ns != UINT64_C(-1);
+
+       ret = add_range(group_info, stream_range, "range-ns");
+       if (ret) {
+               goto end;
+       }
+
+       port_name = ctf_fs_make_port_name(group);
+       if (!port_name) {
+               ret = -1;
+               goto end;
+       }
+
+       insert_status = bt_value_map_insert_string_entry(group_info,
+               "port-name", port_name);
+       if (insert_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
+               ret = -1;
+               goto end;
+       }
+
+end:
+       g_free(port_name);
+       return ret;
+}
+
+static
+int populate_trace_info(const struct ctf_fs_trace *trace, bt_value *trace_info,
+               bt_logging_level log_level,
+               bt_self_component_class *self_comp_class)
+{
+       int ret = 0;
+       size_t group_idx;
+       bt_value_map_insert_entry_status insert_status;
+       bt_value_array_append_element_status append_status;
+       bt_value *file_groups = NULL;
+
+       BT_ASSERT(trace->ds_file_groups);
+       /* Add trace range info only if it contains streams. */
+       if (trace->ds_file_groups->len == 0) {
+               ret = -1;
+               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
+                       "Trace has no streams: trace-path=%s", trace->path->str);
+               goto end;
+       }
+
+       insert_status = bt_value_map_insert_empty_array_entry(trace_info,
+               "stream-infos", &file_groups);
+       if (insert_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
+               ret = -1;
+               goto end;
+       }
+
+       /* Find range of all stream groups, and of the trace. */
+       for (group_idx = 0; group_idx < trace->ds_file_groups->len;
+                       group_idx++) {
+               bt_value *group_info;
+               range group_range;
+               ctf_fs_ds_file_group *group =
+                       (ctf_fs_ds_file_group *) g_ptr_array_index(trace->ds_file_groups, group_idx);
+
+               append_status = bt_value_array_append_empty_map_element(
+                       file_groups, &group_info);
+               if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
+                       ret = -1;
+                       goto end;
+               }
+
+               ret = populate_stream_info(group, group_info, &group_range);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+end:
+       return ret;
+}
+
+BT_HIDDEN
+bt_component_class_query_method_status trace_infos_query(
+               bt_self_component_class_source *self_comp_class_src,
+               const bt_value *params, bt_logging_level log_level,
+               const bt_value **user_result)
+{
+       struct ctf_fs_component *ctf_fs = NULL;
+       bt_component_class_query_method_status status =
+               BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_OK;
+       bt_self_component_class *self_comp_class =
+               bt_self_component_class_source_as_self_component_class(
+                       self_comp_class_src);
+       bt_value *result = NULL;
+       const bt_value *inputs_value = NULL;
+       const bt_value *trace_name_value;
+       int ret = 0;
+       bt_value *trace_info = NULL;
+       bt_value_array_append_element_status append_status;
+
+       BT_ASSERT(params);
+
+       if (!bt_value_is_map(params)) {
+               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
+                       "Query parameters is not a map value object.");
+               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
+               goto error;
+       }
+
+       ctf_fs = ctf_fs_component_create(log_level, NULL);
+       if (!ctf_fs) {
+               goto error;
+       }
+
+       if (!read_src_fs_parameters(params, &inputs_value, &trace_name_value,
+                       ctf_fs, NULL, self_comp_class)) {
+               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
+               goto error;
+       }
+
+       if (ctf_fs_component_create_ctf_fs_trace(ctf_fs, inputs_value,
+                       trace_name_value, NULL, self_comp_class)) {
+               goto error;
+       }
+
+       result = bt_value_array_create();
+       if (!result) {
+               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_MEMORY_ERROR;
+               goto error;
+       }
+
+       append_status = bt_value_array_append_empty_map_element(result,
+               &trace_info);
+       if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
+               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
+                       "Failed to create trace info map.");
+               goto error;
+       }
+
+       ret = populate_trace_info(ctf_fs->trace, trace_info, log_level,
+               self_comp_class);
+       if (ret) {
+               goto error;
+       }
+
+       goto end;
+
+error:
+       BT_VALUE_PUT_REF_AND_RESET(result);
+
+       if (status >= 0) {
+               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
+       }
+
+end:
+       if (ctf_fs) {
+               ctf_fs_destroy(ctf_fs);
+               ctf_fs = NULL;
+       }
+
+       *user_result = result;
+       return status;
+}
+
+BT_HIDDEN
+bt_component_class_query_method_status support_info_query(
+               bt_self_component_class_source *comp_class,
+               const bt_value *params, bt_logging_level log_level,
+               const bt_value **user_result)
+{
+       const bt_value *input_type_value;
+       const char *input_type;
+       bt_component_class_query_method_status status;
+       bt_value_map_insert_entry_status insert_entry_status;
+       double weight = 0;
+       gchar *metadata_path = NULL;
+       bt_value *result = NULL;
+       struct ctf_metadata_decoder *metadata_decoder = NULL;
+       FILE *metadata_file = NULL;
+       char uuid_str[BT_UUID_STR_LEN + 1];
+       bool has_uuid = false;
+       const bt_value *input_value;
+       const char *input;
+
+       input_type_value = bt_value_map_borrow_entry_value_const(params, "type");
+       BT_ASSERT(input_type_value);
+       BT_ASSERT(bt_value_get_type(input_type_value) == BT_VALUE_TYPE_STRING);
+       input_type = bt_value_string_get(input_type_value);
+
+       if (strcmp(input_type, "directory") != 0) {
+               goto create_result;
+       }
+
+       input_value = bt_value_map_borrow_entry_value_const(params, "input");
+       BT_ASSERT(input_value);
+       BT_ASSERT(bt_value_get_type(input_value) == BT_VALUE_TYPE_STRING);
+       input = bt_value_string_get(input_value);
+
+       metadata_path = g_build_filename(input, CTF_FS_METADATA_FILENAME, NULL);
+       if (!metadata_path) {
+               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_MEMORY_ERROR;
+               goto end;
+       }
+
+       metadata_file = g_fopen(metadata_path, "rb");
+       if (metadata_file) {
+               ctf_metadata_decoder_config metadata_decoder_config{};
+               enum ctf_metadata_decoder_status decoder_status;
+               bt_uuid_t uuid;
+
+               metadata_decoder_config.log_level = log_level;
+               metadata_decoder_config.self_comp_class =
+                       bt_self_component_class_source_as_self_component_class(comp_class);
+
+               metadata_decoder = ctf_metadata_decoder_create(
+                       &metadata_decoder_config);
+               if (!metadata_decoder) {
+                       status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
+                       goto end;
+               }
+
+               decoder_status = ctf_metadata_decoder_append_content(
+                       metadata_decoder, metadata_file);
+               if (decoder_status != CTF_METADATA_DECODER_STATUS_OK) {
+                       BT_LOGW("cannot append metadata content: metadata-decoder-status=%d",
+                               decoder_status);
+                       status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
+                       goto end;
+               }
+
+               /*
+                * We were able to parse the metadata file, so we are
+                * confident it's a CTF trace.
+                */
+               weight = 0.75;
+
+               /* If the trace has a UUID, return the stringified UUID as the group. */
+               if (ctf_metadata_decoder_get_trace_class_uuid(metadata_decoder, uuid) == 0) {
+                       bt_uuid_to_str(uuid, uuid_str);
+                       has_uuid = true;
+               }
+       }
+
+create_result:
+       result = bt_value_map_create();
+       if (!result) {
+               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_MEMORY_ERROR;
+               goto end;
+       }
+
+       insert_entry_status = bt_value_map_insert_real_entry(result, "weight", weight);
+       if (insert_entry_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
+               status = (bt_component_class_query_method_status) insert_entry_status;
+               goto end;
+       }
+
+       /* We are not supposed to have weight == 0 and a UUID. */
+       BT_ASSERT(weight > 0 || !has_uuid);
+
+       if (weight > 0 && has_uuid) {
+               insert_entry_status = bt_value_map_insert_string_entry(result, "group", uuid_str);
+               if (insert_entry_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
+                       status = (bt_component_class_query_method_status) insert_entry_status;
+                       goto end;
+               }
+       }
+
+       *user_result = result;
+       result = NULL;
+       status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_OK;
+
+end:
+       g_free(metadata_path);
+       bt_value_put_ref(result);
+       ctf_metadata_decoder_destroy(metadata_decoder);
+
+       return status;
+}
diff --git a/src/plugins/ctf/fs-src/query.h b/src/plugins/ctf/fs-src/query.h
deleted file mode 100644 (file)
index bfeebad..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * BabelTrace - CTF on File System Component
- */
-
-#ifndef BABELTRACE_PLUGIN_CTF_FS_QUERY_H
-#define BABELTRACE_PLUGIN_CTF_FS_QUERY_H
-
-#include "common/macros.h"
-#include <babeltrace2/babeltrace.h>
-
-BT_HIDDEN
-bt_component_class_query_method_status metadata_info_query(
-               bt_self_component_class_source *comp_class,
-               const bt_value *params,
-               bt_logging_level log_level, const bt_value **result);
-
-BT_HIDDEN
-bt_component_class_query_method_status trace_infos_query(
-               bt_self_component_class_source *comp_class,
-               const bt_value *params, bt_logging_level log_level,
-               const bt_value **result);
-
-BT_HIDDEN
-bt_component_class_query_method_status support_info_query(
-               bt_self_component_class_source *comp_class,
-               const bt_value *params, bt_logging_level log_level,
-               const bt_value **result);
-
-#endif /* BABELTRACE_PLUGIN_CTF_FS_QUERY_H */
diff --git a/src/plugins/ctf/fs-src/query.hpp b/src/plugins/ctf/fs-src/query.hpp
new file mode 100644 (file)
index 0000000..bfeebad
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * BabelTrace - CTF on File System Component
+ */
+
+#ifndef BABELTRACE_PLUGIN_CTF_FS_QUERY_H
+#define BABELTRACE_PLUGIN_CTF_FS_QUERY_H
+
+#include "common/macros.h"
+#include <babeltrace2/babeltrace.h>
+
+BT_HIDDEN
+bt_component_class_query_method_status metadata_info_query(
+               bt_self_component_class_source *comp_class,
+               const bt_value *params,
+               bt_logging_level log_level, const bt_value **result);
+
+BT_HIDDEN
+bt_component_class_query_method_status trace_infos_query(
+               bt_self_component_class_source *comp_class,
+               const bt_value *params, bt_logging_level log_level,
+               const bt_value **result);
+
+BT_HIDDEN
+bt_component_class_query_method_status support_info_query(
+               bt_self_component_class_source *comp_class,
+               const bt_value *params, bt_logging_level log_level,
+               const bt_value **result);
+
+#endif /* BABELTRACE_PLUGIN_CTF_FS_QUERY_H */
index 070715203bb5ebb031ed64946be8e88202526f16..53761a7142fe8e25fcdd4be8e11570c82f06bcd1 100644 (file)
@@ -1,15 +1,15 @@
 # SPDX-License-Identifier: MIT
 
 libbabeltrace2_plugin_ctf_lttng_live_la_SOURCES = \
-               lttng-live.c \
-               lttng-live.h \
-               data-stream.c \
-               data-stream.h \
-               metadata.c \
-               metadata.h \
-               viewer-connection.c \
-               viewer-connection.h \
-               lttng-viewer-abi.h
+               lttng-live.cpp \
+               lttng-live.hpp \
+               data-stream.cpp \
+               data-stream.hpp \
+               metadata.cpp \
+               metadata.hpp \
+               viewer-connection.cpp \
+               viewer-connection.hpp \
+               lttng-viewer-abi.hpp
 
 libbabeltrace2_plugin_ctf_lttng_live_la_LIBADD =
 
diff --git a/src/plugins/ctf/lttng-live/data-stream.c b/src/plugins/ctf/lttng-live/data-stream.c
deleted file mode 100644 (file)
index e8f00d7..0000000
+++ /dev/null
@@ -1,302 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2019 Francis Deslauriers <francis.deslauriers@efficios.com>
- * Copyright 2016 Philippe Proulx <pproulx@efficios.com>
- * Copyright 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- * Copyright 2010-2011 EfficiOS Inc. and Linux Foundation
- */
-
-#define BT_COMP_LOG_SELF_COMP self_comp
-#define BT_LOG_OUTPUT_LEVEL log_level
-#define BT_LOG_TAG "PLUGIN/SRC.CTF.LTTNG-LIVE/DS"
-#include "logging/comp-logging.h"
-
-#include <inttypes.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <glib.h>
-
-#include <babeltrace2/babeltrace.h>
-
-#include "../common/msg-iter/msg-iter.h"
-#include "common/assert.h"
-#include "compat/mman.h"
-#include "data-stream.h"
-
-#define STREAM_NAME_PREFIX     "stream-"
-
-static
-enum ctf_msg_iter_medium_status medop_request_bytes(
-               size_t request_sz, uint8_t **buffer_addr,
-               size_t *buffer_sz, void *data)
-{
-       enum ctf_msg_iter_medium_status status =
-               CTF_MSG_ITER_MEDIUM_STATUS_OK;
-       struct lttng_live_stream_iterator *stream = data;
-       struct lttng_live_trace *trace = stream->trace;
-       struct lttng_live_session *session = trace->session;
-       struct lttng_live_msg_iter *live_msg_iter = session->lttng_live_msg_iter;
-       uint64_t recv_len = 0;
-       uint64_t len_left;
-       uint64_t read_len;
-
-       BT_ASSERT(request_sz);
-
-       if (stream->has_stream_hung_up) {
-               status = CTF_MSG_ITER_MEDIUM_STATUS_EOF;
-               goto end;
-       }
-
-       len_left = stream->base_offset + stream->len - stream->offset;
-       if (!len_left) {
-               lttng_live_stream_iterator_set_state(stream,
-                               LTTNG_LIVE_STREAM_ACTIVE_NO_DATA);
-               status = CTF_MSG_ITER_MEDIUM_STATUS_AGAIN;
-               goto end;
-       }
-
-       read_len = MIN(request_sz, stream->buflen);
-       read_len = MIN(read_len, len_left);
-       status = lttng_live_get_stream_bytes(live_msg_iter,
-                       stream, stream->buf, stream->offset,
-                       read_len, &recv_len);
-       *buffer_addr = stream->buf;
-       *buffer_sz = recv_len;
-       stream->offset += recv_len;
-end:
-       return status;
-}
-
-static
-bt_stream *medop_borrow_stream(bt_stream_class *stream_class,
-               int64_t stream_id, void *data)
-{
-       struct lttng_live_stream_iterator *lttng_live_stream = data;
-       bt_logging_level log_level = lttng_live_stream->log_level;
-       bt_self_component *self_comp = lttng_live_stream->self_comp;
-
-       if (!lttng_live_stream->stream) {
-               uint64_t stream_class_id = bt_stream_class_get_id(stream_class);
-
-               BT_COMP_LOGI("Creating stream %s (ID: %" PRIu64 ") out of stream "
-                       "class %" PRId64, lttng_live_stream->name->str,
-                       stream_id, stream_class_id);
-
-               if (stream_id < 0) {
-                       /*
-                        * No stream instance ID in the stream. It's possible
-                        * to encounter this situation with older version of
-                        * LTTng. In these cases, use the viewer_stream_id that
-                        * is unique for a live viewer session.
-                        */
-                       lttng_live_stream->stream = bt_stream_create_with_id(
-                               stream_class, lttng_live_stream->trace->trace,
-                               lttng_live_stream->viewer_stream_id);
-               } else {
-                       lttng_live_stream->stream = bt_stream_create_with_id(
-                               stream_class, lttng_live_stream->trace->trace,
-                               (uint64_t) stream_id);
-               }
-
-               if (!lttng_live_stream->stream) {
-                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                               "Cannot create stream %s (stream class ID "
-                               "%" PRId64 ", stream ID %" PRIu64 ")",
-                               lttng_live_stream->name->str,
-                               stream_class_id, stream_id);
-                       goto end;
-               }
-
-               bt_stream_set_name(lttng_live_stream->stream,
-                       lttng_live_stream->name->str);
-       }
-
-end:
-       return lttng_live_stream->stream;
-}
-
-static struct ctf_msg_iter_medium_ops medops = {
-       .request_bytes = medop_request_bytes,
-       .seek = NULL,
-       .borrow_stream = medop_borrow_stream,
-};
-
-BT_HIDDEN
-enum lttng_live_iterator_status lttng_live_lazy_msg_init(
-               struct lttng_live_session *session,
-               bt_self_message_iterator *self_msg_iter)
-{
-       struct lttng_live_component *lttng_live =
-               session->lttng_live_msg_iter->lttng_live_comp;
-       uint64_t trace_idx, stream_iter_idx;
-       bt_logging_level log_level = session->log_level;
-       bt_self_component *self_comp = session->self_comp;
-
-       if (!session->lazy_stream_msg_init) {
-               return LTTNG_LIVE_ITERATOR_STATUS_OK;
-       }
-
-       BT_COMP_LOGD("Lazily initializing self message iterator for live session: "
-               "session-id=%"PRIu64", self-msg-iter-addr=%p", session->id,
-               self_msg_iter);
-
-       for (trace_idx = 0; trace_idx < session->traces->len; trace_idx++) {
-               struct lttng_live_trace *trace =
-                       g_ptr_array_index(session->traces, trace_idx);
-
-               for (stream_iter_idx = 0;
-                               stream_iter_idx < trace->stream_iterators->len;
-                               stream_iter_idx++) {
-                       struct ctf_trace_class *ctf_tc;
-                       struct lttng_live_stream_iterator *stream_iter =
-                               g_ptr_array_index(trace->stream_iterators,
-                                       stream_iter_idx);
-
-                       if (stream_iter->msg_iter) {
-                               continue;
-                       }
-                       ctf_tc = ctf_metadata_decoder_borrow_ctf_trace_class(
-                               trace->metadata->decoder);
-                       BT_COMP_LOGD("Creating CTF message iterator: "
-                               "session-id=%"PRIu64", ctf-tc-addr=%p, "
-                               "stream-iter-name=%s, self-msg-iter-addr=%p",
-                               session->id, ctf_tc, stream_iter->name->str, self_msg_iter);
-                       stream_iter->msg_iter = ctf_msg_iter_create(ctf_tc,
-                               lttng_live->max_query_size, medops, stream_iter,
-                               log_level, self_comp, self_msg_iter);
-                       if (!stream_iter->msg_iter) {
-                               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                       "Failed to create CTF message iterator");
-                               goto error;
-                       }
-               }
-       }
-
-       session->lazy_stream_msg_init = false;
-
-       return LTTNG_LIVE_ITERATOR_STATUS_OK;
-
-error:
-       return LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-}
-
-BT_HIDDEN
-struct lttng_live_stream_iterator *lttng_live_stream_iterator_create(
-               struct lttng_live_session *session,
-               uint64_t ctf_trace_id,
-               uint64_t stream_id,
-               bt_self_message_iterator *self_msg_iter)
-{
-       struct lttng_live_stream_iterator *stream_iter;
-       struct lttng_live_component *lttng_live;
-       struct lttng_live_trace *trace;
-       bt_logging_level log_level;
-       bt_self_component *self_comp;
-
-       BT_ASSERT(session);
-       BT_ASSERT(session->lttng_live_msg_iter);
-       BT_ASSERT(session->lttng_live_msg_iter->lttng_live_comp);
-       log_level = session->log_level;
-       self_comp = session->self_comp;
-
-       lttng_live = session->lttng_live_msg_iter->lttng_live_comp;
-
-       stream_iter = g_new0(struct lttng_live_stream_iterator, 1);
-       if (!stream_iter) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Failed to allocate struct lttng_live_stream_iterator");
-               goto error;
-       }
-
-       stream_iter->log_level = log_level;
-       stream_iter->self_comp = self_comp;
-       trace = lttng_live_session_borrow_or_create_trace_by_id(session, ctf_trace_id);
-       if (!trace) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Failed to borrow CTF trace.");
-               goto error;
-       }
-
-       stream_iter->trace = trace;
-       stream_iter->state = LTTNG_LIVE_STREAM_ACTIVE_NO_DATA;
-       stream_iter->viewer_stream_id = stream_id;
-
-       stream_iter->ctf_stream_class_id.is_set = false;
-       stream_iter->ctf_stream_class_id.value = UINT64_MAX;
-
-       stream_iter->last_inactivity_ts.is_set = false;
-       stream_iter->last_inactivity_ts.value = 0;
-
-       if (trace->trace) {
-               struct ctf_trace_class *ctf_tc =
-                       ctf_metadata_decoder_borrow_ctf_trace_class(
-                               trace->metadata->decoder);
-               BT_ASSERT(!stream_iter->msg_iter);
-               stream_iter->msg_iter = ctf_msg_iter_create(ctf_tc,
-                       lttng_live->max_query_size, medops, stream_iter,
-                       log_level, self_comp, self_msg_iter);
-               if (!stream_iter->msg_iter) {
-                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                               "Failed to create CTF message iterator");
-                       goto error;
-               }
-       }
-       stream_iter->buf = g_new0(uint8_t, lttng_live->max_query_size);
-       if (!stream_iter->buf) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Failed to allocate live stream iterator buffer");
-               goto error;
-       }
-
-       stream_iter->buflen = lttng_live->max_query_size;
-       stream_iter->name = g_string_new(NULL);
-       if (!stream_iter->name) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Failed to allocate live stream iterator name buffer");
-               goto error;
-       }
-
-       g_string_printf(stream_iter->name, STREAM_NAME_PREFIX "%" PRIu64,
-               stream_iter->viewer_stream_id);
-       g_ptr_array_add(trace->stream_iterators, stream_iter);
-
-       /* Track the number of active stream iterator. */
-       session->lttng_live_msg_iter->active_stream_iter++;
-
-       goto end;
-error:
-       lttng_live_stream_iterator_destroy(stream_iter);
-       stream_iter = NULL;
-end:
-       return stream_iter;
-}
-
-BT_HIDDEN
-void lttng_live_stream_iterator_destroy(
-               struct lttng_live_stream_iterator *stream_iter)
-{
-       if (!stream_iter) {
-               return;
-       }
-
-       if (stream_iter->stream) {
-               BT_STREAM_PUT_REF_AND_RESET(stream_iter->stream);
-       }
-
-       if (stream_iter->msg_iter) {
-               ctf_msg_iter_destroy(stream_iter->msg_iter);
-       }
-       g_free(stream_iter->buf);
-       if (stream_iter->name) {
-               g_string_free(stream_iter->name, TRUE);
-       }
-
-       bt_message_put_ref(stream_iter->current_msg);
-
-       /* Track the number of active stream iterator. */
-       stream_iter->trace->session->lttng_live_msg_iter->active_stream_iter--;
-
-       g_free(stream_iter);
-}
diff --git a/src/plugins/ctf/lttng-live/data-stream.cpp b/src/plugins/ctf/lttng-live/data-stream.cpp
new file mode 100644 (file)
index 0000000..50071a0
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2019 Francis Deslauriers <francis.deslauriers@efficios.com>
+ * Copyright 2016 Philippe Proulx <pproulx@efficios.com>
+ * Copyright 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ * Copyright 2010-2011 EfficiOS Inc. and Linux Foundation
+ */
+
+#define BT_COMP_LOG_SELF_COMP self_comp
+#define BT_LOG_OUTPUT_LEVEL log_level
+#define BT_LOG_TAG "PLUGIN/SRC.CTF.LTTNG-LIVE/DS"
+#include "logging/comp-logging.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <glib.h>
+
+#include <babeltrace2/babeltrace.h>
+
+#include "../common/msg-iter/msg-iter.hpp"
+#include "common/assert.h"
+#include "compat/mman.h"
+#include "data-stream.hpp"
+
+#define STREAM_NAME_PREFIX     "stream-"
+
+static
+enum ctf_msg_iter_medium_status medop_request_bytes(
+               size_t request_sz, uint8_t **buffer_addr,
+               size_t *buffer_sz, void *data)
+{
+       enum ctf_msg_iter_medium_status status =
+               CTF_MSG_ITER_MEDIUM_STATUS_OK;
+       lttng_live_stream_iterator *stream = (lttng_live_stream_iterator *) data;
+       struct lttng_live_trace *trace = stream->trace;
+       struct lttng_live_session *session = trace->session;
+       struct lttng_live_msg_iter *live_msg_iter = session->lttng_live_msg_iter;
+       uint64_t recv_len = 0;
+       uint64_t len_left;
+       uint64_t read_len;
+
+       BT_ASSERT(request_sz);
+
+       if (stream->has_stream_hung_up) {
+               status = CTF_MSG_ITER_MEDIUM_STATUS_EOF;
+               goto end;
+       }
+
+       len_left = stream->base_offset + stream->len - stream->offset;
+       if (!len_left) {
+               lttng_live_stream_iterator_set_state(stream,
+                               LTTNG_LIVE_STREAM_ACTIVE_NO_DATA);
+               status = CTF_MSG_ITER_MEDIUM_STATUS_AGAIN;
+               goto end;
+       }
+
+       read_len = MIN(request_sz, stream->buflen);
+       read_len = MIN(read_len, len_left);
+       status = lttng_live_get_stream_bytes(live_msg_iter,
+                       stream, stream->buf, stream->offset,
+                       read_len, &recv_len);
+       *buffer_addr = stream->buf;
+       *buffer_sz = recv_len;
+       stream->offset += recv_len;
+end:
+       return status;
+}
+
+static
+bt_stream *medop_borrow_stream(bt_stream_class *stream_class,
+               int64_t stream_id, void *data)
+{
+       lttng_live_stream_iterator *lttng_live_stream = (lttng_live_stream_iterator *) data;
+       bt_logging_level log_level = lttng_live_stream->log_level;
+       bt_self_component *self_comp = lttng_live_stream->self_comp;
+
+       if (!lttng_live_stream->stream) {
+               uint64_t stream_class_id = bt_stream_class_get_id(stream_class);
+
+               BT_COMP_LOGI("Creating stream %s (ID: %" PRIu64 ") out of stream "
+                       "class %" PRId64, lttng_live_stream->name->str,
+                       stream_id, stream_class_id);
+
+               if (stream_id < 0) {
+                       /*
+                        * No stream instance ID in the stream. It's possible
+                        * to encounter this situation with older version of
+                        * LTTng. In these cases, use the viewer_stream_id that
+                        * is unique for a live viewer session.
+                        */
+                       lttng_live_stream->stream = bt_stream_create_with_id(
+                               stream_class, lttng_live_stream->trace->trace,
+                               lttng_live_stream->viewer_stream_id);
+               } else {
+                       lttng_live_stream->stream = bt_stream_create_with_id(
+                               stream_class, lttng_live_stream->trace->trace,
+                               (uint64_t) stream_id);
+               }
+
+               if (!lttng_live_stream->stream) {
+                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                               "Cannot create stream %s (stream class ID "
+                               "%" PRId64 ", stream ID %" PRIu64 ")",
+                               lttng_live_stream->name->str,
+                               stream_class_id, stream_id);
+                       goto end;
+               }
+
+               bt_stream_set_name(lttng_live_stream->stream,
+                       lttng_live_stream->name->str);
+       }
+
+end:
+       return lttng_live_stream->stream;
+}
+
+static struct ctf_msg_iter_medium_ops medops = {
+       medop_request_bytes,
+       nullptr,
+       nullptr,
+       medop_borrow_stream,
+};
+
+BT_HIDDEN
+enum lttng_live_iterator_status lttng_live_lazy_msg_init(
+               struct lttng_live_session *session,
+               bt_self_message_iterator *self_msg_iter)
+{
+       struct lttng_live_component *lttng_live =
+               session->lttng_live_msg_iter->lttng_live_comp;
+       uint64_t trace_idx, stream_iter_idx;
+       bt_logging_level log_level = session->log_level;
+       bt_self_component *self_comp = session->self_comp;
+
+       if (!session->lazy_stream_msg_init) {
+               return LTTNG_LIVE_ITERATOR_STATUS_OK;
+       }
+
+       BT_COMP_LOGD("Lazily initializing self message iterator for live session: "
+               "session-id=%" PRIu64 ", self-msg-iter-addr=%p", session->id,
+               self_msg_iter);
+
+       for (trace_idx = 0; trace_idx < session->traces->len; trace_idx++) {
+               struct lttng_live_trace *trace =
+                       (lttng_live_trace *) g_ptr_array_index(session->traces, trace_idx);
+
+               for (stream_iter_idx = 0;
+                               stream_iter_idx < trace->stream_iterators->len;
+                               stream_iter_idx++) {
+                       struct ctf_trace_class *ctf_tc;
+                       struct lttng_live_stream_iterator *stream_iter =
+                               (lttng_live_stream_iterator *) g_ptr_array_index(trace->stream_iterators,
+                                       stream_iter_idx);
+
+                       if (stream_iter->msg_iter) {
+                               continue;
+                       }
+                       ctf_tc = ctf_metadata_decoder_borrow_ctf_trace_class(
+                               trace->metadata->decoder);
+                       BT_COMP_LOGD("Creating CTF message iterator: "
+                               "session-id=%" PRIu64 ", ctf-tc-addr=%p, "
+                               "stream-iter-name=%s, self-msg-iter-addr=%p",
+                               session->id, ctf_tc, stream_iter->name->str, self_msg_iter);
+                       stream_iter->msg_iter = ctf_msg_iter_create(ctf_tc,
+                               lttng_live->max_query_size, medops, stream_iter,
+                               log_level, self_comp, self_msg_iter);
+                       if (!stream_iter->msg_iter) {
+                               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                                       "Failed to create CTF message iterator");
+                               goto error;
+                       }
+               }
+       }
+
+       session->lazy_stream_msg_init = false;
+
+       return LTTNG_LIVE_ITERATOR_STATUS_OK;
+
+error:
+       return LTTNG_LIVE_ITERATOR_STATUS_ERROR;
+}
+
+BT_HIDDEN
+struct lttng_live_stream_iterator *lttng_live_stream_iterator_create(
+               struct lttng_live_session *session,
+               uint64_t ctf_trace_id,
+               uint64_t stream_id,
+               bt_self_message_iterator *self_msg_iter)
+{
+       struct lttng_live_stream_iterator *stream_iter;
+       struct lttng_live_component *lttng_live;
+       struct lttng_live_trace *trace;
+       bt_logging_level log_level;
+       bt_self_component *self_comp;
+
+       BT_ASSERT(session);
+       BT_ASSERT(session->lttng_live_msg_iter);
+       BT_ASSERT(session->lttng_live_msg_iter->lttng_live_comp);
+       log_level = session->log_level;
+       self_comp = session->self_comp;
+
+       lttng_live = session->lttng_live_msg_iter->lttng_live_comp;
+
+       stream_iter = g_new0(struct lttng_live_stream_iterator, 1);
+       if (!stream_iter) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Failed to allocate struct lttng_live_stream_iterator");
+               goto error;
+       }
+
+       stream_iter->log_level = log_level;
+       stream_iter->self_comp = self_comp;
+       trace = lttng_live_session_borrow_or_create_trace_by_id(session, ctf_trace_id);
+       if (!trace) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Failed to borrow CTF trace.");
+               goto error;
+       }
+
+       stream_iter->trace = trace;
+       stream_iter->state = LTTNG_LIVE_STREAM_ACTIVE_NO_DATA;
+       stream_iter->viewer_stream_id = stream_id;
+
+       stream_iter->ctf_stream_class_id.is_set = false;
+       stream_iter->ctf_stream_class_id.value = UINT64_MAX;
+
+       stream_iter->last_inactivity_ts.is_set = false;
+       stream_iter->last_inactivity_ts.value = 0;
+
+       if (trace->trace) {
+               struct ctf_trace_class *ctf_tc =
+                       ctf_metadata_decoder_borrow_ctf_trace_class(
+                               trace->metadata->decoder);
+               BT_ASSERT(!stream_iter->msg_iter);
+               stream_iter->msg_iter = ctf_msg_iter_create(ctf_tc,
+                       lttng_live->max_query_size, medops, stream_iter,
+                       log_level, self_comp, self_msg_iter);
+               if (!stream_iter->msg_iter) {
+                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                               "Failed to create CTF message iterator");
+                       goto error;
+               }
+       }
+       stream_iter->buf = g_new0(uint8_t, lttng_live->max_query_size);
+       if (!stream_iter->buf) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Failed to allocate live stream iterator buffer");
+               goto error;
+       }
+
+       stream_iter->buflen = lttng_live->max_query_size;
+       stream_iter->name = g_string_new(NULL);
+       if (!stream_iter->name) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Failed to allocate live stream iterator name buffer");
+               goto error;
+       }
+
+       g_string_printf(stream_iter->name, STREAM_NAME_PREFIX "%" PRIu64,
+               stream_iter->viewer_stream_id);
+       g_ptr_array_add(trace->stream_iterators, stream_iter);
+
+       /* Track the number of active stream iterator. */
+       session->lttng_live_msg_iter->active_stream_iter++;
+
+       goto end;
+error:
+       lttng_live_stream_iterator_destroy(stream_iter);
+       stream_iter = NULL;
+end:
+       return stream_iter;
+}
+
+BT_HIDDEN
+void lttng_live_stream_iterator_destroy(
+               struct lttng_live_stream_iterator *stream_iter)
+{
+       if (!stream_iter) {
+               return;
+       }
+
+       if (stream_iter->stream) {
+               BT_STREAM_PUT_REF_AND_RESET(stream_iter->stream);
+       }
+
+       if (stream_iter->msg_iter) {
+               ctf_msg_iter_destroy(stream_iter->msg_iter);
+       }
+       g_free(stream_iter->buf);
+       if (stream_iter->name) {
+               g_string_free(stream_iter->name, TRUE);
+       }
+
+       bt_message_put_ref(stream_iter->current_msg);
+
+       /* Track the number of active stream iterator. */
+       stream_iter->trace->session->lttng_live_msg_iter->active_stream_iter--;
+
+       g_free(stream_iter);
+}
diff --git a/src/plugins/ctf/lttng-live/data-stream.h b/src/plugins/ctf/lttng-live/data-stream.h
deleted file mode 100644 (file)
index d2c588e..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2016 Philippe Proulx <pproulx@efficios.com>
- */
-
-#ifndef LTTNG_LIVE_DATA_STREAM_H
-#define LTTNG_LIVE_DATA_STREAM_H
-
-#include <stdio.h>
-#include <stdint.h>
-
-#include <glib.h>
-
-#include "../common/msg-iter/msg-iter.h"
-#include "lttng-live.h"
-
-enum lttng_live_iterator_status lttng_live_lazy_msg_init(
-               struct lttng_live_session *session,
-               bt_self_message_iterator *self_msg_iter);
-
-struct lttng_live_stream_iterator *lttng_live_stream_iterator_create(
-               struct lttng_live_session *session,
-               uint64_t ctf_trace_id,
-               uint64_t stream_id,
-               bt_self_message_iterator *self_msg_iter);
-
-void lttng_live_stream_iterator_destroy(
-               struct lttng_live_stream_iterator *stream);
-
-#endif /* LTTNG_LIVE_DATA_STREAM_H */
diff --git a/src/plugins/ctf/lttng-live/data-stream.hpp b/src/plugins/ctf/lttng-live/data-stream.hpp
new file mode 100644 (file)
index 0000000..5c9e9a6
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2016 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#ifndef LTTNG_LIVE_DATA_STREAM_H
+#define LTTNG_LIVE_DATA_STREAM_H
+
+#include <stdio.h>
+#include <stdint.h>
+
+#include <glib.h>
+
+#include "../common/msg-iter/msg-iter.hpp"
+#include "lttng-live.hpp"
+
+enum lttng_live_iterator_status lttng_live_lazy_msg_init(
+               struct lttng_live_session *session,
+               bt_self_message_iterator *self_msg_iter);
+
+struct lttng_live_stream_iterator *lttng_live_stream_iterator_create(
+               struct lttng_live_session *session,
+               uint64_t ctf_trace_id,
+               uint64_t stream_id,
+               bt_self_message_iterator *self_msg_iter);
+
+void lttng_live_stream_iterator_destroy(
+               struct lttng_live_stream_iterator *stream);
+
+#endif /* LTTNG_LIVE_DATA_STREAM_H */
diff --git a/src/plugins/ctf/lttng-live/lttng-live.c b/src/plugins/ctf/lttng-live/lttng-live.c
deleted file mode 100644 (file)
index 6c412dd..0000000
+++ /dev/null
@@ -1,2324 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2019 Francis Deslauriers <francis.deslauriers@efficios.com>
- * Copyright 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- * Copyright 2016 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * Babeltrace CTF LTTng-live Client Component
- */
-
-#define BT_COMP_LOG_SELF_COMP self_comp
-#define BT_LOG_OUTPUT_LEVEL log_level
-#define BT_LOG_TAG "PLUGIN/SRC.CTF.LTTNG-LIVE"
-#include "logging/comp-logging.h"
-
-#include <inttypes.h>
-#include <stdbool.h>
-#include <unistd.h>
-
-#include <glib.h>
-
-#include "common/assert.h"
-#include <babeltrace2/babeltrace.h>
-#include "compat/compiler.h"
-#include <babeltrace2/types.h>
-
-#include "plugins/common/muxing/muxing.h"
-#include "plugins/common/param-validation/param-validation.h"
-
-#include "data-stream.h"
-#include "metadata.h"
-#include "lttng-live.h"
-
-#define MAX_QUERY_SIZE                     (256*1024)
-#define URL_PARAM                          "url"
-#define INPUTS_PARAM                       "inputs"
-#define SESS_NOT_FOUND_ACTION_PARAM        "session-not-found-action"
-#define SESS_NOT_FOUND_ACTION_CONTINUE_STR  "continue"
-#define SESS_NOT_FOUND_ACTION_FAIL_STR     "fail"
-#define SESS_NOT_FOUND_ACTION_END_STR      "end"
-
-#define print_dbg(fmt, ...)    BT_COMP_LOGD(fmt, ## __VA_ARGS__)
-
-static
-const char *lttng_live_iterator_status_string(
-               enum lttng_live_iterator_status status)
-{
-       switch (status) {
-       case LTTNG_LIVE_ITERATOR_STATUS_CONTINUE:
-               return "LTTNG_LIVE_ITERATOR_STATUS_CONTINUE";
-       case LTTNG_LIVE_ITERATOR_STATUS_AGAIN:
-               return "LTTNG_LIVE_ITERATOR_STATUS_AGAIN";
-       case LTTNG_LIVE_ITERATOR_STATUS_END:
-               return "LTTNG_LIVE_ITERATOR_STATUS_END";
-       case LTTNG_LIVE_ITERATOR_STATUS_OK:
-               return "LTTNG_LIVE_ITERATOR_STATUS_OK";
-       case LTTNG_LIVE_ITERATOR_STATUS_INVAL:
-               return "LTTNG_LIVE_ITERATOR_STATUS_INVAL";
-       case LTTNG_LIVE_ITERATOR_STATUS_ERROR:
-               return "LTTNG_LIVE_ITERATOR_STATUS_ERROR";
-       case LTTNG_LIVE_ITERATOR_STATUS_NOMEM:
-               return "LTTNG_LIVE_ITERATOR_STATUS_NOMEM";
-       case LTTNG_LIVE_ITERATOR_STATUS_UNSUPPORTED:
-               return "LTTNG_LIVE_ITERATOR_STATUS_UNSUPPORTED";
-       default:
-               bt_common_abort();
-       }
-}
-
-static
-const char *lttng_live_stream_state_string(enum lttng_live_stream_state state)
-{
-       switch (state) {
-       case LTTNG_LIVE_STREAM_ACTIVE_NO_DATA:
-               return "ACTIVE_NO_DATA";
-       case LTTNG_LIVE_STREAM_QUIESCENT_NO_DATA:
-               return "QUIESCENT_NO_DATA";
-       case LTTNG_LIVE_STREAM_QUIESCENT:
-               return "QUIESCENT";
-       case LTTNG_LIVE_STREAM_ACTIVE_DATA:
-               return "ACTIVE_DATA";
-       case LTTNG_LIVE_STREAM_EOF:
-               return "EOF";
-       default:
-               return "ERROR";
-       }
-}
-
-void lttng_live_stream_iterator_set_state(struct lttng_live_stream_iterator *stream_iter,
-               enum lttng_live_stream_state new_state)
-{
-       bt_self_component *self_comp = stream_iter->self_comp;
-       bt_logging_level log_level = stream_iter->log_level;
-
-       BT_COMP_LOGD("Setting live stream iterator state: viewer-stream-id=%" PRIu64
-               ", old-state=%s, new-state=%s",
-               stream_iter->viewer_stream_id,
-               lttng_live_stream_state_string(stream_iter->state),
-               lttng_live_stream_state_string(new_state));
-
-       stream_iter->state = new_state;
-}
-
-#define LTTNG_LIVE_LOGD_STREAM_ITER(live_stream_iter) \
-       do { \
-               BT_COMP_LOGD("Live stream iterator state=%s, " \
-                       "last-inact-ts-is-set=%d, last-inact-ts-value=%" PRId64 ", " \
-                       "curr-inact-ts=%" PRId64, \
-                       lttng_live_stream_state_string(live_stream_iter->state), \
-                       live_stream_iter->last_inactivity_ts.is_set, \
-                       live_stream_iter->last_inactivity_ts.value, \
-                       live_stream_iter->current_inactivity_ts); \
-       } while (0);
-
-BT_HIDDEN
-bool lttng_live_graph_is_canceled(struct lttng_live_msg_iter *msg_iter)
-{
-       bool ret;
-
-       if (!msg_iter) {
-               ret = false;
-               goto end;
-       }
-
-       ret = bt_self_message_iterator_is_interrupted(
-               msg_iter->self_msg_iter);
-
-end:
-       return ret;
-}
-
-static
-struct lttng_live_trace *lttng_live_session_borrow_trace_by_id(struct lttng_live_session *session,
-               uint64_t trace_id)
-{
-       uint64_t trace_idx;
-       struct lttng_live_trace *ret_trace = NULL;
-
-       for (trace_idx = 0; trace_idx < session->traces->len; trace_idx++) {
-               struct lttng_live_trace *trace =
-                       g_ptr_array_index(session->traces, trace_idx);
-               if (trace->id == trace_id) {
-                       ret_trace = trace;
-                       goto end;
-               }
-       }
-
-end:
-       return ret_trace;
-}
-
-static
-void lttng_live_destroy_trace(struct lttng_live_trace *trace)
-{
-       bt_logging_level log_level = trace->log_level;
-       bt_self_component *self_comp = trace->self_comp;
-
-       BT_COMP_LOGD("Destroying live trace: trace-id=%"PRIu64, trace->id);
-
-       BT_ASSERT(trace->stream_iterators);
-       g_ptr_array_free(trace->stream_iterators, TRUE);
-
-       BT_TRACE_PUT_REF_AND_RESET(trace->trace);
-       BT_TRACE_CLASS_PUT_REF_AND_RESET(trace->trace_class);
-
-       lttng_live_metadata_fini(trace);
-       g_free(trace);
-}
-
-static
-struct lttng_live_trace *lttng_live_create_trace(struct lttng_live_session *session,
-               uint64_t trace_id)
-{
-       struct lttng_live_trace *trace = NULL;
-       bt_logging_level log_level = session->log_level;
-       bt_self_component *self_comp = session->self_comp;
-
-       BT_COMP_LOGD("Creating live trace: "
-               "session-id=%"PRIu64", trace-id=%"PRIu64,
-               session->id, trace_id);
-       trace = g_new0(struct lttng_live_trace, 1);
-       if (!trace) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Failed to allocate live trace");
-               goto error;
-       }
-       trace->log_level = session->log_level;
-       trace->self_comp = session->self_comp;
-       trace->session = session;
-       trace->id = trace_id;
-       trace->trace_class = NULL;
-       trace->trace = NULL;
-       trace->stream_iterators = g_ptr_array_new_with_free_func(
-               (GDestroyNotify) lttng_live_stream_iterator_destroy);
-       BT_ASSERT(trace->stream_iterators);
-       trace->metadata_stream_state = LTTNG_LIVE_METADATA_STREAM_STATE_NEEDED;
-       g_ptr_array_add(session->traces, trace);
-
-       goto end;
-error:
-       g_free(trace);
-       trace = NULL;
-end:
-       return trace;
-}
-
-BT_HIDDEN
-struct lttng_live_trace *lttng_live_session_borrow_or_create_trace_by_id(
-               struct lttng_live_session *session, uint64_t trace_id)
-{
-       struct lttng_live_trace *trace;
-
-       trace = lttng_live_session_borrow_trace_by_id(session, trace_id);
-       if (trace) {
-               goto end;
-       }
-
-       /* The session is the owner of the newly created trace. */
-       trace = lttng_live_create_trace(session, trace_id);
-
-end:
-       return trace;
-}
-
-BT_HIDDEN
-int lttng_live_add_session(struct lttng_live_msg_iter *lttng_live_msg_iter,
-               uint64_t session_id, const char *hostname,
-               const char *session_name)
-{
-       int ret = 0;
-       struct lttng_live_session *session;
-       bt_logging_level log_level = lttng_live_msg_iter->log_level;
-       bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
-
-       BT_COMP_LOGD("Adding live session: "
-               "session-id=%" PRIu64 ", hostname=\"%s\" session-name=\"%s\"",
-               session_id, hostname, session_name);
-
-       session = g_new0(struct lttng_live_session, 1);
-       if (!session) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Failed to allocate live session");
-               goto error;
-       }
-
-       session->log_level = lttng_live_msg_iter->log_level;
-       session->self_comp = lttng_live_msg_iter->self_comp;
-       session->id = session_id;
-       session->traces = g_ptr_array_new_with_free_func(
-               (GDestroyNotify) lttng_live_destroy_trace);
-       BT_ASSERT(session->traces);
-       session->lttng_live_msg_iter = lttng_live_msg_iter;
-       session->new_streams_needed = true;
-       session->hostname = g_string_new(hostname);
-       BT_ASSERT(session->hostname);
-
-       session->session_name = g_string_new(session_name);
-       BT_ASSERT(session->session_name);
-
-       g_ptr_array_add(lttng_live_msg_iter->sessions, session);
-       goto end;
-error:
-       g_free(session);
-       ret = -1;
-end:
-       return ret;
-}
-
-static
-void lttng_live_destroy_session(struct lttng_live_session *session)
-{
-       bt_logging_level log_level;
-       bt_self_component *self_comp;
-
-       if (!session) {
-               goto end;
-       }
-
-       log_level = session->log_level;
-       self_comp = session->self_comp;
-       BT_COMP_LOGD("Destroying live session: "
-               "session-id=%"PRIu64", session-name=\"%s\"",
-               session->id, session->session_name->str);
-       if (session->id != -1ULL) {
-               if (lttng_live_session_detach(session)) {
-                       if (!lttng_live_graph_is_canceled(
-                                       session->lttng_live_msg_iter)) {
-                               /* Old relayd cannot detach sessions. */
-                               BT_COMP_LOGD("Unable to detach lttng live session %" PRIu64,
-                                       session->id);
-                       }
-               }
-               session->id = -1ULL;
-       }
-
-       if (session->traces) {
-               g_ptr_array_free(session->traces, TRUE);
-       }
-
-       if (session->hostname) {
-               g_string_free(session->hostname, TRUE);
-       }
-       if (session->session_name) {
-               g_string_free(session->session_name, TRUE);
-       }
-       g_free(session);
-
-end:
-       return;
-}
-
-static
-void lttng_live_msg_iter_destroy(struct lttng_live_msg_iter *lttng_live_msg_iter)
-{
-       if (!lttng_live_msg_iter) {
-               goto end;
-       }
-
-       if (lttng_live_msg_iter->sessions) {
-               g_ptr_array_free(lttng_live_msg_iter->sessions, TRUE);
-       }
-
-       if (lttng_live_msg_iter->viewer_connection) {
-               live_viewer_connection_destroy(lttng_live_msg_iter->viewer_connection);
-       }
-       BT_ASSERT(lttng_live_msg_iter->lttng_live_comp);
-       BT_ASSERT(lttng_live_msg_iter->lttng_live_comp->has_msg_iter);
-
-       /* All stream iterators must be destroyed at this point. */
-       BT_ASSERT(lttng_live_msg_iter->active_stream_iter == 0);
-       lttng_live_msg_iter->lttng_live_comp->has_msg_iter = false;
-
-       g_free(lttng_live_msg_iter);
-
-end:
-       return;
-}
-
-BT_HIDDEN
-void lttng_live_msg_iter_finalize(bt_self_message_iterator *self_msg_iter)
-{
-       struct lttng_live_msg_iter *lttng_live_msg_iter;
-
-       BT_ASSERT(self_msg_iter);
-
-       lttng_live_msg_iter = bt_self_message_iterator_get_data(self_msg_iter);
-       BT_ASSERT(lttng_live_msg_iter);
-       lttng_live_msg_iter_destroy(lttng_live_msg_iter);
-}
-
-static
-enum lttng_live_iterator_status lttng_live_iterator_next_check_stream_state(
-               struct lttng_live_stream_iterator *lttng_live_stream)
-{
-       bt_logging_level log_level = lttng_live_stream->log_level;
-       bt_self_component *self_comp = lttng_live_stream->self_comp;
-
-       switch (lttng_live_stream->state) {
-       case LTTNG_LIVE_STREAM_QUIESCENT:
-       case LTTNG_LIVE_STREAM_ACTIVE_DATA:
-               break;
-       case LTTNG_LIVE_STREAM_ACTIVE_NO_DATA:
-               /* Invalid state. */
-               BT_COMP_LOGF("Unexpected stream state \"ACTIVE_NO_DATA\"");
-               bt_common_abort();
-       case LTTNG_LIVE_STREAM_QUIESCENT_NO_DATA:
-               /* Invalid state. */
-               BT_COMP_LOGF("Unexpected stream state \"QUIESCENT_NO_DATA\"");
-               bt_common_abort();
-       case LTTNG_LIVE_STREAM_EOF:
-               break;
-       }
-       return LTTNG_LIVE_ITERATOR_STATUS_OK;
-}
-
-/*
- * For active no data stream, fetch next index. As a result of that it can
- * become either:
- * - quiescent: won't have events for a bit,
- * - have data: need to get that data and produce the event,
- * - have no data on this stream at this point: need to retry (AGAIN) or return
- *   EOF.
- */
-static
-enum lttng_live_iterator_status lttng_live_iterator_next_handle_one_no_data_stream(
-               struct lttng_live_msg_iter *lttng_live_msg_iter,
-               struct lttng_live_stream_iterator *lttng_live_stream)
-{
-       bt_logging_level log_level = lttng_live_msg_iter->log_level;
-       bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
-       enum lttng_live_iterator_status ret = LTTNG_LIVE_ITERATOR_STATUS_OK;
-       enum lttng_live_stream_state orig_state = lttng_live_stream->state;
-       struct packet_index index;
-
-       if (lttng_live_stream->trace->metadata_stream_state ==
-                       LTTNG_LIVE_METADATA_STREAM_STATE_NEEDED) {
-               BT_COMP_LOGD("Need to get an update for the metadata stream before proceeding further with this stream: "
-                       "stream-name=\"%s\"", lttng_live_stream->name->str);
-               ret = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE;
-               goto end;
-       }
-
-       if (lttng_live_stream->trace->session->new_streams_needed) {
-               BT_COMP_LOGD("Need to get an update of all streams before proceeding further with this stream: "
-                       "stream-name=\"%s\"", lttng_live_stream->name->str);
-               ret = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE;
-               goto end;
-       }
-
-       if (lttng_live_stream->state != LTTNG_LIVE_STREAM_ACTIVE_NO_DATA &&
-                       lttng_live_stream->state != LTTNG_LIVE_STREAM_QUIESCENT_NO_DATA) {
-               goto end;
-       }
-       ret = lttng_live_get_next_index(lttng_live_msg_iter, lttng_live_stream,
-                       &index);
-       if (ret != LTTNG_LIVE_ITERATOR_STATUS_OK) {
-               goto end;
-       }
-
-       BT_ASSERT_DBG(lttng_live_stream->state != LTTNG_LIVE_STREAM_EOF);
-
-       if (lttng_live_stream->state == LTTNG_LIVE_STREAM_QUIESCENT) {
-               uint64_t last_inact_ts = lttng_live_stream->last_inactivity_ts.value,
-                        curr_inact_ts = lttng_live_stream->current_inactivity_ts;
-
-               if (orig_state == LTTNG_LIVE_STREAM_QUIESCENT_NO_DATA &&
-                               last_inact_ts == curr_inact_ts) {
-                       /*
-                        * Because the stream is in the QUIESCENT_NO_DATA
-                        * state, we can assert that the last_inactivity_ts was
-                        * set and can be safely used in the `if` above.
-                        */
-                       BT_ASSERT(lttng_live_stream->last_inactivity_ts.is_set);
-
-                       ret = LTTNG_LIVE_ITERATOR_STATUS_AGAIN;
-                       LTTNG_LIVE_LOGD_STREAM_ITER(lttng_live_stream);
-               } else {
-                       ret = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE;
-               }
-               goto end;
-       }
-
-       lttng_live_stream->base_offset = index.offset;
-       lttng_live_stream->offset = index.offset;
-       lttng_live_stream->len = index.packet_size / CHAR_BIT;
-
-       BT_COMP_LOGD("Setting live stream reading info: stream-name=\"%s\", "
-                       "viewer-stream-id=%" PRIu64 ", stream-base-offset=%" PRIu64
-                       ", stream-offset=%" PRIu64 ", stream-len=%" PRIu64,
-                       lttng_live_stream->name->str,
-                       lttng_live_stream->viewer_stream_id,
-                       lttng_live_stream->base_offset,
-                       lttng_live_stream->offset, lttng_live_stream->len);
-
-end:
-       if (ret == LTTNG_LIVE_ITERATOR_STATUS_OK) {
-               ret = lttng_live_iterator_next_check_stream_state(lttng_live_stream);
-       }
-       return ret;
-}
-
-/*
- * Creation of the message requires the ctf trace class to be created
- * beforehand, but the live protocol gives us all streams (including metadata)
- * at once. So we split it in three steps: getting streams, getting metadata
- * (which creates the ctf trace class), and then creating the per-stream
- * messages.
- */
-static
-enum lttng_live_iterator_status lttng_live_get_session(
-               struct lttng_live_msg_iter *lttng_live_msg_iter,
-               struct lttng_live_session *session)
-{
-       bt_logging_level log_level = lttng_live_msg_iter->log_level;
-       bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
-       enum lttng_live_iterator_status status;
-       uint64_t trace_idx;
-
-       if (!session->attached) {
-               BT_COMP_LOGD("Attach to session: session-id=%" PRIu64,
-                       session->id);
-               enum lttng_live_viewer_status attach_status =
-                       lttng_live_session_attach(session,
-                               lttng_live_msg_iter->self_msg_iter);
-               if (attach_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-                       if (lttng_live_graph_is_canceled(lttng_live_msg_iter)) {
-                               /*
-                                * Clear any causes appended in
-                                * `lttng_live_attach_session()` as we want to
-                                * return gracefully since the graph was
-                                * cancelled.
-                                */
-                               bt_current_thread_clear_error();
-                               status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN;
-                       } else {
-                               status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-                               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                       "Error attaching to LTTng live session");
-                       }
-                       goto end;
-               }
-       }
-
-       BT_COMP_LOGD("Updating all streams and metadata for session: "
-               "session-id=%" PRIu64 ", session-name=\"%s\"",
-               session->id, session->session_name->str);
-
-       status = lttng_live_session_get_new_streams(session,
-               lttng_live_msg_iter->self_msg_iter);
-       if (status != LTTNG_LIVE_ITERATOR_STATUS_OK &&
-                       status != LTTNG_LIVE_ITERATOR_STATUS_END) {
-               goto end;
-       }
-
-       trace_idx = 0;
-       while (trace_idx < session->traces->len) {
-               struct lttng_live_trace *trace =
-                       g_ptr_array_index(session->traces, trace_idx);
-
-               status = lttng_live_metadata_update(trace);
-               switch (status) {
-               case LTTNG_LIVE_ITERATOR_STATUS_END:
-               case LTTNG_LIVE_ITERATOR_STATUS_OK:
-                       trace_idx++;
-                       break;
-               case LTTNG_LIVE_ITERATOR_STATUS_CONTINUE:
-               case LTTNG_LIVE_ITERATOR_STATUS_AGAIN:
-                       goto end;
-               default:
-                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                               "Error updating trace metadata: "
-                               "stream-iter-status=%s, trace-id=%"PRIu64,
-                               lttng_live_iterator_status_string(status),
-                               trace->id);
-                       goto end;
-               }
-       }
-
-       /*
-        * Now that we have the metadata we can initialize the downstream
-        * iterator.
-        */
-       status = lttng_live_lazy_msg_init(session,
-               lttng_live_msg_iter->self_msg_iter);
-
-end:
-       return status;
-}
-
-static
-void lttng_live_force_new_streams_and_metadata(struct lttng_live_msg_iter *lttng_live_msg_iter)
-{
-       uint64_t session_idx, trace_idx;
-       bt_logging_level log_level = lttng_live_msg_iter->log_level;
-       bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
-
-       for (session_idx = 0; session_idx < lttng_live_msg_iter->sessions->len;
-                       session_idx++) {
-               struct lttng_live_session *session =
-                       g_ptr_array_index(lttng_live_msg_iter->sessions, session_idx);
-               BT_COMP_LOGD("Force marking session as needing new streams: "
-                               "session-id=%" PRIu64, session->id);
-               session->new_streams_needed = true;
-               for (trace_idx = 0; trace_idx < session->traces->len;
-                               trace_idx++) {
-                       struct lttng_live_trace *trace =
-                               g_ptr_array_index(session->traces, trace_idx);
-                       BT_COMP_LOGD("Force marking trace metadata state as needing an update: "
-                                       "session-id=%" PRIu64 ", trace-id=%" PRIu64,
-                                       session->id, trace->id);
-
-                       BT_ASSERT(trace->metadata_stream_state !=
-                               LTTNG_LIVE_METADATA_STREAM_STATE_CLOSED);
-
-                       trace->metadata_stream_state = LTTNG_LIVE_METADATA_STREAM_STATE_NEEDED;
-               }
-       }
-}
-
-static
-enum lttng_live_iterator_status
-lttng_live_iterator_handle_new_streams_and_metadata(
-               struct lttng_live_msg_iter *lttng_live_msg_iter)
-{
-       enum lttng_live_iterator_status status;
-       enum lttng_live_viewer_status viewer_status;
-       bt_logging_level log_level = lttng_live_msg_iter->log_level;
-       bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
-       uint64_t session_idx = 0, nr_sessions_opened = 0;
-       struct lttng_live_session *session;
-       enum session_not_found_action sess_not_found_act =
-               lttng_live_msg_iter->lttng_live_comp->params.sess_not_found_act;
-
-       BT_COMP_LOGD("Update data and metadata of all sessions: "
-               "live-msg-iter-addr=%p", lttng_live_msg_iter);
-       /*
-        * In a remotely distant future, we could add a "new
-        * session" flag to the protocol, which would tell us that we
-        * need to query for new sessions even though we have sessions
-        * currently ongoing.
-        */
-       if (lttng_live_msg_iter->sessions->len == 0) {
-               if (sess_not_found_act != SESSION_NOT_FOUND_ACTION_CONTINUE) {
-                       BT_COMP_LOGD("No session found. Exiting in accordance with the `session-not-found-action` parameter");
-                       status = LTTNG_LIVE_ITERATOR_STATUS_END;
-                       goto end;
-               } else {
-                       BT_COMP_LOGD("No session found. Try creating a new one in accordance with the `session-not-found-action` parameter");
-                       /*
-                        * Retry to create a viewer session for the requested
-                        * session name.
-                        */
-                       viewer_status = lttng_live_create_viewer_session(lttng_live_msg_iter);
-                       if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-                               if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
-                                       status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-                                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                               "Error creating LTTng live viewer session");
-                               } else if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
-                                       status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN;
-                               } else {
-                                       bt_common_abort();
-                               }
-                               goto end;
-                       }
-               }
-       }
-
-       for (session_idx = 0; session_idx < lttng_live_msg_iter->sessions->len;
-                       session_idx++) {
-               session = g_ptr_array_index(lttng_live_msg_iter->sessions,
-                               session_idx);
-               status = lttng_live_get_session(lttng_live_msg_iter, session);
-               switch (status) {
-               case LTTNG_LIVE_ITERATOR_STATUS_OK:
-                       break;
-               case LTTNG_LIVE_ITERATOR_STATUS_END:
-                       status = LTTNG_LIVE_ITERATOR_STATUS_OK;
-                       break;
-               default:
-                       goto end;
-               }
-               if (!session->closed) {
-                       nr_sessions_opened++;
-               }
-       }
-
-       if (sess_not_found_act != SESSION_NOT_FOUND_ACTION_CONTINUE &&
-                       nr_sessions_opened == 0) {
-               status = LTTNG_LIVE_ITERATOR_STATUS_END;
-       } else {
-               status = LTTNG_LIVE_ITERATOR_STATUS_OK;
-       }
-
-end:
-       return status;
-}
-
-static
-enum lttng_live_iterator_status emit_inactivity_message(
-               struct lttng_live_msg_iter *lttng_live_msg_iter,
-               struct lttng_live_stream_iterator *stream_iter,
-               const bt_message **message, uint64_t timestamp)
-{
-       enum lttng_live_iterator_status ret = LTTNG_LIVE_ITERATOR_STATUS_OK;
-       bt_logging_level log_level = lttng_live_msg_iter->log_level;
-       bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
-       bt_message *msg = NULL;
-
-       BT_ASSERT(stream_iter->trace->clock_class);
-
-       BT_COMP_LOGD("Emitting inactivity message for stream: ctf-stream-id=%" PRIu64
-               ", viewer-stream-id=%" PRIu64 ", timestamp=%" PRIu64,
-               stream_iter->ctf_stream_class_id.value,
-               stream_iter->viewer_stream_id, timestamp);
-
-       msg = bt_message_message_iterator_inactivity_create(
-               lttng_live_msg_iter->self_msg_iter,
-               stream_iter->trace->clock_class, timestamp);
-       if (!msg) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Error emitting message iterator inactivity message");
-               goto error;
-       }
-
-       *message = msg;
-end:
-       return ret;
-
-error:
-       ret = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-       bt_message_put_ref(msg);
-       goto end;
-}
-
-static
-enum lttng_live_iterator_status lttng_live_iterator_next_handle_one_quiescent_stream(
-               struct lttng_live_msg_iter *lttng_live_msg_iter,
-               struct lttng_live_stream_iterator *lttng_live_stream,
-               const bt_message **message)
-{
-       enum lttng_live_iterator_status ret = LTTNG_LIVE_ITERATOR_STATUS_OK;
-
-       if (lttng_live_stream->state != LTTNG_LIVE_STREAM_QUIESCENT) {
-               return LTTNG_LIVE_ITERATOR_STATUS_OK;
-       }
-
-       /*
-        * Check if we already sent an inactivty message downstream for this
-        * `current_inactivity_ts` value.
-        */
-       if (lttng_live_stream->last_inactivity_ts.is_set &&
-                       lttng_live_stream->current_inactivity_ts ==
-                               lttng_live_stream->last_inactivity_ts.value) {
-               lttng_live_stream_iterator_set_state(lttng_live_stream,
-                       LTTNG_LIVE_STREAM_QUIESCENT_NO_DATA);
-
-               ret = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE;
-               goto end;
-       }
-
-       ret = emit_inactivity_message(lttng_live_msg_iter, lttng_live_stream,
-               message, lttng_live_stream->current_inactivity_ts);
-
-       lttng_live_stream->last_inactivity_ts.value =
-               lttng_live_stream->current_inactivity_ts;
-       lttng_live_stream->last_inactivity_ts.is_set = true;
-end:
-       return ret;
-}
-
-static
-int live_get_msg_ts_ns(struct lttng_live_stream_iterator *stream_iter,
-               struct lttng_live_msg_iter *lttng_live_msg_iter,
-               const bt_message *msg, int64_t last_msg_ts_ns,
-               int64_t *ts_ns)
-{
-       const bt_clock_class *clock_class = NULL;
-       const bt_clock_snapshot *clock_snapshot = NULL;
-       int ret = 0;
-       bt_logging_level log_level = lttng_live_msg_iter->log_level;
-       bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
-
-       BT_ASSERT_DBG(msg);
-       BT_ASSERT_DBG(ts_ns);
-
-       BT_COMP_LOGD("Getting message's timestamp: iter-data-addr=%p, msg-addr=%p, "
-               "last-msg-ts=%" PRId64, lttng_live_msg_iter, msg,
-               last_msg_ts_ns);
-
-       switch (bt_message_get_type(msg)) {
-       case BT_MESSAGE_TYPE_EVENT:
-               clock_class = bt_message_event_borrow_stream_class_default_clock_class_const(
-                               msg);
-               BT_ASSERT_DBG(clock_class);
-
-               clock_snapshot = bt_message_event_borrow_default_clock_snapshot_const(
-                       msg);
-               break;
-       case BT_MESSAGE_TYPE_PACKET_BEGINNING:
-               clock_class = bt_message_packet_beginning_borrow_stream_class_default_clock_class_const(
-                       msg);
-               BT_ASSERT(clock_class);
-
-               clock_snapshot = bt_message_packet_beginning_borrow_default_clock_snapshot_const(
-                       msg);
-               break;
-       case BT_MESSAGE_TYPE_PACKET_END:
-               clock_class = bt_message_packet_end_borrow_stream_class_default_clock_class_const(
-                       msg);
-               BT_ASSERT(clock_class);
-
-               clock_snapshot = bt_message_packet_end_borrow_default_clock_snapshot_const(
-                       msg);
-               break;
-       case BT_MESSAGE_TYPE_DISCARDED_EVENTS:
-               clock_class = bt_message_discarded_events_borrow_stream_class_default_clock_class_const(
-                       msg);
-               BT_ASSERT(clock_class);
-
-               clock_snapshot = bt_message_discarded_events_borrow_beginning_default_clock_snapshot_const(
-                       msg);
-               break;
-       case BT_MESSAGE_TYPE_DISCARDED_PACKETS:
-               clock_class = bt_message_discarded_packets_borrow_stream_class_default_clock_class_const(
-                       msg);
-               BT_ASSERT(clock_class);
-
-               clock_snapshot = bt_message_discarded_packets_borrow_beginning_default_clock_snapshot_const(
-                       msg);
-               break;
-       case BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY:
-               clock_snapshot = bt_message_message_iterator_inactivity_borrow_clock_snapshot_const(
-                       msg);
-               break;
-       default:
-               /* All the other messages have a higher priority */
-               BT_COMP_LOGD_STR("Message has no timestamp: using the last message timestamp.");
-               *ts_ns = last_msg_ts_ns;
-               goto end;
-       }
-
-       clock_class = bt_clock_snapshot_borrow_clock_class_const(clock_snapshot);
-       BT_ASSERT_DBG(clock_class);
-
-       ret = bt_clock_snapshot_get_ns_from_origin(clock_snapshot, ts_ns);
-       if (ret) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Cannot get nanoseconds from Epoch of clock snapshot: "
-                       "clock-snapshot-addr=%p", clock_snapshot);
-               goto error;
-       }
-
-       goto end;
-
-error:
-       ret = -1;
-
-end:
-       if (ret == 0) {
-               BT_COMP_LOGD("Found message's timestamp: "
-                       "iter-data-addr=%p, msg-addr=%p, "
-                       "last-msg-ts=%" PRId64 ", ts=%" PRId64,
-                       lttng_live_msg_iter, msg, last_msg_ts_ns, *ts_ns);
-       }
-
-       return ret;
-}
-
-static
-enum lttng_live_iterator_status lttng_live_iterator_next_handle_one_active_data_stream(
-               struct lttng_live_msg_iter *lttng_live_msg_iter,
-               struct lttng_live_stream_iterator *lttng_live_stream,
-               const bt_message **message)
-{
-       enum lttng_live_iterator_status ret = LTTNG_LIVE_ITERATOR_STATUS_OK;
-       bt_logging_level log_level = lttng_live_msg_iter->log_level;
-       bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
-       enum ctf_msg_iter_status status;
-       uint64_t session_idx, trace_idx;
-
-       for (session_idx = 0; session_idx < lttng_live_msg_iter->sessions->len;
-                       session_idx++) {
-               struct lttng_live_session *session =
-                       g_ptr_array_index(lttng_live_msg_iter->sessions, session_idx);
-
-               if (session->new_streams_needed) {
-                       BT_COMP_LOGD("Need an update for streams: "
-                                       "session-id=%" PRIu64, session->id);
-                       ret = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE;
-                       goto end;
-               }
-               for (trace_idx = 0; trace_idx < session->traces->len;
-                               trace_idx++) {
-                       struct lttng_live_trace *trace =
-                               g_ptr_array_index(session->traces, trace_idx);
-                       if (trace->metadata_stream_state == LTTNG_LIVE_METADATA_STREAM_STATE_NEEDED) {
-                               BT_COMP_LOGD("Need an update for metadata stream: "
-                                               "session-id=%" PRIu64 ", trace-id=%" PRIu64,
-                                               session->id, trace->id);
-                               ret = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE;
-                               goto end;
-                       }
-               }
-       }
-
-       if (lttng_live_stream->state != LTTNG_LIVE_STREAM_ACTIVE_DATA) {
-               ret = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Invalid state of live stream iterator"
-                       "stream-iter-status=%s",
-                       lttng_live_stream_state_string(lttng_live_stream->state));
-               goto end;
-       }
-
-       status = ctf_msg_iter_get_next_message(
-               lttng_live_stream->msg_iter, message);
-       switch (status) {
-       case CTF_MSG_ITER_STATUS_EOF:
-               ret = LTTNG_LIVE_ITERATOR_STATUS_END;
-               break;
-       case CTF_MSG_ITER_STATUS_OK:
-               ret = LTTNG_LIVE_ITERATOR_STATUS_OK;
-               break;
-       case CTF_MSG_ITER_STATUS_AGAIN:
-               /*
-                * Continue immediately (end of packet). The next
-                * get_index may return AGAIN to delay the following
-                * attempt.
-                */
-               ret = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE;
-               break;
-       case CTF_MSG_ITER_STATUS_ERROR:
-       default:
-               ret = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "CTF message iterator failed to get next message: "
-                       "msg-iter=%p, msg-iter-status=%s",
-                       lttng_live_stream->msg_iter,
-                       ctf_msg_iter_status_string(status));
-               break;
-       }
-
-end:
-       return ret;
-}
-
-static
-enum lttng_live_iterator_status lttng_live_iterator_close_stream(
-               struct lttng_live_msg_iter *lttng_live_msg_iter,
-               struct lttng_live_stream_iterator *stream_iter,
-               const bt_message **curr_msg)
-{
-       enum lttng_live_iterator_status live_status =
-               LTTNG_LIVE_ITERATOR_STATUS_OK;
-       bt_logging_level log_level = lttng_live_msg_iter->log_level;
-       bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
-
-       BT_COMP_LOGD("Closing live stream iterator: stream-name=\"%s\", "
-                       "viewer-stream-id=%" PRIu64, stream_iter->name->str,
-                       stream_iter->viewer_stream_id);
-
-       /*
-        * The viewer has hung up on us so we are closing the stream. The
-        * `ctf_msg_iter` should simply realize that it needs to close the
-        * stream properly by emitting the necessary stream end message.
-        */
-       enum ctf_msg_iter_status status = ctf_msg_iter_get_next_message(
-               stream_iter->msg_iter, curr_msg);
-
-       if (status == CTF_MSG_ITER_STATUS_ERROR) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Error getting the next message from CTF message iterator");
-               live_status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-               goto end;
-       } else if (status == CTF_MSG_ITER_STATUS_EOF) {
-               BT_COMP_LOGI("Reached the end of the live stream iterator.");
-               live_status = LTTNG_LIVE_ITERATOR_STATUS_END;
-               goto end;
-       }
-
-       BT_ASSERT(status == CTF_MSG_ITER_STATUS_OK);
-
-end:
-       return live_status;
-}
-
-/*
- * helper function:
- *            handle_no_data_streams()
- *              retry:
- *                - for each ACTIVE_NO_DATA stream:
- *                  - query relayd for stream data, or quiescence info.
- *                    - if need metadata, get metadata, goto retry.
- *                    - if new stream, get new stream as ACTIVE_NO_DATA, goto retry
- *                  - if quiescent, move to QUIESCENT streams
- *                  - if fetched data, move to ACTIVE_DATA streams
- *                (at this point each stream either has data, or is quiescent)
- *
- *
- * iterator_next:
- *            handle_new_streams_and_metadata()
- *                  - query relayd for known streams, add them as ACTIVE_NO_DATA
- *                  - query relayd for metadata
- *
- *            call handle_active_no_data_streams()
- *
- *            handle_quiescent_streams()
- *                - if at least one stream is ACTIVE_DATA:
- *                  - peek stream event with lowest timestamp -> next_ts
- *                  - for each quiescent stream
- *                    - if next_ts >= quiescent end
- *                      - set state to ACTIVE_NO_DATA
- *                - else
- *                  - for each quiescent stream
- *                      - set state to ACTIVE_NO_DATA
- *
- *            call handle_active_no_data_streams()
- *
- *            handle_active_data_streams()
- *                - if at least one stream is ACTIVE_DATA:
- *                    - get stream event with lowest timestamp from heap
- *                    - make that stream event the current message.
- *                    - move this stream heap position to its next event
- *                      - if we need to fetch data from relayd, move
- *                        stream to ACTIVE_NO_DATA.
- *                    - return OK
- *                - return AGAIN
- *
- * end criterion: ctrl-c on client. If relayd exits or the session
- * closes on the relay daemon side, we keep on waiting for streams.
- * Eventually handle --end timestamp (also an end criterion).
- *
- * When disconnected from relayd: try to re-connect endlessly.
- */
-static
-enum lttng_live_iterator_status lttng_live_iterator_next_msg_on_stream(
-               struct lttng_live_msg_iter *lttng_live_msg_iter,
-               struct lttng_live_stream_iterator *stream_iter,
-               const bt_message **curr_msg)
-{
-       bt_logging_level log_level = lttng_live_msg_iter->log_level;
-       bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
-       enum lttng_live_iterator_status live_status;
-
-       BT_COMP_LOGD("Advancing live stream iterator until next message if possible: "
-               "stream-name=\"%s\", viewer-stream-id=%" PRIu64,
-               stream_iter->name->str, stream_iter->viewer_stream_id);
-
-       if (stream_iter->has_stream_hung_up) {
-               /*
-                * The stream has hung up and the stream was properly closed
-                * during the last call to the current function. Return _END
-                * status now so that this stream iterator is removed for the
-                * stream iterator list.
-                */
-               live_status = LTTNG_LIVE_ITERATOR_STATUS_END;
-               goto end;
-       }
-
-retry:
-       LTTNG_LIVE_LOGD_STREAM_ITER(stream_iter);
-
-       /*
-        * Make sure we have the most recent metadata and possibly some new
-        * streams.
-        */
-       live_status = lttng_live_iterator_handle_new_streams_and_metadata(
-               lttng_live_msg_iter);
-       if (live_status != LTTNG_LIVE_ITERATOR_STATUS_OK) {
-               goto end;
-       }
-
-       live_status = lttng_live_iterator_next_handle_one_no_data_stream(
-               lttng_live_msg_iter, stream_iter);
-       if (live_status != LTTNG_LIVE_ITERATOR_STATUS_OK) {
-               if (live_status == LTTNG_LIVE_ITERATOR_STATUS_END) {
-                       /*
-                        * We overwrite `live_status` since `curr_msg` is
-                        * likely set to a valid message in this function.
-                        */
-                       live_status = lttng_live_iterator_close_stream(
-                               lttng_live_msg_iter, stream_iter, curr_msg);
-               }
-               goto end;
-       }
-
-       live_status = lttng_live_iterator_next_handle_one_quiescent_stream(
-               lttng_live_msg_iter, stream_iter, curr_msg);
-       if (live_status != LTTNG_LIVE_ITERATOR_STATUS_OK) {
-               BT_ASSERT(!*curr_msg);
-               goto end;
-       }
-       if (*curr_msg) {
-               goto end;
-       }
-       live_status = lttng_live_iterator_next_handle_one_active_data_stream(
-               lttng_live_msg_iter, stream_iter, curr_msg);
-       if (live_status != LTTNG_LIVE_ITERATOR_STATUS_OK) {
-               BT_ASSERT(!*curr_msg);
-       }
-
-end:
-       if (live_status == LTTNG_LIVE_ITERATOR_STATUS_CONTINUE) {
-               BT_COMP_LOGD("Ask the relay daemon for an updated view of the data and metadata streams");
-               goto retry;
-       }
-
-       BT_COMP_LOGD("Returning from advancing live stream iterator: status=%s, "
-                       "stream-name=\"%s\", viewer-stream-id=%" PRIu64,
-                       lttng_live_iterator_status_string(live_status),
-                       stream_iter->name->str, stream_iter->viewer_stream_id);
-
-       return live_status;
-}
-
-static
-bool is_discarded_packet_or_event_message(const bt_message *msg)
-{
-       const enum bt_message_type msg_type = bt_message_get_type(msg);
-
-       return msg_type == BT_MESSAGE_TYPE_DISCARDED_EVENTS ||
-                       msg_type == BT_MESSAGE_TYPE_DISCARDED_PACKETS;
-}
-
-static
-enum lttng_live_iterator_status adjust_discarded_packets_message(
-               bt_self_message_iterator *iter,
-               const bt_stream *stream,
-               const bt_message *msg_in, bt_message **msg_out,
-               uint64_t new_begin_ts)
-{
-       enum lttng_live_iterator_status status = LTTNG_LIVE_ITERATOR_STATUS_OK;
-       enum bt_property_availability availability;
-       const bt_clock_snapshot *clock_snapshot;
-       uint64_t end_ts;
-       uint64_t count;
-
-       clock_snapshot = bt_message_discarded_packets_borrow_end_default_clock_snapshot_const(msg_in);
-       end_ts = bt_clock_snapshot_get_value(clock_snapshot);
-
-       availability = bt_message_discarded_packets_get_count(msg_in, &count);
-       BT_ASSERT_DBG(availability == BT_PROPERTY_AVAILABILITY_AVAILABLE);
-
-       *msg_out = bt_message_discarded_packets_create_with_default_clock_snapshots(
-               iter, stream, new_begin_ts, end_ts);
-       if (!*msg_out) {
-               status = LTTNG_LIVE_ITERATOR_STATUS_NOMEM;
-               goto end;
-       }
-
-       bt_message_discarded_packets_set_count(*msg_out, count);
-end:
-       return status;
-}
-
-static
-enum lttng_live_iterator_status adjust_discarded_events_message(
-               bt_self_message_iterator *iter,
-               const bt_stream *stream,
-               const bt_message *msg_in, bt_message **msg_out,
-               uint64_t new_begin_ts)
-{
-       enum lttng_live_iterator_status status = LTTNG_LIVE_ITERATOR_STATUS_OK;
-       enum bt_property_availability availability;
-       const bt_clock_snapshot *clock_snapshot;
-       uint64_t end_ts;
-       uint64_t count;
-
-       clock_snapshot = bt_message_discarded_events_borrow_end_default_clock_snapshot_const(msg_in);
-       end_ts = bt_clock_snapshot_get_value(clock_snapshot);
-
-       availability = bt_message_discarded_events_get_count(msg_in, &count);
-       BT_ASSERT_DBG(availability == BT_PROPERTY_AVAILABILITY_AVAILABLE);
-
-       *msg_out = bt_message_discarded_events_create_with_default_clock_snapshots(
-               iter, stream, new_begin_ts, end_ts);
-       if (!*msg_out) {
-               status = LTTNG_LIVE_ITERATOR_STATUS_NOMEM;
-               goto end;
-       }
-
-       bt_message_discarded_events_set_count(*msg_out, count);
-end:
-       return status;
-}
-
-static
-enum lttng_live_iterator_status handle_late_message(
-               struct lttng_live_msg_iter *lttng_live_msg_iter,
-               struct lttng_live_stream_iterator *stream_iter,
-               int64_t late_msg_ts_ns, const bt_message *late_msg)
-{
-       bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
-       bt_logging_level log_level = lttng_live_msg_iter->log_level;
-       const bt_clock_class *clock_class;
-       const bt_stream_class *stream_class;
-       enum bt_clock_class_cycles_to_ns_from_origin_status ts_ns_status;
-       int64_t last_inactivity_ts_ns;
-       enum lttng_live_iterator_status stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_OK;
-       enum lttng_live_iterator_status adjust_status;
-       bt_message *adjusted_message;
-
-       /*
-        * The timestamp of the current message is before the last message sent
-        * by this component. We CANNOT send it as is.
-        *
-        * The only expected scenario in which that could happen is the
-        * following, everything else is a bug in this component, relay deamon,
-        * or CTF parser.
-        *
-        * Expected scenario: The CTF message iterator emitted discarded
-        * packets and discarded events with synthesized beginning and end
-        * timestamps from the bounds of the last known packet and the newly
-        * decoded packet header. The CTF message iterator is not aware of
-        * stream inactivity beacons. Hence, we have to adjust the beginning
-        * timestamp of those types of messages if a stream signalled its
-        * inactivity up until _after_ the last known packet's beginning
-        * timestamp.
-        *
-        * Otherwise, the monotonicity guarantee of message timestamps would
-        * not be preserved.
-        *
-        * In short, the only scenario in which it's okay and fixable to
-        * received a late message is when:
-        *  1. the late message is a discarded packets or discarded events
-        *      message,
-        *  2. this stream produced an inactivity message downstream, and
-        *  3. the timestamp of the late message is within the inactivity
-        *      timespan we sent downstream through the inactivity message.
-        */
-
-       BT_COMP_LOGD("Handling late message on live stream iterator: "
-               "stream-name=\"%s\", viewer-stream-id=%" PRIu64,
-               stream_iter->name->str, stream_iter->viewer_stream_id);
-
-       if (!stream_iter->last_inactivity_ts.is_set) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Invalid live stream state: "
-                       "have a late message when no inactivity message "
-                       "was ever sent for that stream.");
-               stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-               goto end;
-       }
-
-       if (!is_discarded_packet_or_event_message(late_msg)) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Invalid live stream state: "
-                       "have a late message that is not a packet discarded or "
-                       "event discarded message: late-msg-type=%s",
-                       bt_common_message_type_string(bt_message_get_type(late_msg)));
-               stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-               goto end;
-       }
-
-       stream_class = bt_stream_borrow_class_const(stream_iter->stream);
-       clock_class = bt_stream_class_borrow_default_clock_class_const(stream_class);
-
-       ts_ns_status = bt_clock_class_cycles_to_ns_from_origin(clock_class,
-                       stream_iter->last_inactivity_ts.value, &last_inactivity_ts_ns);
-       if (ts_ns_status != BT_CLOCK_CLASS_CYCLES_TO_NS_FROM_ORIGIN_STATUS_OK) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,"Error converting last "
-                       "inactivity message timestamp to nanoseconds");
-               stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-               goto end;
-       }
-
-       if (last_inactivity_ts_ns <= late_msg_ts_ns) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Invalid live stream state: "
-                       "have a late message that is none included in a stream "
-                       "inactivity timespan: last-inactivity-ts-ns=%" PRIu64
-                       "late-msg-ts-ns=%" PRIu64, last_inactivity_ts_ns, late_msg_ts_ns);
-               stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-               goto end;
-       }
-
-       /*
-        * We now know that it's okay for this message to be late, we can now
-        * adjust its timestamp to ensure monotonicity.
-        */
-       BT_COMP_LOGD("Adjusting the timestamp of late message: late-msg-type=%s, "
-               "msg-new-ts-ns=%" PRIu64,
-               bt_common_message_type_string(bt_message_get_type(late_msg)),
-               stream_iter->last_inactivity_ts.value);
-       switch (bt_message_get_type(late_msg)) {
-       case BT_MESSAGE_TYPE_DISCARDED_EVENTS:
-               adjust_status = adjust_discarded_events_message(
-                               lttng_live_msg_iter->self_msg_iter,
-                               stream_iter->stream,
-                               late_msg, &adjusted_message,
-                               stream_iter->last_inactivity_ts.value);
-               break;
-       case BT_MESSAGE_TYPE_DISCARDED_PACKETS:
-               adjust_status = adjust_discarded_packets_message(
-                               lttng_live_msg_iter->self_msg_iter,
-                               stream_iter->stream,
-                               late_msg, &adjusted_message,
-                               stream_iter->last_inactivity_ts.value);
-               break;
-       default:
-               bt_common_abort();
-       }
-
-       if (adjust_status != LTTNG_LIVE_ITERATOR_STATUS_OK) {
-               stream_iter_status = adjust_status;
-               goto end;
-       }
-
-       BT_ASSERT_DBG(adjusted_message);
-       stream_iter->current_msg = adjusted_message;
-       stream_iter->current_msg_ts_ns = last_inactivity_ts_ns;
-       bt_message_put_ref(late_msg);
-
-end:
-       return stream_iter_status;
-}
-
-static
-enum lttng_live_iterator_status next_stream_iterator_for_trace(
-               struct lttng_live_msg_iter *lttng_live_msg_iter,
-               struct lttng_live_trace *live_trace,
-               struct lttng_live_stream_iterator **youngest_trace_stream_iter)
-{
-       struct lttng_live_stream_iterator *youngest_candidate_stream_iter = NULL;
-       bt_logging_level log_level = lttng_live_msg_iter->log_level;
-       bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
-       enum lttng_live_iterator_status stream_iter_status;;
-       int64_t youngest_candidate_msg_ts = INT64_MAX;
-       uint64_t stream_iter_idx;
-
-       BT_ASSERT_DBG(live_trace);
-       BT_ASSERT_DBG(live_trace->stream_iterators);
-
-       BT_COMP_LOGD("Finding the next stream iterator for trace: "
-               "trace-id=%"PRIu64, live_trace->id);
-       /*
-        * Update the current message of every stream iterators of this trace.
-        * The current msg of every stream must have a timestamp equal or
-        * larger than the last message returned by this iterator. We must
-        * ensure monotonicity.
-        */
-       stream_iter_idx = 0;
-       while (stream_iter_idx < live_trace->stream_iterators->len) {
-               bool stream_iter_is_ended = false;
-               struct lttng_live_stream_iterator *stream_iter =
-                       g_ptr_array_index(live_trace->stream_iterators,
-                               stream_iter_idx);
-
-               /*
-                * If there is no current message for this stream, go fetch
-                * one.
-                */
-               while (!stream_iter->current_msg) {
-                       const bt_message *msg = NULL;
-                       int64_t curr_msg_ts_ns = INT64_MAX;
-
-                       stream_iter_status = lttng_live_iterator_next_msg_on_stream(
-                               lttng_live_msg_iter, stream_iter, &msg);
-
-                       if (stream_iter_status == LTTNG_LIVE_ITERATOR_STATUS_END) {
-                               stream_iter_is_ended = true;
-                               break;
-                       }
-
-                       if (stream_iter_status != LTTNG_LIVE_ITERATOR_STATUS_OK) {
-                               goto end;
-                       }
-
-                       BT_ASSERT_DBG(msg);
-
-                       BT_COMP_LOGD("Live stream iterator returned message: msg-type=%s, "
-                                       "stream-name=\"%s\", viewer-stream-id=%" PRIu64,
-                                       bt_common_message_type_string(bt_message_get_type(msg)),
-                                       stream_iter->name->str, stream_iter->viewer_stream_id);
-
-                       /*
-                        * Get the timestamp in nanoseconds from origin of this
-                        * messsage.
-                        */
-                       live_get_msg_ts_ns(stream_iter, lttng_live_msg_iter,
-                               msg, lttng_live_msg_iter->last_msg_ts_ns,
-                               &curr_msg_ts_ns);
-
-                       /*
-                        * Check if the message of the current live stream
-                        * iterator occurred at the exact same time or after the
-                        * last message returned by this component's message
-                        * iterator. If not, we need to handle it with care.
-                        */
-                       if (curr_msg_ts_ns >= lttng_live_msg_iter->last_msg_ts_ns) {
-                               stream_iter->current_msg = msg;
-                               stream_iter->current_msg_ts_ns = curr_msg_ts_ns;
-                       } else {
-                               /*
-                                * We received a message from the past. This
-                                * may be fixable but it can also be an error.
-                                */
-                               stream_iter_status = handle_late_message(
-                                               lttng_live_msg_iter, stream_iter,
-                                               curr_msg_ts_ns, msg);
-                               if (stream_iter_status != LTTNG_LIVE_ITERATOR_STATUS_OK) {
-                                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                               "Late message could not be handled correctly: "
-                                               "lttng-live-msg-iter-addr=%p, "
-                                               "stream-name=\"%s\", "
-                                               "curr-msg-ts=%" PRId64
-                                               ", last-msg-ts=%" PRId64,
-                                               lttng_live_msg_iter,
-                                               stream_iter->name->str,
-                                               curr_msg_ts_ns,
-                                               lttng_live_msg_iter->last_msg_ts_ns);
-                                       stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-                                       goto end;
-                               }
-                       }
-               }
-
-               BT_ASSERT_DBG(stream_iter != youngest_candidate_stream_iter);
-
-               if (!stream_iter_is_ended) {
-                       if (G_UNLIKELY(youngest_candidate_stream_iter == NULL) ||
-                                       stream_iter->current_msg_ts_ns < youngest_candidate_msg_ts) {
-                               /*
-                                * Update the current best candidate message
-                                * for the stream iterator of this live trace
-                                * to be forwarded downstream.
-                                */
-                               youngest_candidate_msg_ts = stream_iter->current_msg_ts_ns;
-                               youngest_candidate_stream_iter = stream_iter;
-                       } else if (stream_iter->current_msg_ts_ns == youngest_candidate_msg_ts) {
-                               /*
-                                * Order the messages in an arbitrary but
-                                * deterministic way.
-                                */
-                               BT_ASSERT_DBG(stream_iter != youngest_candidate_stream_iter);
-                               int ret = common_muxing_compare_messages(
-                                       stream_iter->current_msg,
-                                       youngest_candidate_stream_iter->current_msg);
-                               if (ret < 0) {
-                                       /*
-                                        * The `youngest_candidate_stream_iter->current_msg`
-                                        * should go first. Update the next
-                                        * iterator and the current timestamp.
-                                        */
-                                       youngest_candidate_msg_ts = stream_iter->current_msg_ts_ns;
-                                       youngest_candidate_stream_iter = stream_iter;
-                               } else if (ret == 0) {
-                                       /*
-                                        * Unable to pick which one should go
-                                        * first.
-                                        */
-                                       BT_COMP_LOGW("Cannot deterministically pick next live stream message iterator because they have identical next messages: "
-                                               "stream-iter-addr=%p"
-                                               "stream-iter-addr=%p",
-                                               stream_iter,
-                                               youngest_candidate_stream_iter);
-                               }
-                       }
-
-                       stream_iter_idx++;
-               } else {
-                       /*
-                        * The live stream iterator has ended. That
-                        * iterator is removed from the array, but
-                        * there is no need to increment
-                        * stream_iter_idx as
-                        * g_ptr_array_remove_index_fast replaces the
-                        * removed element with the array's last
-                        * element.
-                        */
-                       g_ptr_array_remove_index_fast(
-                               live_trace->stream_iterators,
-                               stream_iter_idx);
-               }
-       }
-
-       if (youngest_candidate_stream_iter) {
-               *youngest_trace_stream_iter = youngest_candidate_stream_iter;
-               stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_OK;
-       } else {
-               /*
-                * The only case where we don't have a candidate for this trace
-                * is if we reached the end of all the iterators.
-                */
-               BT_ASSERT(live_trace->stream_iterators->len == 0);
-               stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_END;
-       }
-
-end:
-       return stream_iter_status;
-}
-
-static
-enum lttng_live_iterator_status next_stream_iterator_for_session(
-               struct lttng_live_msg_iter *lttng_live_msg_iter,
-               struct lttng_live_session *session,
-               struct lttng_live_stream_iterator **youngest_session_stream_iter)
-{
-       bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
-       bt_logging_level log_level = lttng_live_msg_iter->log_level;
-       enum lttng_live_iterator_status stream_iter_status;
-       uint64_t trace_idx = 0;
-       int64_t youngest_candidate_msg_ts = INT64_MAX;
-       struct lttng_live_stream_iterator *youngest_candidate_stream_iter = NULL;
-
-       BT_COMP_LOGD("Finding the next stream iterator for session: "
-               "session-id=%"PRIu64, session->id);
-       /*
-        * Make sure we are attached to the session and look for new streams
-        * and metadata.
-        */
-       stream_iter_status = lttng_live_get_session(lttng_live_msg_iter, session);
-       if (stream_iter_status != LTTNG_LIVE_ITERATOR_STATUS_OK &&
-                       stream_iter_status != LTTNG_LIVE_ITERATOR_STATUS_CONTINUE &&
-                       stream_iter_status != LTTNG_LIVE_ITERATOR_STATUS_END) {
-               goto end;
-       }
-
-       BT_ASSERT_DBG(session->traces);
-
-       while (trace_idx < session->traces->len) {
-               bool trace_is_ended = false;
-               struct lttng_live_stream_iterator *stream_iter;
-               struct lttng_live_trace *trace =
-                       g_ptr_array_index(session->traces, trace_idx);
-
-               stream_iter_status = next_stream_iterator_for_trace(
-                       lttng_live_msg_iter, trace, &stream_iter);
-               if (stream_iter_status == LTTNG_LIVE_ITERATOR_STATUS_END) {
-                       /*
-                        * All the live stream iterators for this trace are
-                        * ENDed. Remove the trace from this session.
-                        */
-                       trace_is_ended = true;
-               } else if (stream_iter_status != LTTNG_LIVE_ITERATOR_STATUS_OK) {
-                       goto end;
-               }
-
-               if (!trace_is_ended) {
-                       BT_ASSERT_DBG(stream_iter);
-
-                       if (G_UNLIKELY(youngest_candidate_stream_iter == NULL) ||
-                                       stream_iter->current_msg_ts_ns < youngest_candidate_msg_ts) {
-                               youngest_candidate_msg_ts = stream_iter->current_msg_ts_ns;
-                               youngest_candidate_stream_iter = stream_iter;
-                       } else if (stream_iter->current_msg_ts_ns == youngest_candidate_msg_ts) {
-                               /*
-                                * Order the messages in an arbitrary but
-                                * deterministic way.
-                                */
-                               int ret = common_muxing_compare_messages(
-                                       stream_iter->current_msg,
-                                       youngest_candidate_stream_iter->current_msg);
-                               if (ret < 0) {
-                                       /*
-                                        * The `youngest_candidate_stream_iter->current_msg`
-                                        * should go first. Update the next iterator
-                                        * and the current timestamp.
-                                        */
-                                       youngest_candidate_msg_ts = stream_iter->current_msg_ts_ns;
-                                       youngest_candidate_stream_iter = stream_iter;
-                               } else if (ret == 0) {
-                                       /* Unable to pick which one should go first. */
-                                       BT_COMP_LOGW("Cannot deterministically pick next live stream message iterator because they have identical next messages: "
-                                               "stream-iter-addr=%p" "stream-iter-addr=%p",
-                                               stream_iter, youngest_candidate_stream_iter);
-                               }
-                       }
-                       trace_idx++;
-               } else {
-                       /*
-                        * trace_idx is not incremented since
-                        * g_ptr_array_remove_index_fast replaces the
-                        * element at trace_idx with the array's last element.
-                        */
-                       g_ptr_array_remove_index_fast(session->traces,
-                               trace_idx);
-               }
-       }
-       if (youngest_candidate_stream_iter) {
-               *youngest_session_stream_iter = youngest_candidate_stream_iter;
-               stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_OK;
-       } else {
-               /*
-                * The only cases where we don't have a candidate for this
-                * trace is:
-                *  1. if we reached the end of all the iterators of all the
-                *  traces of this session,
-                *  2. if we never had live stream iterator in the first place.
-                *
-                * In either cases, we return END.
-                */
-               BT_ASSERT(session->traces->len == 0);
-               stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_END;
-       }
-end:
-       return stream_iter_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_message_iterator_class_next_method_status lttng_live_msg_iter_next(
-               bt_self_message_iterator *self_msg_it,
-               bt_message_array_const msgs, uint64_t capacity,
-               uint64_t *count)
-{
-       bt_message_iterator_class_next_method_status status;
-       enum lttng_live_viewer_status viewer_status;
-       struct lttng_live_msg_iter *lttng_live_msg_iter =
-               bt_self_message_iterator_get_data(self_msg_it);
-       struct lttng_live_component *lttng_live =
-               lttng_live_msg_iter->lttng_live_comp;
-       bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
-       bt_logging_level log_level = lttng_live_msg_iter->log_level;
-       enum lttng_live_iterator_status stream_iter_status;
-       uint64_t session_idx;
-
-       *count = 0;
-
-       BT_ASSERT_DBG(lttng_live_msg_iter);
-
-       if (G_UNLIKELY(lttng_live_msg_iter->was_interrupted)) {
-               /*
-                * The iterator was interrupted in a previous call to the
-                * `_next()` method. We currently do not support generating
-                * messages after such event. The babeltrace2 CLI should never
-                * be running the graph after being interrupted. So this check
-                * is to prevent other graph users from using this live
-                * iterator in an messed up internal state.
-                */
-               status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR;
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Message iterator was interrupted during a previous call to the `next()` and currently does not support continuing after such event.");
-               goto end;
-       }
-
-       /*
-        * Clear all the invalid message reference that might be left over in
-        * the output array.
-        */
-       memset(msgs, 0, capacity * sizeof(*msgs));
-
-       /*
-        * If no session are exposed on the relay found at the url provided by
-        * the user, session count will be 0. In this case, we return status
-        * end to return gracefully.
-        */
-       if (lttng_live_msg_iter->sessions->len == 0) {
-               if (lttng_live->params.sess_not_found_act !=
-                               SESSION_NOT_FOUND_ACTION_CONTINUE) {
-                       status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_END;
-                       goto end;
-               } else {
-                       /*
-                        * The are no more active session for this session
-                        * name. Retry to create a viewer session for the
-                        * requested session name.
-                        */
-                       viewer_status = lttng_live_create_viewer_session(lttng_live_msg_iter);
-                       if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-                               if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
-                                       status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR;
-                                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                               "Error creating LTTng live viewer session");
-                               } else if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
-                                       status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_AGAIN;
-                               } else {
-                                       bt_common_abort();
-                               }
-                               goto end;
-                       }
-               }
-       }
-
-       if (lttng_live_msg_iter->active_stream_iter == 0) {
-               lttng_live_force_new_streams_and_metadata(lttng_live_msg_iter);
-       }
-
-       /*
-        * Here the muxing of message is done.
-        *
-        * We need to iterate over all the streams of all the traces of all the
-        * viewer sessions in order to get the message with the smallest
-        * timestamp. In this case, a session is a viewer session and there is
-        * one viewer session per consumer daemon. (UST 32bit, UST 64bit and/or
-        * kernel). Each viewer session can have multiple traces, for example,
-        * 64bit UST viewer sessions could have multiple per-pid traces.
-        *
-        * We iterate over the streams of each traces to update and see what is
-        * their next message's timestamp. From those timestamps, we select the
-        * message with the smallest timestamp as the best candidate message
-        * for that trace and do the same thing across all the sessions.
-        *
-        * We then compare the timestamp of best candidate message of all the
-        * sessions to pick the message with the smallest timestamp and we
-        * return it.
-        */
-       while (*count < capacity) {
-               struct lttng_live_stream_iterator *youngest_stream_iter = NULL,
-                         *candidate_stream_iter = NULL;
-               int64_t youngest_msg_ts_ns = INT64_MAX;
-
-               BT_ASSERT_DBG(lttng_live_msg_iter->sessions);
-               session_idx = 0;
-               while (session_idx < lttng_live_msg_iter->sessions->len) {
-                       struct lttng_live_session *session =
-                               g_ptr_array_index(lttng_live_msg_iter->sessions,
-                                       session_idx);
-
-                       /* Find the best candidate message to send downstream. */
-                       stream_iter_status = next_stream_iterator_for_session(
-                               lttng_live_msg_iter, session,
-                               &candidate_stream_iter);
-
-                       /* If we receive an END status, it means that either:
-                        * - Those traces never had active streams (UST with no
-                        *   data produced yet),
-                        * - All live stream iterators have ENDed.*/
-                       if (stream_iter_status == LTTNG_LIVE_ITERATOR_STATUS_END) {
-                               if (session->closed && session->traces->len == 0) {
-                                       /*
-                                        * Remove the session from the list.
-                                        * session_idx is not modified since
-                                        * g_ptr_array_remove_index_fast
-                                        * replaces the the removed element with
-                                        * the array's last element.
-                                        */
-                                       g_ptr_array_remove_index_fast(
-                                               lttng_live_msg_iter->sessions,
-                                               session_idx);
-                               } else {
-                                       session_idx++;
-                               }
-                               continue;
-                       }
-
-                       if (stream_iter_status != LTTNG_LIVE_ITERATOR_STATUS_OK) {
-                               goto return_status;
-                       }
-
-                       if (G_UNLIKELY(youngest_stream_iter == NULL) ||
-                                       candidate_stream_iter->current_msg_ts_ns < youngest_msg_ts_ns) {
-                               youngest_msg_ts_ns = candidate_stream_iter->current_msg_ts_ns;
-                               youngest_stream_iter = candidate_stream_iter;
-                       } else if (candidate_stream_iter->current_msg_ts_ns == youngest_msg_ts_ns) {
-                               /*
-                                * The currently selected message to be sent
-                                * downstream next has the exact same timestamp
-                                * that of the current candidate message. We
-                                * must break the tie in a predictable manner.
-                                */
-                               BT_COMP_LOGD_STR("Two of the next message candidates have the same timestamps, pick one deterministically.");
-                               /*
-                                * Order the messages in an arbitrary but
-                                * deterministic way.
-                                */
-                               int ret = common_muxing_compare_messages(
-                                       candidate_stream_iter->current_msg,
-                                       youngest_stream_iter->current_msg);
-                               if (ret < 0) {
-                                       /*
-                                        * The `candidate_stream_iter->current_msg`
-                                        * should go first. Update the next
-                                        * iterator and the current timestamp.
-                                        */
-                                       youngest_msg_ts_ns = candidate_stream_iter->current_msg_ts_ns;
-                                       youngest_stream_iter = candidate_stream_iter;
-                               } else if (ret == 0) {
-                                       /* Unable to pick which one should go first. */
-                                       BT_COMP_LOGW("Cannot deterministically pick next live stream message iterator because they have identical next messages: "
-                                               "next-stream-iter-addr=%p" "candidate-stream-iter-addr=%p",
-                                               youngest_stream_iter, candidate_stream_iter);
-                               }
-                       }
-
-                       session_idx++;
-               }
-
-               if (!youngest_stream_iter) {
-                       stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN;
-                       goto return_status;
-               }
-
-               BT_ASSERT_DBG(youngest_stream_iter->current_msg);
-               /* Ensure monotonicity. */
-               BT_ASSERT_DBG(lttng_live_msg_iter->last_msg_ts_ns <=
-                       youngest_stream_iter->current_msg_ts_ns);
-
-               /*
-                * Insert the next message to the message batch. This will set
-                * stream iterator current messsage to NULL so that next time
-                * we fetch the next message of that stream iterator
-                */
-               BT_MESSAGE_MOVE_REF(msgs[*count], youngest_stream_iter->current_msg);
-               (*count)++;
-
-               /* Update the last timestamp in nanoseconds sent downstream. */
-               lttng_live_msg_iter->last_msg_ts_ns = youngest_msg_ts_ns;
-               youngest_stream_iter->current_msg_ts_ns = INT64_MAX;
-
-               stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_OK;
-       }
-
-return_status:
-       switch (stream_iter_status) {
-       case LTTNG_LIVE_ITERATOR_STATUS_OK:
-       case LTTNG_LIVE_ITERATOR_STATUS_AGAIN:
-               /*
-                * If we gathered messages, return _OK even if the graph was
-                * interrupted. This allows for the components downstream to at
-                * least get the thoses messages. If the graph was indeed
-                * interrupted there should not be another _next() call as the
-                * application will tear down the graph. This component class
-                * doesn't support restarting after an interruption.
-                */
-               if (*count > 0) {
-                       status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK;
-               } else {
-                       status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_AGAIN;
-               }
-               break;
-       case LTTNG_LIVE_ITERATOR_STATUS_END:
-               status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_END;
-               break;
-       case LTTNG_LIVE_ITERATOR_STATUS_NOMEM:
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Memory error preparing the next batch of messages: "
-                       "live-iter-status=%s",
-                       lttng_live_iterator_status_string(stream_iter_status));
-               status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_MEMORY_ERROR;
-               break;
-       case LTTNG_LIVE_ITERATOR_STATUS_ERROR:
-       case LTTNG_LIVE_ITERATOR_STATUS_INVAL:
-       case LTTNG_LIVE_ITERATOR_STATUS_UNSUPPORTED:
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Error preparing the next batch of messages: "
-                       "live-iter-status=%s",
-                       lttng_live_iterator_status_string(stream_iter_status));
-
-               status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR;
-               /* Put all existing messages on error. */
-               put_messages(msgs, *count);
-               break;
-       default:
-               bt_common_abort();
-       }
-
-end:
-       return status;
-}
-
-static
-struct lttng_live_msg_iter *lttng_live_msg_iter_create(
-               struct lttng_live_component *lttng_live_comp,
-               bt_self_message_iterator *self_msg_it)
-{
-       bt_self_component *self_comp = lttng_live_comp->self_comp;
-       bt_logging_level log_level = lttng_live_comp->log_level;
-
-       struct lttng_live_msg_iter *lttng_live_msg_iter =
-               g_new0(struct lttng_live_msg_iter, 1);
-       if (!lttng_live_msg_iter) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Failed to allocate lttng_live_msg_iter");
-               goto end;
-       }
-
-       lttng_live_msg_iter->log_level = lttng_live_comp->log_level;
-       lttng_live_msg_iter->self_comp = lttng_live_comp->self_comp;
-       lttng_live_msg_iter->lttng_live_comp = lttng_live_comp;
-       lttng_live_msg_iter->self_msg_iter = self_msg_it;
-
-       lttng_live_msg_iter->active_stream_iter = 0;
-       lttng_live_msg_iter->last_msg_ts_ns = INT64_MIN;
-       lttng_live_msg_iter->was_interrupted = false;
-
-       lttng_live_msg_iter->sessions = g_ptr_array_new_with_free_func(
-               (GDestroyNotify) lttng_live_destroy_session);
-       BT_ASSERT(lttng_live_msg_iter->sessions);
-
-end:
-       return lttng_live_msg_iter;
-
-}
-
-BT_HIDDEN
-bt_message_iterator_class_initialize_method_status lttng_live_msg_iter_init(
-               bt_self_message_iterator *self_msg_it,
-               bt_self_message_iterator_configuration *config,
-               bt_self_component_port_output *self_port)
-{
-       bt_message_iterator_class_initialize_method_status status;
-       struct lttng_live_component *lttng_live;
-       struct lttng_live_msg_iter *lttng_live_msg_iter;
-       enum lttng_live_viewer_status viewer_status;
-       bt_logging_level log_level;
-       bt_self_component *self_comp =
-               bt_self_message_iterator_borrow_component(self_msg_it);
-
-       lttng_live = bt_self_component_get_data(self_comp);
-       log_level = lttng_live->log_level;
-       self_comp = lttng_live->self_comp;
-
-
-       /* There can be only one downstream iterator at the same time. */
-       BT_ASSERT(!lttng_live->has_msg_iter);
-       lttng_live->has_msg_iter = true;
-
-       lttng_live_msg_iter = lttng_live_msg_iter_create(lttng_live,
-               self_msg_it);
-       if (!lttng_live_msg_iter) {
-               status = BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Failed to create lttng_live_msg_iter");
-               goto error;
-       }
-
-        viewer_status = live_viewer_connection_create(self_comp, NULL,
-               log_level, lttng_live->params.url->str, false,
-               lttng_live_msg_iter, &lttng_live_msg_iter->viewer_connection);
-       if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-               if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
-                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                               "Failed to create viewer connection");
-               } else if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
-                       /*
-                        * Interruption in the _iter_init() method is not
-                        * supported. Return an error.
-                        */
-                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                               "Interrupted while creating viewer connection");
-               }
-               goto error;
-       }
-
-       viewer_status = lttng_live_create_viewer_session(lttng_live_msg_iter);
-       if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-               if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
-                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                               "Failed to create viewer session");
-               } else if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
-                       /*
-                        * Interruption in the _iter_init() method is not
-                        * supported. Return an error.
-                        */
-                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                               "Interrupted when creating viewer session");
-               }
-               goto error;
-       }
-
-       if (lttng_live_msg_iter->sessions->len == 0) {
-               switch (lttng_live->params.sess_not_found_act) {
-               case SESSION_NOT_FOUND_ACTION_CONTINUE:
-                       BT_COMP_LOGI("Unable to connect to the requested live viewer session. Keep trying to connect because of "
-                               "%s=\"%s\" component parameter: url=\"%s\"",
-                               SESS_NOT_FOUND_ACTION_PARAM,
-                               SESS_NOT_FOUND_ACTION_CONTINUE_STR,
-                               lttng_live->params.url->str);
-                       break;
-               case SESSION_NOT_FOUND_ACTION_FAIL:
-                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                               "Unable to connect to the requested live viewer session. Fail the message iterator initialization because of %s=\"%s\" "
-                               "component parameter: url =\"%s\"",
-                               SESS_NOT_FOUND_ACTION_PARAM,
-                               SESS_NOT_FOUND_ACTION_FAIL_STR,
-                               lttng_live->params.url->str);
-                       goto error;
-               case SESSION_NOT_FOUND_ACTION_END:
-                       BT_COMP_LOGI("Unable to connect to the requested live viewer session. End gracefully at the first _next() "
-                               "call because of %s=\"%s\" component parameter: "
-                               "url=\"%s\"", SESS_NOT_FOUND_ACTION_PARAM,
-                               SESS_NOT_FOUND_ACTION_END_STR,
-                               lttng_live->params.url->str);
-                       break;
-               default:
-                       bt_common_abort();
-               }
-       }
-
-       bt_self_message_iterator_set_data(self_msg_it, lttng_live_msg_iter);
-       status = BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_OK;
-       goto end;
-
-error:
-       status = BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
-       lttng_live_msg_iter_destroy(lttng_live_msg_iter);
-end:
-       return status;
-}
-
-static struct bt_param_validation_map_value_entry_descr list_sessions_params[] = {
-       { URL_PARAM, BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, { .type = BT_VALUE_TYPE_STRING } },
-       BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
-};
-
-static
-bt_component_class_query_method_status lttng_live_query_list_sessions(
-               const bt_value *params, const bt_value **result,
-               bt_self_component_class *self_comp_class,
-               bt_logging_level log_level)
-{
-       bt_component_class_query_method_status status;
-       const bt_value *url_value = NULL;
-       const char *url;
-       struct live_viewer_connection *viewer_connection = NULL;
-       enum lttng_live_viewer_status viewer_status;
-       enum bt_param_validation_status validation_status;
-       gchar *validate_error = NULL;
-
-       validation_status = bt_param_validation_validate(params,
-               list_sessions_params, &validate_error);
-       if (validation_status == BT_PARAM_VALIDATION_STATUS_MEMORY_ERROR) {
-               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_MEMORY_ERROR;
-               goto error;
-       } else if (validation_status == BT_PARAM_VALIDATION_STATUS_VALIDATION_ERROR) {
-               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
-               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class, "%s",
-                       validate_error);
-               goto error;
-       }
-
-       url_value = bt_value_map_borrow_entry_value_const(params, URL_PARAM);
-       url = bt_value_string_get(url_value);
-
-       viewer_status = live_viewer_connection_create(NULL, self_comp_class,
-               log_level, url, true, NULL, &viewer_connection);
-       if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-               if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
-                       BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                               "Failed to create viewer connection");
-                       status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
-               } else if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
-                       status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_AGAIN;
-               } else {
-                       bt_common_abort();
-               }
-               goto error;
-       }
-
-       status = live_viewer_connection_list_sessions(viewer_connection,
-               result);
-       if (status != BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_OK) {
-               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                       "Failed to list viewer sessions");
-               goto error;
-       }
-
-       goto end;
-
-error:
-       BT_VALUE_PUT_REF_AND_RESET(*result);
-
-       if (status >= 0) {
-               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
-       }
-
-end:
-       if (viewer_connection) {
-               live_viewer_connection_destroy(viewer_connection);
-       }
-
-       g_free(validate_error);
-
-       return status;
-}
-
-static
-bt_component_class_query_method_status lttng_live_query_support_info(
-               const bt_value *params, const bt_value **result,
-               bt_self_component_class *self_comp_class,
-               bt_logging_level log_level)
-{
-       bt_component_class_query_method_status status =
-               BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_OK;
-       const bt_value *input_type_value;
-       const bt_value *input_value;
-       double weight = 0;
-       struct bt_common_lttng_live_url_parts parts = { 0 };
-
-       /* Used by the logging macros */
-       __attribute__((unused)) bt_self_component *self_comp = NULL;
-
-       *result = NULL;
-       input_type_value = bt_value_map_borrow_entry_value_const(params,
-               "type");
-       if (!input_type_value) {
-               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                       "Missing expected `type` parameter.");
-               goto error;
-       }
-
-       if (!bt_value_is_string(input_type_value)) {
-               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                       "`type` parameter is not a string value.");
-               goto error;
-       }
-
-       if (strcmp(bt_value_string_get(input_type_value), "string") != 0) {
-               /* We don't handle file system paths */
-               goto create_result;
-       }
-
-       input_value = bt_value_map_borrow_entry_value_const(params, "input");
-       if (!input_value) {
-               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                       "Missing expected `input` parameter.");
-               goto error;
-       }
-
-       if (!bt_value_is_string(input_value)) {
-               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                       "`input` parameter is not a string value.");
-               goto error;
-       }
-
-       parts = bt_common_parse_lttng_live_url(bt_value_string_get(input_value),
-               NULL, 0);
-       if (parts.session_name) {
-               /*
-                * Looks pretty much like an LTTng live URL: we got the
-                * session name part, which forms a complete URL.
-                */
-               weight = .75;
-       }
-
-create_result:
-       *result = bt_value_real_create_init(weight);
-       if (!*result) {
-               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_MEMORY_ERROR;
-               goto error;
-       }
-
-       goto end;
-
-error:
-       if (status >= 0) {
-               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
-       }
-
-       BT_ASSERT(!*result);
-
-end:
-       bt_common_destroy_lttng_live_url_parts(&parts);
-       return status;
-}
-
-BT_HIDDEN
-bt_component_class_query_method_status lttng_live_query(
-               bt_self_component_class_source *comp_class,
-               bt_private_query_executor *priv_query_exec,
-               const char *object, const bt_value *params,
-               __attribute__((unused)) void *method_data,
-               const bt_value **result)
-{
-       bt_component_class_query_method_status status =
-               BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_OK;
-       bt_self_component *self_comp = NULL;
-       bt_self_component_class *self_comp_class =
-               bt_self_component_class_source_as_self_component_class(comp_class);
-       bt_logging_level log_level = bt_query_executor_get_logging_level(
-               bt_private_query_executor_as_query_executor_const(
-                       priv_query_exec));
-
-       if (strcmp(object, "sessions") == 0) {
-               status = lttng_live_query_list_sessions(params, result,
-                       self_comp_class, log_level);
-       } else if (strcmp(object, "babeltrace.support-info") == 0) {
-               status = lttng_live_query_support_info(params, result,
-                       self_comp_class, log_level);
-       } else {
-               BT_COMP_LOGI("Unknown query object `%s`", object);
-               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_UNKNOWN_OBJECT;
-               goto end;
-       }
-
-end:
-       return status;
-}
-
-static
-void lttng_live_component_destroy_data(struct lttng_live_component *lttng_live)
-{
-       if (!lttng_live) {
-               return;
-       }
-       if (lttng_live->params.url) {
-               g_string_free(lttng_live->params.url, TRUE);
-       }
-       g_free(lttng_live);
-}
-
-BT_HIDDEN
-void lttng_live_component_finalize(bt_self_component_source *component)
-{
-       void *data = bt_self_component_get_data(
-               bt_self_component_source_as_self_component(component));
-
-       if (!data) {
-               return;
-       }
-       lttng_live_component_destroy_data(data);
-}
-
-static
-enum session_not_found_action parse_session_not_found_action_param(
-       const bt_value *no_session_param)
-{
-       enum session_not_found_action action;
-       const char *no_session_act_str = bt_value_string_get(no_session_param);
-
-       if (strcmp(no_session_act_str, SESS_NOT_FOUND_ACTION_CONTINUE_STR) == 0) {
-               action = SESSION_NOT_FOUND_ACTION_CONTINUE;
-       } else if (strcmp(no_session_act_str, SESS_NOT_FOUND_ACTION_FAIL_STR) == 0) {
-               action = SESSION_NOT_FOUND_ACTION_FAIL;
-       } else {
-               BT_ASSERT(strcmp(no_session_act_str, SESS_NOT_FOUND_ACTION_END_STR) == 0);
-               action = SESSION_NOT_FOUND_ACTION_END;
-       }
-
-       return action;
-}
-
-static struct bt_param_validation_value_descr inputs_elem_descr = {
-       .type = BT_VALUE_TYPE_STRING,
-};
-
-static const char *sess_not_found_action_choices[] = {
-       SESS_NOT_FOUND_ACTION_CONTINUE_STR,
-       SESS_NOT_FOUND_ACTION_FAIL_STR,
-       SESS_NOT_FOUND_ACTION_END_STR,
-};
-
-static struct bt_param_validation_map_value_entry_descr params_descr[] = {
-       { INPUTS_PARAM, BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY, { BT_VALUE_TYPE_ARRAY, .array = {
-               .min_length = 1,
-               .max_length = 1,
-               .element_type = &inputs_elem_descr,
-       } } },
-       { SESS_NOT_FOUND_ACTION_PARAM, BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL, { BT_VALUE_TYPE_STRING, .string = {
-               .choices = sess_not_found_action_choices,
-       } } },
-       BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
-};
-
-static
-bt_component_class_initialize_method_status lttng_live_component_create(
-               const bt_value *params,
-               bt_logging_level log_level,
-               bt_self_component *self_comp,
-               struct lttng_live_component **component)
-{
-       struct lttng_live_component *lttng_live = NULL;
-       const bt_value *inputs_value;
-       const bt_value *url_value;
-       const bt_value *value;
-       const char *url;
-       enum bt_param_validation_status validation_status;
-       gchar *validation_error = NULL;
-       bt_component_class_initialize_method_status status;
-
-       validation_status = bt_param_validation_validate(params, params_descr,
-               &validation_error);
-       if (validation_status == BT_PARAM_VALIDATION_STATUS_MEMORY_ERROR) {
-               status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
-               goto error;
-       } else if (validation_status == BT_PARAM_VALIDATION_STATUS_VALIDATION_ERROR) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp, "%s", validation_error);
-               status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
-               goto error;
-       }
-
-       lttng_live = g_new0(struct lttng_live_component, 1);
-       if (!lttng_live) {
-               status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
-               goto end;
-       }
-       lttng_live->log_level = log_level;
-       lttng_live->self_comp = self_comp;
-       lttng_live->max_query_size = MAX_QUERY_SIZE;
-       lttng_live->has_msg_iter = false;
-
-       inputs_value =
-               bt_value_map_borrow_entry_value_const(params, INPUTS_PARAM);
-       url_value =
-               bt_value_array_borrow_element_by_index_const(inputs_value, 0);
-       url = bt_value_string_get(url_value);
-
-       lttng_live->params.url = g_string_new(url);
-       if (!lttng_live->params.url) {
-               status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
-               goto error;
-       }
-
-       value = bt_value_map_borrow_entry_value_const(params,
-               SESS_NOT_FOUND_ACTION_PARAM);
-       if (value) {
-               lttng_live->params.sess_not_found_act =
-                       parse_session_not_found_action_param(value);
-       } else {
-               BT_COMP_LOGI("Optional `%s` parameter is missing: "
-                       "defaulting to `%s`.",
-                       SESS_NOT_FOUND_ACTION_PARAM,
-                       SESS_NOT_FOUND_ACTION_CONTINUE_STR);
-               lttng_live->params.sess_not_found_act =
-                       SESSION_NOT_FOUND_ACTION_CONTINUE;
-       }
-
-       status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
-       goto end;
-
-error:
-       lttng_live_component_destroy_data(lttng_live);
-       lttng_live = NULL;
-end:
-       g_free(validation_error);
-
-       *component = lttng_live;
-       return status;
-}
-
-BT_HIDDEN
-bt_component_class_initialize_method_status lttng_live_component_init(
-               bt_self_component_source *self_comp_src,
-               bt_self_component_source_configuration *config,
-               const bt_value *params,
-               __attribute__((unused)) void *init_method_data)
-{
-       struct lttng_live_component *lttng_live;
-       bt_component_class_initialize_method_status ret;
-       bt_self_component *self_comp =
-               bt_self_component_source_as_self_component(self_comp_src);
-       bt_logging_level log_level = bt_component_get_logging_level(
-               bt_self_component_as_component(self_comp));
-       bt_self_component_add_port_status add_port_status;
-
-       ret = lttng_live_component_create(params, log_level, self_comp, &lttng_live);
-       if (ret != BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK) {
-               goto error;
-       }
-
-       add_port_status = bt_self_component_source_add_output_port(
-               self_comp_src, "out", NULL, NULL);
-       if (add_port_status != BT_SELF_COMPONENT_ADD_PORT_STATUS_OK) {
-               ret = (int) add_port_status;
-               goto end;
-       }
-
-       bt_self_component_set_data(self_comp, lttng_live);
-       goto end;
-
-error:
-       lttng_live_component_destroy_data(lttng_live);
-       lttng_live = NULL;
-end:
-       return ret;
-}
diff --git a/src/plugins/ctf/lttng-live/lttng-live.cpp b/src/plugins/ctf/lttng-live/lttng-live.cpp
new file mode 100644 (file)
index 0000000..7987532
--- /dev/null
@@ -0,0 +1,2325 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2019 Francis Deslauriers <francis.deslauriers@efficios.com>
+ * Copyright 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ * Copyright 2016 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * Babeltrace CTF LTTng-live Client Component
+ */
+
+#define BT_COMP_LOG_SELF_COMP self_comp
+#define BT_LOG_OUTPUT_LEVEL log_level
+#define BT_LOG_TAG "PLUGIN/SRC.CTF.LTTNG-LIVE"
+#include "logging/comp-logging.h"
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#include "common/assert.h"
+#include <babeltrace2/babeltrace.h>
+#include "compat/compiler.h"
+#include <babeltrace2/types.h>
+
+#include "plugins/common/muxing/muxing.h"
+#include "plugins/common/param-validation/param-validation.h"
+
+#include "data-stream.hpp"
+#include "metadata.hpp"
+#include "lttng-live.hpp"
+
+#define MAX_QUERY_SIZE                     (256*1024)
+#define URL_PARAM                          "url"
+#define INPUTS_PARAM                       "inputs"
+#define SESS_NOT_FOUND_ACTION_PARAM        "session-not-found-action"
+#define SESS_NOT_FOUND_ACTION_CONTINUE_STR  "continue"
+#define SESS_NOT_FOUND_ACTION_FAIL_STR     "fail"
+#define SESS_NOT_FOUND_ACTION_END_STR      "end"
+
+#define print_dbg(fmt, ...)    BT_COMP_LOGD(fmt, ## __VA_ARGS__)
+
+static
+const char *lttng_live_iterator_status_string(
+               enum lttng_live_iterator_status status)
+{
+       switch (status) {
+       case LTTNG_LIVE_ITERATOR_STATUS_CONTINUE:
+               return "LTTNG_LIVE_ITERATOR_STATUS_CONTINUE";
+       case LTTNG_LIVE_ITERATOR_STATUS_AGAIN:
+               return "LTTNG_LIVE_ITERATOR_STATUS_AGAIN";
+       case LTTNG_LIVE_ITERATOR_STATUS_END:
+               return "LTTNG_LIVE_ITERATOR_STATUS_END";
+       case LTTNG_LIVE_ITERATOR_STATUS_OK:
+               return "LTTNG_LIVE_ITERATOR_STATUS_OK";
+       case LTTNG_LIVE_ITERATOR_STATUS_INVAL:
+               return "LTTNG_LIVE_ITERATOR_STATUS_INVAL";
+       case LTTNG_LIVE_ITERATOR_STATUS_ERROR:
+               return "LTTNG_LIVE_ITERATOR_STATUS_ERROR";
+       case LTTNG_LIVE_ITERATOR_STATUS_NOMEM:
+               return "LTTNG_LIVE_ITERATOR_STATUS_NOMEM";
+       case LTTNG_LIVE_ITERATOR_STATUS_UNSUPPORTED:
+               return "LTTNG_LIVE_ITERATOR_STATUS_UNSUPPORTED";
+       default:
+               bt_common_abort();
+       }
+}
+
+static
+const char *lttng_live_stream_state_string(enum lttng_live_stream_state state)
+{
+       switch (state) {
+       case LTTNG_LIVE_STREAM_ACTIVE_NO_DATA:
+               return "ACTIVE_NO_DATA";
+       case LTTNG_LIVE_STREAM_QUIESCENT_NO_DATA:
+               return "QUIESCENT_NO_DATA";
+       case LTTNG_LIVE_STREAM_QUIESCENT:
+               return "QUIESCENT";
+       case LTTNG_LIVE_STREAM_ACTIVE_DATA:
+               return "ACTIVE_DATA";
+       case LTTNG_LIVE_STREAM_EOF:
+               return "EOF";
+       default:
+               return "ERROR";
+       }
+}
+
+void lttng_live_stream_iterator_set_state(struct lttng_live_stream_iterator *stream_iter,
+               enum lttng_live_stream_state new_state)
+{
+       bt_self_component *self_comp = stream_iter->self_comp;
+       bt_logging_level log_level = stream_iter->log_level;
+
+       BT_COMP_LOGD("Setting live stream iterator state: viewer-stream-id=%" PRIu64
+               ", old-state=%s, new-state=%s",
+               stream_iter->viewer_stream_id,
+               lttng_live_stream_state_string(stream_iter->state),
+               lttng_live_stream_state_string(new_state));
+
+       stream_iter->state = new_state;
+}
+
+#define LTTNG_LIVE_LOGD_STREAM_ITER(live_stream_iter) \
+       do { \
+               BT_COMP_LOGD("Live stream iterator state=%s, " \
+                       "last-inact-ts-is-set=%d, last-inact-ts-value=%" PRId64 ", " \
+                       "curr-inact-ts=%" PRId64, \
+                       lttng_live_stream_state_string(live_stream_iter->state), \
+                       live_stream_iter->last_inactivity_ts.is_set, \
+                       live_stream_iter->last_inactivity_ts.value, \
+                       live_stream_iter->current_inactivity_ts); \
+       } while (0);
+
+BT_HIDDEN
+bool lttng_live_graph_is_canceled(struct lttng_live_msg_iter *msg_iter)
+{
+       bool ret;
+
+       if (!msg_iter) {
+               ret = false;
+               goto end;
+       }
+
+       ret = bt_self_message_iterator_is_interrupted(
+               msg_iter->self_msg_iter);
+
+end:
+       return ret;
+}
+
+static
+struct lttng_live_trace *lttng_live_session_borrow_trace_by_id(struct lttng_live_session *session,
+               uint64_t trace_id)
+{
+       uint64_t trace_idx;
+       struct lttng_live_trace *ret_trace = NULL;
+
+       for (trace_idx = 0; trace_idx < session->traces->len; trace_idx++) {
+               struct lttng_live_trace *trace =
+                       (lttng_live_trace *) g_ptr_array_index(session->traces, trace_idx);
+               if (trace->id == trace_id) {
+                       ret_trace = trace;
+                       goto end;
+               }
+       }
+
+end:
+       return ret_trace;
+}
+
+static
+void lttng_live_destroy_trace(struct lttng_live_trace *trace)
+{
+       bt_logging_level log_level = trace->log_level;
+       bt_self_component *self_comp = trace->self_comp;
+
+       BT_COMP_LOGD("Destroying live trace: trace-id=%" PRIu64, trace->id);
+
+       BT_ASSERT(trace->stream_iterators);
+       g_ptr_array_free(trace->stream_iterators, TRUE);
+
+       BT_TRACE_PUT_REF_AND_RESET(trace->trace);
+       BT_TRACE_CLASS_PUT_REF_AND_RESET(trace->trace_class);
+
+       lttng_live_metadata_fini(trace);
+       g_free(trace);
+}
+
+static
+struct lttng_live_trace *lttng_live_create_trace(struct lttng_live_session *session,
+               uint64_t trace_id)
+{
+       struct lttng_live_trace *trace = NULL;
+       bt_logging_level log_level = session->log_level;
+       bt_self_component *self_comp = session->self_comp;
+
+       BT_COMP_LOGD("Creating live trace: "
+               "session-id=%" PRIu64 ", trace-id=%" PRIu64,
+               session->id, trace_id);
+       trace = g_new0(struct lttng_live_trace, 1);
+       if (!trace) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Failed to allocate live trace");
+               goto error;
+       }
+       trace->log_level = session->log_level;
+       trace->self_comp = session->self_comp;
+       trace->session = session;
+       trace->id = trace_id;
+       trace->trace_class = NULL;
+       trace->trace = NULL;
+       trace->stream_iterators = g_ptr_array_new_with_free_func(
+               (GDestroyNotify) lttng_live_stream_iterator_destroy);
+       BT_ASSERT(trace->stream_iterators);
+       trace->metadata_stream_state = LTTNG_LIVE_METADATA_STREAM_STATE_NEEDED;
+       g_ptr_array_add(session->traces, trace);
+
+       goto end;
+error:
+       g_free(trace);
+       trace = NULL;
+end:
+       return trace;
+}
+
+BT_HIDDEN
+struct lttng_live_trace *lttng_live_session_borrow_or_create_trace_by_id(
+               struct lttng_live_session *session, uint64_t trace_id)
+{
+       struct lttng_live_trace *trace;
+
+       trace = lttng_live_session_borrow_trace_by_id(session, trace_id);
+       if (trace) {
+               goto end;
+       }
+
+       /* The session is the owner of the newly created trace. */
+       trace = lttng_live_create_trace(session, trace_id);
+
+end:
+       return trace;
+}
+
+BT_HIDDEN
+int lttng_live_add_session(struct lttng_live_msg_iter *lttng_live_msg_iter,
+               uint64_t session_id, const char *hostname,
+               const char *session_name)
+{
+       int ret = 0;
+       struct lttng_live_session *session;
+       bt_logging_level log_level = lttng_live_msg_iter->log_level;
+       bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
+
+       BT_COMP_LOGD("Adding live session: "
+               "session-id=%" PRIu64 ", hostname=\"%s\" session-name=\"%s\"",
+               session_id, hostname, session_name);
+
+       session = g_new0(struct lttng_live_session, 1);
+       if (!session) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Failed to allocate live session");
+               goto error;
+       }
+
+       session->log_level = lttng_live_msg_iter->log_level;
+       session->self_comp = lttng_live_msg_iter->self_comp;
+       session->id = session_id;
+       session->traces = g_ptr_array_new_with_free_func(
+               (GDestroyNotify) lttng_live_destroy_trace);
+       BT_ASSERT(session->traces);
+       session->lttng_live_msg_iter = lttng_live_msg_iter;
+       session->new_streams_needed = true;
+       session->hostname = g_string_new(hostname);
+       BT_ASSERT(session->hostname);
+
+       session->session_name = g_string_new(session_name);
+       BT_ASSERT(session->session_name);
+
+       g_ptr_array_add(lttng_live_msg_iter->sessions, session);
+       goto end;
+error:
+       g_free(session);
+       ret = -1;
+end:
+       return ret;
+}
+
+static
+void lttng_live_destroy_session(struct lttng_live_session *session)
+{
+       bt_logging_level log_level;
+       bt_self_component *self_comp;
+
+       if (!session) {
+               goto end;
+       }
+
+       log_level = session->log_level;
+       self_comp = session->self_comp;
+       BT_COMP_LOGD("Destroying live session: "
+               "session-id=%" PRIu64 ", session-name=\"%s\"",
+               session->id, session->session_name->str);
+       if (session->id != -1ULL) {
+               if (lttng_live_session_detach(session)) {
+                       if (!lttng_live_graph_is_canceled(
+                                       session->lttng_live_msg_iter)) {
+                               /* Old relayd cannot detach sessions. */
+                               BT_COMP_LOGD("Unable to detach lttng live session %" PRIu64,
+                                       session->id);
+                       }
+               }
+               session->id = -1ULL;
+       }
+
+       if (session->traces) {
+               g_ptr_array_free(session->traces, TRUE);
+       }
+
+       if (session->hostname) {
+               g_string_free(session->hostname, TRUE);
+       }
+       if (session->session_name) {
+               g_string_free(session->session_name, TRUE);
+       }
+       g_free(session);
+
+end:
+       return;
+}
+
+static
+void lttng_live_msg_iter_destroy(struct lttng_live_msg_iter *lttng_live_msg_iter)
+{
+       if (!lttng_live_msg_iter) {
+               goto end;
+       }
+
+       if (lttng_live_msg_iter->sessions) {
+               g_ptr_array_free(lttng_live_msg_iter->sessions, TRUE);
+       }
+
+       if (lttng_live_msg_iter->viewer_connection) {
+               live_viewer_connection_destroy(lttng_live_msg_iter->viewer_connection);
+       }
+       BT_ASSERT(lttng_live_msg_iter->lttng_live_comp);
+       BT_ASSERT(lttng_live_msg_iter->lttng_live_comp->has_msg_iter);
+
+       /* All stream iterators must be destroyed at this point. */
+       BT_ASSERT(lttng_live_msg_iter->active_stream_iter == 0);
+       lttng_live_msg_iter->lttng_live_comp->has_msg_iter = false;
+
+       g_free(lttng_live_msg_iter);
+
+end:
+       return;
+}
+
+BT_HIDDEN
+void lttng_live_msg_iter_finalize(bt_self_message_iterator *self_msg_iter)
+{
+       struct lttng_live_msg_iter *lttng_live_msg_iter;
+
+       BT_ASSERT(self_msg_iter);
+
+       lttng_live_msg_iter = (struct lttng_live_msg_iter *)
+               bt_self_message_iterator_get_data(self_msg_iter);
+       BT_ASSERT(lttng_live_msg_iter);
+       lttng_live_msg_iter_destroy(lttng_live_msg_iter);
+}
+
+static
+enum lttng_live_iterator_status lttng_live_iterator_next_check_stream_state(
+               struct lttng_live_stream_iterator *lttng_live_stream)
+{
+       bt_logging_level log_level = lttng_live_stream->log_level;
+       bt_self_component *self_comp = lttng_live_stream->self_comp;
+
+       switch (lttng_live_stream->state) {
+       case LTTNG_LIVE_STREAM_QUIESCENT:
+       case LTTNG_LIVE_STREAM_ACTIVE_DATA:
+               break;
+       case LTTNG_LIVE_STREAM_ACTIVE_NO_DATA:
+               /* Invalid state. */
+               BT_COMP_LOGF("Unexpected stream state \"ACTIVE_NO_DATA\"");
+               bt_common_abort();
+       case LTTNG_LIVE_STREAM_QUIESCENT_NO_DATA:
+               /* Invalid state. */
+               BT_COMP_LOGF("Unexpected stream state \"QUIESCENT_NO_DATA\"");
+               bt_common_abort();
+       case LTTNG_LIVE_STREAM_EOF:
+               break;
+       }
+       return LTTNG_LIVE_ITERATOR_STATUS_OK;
+}
+
+/*
+ * For active no data stream, fetch next index. As a result of that it can
+ * become either:
+ * - quiescent: won't have events for a bit,
+ * - have data: need to get that data and produce the event,
+ * - have no data on this stream at this point: need to retry (AGAIN) or return
+ *   EOF.
+ */
+static
+enum lttng_live_iterator_status lttng_live_iterator_next_handle_one_no_data_stream(
+               struct lttng_live_msg_iter *lttng_live_msg_iter,
+               struct lttng_live_stream_iterator *lttng_live_stream)
+{
+       bt_logging_level log_level = lttng_live_msg_iter->log_level;
+       bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
+       enum lttng_live_iterator_status ret = LTTNG_LIVE_ITERATOR_STATUS_OK;
+       enum lttng_live_stream_state orig_state = lttng_live_stream->state;
+       struct packet_index index;
+
+       if (lttng_live_stream->trace->metadata_stream_state ==
+                       LTTNG_LIVE_METADATA_STREAM_STATE_NEEDED) {
+               BT_COMP_LOGD("Need to get an update for the metadata stream before proceeding further with this stream: "
+                       "stream-name=\"%s\"", lttng_live_stream->name->str);
+               ret = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE;
+               goto end;
+       }
+
+       if (lttng_live_stream->trace->session->new_streams_needed) {
+               BT_COMP_LOGD("Need to get an update of all streams before proceeding further with this stream: "
+                       "stream-name=\"%s\"", lttng_live_stream->name->str);
+               ret = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE;
+               goto end;
+       }
+
+       if (lttng_live_stream->state != LTTNG_LIVE_STREAM_ACTIVE_NO_DATA &&
+                       lttng_live_stream->state != LTTNG_LIVE_STREAM_QUIESCENT_NO_DATA) {
+               goto end;
+       }
+       ret = lttng_live_get_next_index(lttng_live_msg_iter, lttng_live_stream,
+                       &index);
+       if (ret != LTTNG_LIVE_ITERATOR_STATUS_OK) {
+               goto end;
+       }
+
+       BT_ASSERT_DBG(lttng_live_stream->state != LTTNG_LIVE_STREAM_EOF);
+
+       if (lttng_live_stream->state == LTTNG_LIVE_STREAM_QUIESCENT) {
+               uint64_t last_inact_ts = lttng_live_stream->last_inactivity_ts.value,
+                        curr_inact_ts = lttng_live_stream->current_inactivity_ts;
+
+               if (orig_state == LTTNG_LIVE_STREAM_QUIESCENT_NO_DATA &&
+                               last_inact_ts == curr_inact_ts) {
+                       /*
+                        * Because the stream is in the QUIESCENT_NO_DATA
+                        * state, we can assert that the last_inactivity_ts was
+                        * set and can be safely used in the `if` above.
+                        */
+                       BT_ASSERT(lttng_live_stream->last_inactivity_ts.is_set);
+
+                       ret = LTTNG_LIVE_ITERATOR_STATUS_AGAIN;
+                       LTTNG_LIVE_LOGD_STREAM_ITER(lttng_live_stream);
+               } else {
+                       ret = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE;
+               }
+               goto end;
+       }
+
+       lttng_live_stream->base_offset = index.offset;
+       lttng_live_stream->offset = index.offset;
+       lttng_live_stream->len = index.packet_size / CHAR_BIT;
+
+       BT_COMP_LOGD("Setting live stream reading info: stream-name=\"%s\", "
+                       "viewer-stream-id=%" PRIu64 ", stream-base-offset=%" PRIu64
+                       ", stream-offset=%" PRIu64 ", stream-len=%" PRIu64,
+                       lttng_live_stream->name->str,
+                       lttng_live_stream->viewer_stream_id,
+                       lttng_live_stream->base_offset,
+                       lttng_live_stream->offset, lttng_live_stream->len);
+
+end:
+       if (ret == LTTNG_LIVE_ITERATOR_STATUS_OK) {
+               ret = lttng_live_iterator_next_check_stream_state(lttng_live_stream);
+       }
+       return ret;
+}
+
+/*
+ * Creation of the message requires the ctf trace class to be created
+ * beforehand, but the live protocol gives us all streams (including metadata)
+ * at once. So we split it in three steps: getting streams, getting metadata
+ * (which creates the ctf trace class), and then creating the per-stream
+ * messages.
+ */
+static
+enum lttng_live_iterator_status lttng_live_get_session(
+               struct lttng_live_msg_iter *lttng_live_msg_iter,
+               struct lttng_live_session *session)
+{
+       bt_logging_level log_level = lttng_live_msg_iter->log_level;
+       bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
+       enum lttng_live_iterator_status status;
+       uint64_t trace_idx;
+
+       if (!session->attached) {
+               BT_COMP_LOGD("Attach to session: session-id=%" PRIu64,
+                       session->id);
+               enum lttng_live_viewer_status attach_status =
+                       lttng_live_session_attach(session,
+                               lttng_live_msg_iter->self_msg_iter);
+               if (attach_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
+                       if (lttng_live_graph_is_canceled(lttng_live_msg_iter)) {
+                               /*
+                                * Clear any causes appended in
+                                * `lttng_live_attach_session()` as we want to
+                                * return gracefully since the graph was
+                                * cancelled.
+                                */
+                               bt_current_thread_clear_error();
+                               status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN;
+                       } else {
+                               status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
+                               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                                       "Error attaching to LTTng live session");
+                       }
+                       goto end;
+               }
+       }
+
+       BT_COMP_LOGD("Updating all streams and metadata for session: "
+               "session-id=%" PRIu64 ", session-name=\"%s\"",
+               session->id, session->session_name->str);
+
+       status = lttng_live_session_get_new_streams(session,
+               lttng_live_msg_iter->self_msg_iter);
+       if (status != LTTNG_LIVE_ITERATOR_STATUS_OK &&
+                       status != LTTNG_LIVE_ITERATOR_STATUS_END) {
+               goto end;
+       }
+
+       trace_idx = 0;
+       while (trace_idx < session->traces->len) {
+               struct lttng_live_trace *trace =
+                       (lttng_live_trace *) g_ptr_array_index(session->traces, trace_idx);
+
+               status = lttng_live_metadata_update(trace);
+               switch (status) {
+               case LTTNG_LIVE_ITERATOR_STATUS_END:
+               case LTTNG_LIVE_ITERATOR_STATUS_OK:
+                       trace_idx++;
+                       break;
+               case LTTNG_LIVE_ITERATOR_STATUS_CONTINUE:
+               case LTTNG_LIVE_ITERATOR_STATUS_AGAIN:
+                       goto end;
+               default:
+                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                               "Error updating trace metadata: "
+                               "stream-iter-status=%s, trace-id=%" PRIu64,
+                               lttng_live_iterator_status_string(status),
+                               trace->id);
+                       goto end;
+               }
+       }
+
+       /*
+        * Now that we have the metadata we can initialize the downstream
+        * iterator.
+        */
+       status = lttng_live_lazy_msg_init(session,
+               lttng_live_msg_iter->self_msg_iter);
+
+end:
+       return status;
+}
+
+static
+void lttng_live_force_new_streams_and_metadata(struct lttng_live_msg_iter *lttng_live_msg_iter)
+{
+       uint64_t session_idx, trace_idx;
+       bt_logging_level log_level = lttng_live_msg_iter->log_level;
+       bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
+
+       for (session_idx = 0; session_idx < lttng_live_msg_iter->sessions->len;
+                       session_idx++) {
+               struct lttng_live_session *session =
+                       (lttng_live_session *) g_ptr_array_index(lttng_live_msg_iter->sessions, session_idx);
+               BT_COMP_LOGD("Force marking session as needing new streams: "
+                               "session-id=%" PRIu64, session->id);
+               session->new_streams_needed = true;
+               for (trace_idx = 0; trace_idx < session->traces->len;
+                               trace_idx++) {
+                       struct lttng_live_trace *trace =
+                               (lttng_live_trace *) g_ptr_array_index(session->traces, trace_idx);
+                       BT_COMP_LOGD("Force marking trace metadata state as needing an update: "
+                                       "session-id=%" PRIu64 ", trace-id=%" PRIu64,
+                                       session->id, trace->id);
+
+                       BT_ASSERT(trace->metadata_stream_state !=
+                               LTTNG_LIVE_METADATA_STREAM_STATE_CLOSED);
+
+                       trace->metadata_stream_state = LTTNG_LIVE_METADATA_STREAM_STATE_NEEDED;
+               }
+       }
+}
+
+static
+enum lttng_live_iterator_status
+lttng_live_iterator_handle_new_streams_and_metadata(
+               struct lttng_live_msg_iter *lttng_live_msg_iter)
+{
+       enum lttng_live_iterator_status status;
+       enum lttng_live_viewer_status viewer_status;
+       bt_logging_level log_level = lttng_live_msg_iter->log_level;
+       bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
+       uint64_t session_idx = 0, nr_sessions_opened = 0;
+       struct lttng_live_session *session;
+       enum session_not_found_action sess_not_found_act =
+               lttng_live_msg_iter->lttng_live_comp->params.sess_not_found_act;
+
+       BT_COMP_LOGD("Update data and metadata of all sessions: "
+               "live-msg-iter-addr=%p", lttng_live_msg_iter);
+       /*
+        * In a remotely distant future, we could add a "new
+        * session" flag to the protocol, which would tell us that we
+        * need to query for new sessions even though we have sessions
+        * currently ongoing.
+        */
+       if (lttng_live_msg_iter->sessions->len == 0) {
+               if (sess_not_found_act != SESSION_NOT_FOUND_ACTION_CONTINUE) {
+                       BT_COMP_LOGD("No session found. Exiting in accordance with the `session-not-found-action` parameter");
+                       status = LTTNG_LIVE_ITERATOR_STATUS_END;
+                       goto end;
+               } else {
+                       BT_COMP_LOGD("No session found. Try creating a new one in accordance with the `session-not-found-action` parameter");
+                       /*
+                        * Retry to create a viewer session for the requested
+                        * session name.
+                        */
+                       viewer_status = lttng_live_create_viewer_session(lttng_live_msg_iter);
+                       if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
+                               if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
+                                       status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
+                                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                                               "Error creating LTTng live viewer session");
+                               } else if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
+                                       status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN;
+                               } else {
+                                       bt_common_abort();
+                               }
+                               goto end;
+                       }
+               }
+       }
+
+       for (session_idx = 0; session_idx < lttng_live_msg_iter->sessions->len;
+                       session_idx++) {
+               session = (lttng_live_session *) g_ptr_array_index(lttng_live_msg_iter->sessions,
+                               session_idx);
+               status = lttng_live_get_session(lttng_live_msg_iter, session);
+               switch (status) {
+               case LTTNG_LIVE_ITERATOR_STATUS_OK:
+                       break;
+               case LTTNG_LIVE_ITERATOR_STATUS_END:
+                       status = LTTNG_LIVE_ITERATOR_STATUS_OK;
+                       break;
+               default:
+                       goto end;
+               }
+               if (!session->closed) {
+                       nr_sessions_opened++;
+               }
+       }
+
+       if (sess_not_found_act != SESSION_NOT_FOUND_ACTION_CONTINUE &&
+                       nr_sessions_opened == 0) {
+               status = LTTNG_LIVE_ITERATOR_STATUS_END;
+       } else {
+               status = LTTNG_LIVE_ITERATOR_STATUS_OK;
+       }
+
+end:
+       return status;
+}
+
+static
+enum lttng_live_iterator_status emit_inactivity_message(
+               struct lttng_live_msg_iter *lttng_live_msg_iter,
+               struct lttng_live_stream_iterator *stream_iter,
+               const bt_message **message, uint64_t timestamp)
+{
+       enum lttng_live_iterator_status ret = LTTNG_LIVE_ITERATOR_STATUS_OK;
+       bt_logging_level log_level = lttng_live_msg_iter->log_level;
+       bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
+       bt_message *msg = NULL;
+
+       BT_ASSERT(stream_iter->trace->clock_class);
+
+       BT_COMP_LOGD("Emitting inactivity message for stream: ctf-stream-id=%" PRIu64
+               ", viewer-stream-id=%" PRIu64 ", timestamp=%" PRIu64,
+               stream_iter->ctf_stream_class_id.value,
+               stream_iter->viewer_stream_id, timestamp);
+
+       msg = bt_message_message_iterator_inactivity_create(
+               lttng_live_msg_iter->self_msg_iter,
+               stream_iter->trace->clock_class, timestamp);
+       if (!msg) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Error emitting message iterator inactivity message");
+               goto error;
+       }
+
+       *message = msg;
+end:
+       return ret;
+
+error:
+       ret = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
+       bt_message_put_ref(msg);
+       goto end;
+}
+
+static
+enum lttng_live_iterator_status lttng_live_iterator_next_handle_one_quiescent_stream(
+               struct lttng_live_msg_iter *lttng_live_msg_iter,
+               struct lttng_live_stream_iterator *lttng_live_stream,
+               const bt_message **message)
+{
+       enum lttng_live_iterator_status ret = LTTNG_LIVE_ITERATOR_STATUS_OK;
+
+       if (lttng_live_stream->state != LTTNG_LIVE_STREAM_QUIESCENT) {
+               return LTTNG_LIVE_ITERATOR_STATUS_OK;
+       }
+
+       /*
+        * Check if we already sent an inactivty message downstream for this
+        * `current_inactivity_ts` value.
+        */
+       if (lttng_live_stream->last_inactivity_ts.is_set &&
+                       lttng_live_stream->current_inactivity_ts ==
+                               lttng_live_stream->last_inactivity_ts.value) {
+               lttng_live_stream_iterator_set_state(lttng_live_stream,
+                       LTTNG_LIVE_STREAM_QUIESCENT_NO_DATA);
+
+               ret = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE;
+               goto end;
+       }
+
+       ret = emit_inactivity_message(lttng_live_msg_iter, lttng_live_stream,
+               message, lttng_live_stream->current_inactivity_ts);
+
+       lttng_live_stream->last_inactivity_ts.value =
+               lttng_live_stream->current_inactivity_ts;
+       lttng_live_stream->last_inactivity_ts.is_set = true;
+end:
+       return ret;
+}
+
+static
+int live_get_msg_ts_ns(struct lttng_live_stream_iterator *stream_iter,
+               struct lttng_live_msg_iter *lttng_live_msg_iter,
+               const bt_message *msg, int64_t last_msg_ts_ns,
+               int64_t *ts_ns)
+{
+       const bt_clock_class *clock_class = NULL;
+       const bt_clock_snapshot *clock_snapshot = NULL;
+       int ret = 0;
+       bt_logging_level log_level = lttng_live_msg_iter->log_level;
+       bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
+
+       BT_ASSERT_DBG(msg);
+       BT_ASSERT_DBG(ts_ns);
+
+       BT_COMP_LOGD("Getting message's timestamp: iter-data-addr=%p, msg-addr=%p, "
+               "last-msg-ts=%" PRId64, lttng_live_msg_iter, msg,
+               last_msg_ts_ns);
+
+       switch (bt_message_get_type(msg)) {
+       case BT_MESSAGE_TYPE_EVENT:
+               clock_class = bt_message_event_borrow_stream_class_default_clock_class_const(
+                               msg);
+               BT_ASSERT_DBG(clock_class);
+
+               clock_snapshot = bt_message_event_borrow_default_clock_snapshot_const(
+                       msg);
+               break;
+       case BT_MESSAGE_TYPE_PACKET_BEGINNING:
+               clock_class = bt_message_packet_beginning_borrow_stream_class_default_clock_class_const(
+                       msg);
+               BT_ASSERT(clock_class);
+
+               clock_snapshot = bt_message_packet_beginning_borrow_default_clock_snapshot_const(
+                       msg);
+               break;
+       case BT_MESSAGE_TYPE_PACKET_END:
+               clock_class = bt_message_packet_end_borrow_stream_class_default_clock_class_const(
+                       msg);
+               BT_ASSERT(clock_class);
+
+               clock_snapshot = bt_message_packet_end_borrow_default_clock_snapshot_const(
+                       msg);
+               break;
+       case BT_MESSAGE_TYPE_DISCARDED_EVENTS:
+               clock_class = bt_message_discarded_events_borrow_stream_class_default_clock_class_const(
+                       msg);
+               BT_ASSERT(clock_class);
+
+               clock_snapshot = bt_message_discarded_events_borrow_beginning_default_clock_snapshot_const(
+                       msg);
+               break;
+       case BT_MESSAGE_TYPE_DISCARDED_PACKETS:
+               clock_class = bt_message_discarded_packets_borrow_stream_class_default_clock_class_const(
+                       msg);
+               BT_ASSERT(clock_class);
+
+               clock_snapshot = bt_message_discarded_packets_borrow_beginning_default_clock_snapshot_const(
+                       msg);
+               break;
+       case BT_MESSAGE_TYPE_MESSAGE_ITERATOR_INACTIVITY:
+               clock_snapshot = bt_message_message_iterator_inactivity_borrow_clock_snapshot_const(
+                       msg);
+               break;
+       default:
+               /* All the other messages have a higher priority */
+               BT_COMP_LOGD_STR("Message has no timestamp: using the last message timestamp.");
+               *ts_ns = last_msg_ts_ns;
+               goto end;
+       }
+
+       clock_class = bt_clock_snapshot_borrow_clock_class_const(clock_snapshot);
+       BT_ASSERT_DBG(clock_class);
+
+       ret = bt_clock_snapshot_get_ns_from_origin(clock_snapshot, ts_ns);
+       if (ret) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Cannot get nanoseconds from Epoch of clock snapshot: "
+                       "clock-snapshot-addr=%p", clock_snapshot);
+               goto error;
+       }
+
+       goto end;
+
+error:
+       ret = -1;
+
+end:
+       if (ret == 0) {
+               BT_COMP_LOGD("Found message's timestamp: "
+                       "iter-data-addr=%p, msg-addr=%p, "
+                       "last-msg-ts=%" PRId64 ", ts=%" PRId64,
+                       lttng_live_msg_iter, msg, last_msg_ts_ns, *ts_ns);
+       }
+
+       return ret;
+}
+
+static
+enum lttng_live_iterator_status lttng_live_iterator_next_handle_one_active_data_stream(
+               struct lttng_live_msg_iter *lttng_live_msg_iter,
+               struct lttng_live_stream_iterator *lttng_live_stream,
+               const bt_message **message)
+{
+       enum lttng_live_iterator_status ret = LTTNG_LIVE_ITERATOR_STATUS_OK;
+       bt_logging_level log_level = lttng_live_msg_iter->log_level;
+       bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
+       enum ctf_msg_iter_status status;
+       uint64_t session_idx, trace_idx;
+
+       for (session_idx = 0; session_idx < lttng_live_msg_iter->sessions->len;
+                       session_idx++) {
+               struct lttng_live_session *session =
+                       (lttng_live_session *) g_ptr_array_index(lttng_live_msg_iter->sessions, session_idx);
+
+               if (session->new_streams_needed) {
+                       BT_COMP_LOGD("Need an update for streams: "
+                                       "session-id=%" PRIu64, session->id);
+                       ret = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE;
+                       goto end;
+               }
+               for (trace_idx = 0; trace_idx < session->traces->len;
+                               trace_idx++) {
+                       struct lttng_live_trace *trace =
+                               (lttng_live_trace *) g_ptr_array_index(session->traces, trace_idx);
+                       if (trace->metadata_stream_state == LTTNG_LIVE_METADATA_STREAM_STATE_NEEDED) {
+                               BT_COMP_LOGD("Need an update for metadata stream: "
+                                               "session-id=%" PRIu64 ", trace-id=%" PRIu64,
+                                               session->id, trace->id);
+                               ret = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE;
+                               goto end;
+                       }
+               }
+       }
+
+       if (lttng_live_stream->state != LTTNG_LIVE_STREAM_ACTIVE_DATA) {
+               ret = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Invalid state of live stream iterator"
+                       "stream-iter-status=%s",
+                       lttng_live_stream_state_string(lttng_live_stream->state));
+               goto end;
+       }
+
+       status = ctf_msg_iter_get_next_message(
+               lttng_live_stream->msg_iter, message);
+       switch (status) {
+       case CTF_MSG_ITER_STATUS_EOF:
+               ret = LTTNG_LIVE_ITERATOR_STATUS_END;
+               break;
+       case CTF_MSG_ITER_STATUS_OK:
+               ret = LTTNG_LIVE_ITERATOR_STATUS_OK;
+               break;
+       case CTF_MSG_ITER_STATUS_AGAIN:
+               /*
+                * Continue immediately (end of packet). The next
+                * get_index may return AGAIN to delay the following
+                * attempt.
+                */
+               ret = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE;
+               break;
+       case CTF_MSG_ITER_STATUS_ERROR:
+       default:
+               ret = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "CTF message iterator failed to get next message: "
+                       "msg-iter=%p, msg-iter-status=%s",
+                       lttng_live_stream->msg_iter,
+                       ctf_msg_iter_status_string(status));
+               break;
+       }
+
+end:
+       return ret;
+}
+
+static
+enum lttng_live_iterator_status lttng_live_iterator_close_stream(
+               struct lttng_live_msg_iter *lttng_live_msg_iter,
+               struct lttng_live_stream_iterator *stream_iter,
+               const bt_message **curr_msg)
+{
+       enum lttng_live_iterator_status live_status =
+               LTTNG_LIVE_ITERATOR_STATUS_OK;
+       bt_logging_level log_level = lttng_live_msg_iter->log_level;
+       bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
+
+       BT_COMP_LOGD("Closing live stream iterator: stream-name=\"%s\", "
+                       "viewer-stream-id=%" PRIu64, stream_iter->name->str,
+                       stream_iter->viewer_stream_id);
+
+       /*
+        * The viewer has hung up on us so we are closing the stream. The
+        * `ctf_msg_iter` should simply realize that it needs to close the
+        * stream properly by emitting the necessary stream end message.
+        */
+       enum ctf_msg_iter_status status = ctf_msg_iter_get_next_message(
+               stream_iter->msg_iter, curr_msg);
+
+       if (status == CTF_MSG_ITER_STATUS_ERROR) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Error getting the next message from CTF message iterator");
+               live_status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
+               goto end;
+       } else if (status == CTF_MSG_ITER_STATUS_EOF) {
+               BT_COMP_LOGI("Reached the end of the live stream iterator.");
+               live_status = LTTNG_LIVE_ITERATOR_STATUS_END;
+               goto end;
+       }
+
+       BT_ASSERT(status == CTF_MSG_ITER_STATUS_OK);
+
+end:
+       return live_status;
+}
+
+/*
+ * helper function:
+ *            handle_no_data_streams()
+ *              retry:
+ *                - for each ACTIVE_NO_DATA stream:
+ *                  - query relayd for stream data, or quiescence info.
+ *                    - if need metadata, get metadata, goto retry.
+ *                    - if new stream, get new stream as ACTIVE_NO_DATA, goto retry
+ *                  - if quiescent, move to QUIESCENT streams
+ *                  - if fetched data, move to ACTIVE_DATA streams
+ *                (at this point each stream either has data, or is quiescent)
+ *
+ *
+ * iterator_next:
+ *            handle_new_streams_and_metadata()
+ *                  - query relayd for known streams, add them as ACTIVE_NO_DATA
+ *                  - query relayd for metadata
+ *
+ *            call handle_active_no_data_streams()
+ *
+ *            handle_quiescent_streams()
+ *                - if at least one stream is ACTIVE_DATA:
+ *                  - peek stream event with lowest timestamp -> next_ts
+ *                  - for each quiescent stream
+ *                    - if next_ts >= quiescent end
+ *                      - set state to ACTIVE_NO_DATA
+ *                - else
+ *                  - for each quiescent stream
+ *                      - set state to ACTIVE_NO_DATA
+ *
+ *            call handle_active_no_data_streams()
+ *
+ *            handle_active_data_streams()
+ *                - if at least one stream is ACTIVE_DATA:
+ *                    - get stream event with lowest timestamp from heap
+ *                    - make that stream event the current message.
+ *                    - move this stream heap position to its next event
+ *                      - if we need to fetch data from relayd, move
+ *                        stream to ACTIVE_NO_DATA.
+ *                    - return OK
+ *                - return AGAIN
+ *
+ * end criterion: ctrl-c on client. If relayd exits or the session
+ * closes on the relay daemon side, we keep on waiting for streams.
+ * Eventually handle --end timestamp (also an end criterion).
+ *
+ * When disconnected from relayd: try to re-connect endlessly.
+ */
+static
+enum lttng_live_iterator_status lttng_live_iterator_next_msg_on_stream(
+               struct lttng_live_msg_iter *lttng_live_msg_iter,
+               struct lttng_live_stream_iterator *stream_iter,
+               const bt_message **curr_msg)
+{
+       bt_logging_level log_level = lttng_live_msg_iter->log_level;
+       bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
+       enum lttng_live_iterator_status live_status;
+
+       BT_COMP_LOGD("Advancing live stream iterator until next message if possible: "
+               "stream-name=\"%s\", viewer-stream-id=%" PRIu64,
+               stream_iter->name->str, stream_iter->viewer_stream_id);
+
+       if (stream_iter->has_stream_hung_up) {
+               /*
+                * The stream has hung up and the stream was properly closed
+                * during the last call to the current function. Return _END
+                * status now so that this stream iterator is removed for the
+                * stream iterator list.
+                */
+               live_status = LTTNG_LIVE_ITERATOR_STATUS_END;
+               goto end;
+       }
+
+retry:
+       LTTNG_LIVE_LOGD_STREAM_ITER(stream_iter);
+
+       /*
+        * Make sure we have the most recent metadata and possibly some new
+        * streams.
+        */
+       live_status = lttng_live_iterator_handle_new_streams_and_metadata(
+               lttng_live_msg_iter);
+       if (live_status != LTTNG_LIVE_ITERATOR_STATUS_OK) {
+               goto end;
+       }
+
+       live_status = lttng_live_iterator_next_handle_one_no_data_stream(
+               lttng_live_msg_iter, stream_iter);
+       if (live_status != LTTNG_LIVE_ITERATOR_STATUS_OK) {
+               if (live_status == LTTNG_LIVE_ITERATOR_STATUS_END) {
+                       /*
+                        * We overwrite `live_status` since `curr_msg` is
+                        * likely set to a valid message in this function.
+                        */
+                       live_status = lttng_live_iterator_close_stream(
+                               lttng_live_msg_iter, stream_iter, curr_msg);
+               }
+               goto end;
+       }
+
+       live_status = lttng_live_iterator_next_handle_one_quiescent_stream(
+               lttng_live_msg_iter, stream_iter, curr_msg);
+       if (live_status != LTTNG_LIVE_ITERATOR_STATUS_OK) {
+               BT_ASSERT(!*curr_msg);
+               goto end;
+       }
+       if (*curr_msg) {
+               goto end;
+       }
+       live_status = lttng_live_iterator_next_handle_one_active_data_stream(
+               lttng_live_msg_iter, stream_iter, curr_msg);
+       if (live_status != LTTNG_LIVE_ITERATOR_STATUS_OK) {
+               BT_ASSERT(!*curr_msg);
+       }
+
+end:
+       if (live_status == LTTNG_LIVE_ITERATOR_STATUS_CONTINUE) {
+               BT_COMP_LOGD("Ask the relay daemon for an updated view of the data and metadata streams");
+               goto retry;
+       }
+
+       BT_COMP_LOGD("Returning from advancing live stream iterator: status=%s, "
+                       "stream-name=\"%s\", viewer-stream-id=%" PRIu64,
+                       lttng_live_iterator_status_string(live_status),
+                       stream_iter->name->str, stream_iter->viewer_stream_id);
+
+       return live_status;
+}
+
+static
+bool is_discarded_packet_or_event_message(const bt_message *msg)
+{
+       const enum bt_message_type msg_type = bt_message_get_type(msg);
+
+       return msg_type == BT_MESSAGE_TYPE_DISCARDED_EVENTS ||
+                       msg_type == BT_MESSAGE_TYPE_DISCARDED_PACKETS;
+}
+
+static
+enum lttng_live_iterator_status adjust_discarded_packets_message(
+               bt_self_message_iterator *iter,
+               const bt_stream *stream,
+               const bt_message *msg_in, bt_message **msg_out,
+               uint64_t new_begin_ts)
+{
+       enum lttng_live_iterator_status status = LTTNG_LIVE_ITERATOR_STATUS_OK;
+       enum bt_property_availability availability;
+       const bt_clock_snapshot *clock_snapshot;
+       uint64_t end_ts;
+       uint64_t count;
+
+       clock_snapshot = bt_message_discarded_packets_borrow_end_default_clock_snapshot_const(msg_in);
+       end_ts = bt_clock_snapshot_get_value(clock_snapshot);
+
+       availability = bt_message_discarded_packets_get_count(msg_in, &count);
+       BT_ASSERT_DBG(availability == BT_PROPERTY_AVAILABILITY_AVAILABLE);
+
+       *msg_out = bt_message_discarded_packets_create_with_default_clock_snapshots(
+               iter, stream, new_begin_ts, end_ts);
+       if (!*msg_out) {
+               status = LTTNG_LIVE_ITERATOR_STATUS_NOMEM;
+               goto end;
+       }
+
+       bt_message_discarded_packets_set_count(*msg_out, count);
+end:
+       return status;
+}
+
+static
+enum lttng_live_iterator_status adjust_discarded_events_message(
+               bt_self_message_iterator *iter,
+               const bt_stream *stream,
+               const bt_message *msg_in, bt_message **msg_out,
+               uint64_t new_begin_ts)
+{
+       enum lttng_live_iterator_status status = LTTNG_LIVE_ITERATOR_STATUS_OK;
+       enum bt_property_availability availability;
+       const bt_clock_snapshot *clock_snapshot;
+       uint64_t end_ts;
+       uint64_t count;
+
+       clock_snapshot = bt_message_discarded_events_borrow_end_default_clock_snapshot_const(msg_in);
+       end_ts = bt_clock_snapshot_get_value(clock_snapshot);
+
+       availability = bt_message_discarded_events_get_count(msg_in, &count);
+       BT_ASSERT_DBG(availability == BT_PROPERTY_AVAILABILITY_AVAILABLE);
+
+       *msg_out = bt_message_discarded_events_create_with_default_clock_snapshots(
+               iter, stream, new_begin_ts, end_ts);
+       if (!*msg_out) {
+               status = LTTNG_LIVE_ITERATOR_STATUS_NOMEM;
+               goto end;
+       }
+
+       bt_message_discarded_events_set_count(*msg_out, count);
+end:
+       return status;
+}
+
+static
+enum lttng_live_iterator_status handle_late_message(
+               struct lttng_live_msg_iter *lttng_live_msg_iter,
+               struct lttng_live_stream_iterator *stream_iter,
+               int64_t late_msg_ts_ns, const bt_message *late_msg)
+{
+       bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
+       bt_logging_level log_level = lttng_live_msg_iter->log_level;
+       const bt_clock_class *clock_class;
+       const bt_stream_class *stream_class;
+       enum bt_clock_class_cycles_to_ns_from_origin_status ts_ns_status;
+       int64_t last_inactivity_ts_ns;
+       enum lttng_live_iterator_status stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_OK;
+       enum lttng_live_iterator_status adjust_status;
+       bt_message *adjusted_message;
+
+       /*
+        * The timestamp of the current message is before the last message sent
+        * by this component. We CANNOT send it as is.
+        *
+        * The only expected scenario in which that could happen is the
+        * following, everything else is a bug in this component, relay deamon,
+        * or CTF parser.
+        *
+        * Expected scenario: The CTF message iterator emitted discarded
+        * packets and discarded events with synthesized beginning and end
+        * timestamps from the bounds of the last known packet and the newly
+        * decoded packet header. The CTF message iterator is not aware of
+        * stream inactivity beacons. Hence, we have to adjust the beginning
+        * timestamp of those types of messages if a stream signalled its
+        * inactivity up until _after_ the last known packet's beginning
+        * timestamp.
+        *
+        * Otherwise, the monotonicity guarantee of message timestamps would
+        * not be preserved.
+        *
+        * In short, the only scenario in which it's okay and fixable to
+        * received a late message is when:
+        *  1. the late message is a discarded packets or discarded events
+        *      message,
+        *  2. this stream produced an inactivity message downstream, and
+        *  3. the timestamp of the late message is within the inactivity
+        *      timespan we sent downstream through the inactivity message.
+        */
+
+       BT_COMP_LOGD("Handling late message on live stream iterator: "
+               "stream-name=\"%s\", viewer-stream-id=%" PRIu64,
+               stream_iter->name->str, stream_iter->viewer_stream_id);
+
+       if (!stream_iter->last_inactivity_ts.is_set) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Invalid live stream state: "
+                       "have a late message when no inactivity message "
+                       "was ever sent for that stream.");
+               stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
+               goto end;
+       }
+
+       if (!is_discarded_packet_or_event_message(late_msg)) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Invalid live stream state: "
+                       "have a late message that is not a packet discarded or "
+                       "event discarded message: late-msg-type=%s",
+                       bt_common_message_type_string(bt_message_get_type(late_msg)));
+               stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
+               goto end;
+       }
+
+       stream_class = bt_stream_borrow_class_const(stream_iter->stream);
+       clock_class = bt_stream_class_borrow_default_clock_class_const(stream_class);
+
+       ts_ns_status = bt_clock_class_cycles_to_ns_from_origin(clock_class,
+                       stream_iter->last_inactivity_ts.value, &last_inactivity_ts_ns);
+       if (ts_ns_status != BT_CLOCK_CLASS_CYCLES_TO_NS_FROM_ORIGIN_STATUS_OK) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,"Error converting last "
+                       "inactivity message timestamp to nanoseconds");
+               stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
+               goto end;
+       }
+
+       if (last_inactivity_ts_ns <= late_msg_ts_ns) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Invalid live stream state: "
+                       "have a late message that is none included in a stream "
+                       "inactivity timespan: last-inactivity-ts-ns=%" PRIu64
+                       "late-msg-ts-ns=%" PRIu64, last_inactivity_ts_ns, late_msg_ts_ns);
+               stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
+               goto end;
+       }
+
+       /*
+        * We now know that it's okay for this message to be late, we can now
+        * adjust its timestamp to ensure monotonicity.
+        */
+       BT_COMP_LOGD("Adjusting the timestamp of late message: late-msg-type=%s, "
+               "msg-new-ts-ns=%" PRIu64,
+               bt_common_message_type_string(bt_message_get_type(late_msg)),
+               stream_iter->last_inactivity_ts.value);
+       switch (bt_message_get_type(late_msg)) {
+       case BT_MESSAGE_TYPE_DISCARDED_EVENTS:
+               adjust_status = adjust_discarded_events_message(
+                               lttng_live_msg_iter->self_msg_iter,
+                               stream_iter->stream,
+                               late_msg, &adjusted_message,
+                               stream_iter->last_inactivity_ts.value);
+               break;
+       case BT_MESSAGE_TYPE_DISCARDED_PACKETS:
+               adjust_status = adjust_discarded_packets_message(
+                               lttng_live_msg_iter->self_msg_iter,
+                               stream_iter->stream,
+                               late_msg, &adjusted_message,
+                               stream_iter->last_inactivity_ts.value);
+               break;
+       default:
+               bt_common_abort();
+       }
+
+       if (adjust_status != LTTNG_LIVE_ITERATOR_STATUS_OK) {
+               stream_iter_status = adjust_status;
+               goto end;
+       }
+
+       BT_ASSERT_DBG(adjusted_message);
+       stream_iter->current_msg = adjusted_message;
+       stream_iter->current_msg_ts_ns = last_inactivity_ts_ns;
+       bt_message_put_ref(late_msg);
+
+end:
+       return stream_iter_status;
+}
+
+static
+enum lttng_live_iterator_status next_stream_iterator_for_trace(
+               struct lttng_live_msg_iter *lttng_live_msg_iter,
+               struct lttng_live_trace *live_trace,
+               struct lttng_live_stream_iterator **youngest_trace_stream_iter)
+{
+       struct lttng_live_stream_iterator *youngest_candidate_stream_iter = NULL;
+       bt_logging_level log_level = lttng_live_msg_iter->log_level;
+       bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
+       enum lttng_live_iterator_status stream_iter_status;;
+       int64_t youngest_candidate_msg_ts = INT64_MAX;
+       uint64_t stream_iter_idx;
+
+       BT_ASSERT_DBG(live_trace);
+       BT_ASSERT_DBG(live_trace->stream_iterators);
+
+       BT_COMP_LOGD("Finding the next stream iterator for trace: "
+               "trace-id=%" PRIu64, live_trace->id);
+       /*
+        * Update the current message of every stream iterators of this trace.
+        * The current msg of every stream must have a timestamp equal or
+        * larger than the last message returned by this iterator. We must
+        * ensure monotonicity.
+        */
+       stream_iter_idx = 0;
+       while (stream_iter_idx < live_trace->stream_iterators->len) {
+               bool stream_iter_is_ended = false;
+               struct lttng_live_stream_iterator *stream_iter =
+                       (lttng_live_stream_iterator *) g_ptr_array_index(live_trace->stream_iterators,
+                               stream_iter_idx);
+
+               /*
+                * If there is no current message for this stream, go fetch
+                * one.
+                */
+               while (!stream_iter->current_msg) {
+                       const bt_message *msg = NULL;
+                       int64_t curr_msg_ts_ns = INT64_MAX;
+
+                       stream_iter_status = lttng_live_iterator_next_msg_on_stream(
+                               lttng_live_msg_iter, stream_iter, &msg);
+
+                       if (stream_iter_status == LTTNG_LIVE_ITERATOR_STATUS_END) {
+                               stream_iter_is_ended = true;
+                               break;
+                       }
+
+                       if (stream_iter_status != LTTNG_LIVE_ITERATOR_STATUS_OK) {
+                               goto end;
+                       }
+
+                       BT_ASSERT_DBG(msg);
+
+                       BT_COMP_LOGD("Live stream iterator returned message: msg-type=%s, "
+                                       "stream-name=\"%s\", viewer-stream-id=%" PRIu64,
+                                       bt_common_message_type_string(bt_message_get_type(msg)),
+                                       stream_iter->name->str, stream_iter->viewer_stream_id);
+
+                       /*
+                        * Get the timestamp in nanoseconds from origin of this
+                        * messsage.
+                        */
+                       live_get_msg_ts_ns(stream_iter, lttng_live_msg_iter,
+                               msg, lttng_live_msg_iter->last_msg_ts_ns,
+                               &curr_msg_ts_ns);
+
+                       /*
+                        * Check if the message of the current live stream
+                        * iterator occurred at the exact same time or after the
+                        * last message returned by this component's message
+                        * iterator. If not, we need to handle it with care.
+                        */
+                       if (curr_msg_ts_ns >= lttng_live_msg_iter->last_msg_ts_ns) {
+                               stream_iter->current_msg = msg;
+                               stream_iter->current_msg_ts_ns = curr_msg_ts_ns;
+                       } else {
+                               /*
+                                * We received a message from the past. This
+                                * may be fixable but it can also be an error.
+                                */
+                               stream_iter_status = handle_late_message(
+                                               lttng_live_msg_iter, stream_iter,
+                                               curr_msg_ts_ns, msg);
+                               if (stream_iter_status != LTTNG_LIVE_ITERATOR_STATUS_OK) {
+                                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                                               "Late message could not be handled correctly: "
+                                               "lttng-live-msg-iter-addr=%p, "
+                                               "stream-name=\"%s\", "
+                                               "curr-msg-ts=%" PRId64
+                                               ", last-msg-ts=%" PRId64,
+                                               lttng_live_msg_iter,
+                                               stream_iter->name->str,
+                                               curr_msg_ts_ns,
+                                               lttng_live_msg_iter->last_msg_ts_ns);
+                                       stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
+                                       goto end;
+                               }
+                       }
+               }
+
+               BT_ASSERT_DBG(stream_iter != youngest_candidate_stream_iter);
+
+               if (!stream_iter_is_ended) {
+                       if (G_UNLIKELY(youngest_candidate_stream_iter == NULL) ||
+                                       stream_iter->current_msg_ts_ns < youngest_candidate_msg_ts) {
+                               /*
+                                * Update the current best candidate message
+                                * for the stream iterator of this live trace
+                                * to be forwarded downstream.
+                                */
+                               youngest_candidate_msg_ts = stream_iter->current_msg_ts_ns;
+                               youngest_candidate_stream_iter = stream_iter;
+                       } else if (stream_iter->current_msg_ts_ns == youngest_candidate_msg_ts) {
+                               /*
+                                * Order the messages in an arbitrary but
+                                * deterministic way.
+                                */
+                               BT_ASSERT_DBG(stream_iter != youngest_candidate_stream_iter);
+                               int ret = common_muxing_compare_messages(
+                                       stream_iter->current_msg,
+                                       youngest_candidate_stream_iter->current_msg);
+                               if (ret < 0) {
+                                       /*
+                                        * The `youngest_candidate_stream_iter->current_msg`
+                                        * should go first. Update the next
+                                        * iterator and the current timestamp.
+                                        */
+                                       youngest_candidate_msg_ts = stream_iter->current_msg_ts_ns;
+                                       youngest_candidate_stream_iter = stream_iter;
+                               } else if (ret == 0) {
+                                       /*
+                                        * Unable to pick which one should go
+                                        * first.
+                                        */
+                                       BT_COMP_LOGW("Cannot deterministically pick next live stream message iterator because they have identical next messages: "
+                                               "stream-iter-addr=%p"
+                                               "stream-iter-addr=%p",
+                                               stream_iter,
+                                               youngest_candidate_stream_iter);
+                               }
+                       }
+
+                       stream_iter_idx++;
+               } else {
+                       /*
+                        * The live stream iterator has ended. That
+                        * iterator is removed from the array, but
+                        * there is no need to increment
+                        * stream_iter_idx as
+                        * g_ptr_array_remove_index_fast replaces the
+                        * removed element with the array's last
+                        * element.
+                        */
+                       g_ptr_array_remove_index_fast(
+                               live_trace->stream_iterators,
+                               stream_iter_idx);
+               }
+       }
+
+       if (youngest_candidate_stream_iter) {
+               *youngest_trace_stream_iter = youngest_candidate_stream_iter;
+               stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_OK;
+       } else {
+               /*
+                * The only case where we don't have a candidate for this trace
+                * is if we reached the end of all the iterators.
+                */
+               BT_ASSERT(live_trace->stream_iterators->len == 0);
+               stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_END;
+       }
+
+end:
+       return stream_iter_status;
+}
+
+static
+enum lttng_live_iterator_status next_stream_iterator_for_session(
+               struct lttng_live_msg_iter *lttng_live_msg_iter,
+               struct lttng_live_session *session,
+               struct lttng_live_stream_iterator **youngest_session_stream_iter)
+{
+       bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
+       bt_logging_level log_level = lttng_live_msg_iter->log_level;
+       enum lttng_live_iterator_status stream_iter_status;
+       uint64_t trace_idx = 0;
+       int64_t youngest_candidate_msg_ts = INT64_MAX;
+       struct lttng_live_stream_iterator *youngest_candidate_stream_iter = NULL;
+
+       BT_COMP_LOGD("Finding the next stream iterator for session: "
+               "session-id=%" PRIu64, session->id);
+       /*
+        * Make sure we are attached to the session and look for new streams
+        * and metadata.
+        */
+       stream_iter_status = lttng_live_get_session(lttng_live_msg_iter, session);
+       if (stream_iter_status != LTTNG_LIVE_ITERATOR_STATUS_OK &&
+                       stream_iter_status != LTTNG_LIVE_ITERATOR_STATUS_CONTINUE &&
+                       stream_iter_status != LTTNG_LIVE_ITERATOR_STATUS_END) {
+               goto end;
+       }
+
+       BT_ASSERT_DBG(session->traces);
+
+       while (trace_idx < session->traces->len) {
+               bool trace_is_ended = false;
+               struct lttng_live_stream_iterator *stream_iter;
+               struct lttng_live_trace *trace =
+                       (lttng_live_trace *) g_ptr_array_index(session->traces, trace_idx);
+
+               stream_iter_status = next_stream_iterator_for_trace(
+                       lttng_live_msg_iter, trace, &stream_iter);
+               if (stream_iter_status == LTTNG_LIVE_ITERATOR_STATUS_END) {
+                       /*
+                        * All the live stream iterators for this trace are
+                        * ENDed. Remove the trace from this session.
+                        */
+                       trace_is_ended = true;
+               } else if (stream_iter_status != LTTNG_LIVE_ITERATOR_STATUS_OK) {
+                       goto end;
+               }
+
+               if (!trace_is_ended) {
+                       BT_ASSERT_DBG(stream_iter);
+
+                       if (G_UNLIKELY(youngest_candidate_stream_iter == NULL) ||
+                                       stream_iter->current_msg_ts_ns < youngest_candidate_msg_ts) {
+                               youngest_candidate_msg_ts = stream_iter->current_msg_ts_ns;
+                               youngest_candidate_stream_iter = stream_iter;
+                       } else if (stream_iter->current_msg_ts_ns == youngest_candidate_msg_ts) {
+                               /*
+                                * Order the messages in an arbitrary but
+                                * deterministic way.
+                                */
+                               int ret = common_muxing_compare_messages(
+                                       stream_iter->current_msg,
+                                       youngest_candidate_stream_iter->current_msg);
+                               if (ret < 0) {
+                                       /*
+                                        * The `youngest_candidate_stream_iter->current_msg`
+                                        * should go first. Update the next iterator
+                                        * and the current timestamp.
+                                        */
+                                       youngest_candidate_msg_ts = stream_iter->current_msg_ts_ns;
+                                       youngest_candidate_stream_iter = stream_iter;
+                               } else if (ret == 0) {
+                                       /* Unable to pick which one should go first. */
+                                       BT_COMP_LOGW("Cannot deterministically pick next live stream message iterator because they have identical next messages: "
+                                               "stream-iter-addr=%p" "stream-iter-addr=%p",
+                                               stream_iter, youngest_candidate_stream_iter);
+                               }
+                       }
+                       trace_idx++;
+               } else {
+                       /*
+                        * trace_idx is not incremented since
+                        * g_ptr_array_remove_index_fast replaces the
+                        * element at trace_idx with the array's last element.
+                        */
+                       g_ptr_array_remove_index_fast(session->traces,
+                               trace_idx);
+               }
+       }
+       if (youngest_candidate_stream_iter) {
+               *youngest_session_stream_iter = youngest_candidate_stream_iter;
+               stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_OK;
+       } else {
+               /*
+                * The only cases where we don't have a candidate for this
+                * trace is:
+                *  1. if we reached the end of all the iterators of all the
+                *  traces of this session,
+                *  2. if we never had live stream iterator in the first place.
+                *
+                * In either cases, we return END.
+                */
+               BT_ASSERT(session->traces->len == 0);
+               stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_END;
+       }
+end:
+       return stream_iter_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_message_iterator_class_next_method_status lttng_live_msg_iter_next(
+               bt_self_message_iterator *self_msg_it,
+               bt_message_array_const msgs, uint64_t capacity,
+               uint64_t *count)
+{
+       bt_message_iterator_class_next_method_status status;
+       enum lttng_live_viewer_status viewer_status;
+       struct lttng_live_msg_iter *lttng_live_msg_iter =
+               (struct lttng_live_msg_iter *) bt_self_message_iterator_get_data(self_msg_it);
+       struct lttng_live_component *lttng_live =
+               lttng_live_msg_iter->lttng_live_comp;
+       bt_self_component *self_comp = lttng_live_msg_iter->self_comp;
+       bt_logging_level log_level = lttng_live_msg_iter->log_level;
+       enum lttng_live_iterator_status stream_iter_status;
+       uint64_t session_idx;
+
+       *count = 0;
+
+       BT_ASSERT_DBG(lttng_live_msg_iter);
+
+       if (G_UNLIKELY(lttng_live_msg_iter->was_interrupted)) {
+               /*
+                * The iterator was interrupted in a previous call to the
+                * `_next()` method. We currently do not support generating
+                * messages after such event. The babeltrace2 CLI should never
+                * be running the graph after being interrupted. So this check
+                * is to prevent other graph users from using this live
+                * iterator in an messed up internal state.
+                */
+               status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR;
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Message iterator was interrupted during a previous call to the `next()` and currently does not support continuing after such event.");
+               goto end;
+       }
+
+       /*
+        * Clear all the invalid message reference that might be left over in
+        * the output array.
+        */
+       memset(msgs, 0, capacity * sizeof(*msgs));
+
+       /*
+        * If no session are exposed on the relay found at the url provided by
+        * the user, session count will be 0. In this case, we return status
+        * end to return gracefully.
+        */
+       if (lttng_live_msg_iter->sessions->len == 0) {
+               if (lttng_live->params.sess_not_found_act !=
+                               SESSION_NOT_FOUND_ACTION_CONTINUE) {
+                       status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_END;
+                       goto end;
+               } else {
+                       /*
+                        * The are no more active session for this session
+                        * name. Retry to create a viewer session for the
+                        * requested session name.
+                        */
+                       viewer_status = lttng_live_create_viewer_session(lttng_live_msg_iter);
+                       if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
+                               if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
+                                       status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR;
+                                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                                               "Error creating LTTng live viewer session");
+                               } else if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
+                                       status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_AGAIN;
+                               } else {
+                                       bt_common_abort();
+                               }
+                               goto end;
+                       }
+               }
+       }
+
+       if (lttng_live_msg_iter->active_stream_iter == 0) {
+               lttng_live_force_new_streams_and_metadata(lttng_live_msg_iter);
+       }
+
+       /*
+        * Here the muxing of message is done.
+        *
+        * We need to iterate over all the streams of all the traces of all the
+        * viewer sessions in order to get the message with the smallest
+        * timestamp. In this case, a session is a viewer session and there is
+        * one viewer session per consumer daemon. (UST 32bit, UST 64bit and/or
+        * kernel). Each viewer session can have multiple traces, for example,
+        * 64bit UST viewer sessions could have multiple per-pid traces.
+        *
+        * We iterate over the streams of each traces to update and see what is
+        * their next message's timestamp. From those timestamps, we select the
+        * message with the smallest timestamp as the best candidate message
+        * for that trace and do the same thing across all the sessions.
+        *
+        * We then compare the timestamp of best candidate message of all the
+        * sessions to pick the message with the smallest timestamp and we
+        * return it.
+        */
+       while (*count < capacity) {
+               struct lttng_live_stream_iterator *youngest_stream_iter = NULL,
+                         *candidate_stream_iter = NULL;
+               int64_t youngest_msg_ts_ns = INT64_MAX;
+
+               BT_ASSERT_DBG(lttng_live_msg_iter->sessions);
+               session_idx = 0;
+               while (session_idx < lttng_live_msg_iter->sessions->len) {
+                       struct lttng_live_session *session =
+                               (lttng_live_session *) g_ptr_array_index(lttng_live_msg_iter->sessions,
+                                       session_idx);
+
+                       /* Find the best candidate message to send downstream. */
+                       stream_iter_status = next_stream_iterator_for_session(
+                               lttng_live_msg_iter, session,
+                               &candidate_stream_iter);
+
+                       /* If we receive an END status, it means that either:
+                        * - Those traces never had active streams (UST with no
+                        *   data produced yet),
+                        * - All live stream iterators have ENDed.*/
+                       if (stream_iter_status == LTTNG_LIVE_ITERATOR_STATUS_END) {
+                               if (session->closed && session->traces->len == 0) {
+                                       /*
+                                        * Remove the session from the list.
+                                        * session_idx is not modified since
+                                        * g_ptr_array_remove_index_fast
+                                        * replaces the the removed element with
+                                        * the array's last element.
+                                        */
+                                       g_ptr_array_remove_index_fast(
+                                               lttng_live_msg_iter->sessions,
+                                               session_idx);
+                               } else {
+                                       session_idx++;
+                               }
+                               continue;
+                       }
+
+                       if (stream_iter_status != LTTNG_LIVE_ITERATOR_STATUS_OK) {
+                               goto return_status;
+                       }
+
+                       if (G_UNLIKELY(youngest_stream_iter == NULL) ||
+                                       candidate_stream_iter->current_msg_ts_ns < youngest_msg_ts_ns) {
+                               youngest_msg_ts_ns = candidate_stream_iter->current_msg_ts_ns;
+                               youngest_stream_iter = candidate_stream_iter;
+                       } else if (candidate_stream_iter->current_msg_ts_ns == youngest_msg_ts_ns) {
+                               /*
+                                * The currently selected message to be sent
+                                * downstream next has the exact same timestamp
+                                * that of the current candidate message. We
+                                * must break the tie in a predictable manner.
+                                */
+                               BT_COMP_LOGD_STR("Two of the next message candidates have the same timestamps, pick one deterministically.");
+                               /*
+                                * Order the messages in an arbitrary but
+                                * deterministic way.
+                                */
+                               int ret = common_muxing_compare_messages(
+                                       candidate_stream_iter->current_msg,
+                                       youngest_stream_iter->current_msg);
+                               if (ret < 0) {
+                                       /*
+                                        * The `candidate_stream_iter->current_msg`
+                                        * should go first. Update the next
+                                        * iterator and the current timestamp.
+                                        */
+                                       youngest_msg_ts_ns = candidate_stream_iter->current_msg_ts_ns;
+                                       youngest_stream_iter = candidate_stream_iter;
+                               } else if (ret == 0) {
+                                       /* Unable to pick which one should go first. */
+                                       BT_COMP_LOGW("Cannot deterministically pick next live stream message iterator because they have identical next messages: "
+                                               "next-stream-iter-addr=%p" "candidate-stream-iter-addr=%p",
+                                               youngest_stream_iter, candidate_stream_iter);
+                               }
+                       }
+
+                       session_idx++;
+               }
+
+               if (!youngest_stream_iter) {
+                       stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN;
+                       goto return_status;
+               }
+
+               BT_ASSERT_DBG(youngest_stream_iter->current_msg);
+               /* Ensure monotonicity. */
+               BT_ASSERT_DBG(lttng_live_msg_iter->last_msg_ts_ns <=
+                       youngest_stream_iter->current_msg_ts_ns);
+
+               /*
+                * Insert the next message to the message batch. This will set
+                * stream iterator current messsage to NULL so that next time
+                * we fetch the next message of that stream iterator
+                */
+               BT_MESSAGE_MOVE_REF(msgs[*count], youngest_stream_iter->current_msg);
+               (*count)++;
+
+               /* Update the last timestamp in nanoseconds sent downstream. */
+               lttng_live_msg_iter->last_msg_ts_ns = youngest_msg_ts_ns;
+               youngest_stream_iter->current_msg_ts_ns = INT64_MAX;
+
+               stream_iter_status = LTTNG_LIVE_ITERATOR_STATUS_OK;
+       }
+
+return_status:
+       switch (stream_iter_status) {
+       case LTTNG_LIVE_ITERATOR_STATUS_OK:
+       case LTTNG_LIVE_ITERATOR_STATUS_AGAIN:
+               /*
+                * If we gathered messages, return _OK even if the graph was
+                * interrupted. This allows for the components downstream to at
+                * least get the thoses messages. If the graph was indeed
+                * interrupted there should not be another _next() call as the
+                * application will tear down the graph. This component class
+                * doesn't support restarting after an interruption.
+                */
+               if (*count > 0) {
+                       status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_OK;
+               } else {
+                       status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_AGAIN;
+               }
+               break;
+       case LTTNG_LIVE_ITERATOR_STATUS_END:
+               status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_END;
+               break;
+       case LTTNG_LIVE_ITERATOR_STATUS_NOMEM:
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Memory error preparing the next batch of messages: "
+                       "live-iter-status=%s",
+                       lttng_live_iterator_status_string(stream_iter_status));
+               status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_MEMORY_ERROR;
+               break;
+       case LTTNG_LIVE_ITERATOR_STATUS_ERROR:
+       case LTTNG_LIVE_ITERATOR_STATUS_INVAL:
+       case LTTNG_LIVE_ITERATOR_STATUS_UNSUPPORTED:
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Error preparing the next batch of messages: "
+                       "live-iter-status=%s",
+                       lttng_live_iterator_status_string(stream_iter_status));
+
+               status = BT_MESSAGE_ITERATOR_CLASS_NEXT_METHOD_STATUS_ERROR;
+               /* Put all existing messages on error. */
+               put_messages(msgs, *count);
+               break;
+       default:
+               bt_common_abort();
+       }
+
+end:
+       return status;
+}
+
+static
+struct lttng_live_msg_iter *lttng_live_msg_iter_create(
+               struct lttng_live_component *lttng_live_comp,
+               bt_self_message_iterator *self_msg_it)
+{
+       bt_self_component *self_comp = lttng_live_comp->self_comp;
+       bt_logging_level log_level = lttng_live_comp->log_level;
+
+       struct lttng_live_msg_iter *lttng_live_msg_iter =
+               g_new0(struct lttng_live_msg_iter, 1);
+       if (!lttng_live_msg_iter) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Failed to allocate lttng_live_msg_iter");
+               goto end;
+       }
+
+       lttng_live_msg_iter->log_level = lttng_live_comp->log_level;
+       lttng_live_msg_iter->self_comp = lttng_live_comp->self_comp;
+       lttng_live_msg_iter->lttng_live_comp = lttng_live_comp;
+       lttng_live_msg_iter->self_msg_iter = self_msg_it;
+
+       lttng_live_msg_iter->active_stream_iter = 0;
+       lttng_live_msg_iter->last_msg_ts_ns = INT64_MIN;
+       lttng_live_msg_iter->was_interrupted = false;
+
+       lttng_live_msg_iter->sessions = g_ptr_array_new_with_free_func(
+               (GDestroyNotify) lttng_live_destroy_session);
+       BT_ASSERT(lttng_live_msg_iter->sessions);
+
+end:
+       return lttng_live_msg_iter;
+
+}
+
+BT_HIDDEN
+bt_message_iterator_class_initialize_method_status lttng_live_msg_iter_init(
+               bt_self_message_iterator *self_msg_it,
+               bt_self_message_iterator_configuration *config,
+               bt_self_component_port_output *self_port)
+{
+       bt_message_iterator_class_initialize_method_status status;
+       struct lttng_live_component *lttng_live;
+       struct lttng_live_msg_iter *lttng_live_msg_iter;
+       enum lttng_live_viewer_status viewer_status;
+       bt_logging_level log_level;
+       bt_self_component *self_comp =
+               bt_self_message_iterator_borrow_component(self_msg_it);
+
+       lttng_live = (lttng_live_component *) bt_self_component_get_data(self_comp);
+       log_level = lttng_live->log_level;
+       self_comp = lttng_live->self_comp;
+
+
+       /* There can be only one downstream iterator at the same time. */
+       BT_ASSERT(!lttng_live->has_msg_iter);
+       lttng_live->has_msg_iter = true;
+
+       lttng_live_msg_iter = lttng_live_msg_iter_create(lttng_live,
+               self_msg_it);
+       if (!lttng_live_msg_iter) {
+               status = BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Failed to create lttng_live_msg_iter");
+               goto error;
+       }
+
+        viewer_status = live_viewer_connection_create(self_comp, NULL,
+               log_level, lttng_live->params.url->str, false,
+               lttng_live_msg_iter, &lttng_live_msg_iter->viewer_connection);
+       if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
+               if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
+                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                               "Failed to create viewer connection");
+               } else if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
+                       /*
+                        * Interruption in the _iter_init() method is not
+                        * supported. Return an error.
+                        */
+                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                               "Interrupted while creating viewer connection");
+               }
+               goto error;
+       }
+
+       viewer_status = lttng_live_create_viewer_session(lttng_live_msg_iter);
+       if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
+               if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
+                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                               "Failed to create viewer session");
+               } else if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
+                       /*
+                        * Interruption in the _iter_init() method is not
+                        * supported. Return an error.
+                        */
+                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                               "Interrupted when creating viewer session");
+               }
+               goto error;
+       }
+
+       if (lttng_live_msg_iter->sessions->len == 0) {
+               switch (lttng_live->params.sess_not_found_act) {
+               case SESSION_NOT_FOUND_ACTION_CONTINUE:
+                       BT_COMP_LOGI("Unable to connect to the requested live viewer session. Keep trying to connect because of "
+                               "%s=\"%s\" component parameter: url=\"%s\"",
+                               SESS_NOT_FOUND_ACTION_PARAM,
+                               SESS_NOT_FOUND_ACTION_CONTINUE_STR,
+                               lttng_live->params.url->str);
+                       break;
+               case SESSION_NOT_FOUND_ACTION_FAIL:
+                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                               "Unable to connect to the requested live viewer session. Fail the message iterator initialization because of %s=\"%s\" "
+                               "component parameter: url =\"%s\"",
+                               SESS_NOT_FOUND_ACTION_PARAM,
+                               SESS_NOT_FOUND_ACTION_FAIL_STR,
+                               lttng_live->params.url->str);
+                       goto error;
+               case SESSION_NOT_FOUND_ACTION_END:
+                       BT_COMP_LOGI("Unable to connect to the requested live viewer session. End gracefully at the first _next() "
+                               "call because of %s=\"%s\" component parameter: "
+                               "url=\"%s\"", SESS_NOT_FOUND_ACTION_PARAM,
+                               SESS_NOT_FOUND_ACTION_END_STR,
+                               lttng_live->params.url->str);
+                       break;
+               default:
+                       bt_common_abort();
+               }
+       }
+
+       bt_self_message_iterator_set_data(self_msg_it, lttng_live_msg_iter);
+       status = BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_OK;
+       goto end;
+
+error:
+       status = BT_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
+       lttng_live_msg_iter_destroy(lttng_live_msg_iter);
+end:
+       return status;
+}
+
+static struct bt_param_validation_map_value_entry_descr list_sessions_params[] = {
+       { URL_PARAM, BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY,
+               { bt_param_validation_value_descr::string_t } },
+       BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
+};
+
+static
+bt_component_class_query_method_status lttng_live_query_list_sessions(
+               const bt_value *params, const bt_value **result,
+               bt_self_component_class *self_comp_class,
+               bt_logging_level log_level)
+{
+       bt_component_class_query_method_status status;
+       const bt_value *url_value = NULL;
+       const char *url;
+       struct live_viewer_connection *viewer_connection = NULL;
+       enum lttng_live_viewer_status viewer_status;
+       enum bt_param_validation_status validation_status;
+       gchar *validate_error = NULL;
+
+       validation_status = bt_param_validation_validate(params,
+               list_sessions_params, &validate_error);
+       if (validation_status == BT_PARAM_VALIDATION_STATUS_MEMORY_ERROR) {
+               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_MEMORY_ERROR;
+               goto error;
+       } else if (validation_status == BT_PARAM_VALIDATION_STATUS_VALIDATION_ERROR) {
+               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
+               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class, "%s",
+                       validate_error);
+               goto error;
+       }
+
+       url_value = bt_value_map_borrow_entry_value_const(params, URL_PARAM);
+       url = bt_value_string_get(url_value);
+
+       viewer_status = live_viewer_connection_create(NULL, self_comp_class,
+               log_level, url, true, NULL, &viewer_connection);
+       if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
+               if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
+                       BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
+                               "Failed to create viewer connection");
+                       status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
+               } else if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
+                       status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_AGAIN;
+               } else {
+                       bt_common_abort();
+               }
+               goto error;
+       }
+
+       status = live_viewer_connection_list_sessions(viewer_connection,
+               result);
+       if (status != BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_OK) {
+               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
+                       "Failed to list viewer sessions");
+               goto error;
+       }
+
+       goto end;
+
+error:
+       BT_VALUE_PUT_REF_AND_RESET(*result);
+
+       if (status >= 0) {
+               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
+       }
+
+end:
+       if (viewer_connection) {
+               live_viewer_connection_destroy(viewer_connection);
+       }
+
+       g_free(validate_error);
+
+       return status;
+}
+
+static
+bt_component_class_query_method_status lttng_live_query_support_info(
+               const bt_value *params, const bt_value **result,
+               bt_self_component_class *self_comp_class,
+               bt_logging_level log_level)
+{
+       bt_component_class_query_method_status status =
+               BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_OK;
+       const bt_value *input_type_value;
+       const bt_value *input_value;
+       double weight = 0;
+       struct bt_common_lttng_live_url_parts parts = { 0 };
+
+       /* Used by the logging macros */
+       __attribute__((unused)) bt_self_component *self_comp = NULL;
+
+       *result = NULL;
+       input_type_value = bt_value_map_borrow_entry_value_const(params,
+               "type");
+       if (!input_type_value) {
+               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
+                       "Missing expected `type` parameter.");
+               goto error;
+       }
+
+       if (!bt_value_is_string(input_type_value)) {
+               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
+                       "`type` parameter is not a string value.");
+               goto error;
+       }
+
+       if (strcmp(bt_value_string_get(input_type_value), "string") != 0) {
+               /* We don't handle file system paths */
+               goto create_result;
+       }
+
+       input_value = bt_value_map_borrow_entry_value_const(params, "input");
+       if (!input_value) {
+               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
+                       "Missing expected `input` parameter.");
+               goto error;
+       }
+
+       if (!bt_value_is_string(input_value)) {
+               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
+                       "`input` parameter is not a string value.");
+               goto error;
+       }
+
+       parts = bt_common_parse_lttng_live_url(bt_value_string_get(input_value),
+               NULL, 0);
+       if (parts.session_name) {
+               /*
+                * Looks pretty much like an LTTng live URL: we got the
+                * session name part, which forms a complete URL.
+                */
+               weight = .75;
+       }
+
+create_result:
+       *result = bt_value_real_create_init(weight);
+       if (!*result) {
+               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_MEMORY_ERROR;
+               goto error;
+       }
+
+       goto end;
+
+error:
+       if (status >= 0) {
+               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
+       }
+
+       BT_ASSERT(!*result);
+
+end:
+       bt_common_destroy_lttng_live_url_parts(&parts);
+       return status;
+}
+
+BT_HIDDEN
+bt_component_class_query_method_status lttng_live_query(
+               bt_self_component_class_source *comp_class,
+               bt_private_query_executor *priv_query_exec,
+               const char *object, const bt_value *params,
+               __attribute__((unused)) void *method_data,
+               const bt_value **result)
+{
+       bt_component_class_query_method_status status =
+               BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_OK;
+       bt_self_component *self_comp = NULL;
+       bt_self_component_class *self_comp_class =
+               bt_self_component_class_source_as_self_component_class(comp_class);
+       bt_logging_level log_level = bt_query_executor_get_logging_level(
+               bt_private_query_executor_as_query_executor_const(
+                       priv_query_exec));
+
+       if (strcmp(object, "sessions") == 0) {
+               status = lttng_live_query_list_sessions(params, result,
+                       self_comp_class, log_level);
+       } else if (strcmp(object, "babeltrace.support-info") == 0) {
+               status = lttng_live_query_support_info(params, result,
+                       self_comp_class, log_level);
+       } else {
+               BT_COMP_LOGI("Unknown query object `%s`", object);
+               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_UNKNOWN_OBJECT;
+               goto end;
+       }
+
+end:
+       return status;
+}
+
+static
+void lttng_live_component_destroy_data(struct lttng_live_component *lttng_live)
+{
+       if (!lttng_live) {
+               return;
+       }
+       if (lttng_live->params.url) {
+               g_string_free(lttng_live->params.url, TRUE);
+       }
+       g_free(lttng_live);
+}
+
+BT_HIDDEN
+void lttng_live_component_finalize(bt_self_component_source *component)
+{
+       lttng_live_component *data =
+               (lttng_live_component *) bt_self_component_get_data(
+                       bt_self_component_source_as_self_component(component));
+
+       if (!data) {
+               return;
+       }
+       lttng_live_component_destroy_data(data);
+}
+
+static
+enum session_not_found_action parse_session_not_found_action_param(
+       const bt_value *no_session_param)
+{
+       enum session_not_found_action action;
+       const char *no_session_act_str = bt_value_string_get(no_session_param);
+
+       if (strcmp(no_session_act_str, SESS_NOT_FOUND_ACTION_CONTINUE_STR) == 0) {
+               action = SESSION_NOT_FOUND_ACTION_CONTINUE;
+       } else if (strcmp(no_session_act_str, SESS_NOT_FOUND_ACTION_FAIL_STR) == 0) {
+               action = SESSION_NOT_FOUND_ACTION_FAIL;
+       } else {
+               BT_ASSERT(strcmp(no_session_act_str, SESS_NOT_FOUND_ACTION_END_STR) == 0);
+               action = SESSION_NOT_FOUND_ACTION_END;
+       }
+
+       return action;
+}
+
+static bt_param_validation_value_descr inputs_elem_descr{
+       bt_param_validation_value_descr::string_t,
+};
+
+static const char *sess_not_found_action_choices[] = {
+       SESS_NOT_FOUND_ACTION_CONTINUE_STR,
+       SESS_NOT_FOUND_ACTION_FAIL_STR,
+       SESS_NOT_FOUND_ACTION_END_STR,
+};
+
+static struct bt_param_validation_map_value_entry_descr params_descr[] = {
+       { INPUTS_PARAM, BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_MANDATORY,
+               { bt_param_validation_value_descr::array_t,
+                       1, 1, inputs_elem_descr } },
+       { SESS_NOT_FOUND_ACTION_PARAM, BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_OPTIONAL,
+               { bt_param_validation_value_descr::string_t,
+                       sess_not_found_action_choices } },
+       BT_PARAM_VALIDATION_MAP_VALUE_ENTRY_END
+};
+
+static
+bt_component_class_initialize_method_status lttng_live_component_create(
+               const bt_value *params,
+               bt_logging_level log_level,
+               bt_self_component *self_comp,
+               struct lttng_live_component **component)
+{
+       struct lttng_live_component *lttng_live = NULL;
+       const bt_value *inputs_value;
+       const bt_value *url_value;
+       const bt_value *value;
+       const char *url;
+       enum bt_param_validation_status validation_status;
+       gchar *validation_error = NULL;
+       bt_component_class_initialize_method_status status;
+
+       validation_status = bt_param_validation_validate(params, params_descr,
+               &validation_error);
+       if (validation_status == BT_PARAM_VALIDATION_STATUS_MEMORY_ERROR) {
+               status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
+               goto error;
+       } else if (validation_status == BT_PARAM_VALIDATION_STATUS_VALIDATION_ERROR) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp, "%s", validation_error);
+               status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR;
+               goto error;
+       }
+
+       lttng_live = g_new0(struct lttng_live_component, 1);
+       if (!lttng_live) {
+               status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
+               goto end;
+       }
+       lttng_live->log_level = log_level;
+       lttng_live->self_comp = self_comp;
+       lttng_live->max_query_size = MAX_QUERY_SIZE;
+       lttng_live->has_msg_iter = false;
+
+       inputs_value =
+               bt_value_map_borrow_entry_value_const(params, INPUTS_PARAM);
+       url_value =
+               bt_value_array_borrow_element_by_index_const(inputs_value, 0);
+       url = bt_value_string_get(url_value);
+
+       lttng_live->params.url = g_string_new(url);
+       if (!lttng_live->params.url) {
+               status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR;
+               goto error;
+       }
+
+       value = bt_value_map_borrow_entry_value_const(params,
+               SESS_NOT_FOUND_ACTION_PARAM);
+       if (value) {
+               lttng_live->params.sess_not_found_act =
+                       parse_session_not_found_action_param(value);
+       } else {
+               BT_COMP_LOGI("Optional `%s` parameter is missing: "
+                       "defaulting to `%s`.",
+                       SESS_NOT_FOUND_ACTION_PARAM,
+                       SESS_NOT_FOUND_ACTION_CONTINUE_STR);
+               lttng_live->params.sess_not_found_act =
+                       SESSION_NOT_FOUND_ACTION_CONTINUE;
+       }
+
+       status = BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK;
+       goto end;
+
+error:
+       lttng_live_component_destroy_data(lttng_live);
+       lttng_live = NULL;
+end:
+       g_free(validation_error);
+
+       *component = lttng_live;
+       return status;
+}
+
+BT_HIDDEN
+bt_component_class_initialize_method_status lttng_live_component_init(
+               bt_self_component_source *self_comp_src,
+               bt_self_component_source_configuration *config,
+               const bt_value *params,
+               __attribute__((unused)) void *init_method_data)
+{
+       struct lttng_live_component *lttng_live;
+       bt_component_class_initialize_method_status ret;
+       bt_self_component *self_comp =
+               bt_self_component_source_as_self_component(self_comp_src);
+       bt_logging_level log_level = bt_component_get_logging_level(
+               bt_self_component_as_component(self_comp));
+       bt_self_component_add_port_status add_port_status;
+
+       ret = lttng_live_component_create(params, log_level, self_comp, &lttng_live);
+       if (ret != BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK) {
+               goto error;
+       }
+
+       add_port_status = bt_self_component_source_add_output_port(
+               self_comp_src, "out", NULL, NULL);
+       if (add_port_status != BT_SELF_COMPONENT_ADD_PORT_STATUS_OK) {
+               ret = (bt_component_class_initialize_method_status) add_port_status;
+               goto end;
+       }
+
+       bt_self_component_set_data(self_comp, lttng_live);
+       goto end;
+
+error:
+       lttng_live_component_destroy_data(lttng_live);
+       lttng_live = NULL;
+end:
+       return ret;
+}
diff --git a/src/plugins/ctf/lttng-live/lttng-live.h b/src/plugins/ctf/lttng-live/lttng-live.h
deleted file mode 100644 (file)
index d0d5b15..0000000
+++ /dev/null
@@ -1,342 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2019 Francis Deslauriers <francis.deslauriers@efficios.com>
- * Copyright 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- * Copyright 2016 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * BabelTrace - LTTng-live client Component
- */
-
-#ifndef BABELTRACE_PLUGIN_CTF_LTTNG_LIVE_H
-#define BABELTRACE_PLUGIN_CTF_LTTNG_LIVE_H
-
-#include <stdbool.h>
-#include <stdint.h>
-
-#include <glib.h>
-
-#include <babeltrace2/babeltrace.h>
-
-#include "common/macros.h"
-#include "../common/metadata/decoder.h"
-#include "../common/msg-iter/msg-iter.h"
-#include "viewer-connection.h"
-
-struct lttng_live_component;
-struct lttng_live_session;
-struct lttng_live_msg_iter;
-
-enum lttng_live_stream_state {
-       /* This stream won't have data until some known time in the future. */
-       LTTNG_LIVE_STREAM_QUIESCENT,
-       /*
-        * This stream won't have data until some known time in the future and
-        * the message iterator inactivity message was already sent downstream.
-        */
-       LTTNG_LIVE_STREAM_QUIESCENT_NO_DATA, /* */
-       /* This stream has data ready to be consumed. */
-       LTTNG_LIVE_STREAM_ACTIVE_DATA,
-       /*
-        * This stream has no data left to consume. We should asked the relay
-        * for more.
-        */
-       LTTNG_LIVE_STREAM_ACTIVE_NO_DATA,
-       /* This stream won't have anymore data, ever. */
-       LTTNG_LIVE_STREAM_EOF,
-};
-
-/* Iterator over a live stream. */
-struct lttng_live_stream_iterator {
-       bt_logging_level log_level;
-       bt_self_component *self_comp;
-
-       /* Owned by this. */
-       bt_stream *stream;
-
-       /* Weak reference. */
-       struct lttng_live_trace *trace;
-
-       /*
-        * Since only a single iterator per viewer connection, we have
-        * only a single message iterator per stream.
-        */
-       struct ctf_msg_iter *msg_iter;
-
-       uint64_t viewer_stream_id;
-
-       struct {
-               bool is_set;
-               uint64_t value;
-       } ctf_stream_class_id;
-
-       /* base offset in current index. */
-       uint64_t base_offset;
-       /* len to read in current index. */
-       uint64_t len;
-       /* offset in current index. */
-       uint64_t offset;
-
-       /*
-        * Clock Snapshot value of the last message iterator inactivity message
-        * sent downstream.
-        */
-       struct {
-               bool is_set;
-               uint64_t value;
-       } last_inactivity_ts;
-
-       /*
-        * Clock Snapshot value of the current message iterator inactivity
-        * message we might want to send downstream.
-        */
-       uint64_t current_inactivity_ts;
-
-       enum lttng_live_stream_state state;
-
-       /*
-        * The current message produced by this live stream iterator. Owned by
-        * this.
-        */
-       const bt_message *current_msg;
-
-       /* Timestamp in nanoseconds of the current message (current_msg). */
-       int64_t current_msg_ts_ns;
-
-       /* Owned by this. */
-       uint8_t *buf;
-       size_t buflen;
-
-       /* Owned by this. */
-       GString *name;
-
-       bool has_stream_hung_up;
-};
-
-struct lttng_live_metadata {
-       bt_logging_level log_level;
-       bt_self_component *self_comp;
-
-       uint64_t stream_id;
-       /* Weak reference. */
-       struct ctf_metadata_decoder *decoder;
-};
-
-enum lttng_live_metadata_stream_state {
-       /*
-        * The metadata needs to be updated. This is either because we just
-        * created the trace and haven't asked yet, or the relay specifically
-        * told us that new metadata is available.
-        */
-       LTTNG_LIVE_METADATA_STREAM_STATE_NEEDED,
-       /*
-        * The metadata was updated and the relay has not told us we need to
-        * update it yet.
-        */
-       LTTNG_LIVE_METADATA_STREAM_STATE_NOT_NEEDED,
-       /*
-        * The relay has closed this metadata stream. We set this in reaction
-        * to a LTTNG_VIEWER_METADATA_ERR reply to a LTTNG_VIEWER_GET_METADATA
-        * command to the relay. If this field is set, we have received all the
-        * metadata that we are ever going to get for that metadata stream.
-        */
-       LTTNG_LIVE_METADATA_STREAM_STATE_CLOSED,
-};
-
-struct lttng_live_trace {
-       bt_logging_level log_level;
-       bt_self_component *self_comp;
-
-       /* Back reference to session. */
-       struct lttng_live_session *session;
-
-       /* ctf trace ID within the session. */
-       uint64_t id;
-
-       /* Owned by this. */
-       bt_trace *trace;
-
-       /* Weak reference. */
-       bt_trace_class *trace_class;
-
-       struct lttng_live_metadata *metadata;
-
-       const bt_clock_class *clock_class;
-
-       /* Array of pointers to struct lttng_live_stream_iterator. */
-       /* Owned by this. */
-       GPtrArray *stream_iterators;
-
-       enum lttng_live_metadata_stream_state metadata_stream_state;
-};
-
-struct lttng_live_session {
-       bt_logging_level log_level;
-       bt_self_component *self_comp;
-
-       /* Weak reference. */
-       struct lttng_live_msg_iter *lttng_live_msg_iter;
-
-       /* Owned by this. */
-       GString *hostname;
-
-       /* Owned by this. */
-       GString *session_name;
-
-       uint64_t id;
-
-       /* Array of pointers to struct lttng_live_trace. */
-       GPtrArray *traces;
-
-       bool attached;
-       bool new_streams_needed;
-       bool lazy_stream_msg_init;
-       bool closed;
-};
-
-enum session_not_found_action {
-       SESSION_NOT_FOUND_ACTION_CONTINUE,
-       SESSION_NOT_FOUND_ACTION_FAIL,
-       SESSION_NOT_FOUND_ACTION_END,
-};
-
-/*
- * A component instance is an iterator on a single session.
- */
-struct lttng_live_component {
-       bt_logging_level log_level;
-
-       /* Weak reference. */
-       bt_self_component *self_comp;
-
-       struct {
-               GString *url;
-               enum session_not_found_action sess_not_found_act;
-       } params;
-
-       size_t max_query_size;
-
-       /*
-        * Keeps track of whether the downstream component already has a
-        * message iterator on this component.
-        */
-       bool has_msg_iter;
-};
-
-struct lttng_live_msg_iter {
-       bt_logging_level log_level;
-       bt_self_component *self_comp;
-
-       /* Weak reference. */
-       struct lttng_live_component *lttng_live_comp;
-
-       /* Weak reference. */
-       bt_self_message_iterator *self_msg_iter;
-
-       /* Owned by this. */
-       struct live_viewer_connection *viewer_connection;
-
-       /* Array of pointers to struct lttng_live_session. */
-       GPtrArray *sessions;
-
-       /* Number of live stream iterator this message iterator has.*/
-       uint64_t active_stream_iter;
-
-       /* Timestamp in nanosecond of the last message sent downstream. */
-       int64_t last_msg_ts_ns;
-
-       /* True if the iterator was interrupted. */
-       bool was_interrupted;
-};
-
-enum lttng_live_iterator_status {
-       /** Iterator state has progressed. Continue iteration immediately. */
-       LTTNG_LIVE_ITERATOR_STATUS_CONTINUE = 3,
-       /** No message available for now. Try again later. */
-       LTTNG_LIVE_ITERATOR_STATUS_AGAIN = 2,
-       /** No more CTF_LTTNG_LIVEs to be delivered. */
-       LTTNG_LIVE_ITERATOR_STATUS_END = 1,
-       /** No error, okay. */
-       LTTNG_LIVE_ITERATOR_STATUS_OK = 0,
-       /** Invalid arguments. */
-       LTTNG_LIVE_ITERATOR_STATUS_INVAL = -1,
-       /** General error. */
-       LTTNG_LIVE_ITERATOR_STATUS_ERROR = -2,
-       /** Out of memory. */
-       LTTNG_LIVE_ITERATOR_STATUS_NOMEM = -3,
-       /** Unsupported iterator feature. */
-       LTTNG_LIVE_ITERATOR_STATUS_UNSUPPORTED = -4,
-};
-
-bt_component_class_initialize_method_status lttng_live_component_init(
-               bt_self_component_source *self_comp,
-               bt_self_component_source_configuration *config,
-               const bt_value *params, void *init_method_data);
-
-bt_component_class_query_method_status lttng_live_query(
-               bt_self_component_class_source *comp_class,
-               bt_private_query_executor *priv_query_exec,
-               const char *object, const bt_value *params,
-               void *method_data, const bt_value **result);
-
-void lttng_live_component_finalize(bt_self_component_source *component);
-
-bt_message_iterator_class_next_method_status lttng_live_msg_iter_next(
-               bt_self_message_iterator *iterator,
-               bt_message_array_const msgs, uint64_t capacity,
-               uint64_t *count);
-
-bt_message_iterator_class_initialize_method_status lttng_live_msg_iter_init(
-               bt_self_message_iterator *self_msg_it,
-               bt_self_message_iterator_configuration *config,
-               bt_self_component_port_output *self_port);
-
-void lttng_live_msg_iter_finalize(bt_self_message_iterator *it);
-
-enum lttng_live_viewer_status lttng_live_session_attach(
-               struct lttng_live_session *session,
-               bt_self_message_iterator *self_msg_iter);
-
-enum lttng_live_viewer_status lttng_live_session_detach(
-               struct lttng_live_session *session);
-
-enum lttng_live_iterator_status lttng_live_session_get_new_streams(
-               struct lttng_live_session *session,
-               bt_self_message_iterator *self_msg_iter);
-
-struct lttng_live_trace *lttng_live_session_borrow_or_create_trace_by_id(
-               struct lttng_live_session *session, uint64_t trace_id);
-
-int lttng_live_add_session(struct lttng_live_msg_iter *lttng_live_msg_iter,
-               uint64_t session_id,
-               const char *hostname,
-               const char *session_name);
-
-/*
- * lttng_live_get_one_metadata_packet() asks the Relay Daemon for new metadata.
- * If new metadata is received, the function writes it to the provided file
- * handle and updates the reply_len output parameter. This function should be
- * called in loop until _END status is received to ensure all metadata is
- * written to the file.
- */
-enum lttng_live_get_one_metadata_status lttng_live_get_one_metadata_packet(
-               struct lttng_live_trace *trace, FILE *fp, size_t *reply_len);
-
-enum lttng_live_iterator_status lttng_live_get_next_index(
-               struct lttng_live_msg_iter *lttng_live_msg_iter,
-               struct lttng_live_stream_iterator *stream,
-               struct packet_index *index);
-
-enum ctf_msg_iter_medium_status lttng_live_get_stream_bytes(
-               struct lttng_live_msg_iter *lttng_live_msg_iter,
-               struct lttng_live_stream_iterator *stream, uint8_t *buf,
-               uint64_t offset, uint64_t req_len, uint64_t *recv_len);
-
-bool lttng_live_graph_is_canceled(struct lttng_live_msg_iter *msg_iter);
-
-BT_HIDDEN
-void lttng_live_stream_iterator_set_state(
-               struct lttng_live_stream_iterator *stream_iter,
-               enum lttng_live_stream_state new_state);
-
-#endif /* BABELTRACE_PLUGIN_CTF_LTTNG_LIVE_H */
diff --git a/src/plugins/ctf/lttng-live/lttng-live.hpp b/src/plugins/ctf/lttng-live/lttng-live.hpp
new file mode 100644 (file)
index 0000000..c965b8c
--- /dev/null
@@ -0,0 +1,342 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2019 Francis Deslauriers <francis.deslauriers@efficios.com>
+ * Copyright 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ * Copyright 2016 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * BabelTrace - LTTng-live client Component
+ */
+
+#ifndef BABELTRACE_PLUGIN_CTF_LTTNG_LIVE_H
+#define BABELTRACE_PLUGIN_CTF_LTTNG_LIVE_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <glib.h>
+
+#include <babeltrace2/babeltrace.h>
+
+#include "common/macros.h"
+#include "../common/metadata/decoder.hpp"
+#include "../common/msg-iter/msg-iter.hpp"
+#include "viewer-connection.hpp"
+
+struct lttng_live_component;
+struct lttng_live_session;
+struct lttng_live_msg_iter;
+
+enum lttng_live_stream_state {
+       /* This stream won't have data until some known time in the future. */
+       LTTNG_LIVE_STREAM_QUIESCENT,
+       /*
+        * This stream won't have data until some known time in the future and
+        * the message iterator inactivity message was already sent downstream.
+        */
+       LTTNG_LIVE_STREAM_QUIESCENT_NO_DATA, /* */
+       /* This stream has data ready to be consumed. */
+       LTTNG_LIVE_STREAM_ACTIVE_DATA,
+       /*
+        * This stream has no data left to consume. We should asked the relay
+        * for more.
+        */
+       LTTNG_LIVE_STREAM_ACTIVE_NO_DATA,
+       /* This stream won't have anymore data, ever. */
+       LTTNG_LIVE_STREAM_EOF,
+};
+
+/* Iterator over a live stream. */
+struct lttng_live_stream_iterator {
+       bt_logging_level log_level;
+       bt_self_component *self_comp;
+
+       /* Owned by this. */
+       bt_stream *stream;
+
+       /* Weak reference. */
+       struct lttng_live_trace *trace;
+
+       /*
+        * Since only a single iterator per viewer connection, we have
+        * only a single message iterator per stream.
+        */
+       struct ctf_msg_iter *msg_iter;
+
+       uint64_t viewer_stream_id;
+
+       struct {
+               bool is_set;
+               uint64_t value;
+       } ctf_stream_class_id;
+
+       /* base offset in current index. */
+       uint64_t base_offset;
+       /* len to read in current index. */
+       uint64_t len;
+       /* offset in current index. */
+       uint64_t offset;
+
+       /*
+        * Clock Snapshot value of the last message iterator inactivity message
+        * sent downstream.
+        */
+       struct {
+               bool is_set;
+               uint64_t value;
+       } last_inactivity_ts;
+
+       /*
+        * Clock Snapshot value of the current message iterator inactivity
+        * message we might want to send downstream.
+        */
+       uint64_t current_inactivity_ts;
+
+       enum lttng_live_stream_state state;
+
+       /*
+        * The current message produced by this live stream iterator. Owned by
+        * this.
+        */
+       const bt_message *current_msg;
+
+       /* Timestamp in nanoseconds of the current message (current_msg). */
+       int64_t current_msg_ts_ns;
+
+       /* Owned by this. */
+       uint8_t *buf;
+       size_t buflen;
+
+       /* Owned by this. */
+       GString *name;
+
+       bool has_stream_hung_up;
+};
+
+struct lttng_live_metadata {
+       bt_logging_level log_level;
+       bt_self_component *self_comp;
+
+       uint64_t stream_id;
+       /* Weak reference. */
+       struct ctf_metadata_decoder *decoder;
+};
+
+enum lttng_live_metadata_stream_state {
+       /*
+        * The metadata needs to be updated. This is either because we just
+        * created the trace and haven't asked yet, or the relay specifically
+        * told us that new metadata is available.
+        */
+       LTTNG_LIVE_METADATA_STREAM_STATE_NEEDED,
+       /*
+        * The metadata was updated and the relay has not told us we need to
+        * update it yet.
+        */
+       LTTNG_LIVE_METADATA_STREAM_STATE_NOT_NEEDED,
+       /*
+        * The relay has closed this metadata stream. We set this in reaction
+        * to a LTTNG_VIEWER_METADATA_ERR reply to a LTTNG_VIEWER_GET_METADATA
+        * command to the relay. If this field is set, we have received all the
+        * metadata that we are ever going to get for that metadata stream.
+        */
+       LTTNG_LIVE_METADATA_STREAM_STATE_CLOSED,
+};
+
+struct lttng_live_trace {
+       bt_logging_level log_level;
+       bt_self_component *self_comp;
+
+       /* Back reference to session. */
+       struct lttng_live_session *session;
+
+       /* ctf trace ID within the session. */
+       uint64_t id;
+
+       /* Owned by this. */
+       bt_trace *trace;
+
+       /* Weak reference. */
+       bt_trace_class *trace_class;
+
+       struct lttng_live_metadata *metadata;
+
+       const bt_clock_class *clock_class;
+
+       /* Array of pointers to struct lttng_live_stream_iterator. */
+       /* Owned by this. */
+       GPtrArray *stream_iterators;
+
+       enum lttng_live_metadata_stream_state metadata_stream_state;
+};
+
+struct lttng_live_session {
+       bt_logging_level log_level;
+       bt_self_component *self_comp;
+
+       /* Weak reference. */
+       struct lttng_live_msg_iter *lttng_live_msg_iter;
+
+       /* Owned by this. */
+       GString *hostname;
+
+       /* Owned by this. */
+       GString *session_name;
+
+       uint64_t id;
+
+       /* Array of pointers to struct lttng_live_trace. */
+       GPtrArray *traces;
+
+       bool attached;
+       bool new_streams_needed;
+       bool lazy_stream_msg_init;
+       bool closed;
+};
+
+enum session_not_found_action {
+       SESSION_NOT_FOUND_ACTION_CONTINUE,
+       SESSION_NOT_FOUND_ACTION_FAIL,
+       SESSION_NOT_FOUND_ACTION_END,
+};
+
+/*
+ * A component instance is an iterator on a single session.
+ */
+struct lttng_live_component {
+       bt_logging_level log_level;
+
+       /* Weak reference. */
+       bt_self_component *self_comp;
+
+       struct {
+               GString *url;
+               enum session_not_found_action sess_not_found_act;
+       } params;
+
+       size_t max_query_size;
+
+       /*
+        * Keeps track of whether the downstream component already has a
+        * message iterator on this component.
+        */
+       bool has_msg_iter;
+};
+
+struct lttng_live_msg_iter {
+       bt_logging_level log_level;
+       bt_self_component *self_comp;
+
+       /* Weak reference. */
+       struct lttng_live_component *lttng_live_comp;
+
+       /* Weak reference. */
+       bt_self_message_iterator *self_msg_iter;
+
+       /* Owned by this. */
+       struct live_viewer_connection *viewer_connection;
+
+       /* Array of pointers to struct lttng_live_session. */
+       GPtrArray *sessions;
+
+       /* Number of live stream iterator this message iterator has.*/
+       uint64_t active_stream_iter;
+
+       /* Timestamp in nanosecond of the last message sent downstream. */
+       int64_t last_msg_ts_ns;
+
+       /* True if the iterator was interrupted. */
+       bool was_interrupted;
+};
+
+enum lttng_live_iterator_status {
+       /** Iterator state has progressed. Continue iteration immediately. */
+       LTTNG_LIVE_ITERATOR_STATUS_CONTINUE = 3,
+       /** No message available for now. Try again later. */
+       LTTNG_LIVE_ITERATOR_STATUS_AGAIN = 2,
+       /** No more CTF_LTTNG_LIVEs to be delivered. */
+       LTTNG_LIVE_ITERATOR_STATUS_END = 1,
+       /** No error, okay. */
+       LTTNG_LIVE_ITERATOR_STATUS_OK = 0,
+       /** Invalid arguments. */
+       LTTNG_LIVE_ITERATOR_STATUS_INVAL = -1,
+       /** General error. */
+       LTTNG_LIVE_ITERATOR_STATUS_ERROR = -2,
+       /** Out of memory. */
+       LTTNG_LIVE_ITERATOR_STATUS_NOMEM = -3,
+       /** Unsupported iterator feature. */
+       LTTNG_LIVE_ITERATOR_STATUS_UNSUPPORTED = -4,
+};
+
+bt_component_class_initialize_method_status lttng_live_component_init(
+               bt_self_component_source *self_comp,
+               bt_self_component_source_configuration *config,
+               const bt_value *params, void *init_method_data);
+
+bt_component_class_query_method_status lttng_live_query(
+               bt_self_component_class_source *comp_class,
+               bt_private_query_executor *priv_query_exec,
+               const char *object, const bt_value *params,
+               void *method_data, const bt_value **result);
+
+void lttng_live_component_finalize(bt_self_component_source *component);
+
+bt_message_iterator_class_next_method_status lttng_live_msg_iter_next(
+               bt_self_message_iterator *iterator,
+               bt_message_array_const msgs, uint64_t capacity,
+               uint64_t *count);
+
+bt_message_iterator_class_initialize_method_status lttng_live_msg_iter_init(
+               bt_self_message_iterator *self_msg_it,
+               bt_self_message_iterator_configuration *config,
+               bt_self_component_port_output *self_port);
+
+void lttng_live_msg_iter_finalize(bt_self_message_iterator *it);
+
+enum lttng_live_viewer_status lttng_live_session_attach(
+               struct lttng_live_session *session,
+               bt_self_message_iterator *self_msg_iter);
+
+enum lttng_live_viewer_status lttng_live_session_detach(
+               struct lttng_live_session *session);
+
+enum lttng_live_iterator_status lttng_live_session_get_new_streams(
+               struct lttng_live_session *session,
+               bt_self_message_iterator *self_msg_iter);
+
+struct lttng_live_trace *lttng_live_session_borrow_or_create_trace_by_id(
+               struct lttng_live_session *session, uint64_t trace_id);
+
+int lttng_live_add_session(struct lttng_live_msg_iter *lttng_live_msg_iter,
+               uint64_t session_id,
+               const char *hostname,
+               const char *session_name);
+
+/*
+ * lttng_live_get_one_metadata_packet() asks the Relay Daemon for new metadata.
+ * If new metadata is received, the function writes it to the provided file
+ * handle and updates the reply_len output parameter. This function should be
+ * called in loop until _END status is received to ensure all metadata is
+ * written to the file.
+ */
+enum lttng_live_get_one_metadata_status lttng_live_get_one_metadata_packet(
+               struct lttng_live_trace *trace, FILE *fp, size_t *reply_len);
+
+enum lttng_live_iterator_status lttng_live_get_next_index(
+               struct lttng_live_msg_iter *lttng_live_msg_iter,
+               struct lttng_live_stream_iterator *stream,
+               struct packet_index *index);
+
+enum ctf_msg_iter_medium_status lttng_live_get_stream_bytes(
+               struct lttng_live_msg_iter *lttng_live_msg_iter,
+               struct lttng_live_stream_iterator *stream, uint8_t *buf,
+               uint64_t offset, uint64_t req_len, uint64_t *recv_len);
+
+bool lttng_live_graph_is_canceled(struct lttng_live_msg_iter *msg_iter);
+
+BT_HIDDEN
+void lttng_live_stream_iterator_set_state(
+               struct lttng_live_stream_iterator *stream_iter,
+               enum lttng_live_stream_state new_state);
+
+#endif /* BABELTRACE_PLUGIN_CTF_LTTNG_LIVE_H */
diff --git a/src/plugins/ctf/lttng-live/lttng-viewer-abi.h b/src/plugins/ctf/lttng-live/lttng-viewer-abi.h
deleted file mode 100644 (file)
index 85c64ba..0000000
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright (C) 2013 Julien Desfossez <jdesfossez@efficios.com>
- * Copyright (C) 2013 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
- */
-
-#ifndef LTTNG_VIEWER_ABI_H
-#define LTTNG_VIEWER_ABI_H
-
-#include <stdint.h>
-#include "compat/limits.h"
-
-#define LTTNG_VIEWER_PATH_MAX          4096
-#define LTTNG_VIEWER_NAME_MAX          255
-#define LTTNG_VIEWER_HOST_NAME_MAX     64
-
-/* Flags in reply to get_next_index and get_packet. */
-enum {
-       /* New metadata is required to read this packet. */
-       LTTNG_VIEWER_FLAG_NEW_METADATA  = (1 << 0),
-       /* New stream got added to the trace. */
-       LTTNG_VIEWER_FLAG_NEW_STREAM    = (1 << 1),
-};
-
-enum lttng_viewer_command {
-       LTTNG_VIEWER_CONNECT            = 1,
-       LTTNG_VIEWER_LIST_SESSIONS      = 2,
-       LTTNG_VIEWER_ATTACH_SESSION     = 3,
-       LTTNG_VIEWER_GET_NEXT_INDEX     = 4,
-       LTTNG_VIEWER_GET_PACKET         = 5,
-       LTTNG_VIEWER_GET_METADATA       = 6,
-       LTTNG_VIEWER_GET_NEW_STREAMS    = 7,
-       LTTNG_VIEWER_CREATE_SESSION     = 8,
-       LTTNG_VIEWER_DETACH_SESSION     = 9,
-};
-
-enum lttng_viewer_attach_return_code {
-       LTTNG_VIEWER_ATTACH_OK          = 1, /* The attach command succeeded. */
-       LTTNG_VIEWER_ATTACH_ALREADY     = 2, /* A viewer is already attached. */
-       LTTNG_VIEWER_ATTACH_UNK         = 3, /* The session ID is unknown. */
-       LTTNG_VIEWER_ATTACH_NOT_LIVE    = 4, /* The session is not live. */
-       LTTNG_VIEWER_ATTACH_SEEK_ERR    = 5, /* Seek error. */
-       LTTNG_VIEWER_ATTACH_NO_SESSION  = 6, /* No viewer session created. */
-};
-
-enum lttng_viewer_detach_session_return_code {
-       LTTNG_VIEWER_DETACH_SESSION_OK          = 1,
-       LTTNG_VIEWER_DETACH_SESSION_UNK         = 2,
-       LTTNG_VIEWER_DETACH_SESSION_ERR         = 3,
-};
-
-enum lttng_viewer_next_index_return_code {
-       LTTNG_VIEWER_INDEX_OK           = 1, /* Index is available. */
-       LTTNG_VIEWER_INDEX_RETRY        = 2, /* Index not yet available. */
-       LTTNG_VIEWER_INDEX_HUP          = 3, /* Index closed (trace destroyed). */
-       LTTNG_VIEWER_INDEX_ERR          = 4, /* Unknow error. */
-       LTTNG_VIEWER_INDEX_INACTIVE     = 5, /* Inactive stream beacon. */
-       LTTNG_VIEWER_INDEX_EOF          = 6, /* End of index file. */
-};
-
-enum lttng_viewer_get_packet_return_code {
-       LTTNG_VIEWER_GET_PACKET_OK      = 1,
-       LTTNG_VIEWER_GET_PACKET_RETRY   = 2,
-       LTTNG_VIEWER_GET_PACKET_ERR     = 3,
-       LTTNG_VIEWER_GET_PACKET_EOF     = 4,
-};
-
-enum lttng_viewer_get_metadata_return_code {
-       LTTNG_VIEWER_METADATA_OK        = 1,
-       LTTNG_VIEWER_NO_NEW_METADATA    = 2,
-       LTTNG_VIEWER_METADATA_ERR       = 3,
-};
-
-enum lttng_viewer_connection_type {
-       LTTNG_VIEWER_CLIENT_COMMAND             = 1,
-       LTTNG_VIEWER_CLIENT_MESSAGE     = 2,
-};
-
-enum lttng_viewer_seek {
-       /* Receive the trace packets from the beginning. */
-       LTTNG_VIEWER_SEEK_BEGINNING     = 1,
-       /* Receive the trace packets from now. */
-       LTTNG_VIEWER_SEEK_LAST          = 2,
-};
-
-enum lttng_viewer_new_streams_return_code {
-       LTTNG_VIEWER_NEW_STREAMS_OK           = 1, /* If new streams are being sent. */
-       LTTNG_VIEWER_NEW_STREAMS_NO_NEW       = 2, /* If no new streams are available. */
-       LTTNG_VIEWER_NEW_STREAMS_ERR          = 3, /* Error. */
-       LTTNG_VIEWER_NEW_STREAMS_HUP          = 4, /* Session closed. */
-};
-
-enum lttng_viewer_create_session_return_code {
-       LTTNG_VIEWER_CREATE_SESSION_OK          = 1,
-       LTTNG_VIEWER_CREATE_SESSION_ERR         = 2,
-};
-
-struct lttng_viewer_session {
-       uint64_t id;
-       uint32_t live_timer;
-       uint32_t clients;
-       uint32_t streams;
-       char hostname[LTTNG_VIEWER_HOST_NAME_MAX];
-       char session_name[LTTNG_VIEWER_NAME_MAX];
-} __attribute__((__packed__));
-
-struct lttng_viewer_stream {
-       uint64_t id;
-       uint64_t ctf_trace_id;
-       uint32_t metadata_flag;
-       char path_name[LTTNG_VIEWER_PATH_MAX];
-       char channel_name[LTTNG_VIEWER_NAME_MAX];
-} __attribute__((__packed__));
-
-struct lttng_viewer_cmd {
-       uint64_t data_size;     /* data size following this header */
-       uint32_t cmd;           /* enum lttcomm_relayd_command */
-       uint32_t cmd_version;   /* command version */
-} __attribute__((__packed__));
-
-/*
- * LTTNG_VIEWER_CONNECT payload.
- */
-struct lttng_viewer_connect {
-       /* session ID assigned by the relay for command connections */
-       uint64_t viewer_session_id;
-       uint32_t major;
-       uint32_t minor;
-       uint32_t type;          /* enum lttng_viewer_connection_type */
-} __attribute__((__packed__));
-
-/*
- * LTTNG_VIEWER_LIST_SESSIONS payload.
- */
-struct lttng_viewer_list_sessions {
-       uint32_t sessions_count;
-       char session_list[];    /* struct lttng_viewer_session */
-} __attribute__((__packed__));
-
-/*
- * LTTNG_VIEWER_ATTACH_SESSION payload.
- */
-struct lttng_viewer_attach_session_request {
-       uint64_t session_id;
-       uint64_t offset;        /* unused for now */
-       uint32_t seek;          /* enum lttng_viewer_seek */
-} __attribute__((__packed__));
-
-struct lttng_viewer_attach_session_response {
-       /* enum lttng_viewer_attach_return_code */
-       uint32_t status;
-       uint32_t streams_count;
-       /* struct lttng_viewer_stream */
-       char stream_list[];
-} __attribute__((__packed__));
-
-/*
- * LTTNG_VIEWER_GET_NEXT_INDEX payload.
- */
-struct lttng_viewer_get_next_index {
-       uint64_t stream_id;
-} __attribute__ ((__packed__));
-
-struct lttng_viewer_index {
-       uint64_t offset;
-       uint64_t packet_size;
-       uint64_t content_size;
-       uint64_t timestamp_begin;
-       uint64_t timestamp_end;
-       uint64_t events_discarded;
-       uint64_t stream_id;
-       uint32_t status;        /* enum lttng_viewer_next_index_return_code */
-       uint32_t flags;         /* LTTNG_VIEWER_FLAG_* */
-} __attribute__ ((__packed__));
-
-/*
- * LTTNG_VIEWER_GET_PACKET payload.
- */
-struct lttng_viewer_get_packet {
-       uint64_t stream_id;
-       uint64_t offset;
-       uint32_t len;
-} __attribute__((__packed__));
-
-struct lttng_viewer_trace_packet {
-       uint32_t status;        /* enum lttng_viewer_get_packet_return_code */
-       uint32_t len;
-       uint32_t flags;         /* LTTNG_VIEWER_FLAG_* */
-       char data[];
-} __attribute__((__packed__));
-
-/*
- * LTTNG_VIEWER_GET_METADATA payload.
- */
-struct lttng_viewer_get_metadata {
-       uint64_t stream_id;
-} __attribute__((__packed__));
-
-struct lttng_viewer_metadata_packet {
-       uint64_t len;
-       uint32_t status;        /* enum lttng_viewer_get_metadata_return_code */
-       char data[];
-} __attribute__((__packed__));
-
-/*
- * LTTNG_VIEWER_GET_NEW_STREAMS payload.
- */
-struct lttng_viewer_new_streams_request {
-       uint64_t session_id;
-} __attribute__((__packed__));
-
-struct lttng_viewer_new_streams_response {
-       /* enum lttng_viewer_new_streams_return_code */
-       uint32_t status;
-       uint32_t streams_count;
-       /* struct lttng_viewer_stream */
-       char stream_list[];
-} __attribute__((__packed__));
-
-struct lttng_viewer_create_session_response {
-       /* enum lttng_viewer_create_session_return_code */
-       uint32_t status;
-} __attribute__((__packed__));
-
-/*
- * LTTNG_VIEWER_DETACH_SESSION payload.
- */
-struct lttng_viewer_detach_session_request {
-       uint64_t session_id;
-} __attribute__((__packed__));
-
-struct lttng_viewer_detach_session_response {
-       /* enum lttng_viewer_detach_session_return_code */
-       uint32_t status;
-} __attribute__((__packed__));
-
-#endif /* LTTNG_VIEWER_ABI_H */
diff --git a/src/plugins/ctf/lttng-live/lttng-viewer-abi.hpp b/src/plugins/ctf/lttng-live/lttng-viewer-abi.hpp
new file mode 100644 (file)
index 0000000..85c64ba
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2013 Julien Desfossez <jdesfossez@efficios.com>
+ * Copyright (C) 2013 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
+ */
+
+#ifndef LTTNG_VIEWER_ABI_H
+#define LTTNG_VIEWER_ABI_H
+
+#include <stdint.h>
+#include "compat/limits.h"
+
+#define LTTNG_VIEWER_PATH_MAX          4096
+#define LTTNG_VIEWER_NAME_MAX          255
+#define LTTNG_VIEWER_HOST_NAME_MAX     64
+
+/* Flags in reply to get_next_index and get_packet. */
+enum {
+       /* New metadata is required to read this packet. */
+       LTTNG_VIEWER_FLAG_NEW_METADATA  = (1 << 0),
+       /* New stream got added to the trace. */
+       LTTNG_VIEWER_FLAG_NEW_STREAM    = (1 << 1),
+};
+
+enum lttng_viewer_command {
+       LTTNG_VIEWER_CONNECT            = 1,
+       LTTNG_VIEWER_LIST_SESSIONS      = 2,
+       LTTNG_VIEWER_ATTACH_SESSION     = 3,
+       LTTNG_VIEWER_GET_NEXT_INDEX     = 4,
+       LTTNG_VIEWER_GET_PACKET         = 5,
+       LTTNG_VIEWER_GET_METADATA       = 6,
+       LTTNG_VIEWER_GET_NEW_STREAMS    = 7,
+       LTTNG_VIEWER_CREATE_SESSION     = 8,
+       LTTNG_VIEWER_DETACH_SESSION     = 9,
+};
+
+enum lttng_viewer_attach_return_code {
+       LTTNG_VIEWER_ATTACH_OK          = 1, /* The attach command succeeded. */
+       LTTNG_VIEWER_ATTACH_ALREADY     = 2, /* A viewer is already attached. */
+       LTTNG_VIEWER_ATTACH_UNK         = 3, /* The session ID is unknown. */
+       LTTNG_VIEWER_ATTACH_NOT_LIVE    = 4, /* The session is not live. */
+       LTTNG_VIEWER_ATTACH_SEEK_ERR    = 5, /* Seek error. */
+       LTTNG_VIEWER_ATTACH_NO_SESSION  = 6, /* No viewer session created. */
+};
+
+enum lttng_viewer_detach_session_return_code {
+       LTTNG_VIEWER_DETACH_SESSION_OK          = 1,
+       LTTNG_VIEWER_DETACH_SESSION_UNK         = 2,
+       LTTNG_VIEWER_DETACH_SESSION_ERR         = 3,
+};
+
+enum lttng_viewer_next_index_return_code {
+       LTTNG_VIEWER_INDEX_OK           = 1, /* Index is available. */
+       LTTNG_VIEWER_INDEX_RETRY        = 2, /* Index not yet available. */
+       LTTNG_VIEWER_INDEX_HUP          = 3, /* Index closed (trace destroyed). */
+       LTTNG_VIEWER_INDEX_ERR          = 4, /* Unknow error. */
+       LTTNG_VIEWER_INDEX_INACTIVE     = 5, /* Inactive stream beacon. */
+       LTTNG_VIEWER_INDEX_EOF          = 6, /* End of index file. */
+};
+
+enum lttng_viewer_get_packet_return_code {
+       LTTNG_VIEWER_GET_PACKET_OK      = 1,
+       LTTNG_VIEWER_GET_PACKET_RETRY   = 2,
+       LTTNG_VIEWER_GET_PACKET_ERR     = 3,
+       LTTNG_VIEWER_GET_PACKET_EOF     = 4,
+};
+
+enum lttng_viewer_get_metadata_return_code {
+       LTTNG_VIEWER_METADATA_OK        = 1,
+       LTTNG_VIEWER_NO_NEW_METADATA    = 2,
+       LTTNG_VIEWER_METADATA_ERR       = 3,
+};
+
+enum lttng_viewer_connection_type {
+       LTTNG_VIEWER_CLIENT_COMMAND             = 1,
+       LTTNG_VIEWER_CLIENT_MESSAGE     = 2,
+};
+
+enum lttng_viewer_seek {
+       /* Receive the trace packets from the beginning. */
+       LTTNG_VIEWER_SEEK_BEGINNING     = 1,
+       /* Receive the trace packets from now. */
+       LTTNG_VIEWER_SEEK_LAST          = 2,
+};
+
+enum lttng_viewer_new_streams_return_code {
+       LTTNG_VIEWER_NEW_STREAMS_OK           = 1, /* If new streams are being sent. */
+       LTTNG_VIEWER_NEW_STREAMS_NO_NEW       = 2, /* If no new streams are available. */
+       LTTNG_VIEWER_NEW_STREAMS_ERR          = 3, /* Error. */
+       LTTNG_VIEWER_NEW_STREAMS_HUP          = 4, /* Session closed. */
+};
+
+enum lttng_viewer_create_session_return_code {
+       LTTNG_VIEWER_CREATE_SESSION_OK          = 1,
+       LTTNG_VIEWER_CREATE_SESSION_ERR         = 2,
+};
+
+struct lttng_viewer_session {
+       uint64_t id;
+       uint32_t live_timer;
+       uint32_t clients;
+       uint32_t streams;
+       char hostname[LTTNG_VIEWER_HOST_NAME_MAX];
+       char session_name[LTTNG_VIEWER_NAME_MAX];
+} __attribute__((__packed__));
+
+struct lttng_viewer_stream {
+       uint64_t id;
+       uint64_t ctf_trace_id;
+       uint32_t metadata_flag;
+       char path_name[LTTNG_VIEWER_PATH_MAX];
+       char channel_name[LTTNG_VIEWER_NAME_MAX];
+} __attribute__((__packed__));
+
+struct lttng_viewer_cmd {
+       uint64_t data_size;     /* data size following this header */
+       uint32_t cmd;           /* enum lttcomm_relayd_command */
+       uint32_t cmd_version;   /* command version */
+} __attribute__((__packed__));
+
+/*
+ * LTTNG_VIEWER_CONNECT payload.
+ */
+struct lttng_viewer_connect {
+       /* session ID assigned by the relay for command connections */
+       uint64_t viewer_session_id;
+       uint32_t major;
+       uint32_t minor;
+       uint32_t type;          /* enum lttng_viewer_connection_type */
+} __attribute__((__packed__));
+
+/*
+ * LTTNG_VIEWER_LIST_SESSIONS payload.
+ */
+struct lttng_viewer_list_sessions {
+       uint32_t sessions_count;
+       char session_list[];    /* struct lttng_viewer_session */
+} __attribute__((__packed__));
+
+/*
+ * LTTNG_VIEWER_ATTACH_SESSION payload.
+ */
+struct lttng_viewer_attach_session_request {
+       uint64_t session_id;
+       uint64_t offset;        /* unused for now */
+       uint32_t seek;          /* enum lttng_viewer_seek */
+} __attribute__((__packed__));
+
+struct lttng_viewer_attach_session_response {
+       /* enum lttng_viewer_attach_return_code */
+       uint32_t status;
+       uint32_t streams_count;
+       /* struct lttng_viewer_stream */
+       char stream_list[];
+} __attribute__((__packed__));
+
+/*
+ * LTTNG_VIEWER_GET_NEXT_INDEX payload.
+ */
+struct lttng_viewer_get_next_index {
+       uint64_t stream_id;
+} __attribute__ ((__packed__));
+
+struct lttng_viewer_index {
+       uint64_t offset;
+       uint64_t packet_size;
+       uint64_t content_size;
+       uint64_t timestamp_begin;
+       uint64_t timestamp_end;
+       uint64_t events_discarded;
+       uint64_t stream_id;
+       uint32_t status;        /* enum lttng_viewer_next_index_return_code */
+       uint32_t flags;         /* LTTNG_VIEWER_FLAG_* */
+} __attribute__ ((__packed__));
+
+/*
+ * LTTNG_VIEWER_GET_PACKET payload.
+ */
+struct lttng_viewer_get_packet {
+       uint64_t stream_id;
+       uint64_t offset;
+       uint32_t len;
+} __attribute__((__packed__));
+
+struct lttng_viewer_trace_packet {
+       uint32_t status;        /* enum lttng_viewer_get_packet_return_code */
+       uint32_t len;
+       uint32_t flags;         /* LTTNG_VIEWER_FLAG_* */
+       char data[];
+} __attribute__((__packed__));
+
+/*
+ * LTTNG_VIEWER_GET_METADATA payload.
+ */
+struct lttng_viewer_get_metadata {
+       uint64_t stream_id;
+} __attribute__((__packed__));
+
+struct lttng_viewer_metadata_packet {
+       uint64_t len;
+       uint32_t status;        /* enum lttng_viewer_get_metadata_return_code */
+       char data[];
+} __attribute__((__packed__));
+
+/*
+ * LTTNG_VIEWER_GET_NEW_STREAMS payload.
+ */
+struct lttng_viewer_new_streams_request {
+       uint64_t session_id;
+} __attribute__((__packed__));
+
+struct lttng_viewer_new_streams_response {
+       /* enum lttng_viewer_new_streams_return_code */
+       uint32_t status;
+       uint32_t streams_count;
+       /* struct lttng_viewer_stream */
+       char stream_list[];
+} __attribute__((__packed__));
+
+struct lttng_viewer_create_session_response {
+       /* enum lttng_viewer_create_session_return_code */
+       uint32_t status;
+} __attribute__((__packed__));
+
+/*
+ * LTTNG_VIEWER_DETACH_SESSION payload.
+ */
+struct lttng_viewer_detach_session_request {
+       uint64_t session_id;
+} __attribute__((__packed__));
+
+struct lttng_viewer_detach_session_response {
+       /* enum lttng_viewer_detach_session_return_code */
+       uint32_t status;
+} __attribute__((__packed__));
+
+#endif /* LTTNG_VIEWER_ABI_H */
diff --git a/src/plugins/ctf/lttng-live/metadata.c b/src/plugins/ctf/lttng-live/metadata.c
deleted file mode 100644 (file)
index 711f3ca..0000000
+++ /dev/null
@@ -1,360 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2019 Francis Deslauriers <francis.deslauriers@efficios.com>
- * Copyright 2016 Philippe Proulx <pproulx@efficios.com>
- * Copyright 2010-2011 EfficiOS Inc. and Linux Foundation
- */
-
-#define BT_COMP_LOG_SELF_COMP self_comp
-#define BT_LOG_OUTPUT_LEVEL log_level
-#define BT_LOG_TAG "PLUGIN/SRC.CTF.LTTNG-LIVE/META"
-#include "logging/comp-logging.h"
-
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <glib.h>
-#include "compat/memstream.h"
-#include <babeltrace2/babeltrace.h>
-
-#include "metadata.h"
-#include "../common/metadata/decoder.h"
-#include "../common/metadata/ctf-meta-configure-ir-trace.h"
-
-#define TSDL_MAGIC     0x75d11d57
-
-struct packet_header {
-       uint32_t magic;
-       uint8_t  uuid[16];
-       uint32_t checksum;
-       uint32_t content_size;
-       uint32_t packet_size;
-       uint8_t  compression_scheme;
-       uint8_t  encryption_scheme;
-       uint8_t  checksum_scheme;
-       uint8_t  major;
-       uint8_t  minor;
-} __attribute__((__packed__));
-
-
-static
-bool stream_classes_all_have_default_clock_class(bt_trace_class *tc,
-               bt_logging_level log_level,
-               bt_self_component *self_comp)
-{
-       uint64_t i, sc_count;
-       const bt_clock_class *cc = NULL;
-       const bt_stream_class *sc;
-       bool ret = true;
-
-       sc_count = bt_trace_class_get_stream_class_count(tc);
-       for (i = 0; i < sc_count; i++) {
-               sc = bt_trace_class_borrow_stream_class_by_index_const(tc, i);
-
-               BT_ASSERT(sc);
-
-               cc = bt_stream_class_borrow_default_clock_class_const(sc);
-               if (!cc) {
-                       ret = false;
-                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                               "Stream class doesn't have a default clock class: "
-                               "sc-id=%" PRIu64 ", sc-name=\"%s\"",
-                               bt_stream_class_get_id(sc),
-                               bt_stream_class_get_name(sc));
-                       goto end;
-               }
-       }
-
-end:
-       return ret;
-}
-/*
- * Iterate over the stream classes and returns the first clock class
- * encountered. This is useful to create message iterator inactivity message as
- * we don't need a particular clock class.
- */
-static
-const bt_clock_class *borrow_any_clock_class(bt_trace_class *tc)
-{
-       uint64_t i, sc_count;
-       const bt_clock_class *cc = NULL;
-       const bt_stream_class *sc;
-
-       sc_count = bt_trace_class_get_stream_class_count(tc);
-       for (i = 0; i < sc_count; i++) {
-               sc = bt_trace_class_borrow_stream_class_by_index_const(tc, i);
-               BT_ASSERT_DBG(sc);
-
-               cc = bt_stream_class_borrow_default_clock_class_const(sc);
-               if (cc) {
-                       goto end;
-               }
-       }
-end:
-       BT_ASSERT_DBG(cc);
-       return cc;
-}
-
-BT_HIDDEN
-enum lttng_live_iterator_status lttng_live_metadata_update(
-               struct lttng_live_trace *trace)
-{
-       struct lttng_live_session *session = trace->session;
-       struct lttng_live_metadata *metadata = trace->metadata;
-       size_t size, len_read = 0;
-       char *metadata_buf = NULL;
-       bool keep_receiving;
-       FILE *fp = NULL;
-       enum ctf_metadata_decoder_status decoder_status;
-       enum lttng_live_iterator_status status =
-               LTTNG_LIVE_ITERATOR_STATUS_OK;
-       bt_logging_level log_level = trace->log_level;
-       bt_self_component *self_comp = trace->self_comp;
-       enum lttng_live_get_one_metadata_status metadata_status;
-
-       BT_COMP_LOGD("Updating metadata for trace: session-id=%" PRIu64
-               ", trace-id=%" PRIu64, session->id, trace->id);
-
-       /* No metadata stream yet. */
-       if (!metadata) {
-               if (session->new_streams_needed) {
-                       status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN;
-               } else {
-                       session->new_streams_needed = true;
-                       status = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE;
-               }
-               goto end;
-       }
-
-       if (trace->metadata_stream_state != LTTNG_LIVE_METADATA_STREAM_STATE_NEEDED) {
-               goto end;
-       }
-
-       /*
-        * Open a new write only file handle to populate the `metadata_buf`
-        * memory buffer so we can write in loop in it easily.
-        */
-       fp = bt_open_memstream(&metadata_buf, &size);
-       if (!fp) {
-               if (errno == EINTR &&
-                               lttng_live_graph_is_canceled(session->lttng_live_msg_iter)) {
-                       session->lttng_live_msg_iter->was_interrupted = true;
-                       status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN;
-               } else {
-                       BT_COMP_LOGE_APPEND_CAUSE_ERRNO(self_comp,
-                               "Metadata open_memstream", ".");
-                       status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-               }
-               goto end;
-       }
-
-       keep_receiving = true;
-       /* Grab all available metadata. */
-       while (keep_receiving) {
-               size_t reply_len = 0;
-               /*
-                * lttng_live_get_one_metadata_packet() asks the Relay Daemon
-                * for new metadata. If new metadata is received, the function
-                * writes it to the provided file handle and updates the
-                * reply_len output parameter. We call this function in loop
-                * until it returns _END meaning that no new metadata is
-                * available.
-                * We may receive a _CLOSED status if the metadata stream we
-                * are requesting is no longer available on the relay.
-                * If we receive an _ERROR status, it means there was a
-                * networking, allocating, or some other unrecoverable error.
-                */
-               metadata_status = lttng_live_get_one_metadata_packet(trace, fp,
-                       &reply_len);
-
-               switch (metadata_status) {
-               case LTTNG_LIVE_GET_ONE_METADATA_STATUS_OK:
-                       len_read += reply_len;
-                       break;
-               case LTTNG_LIVE_GET_ONE_METADATA_STATUS_END:
-                       keep_receiving = false;
-                       break;
-               case LTTNG_LIVE_GET_ONE_METADATA_STATUS_CLOSED:
-                       BT_COMP_LOGD("Metadata stream was closed by the Relay, the trace is no longer active: "
-                               "trace-id=%"PRIu64", metadata-stream-id=%"PRIu64,
-                               trace->id, metadata->stream_id);
-                       /*
-                        * The stream was closed and we received everything
-                        * there was to receive for this metadata stream.
-                        * We go on with the decoding of what we received. So
-                        * that data stream can be decoded.
-                        */
-                       keep_receiving = false;
-                       trace->metadata_stream_state = LTTNG_LIVE_METADATA_STREAM_STATE_CLOSED;
-                       break;
-               case LTTNG_LIVE_GET_ONE_METADATA_STATUS_ERROR:
-                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                               "Error getting one trace metadata packet: "
-                               "trace-id=%"PRIu64, trace->id);
-                       goto error;
-               default:
-                       bt_common_abort();
-               }
-       }
-
-       /* The memory buffer `metadata_buf` contains all the metadata. */
-       if (bt_close_memstream(&metadata_buf, &size, fp)) {
-               BT_COMP_LOGW_ERRNO("Metadata bt_close_memstream", ".");
-       }
-
-       fp = NULL;
-
-       if (len_read == 0) {
-               if (!trace->trace) {
-                       status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN;
-                       goto end;
-               }
-
-               /* The relay sent zero bytes of metdata. */
-               trace->metadata_stream_state = LTTNG_LIVE_METADATA_STREAM_STATE_NOT_NEEDED;
-               goto end;
-       }
-
-       /*
-        * Open a new reading file handle on the `metadata_buf` and pass it to
-        * the metadata decoder.
-        */
-       fp = bt_fmemopen(metadata_buf, len_read, "rb");
-       if (!fp) {
-               if (errno == EINTR &&
-                               lttng_live_graph_is_canceled(session->lttng_live_msg_iter)) {
-                       session->lttng_live_msg_iter->was_interrupted = true;
-                       status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN;
-               } else {
-                       BT_COMP_LOGE_APPEND_CAUSE_ERRNO(self_comp,
-                               "Cannot memory-open metadata buffer", ".");
-                       status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-               }
-               goto end;
-       }
-
-       /*
-        * The call to ctf_metadata_decoder_append_content() will append
-        * new metadata to our current trace class.
-        */
-       BT_COMP_LOGD("Appending new metadata to the ctf_trace class");
-       decoder_status = ctf_metadata_decoder_append_content(
-               metadata->decoder, fp);
-       switch (decoder_status) {
-       case CTF_METADATA_DECODER_STATUS_OK:
-               if (!trace->trace_class) {
-                       struct ctf_trace_class *tc =
-                               ctf_metadata_decoder_borrow_ctf_trace_class(
-                                       metadata->decoder);
-
-                       trace->trace_class =
-                               ctf_metadata_decoder_get_ir_trace_class(
-                                               metadata->decoder);
-                       trace->trace = bt_trace_create(trace->trace_class);
-                       if (!trace->trace) {
-                               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                       "Failed to create bt_trace");
-                               goto error;
-                       }
-                       if (ctf_trace_class_configure_ir_trace(tc,
-                                       trace->trace)) {
-                               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                       "Failed to configure ctf trace class");
-                               goto error;
-                       }
-                       if (!stream_classes_all_have_default_clock_class(
-                                       trace->trace_class, log_level,
-                                       self_comp)) {
-                               /* Error logged in function. */
-                               goto error;
-                       }
-                       trace->clock_class =
-                               borrow_any_clock_class(trace->trace_class);
-               }
-
-               /* The metadata was updated succesfully. */
-               trace->metadata_stream_state = LTTNG_LIVE_METADATA_STREAM_STATE_NOT_NEEDED;
-
-               break;
-       default:
-               goto error;
-       }
-
-       goto end;
-
-error:
-       status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-end:
-       if (fp) {
-               int closeret;
-
-               closeret = fclose(fp);
-               if (closeret) {
-                       BT_COMP_LOGW_ERRNO("Error on fclose", ".");
-               }
-       }
-       free(metadata_buf);
-       return status;
-}
-
-BT_HIDDEN
-int lttng_live_metadata_create_stream(struct lttng_live_session *session,
-               uint64_t ctf_trace_id, uint64_t stream_id,
-               const char *trace_name)
-{
-       bt_self_component *self_comp = session->self_comp;
-       bt_logging_level log_level = session->log_level;
-       struct lttng_live_metadata *metadata = NULL;
-       struct lttng_live_trace *trace;
-       struct ctf_metadata_decoder_config cfg = {
-               .log_level = session->log_level,
-               .self_comp = session->self_comp,
-               .clock_class_offset_s = 0,
-               .clock_class_offset_ns = 0,
-               .create_trace_class = true,
-       };
-
-       metadata = g_new0(struct lttng_live_metadata, 1);
-       if (!metadata) {
-               return -1;
-       }
-       metadata->log_level = session->log_level;
-       metadata->self_comp = session->self_comp;
-       metadata->stream_id = stream_id;
-
-       metadata->decoder = ctf_metadata_decoder_create(&cfg);
-       if (!metadata->decoder) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Failed to create CTF metadata decoder");
-               goto error;
-       }
-       trace = lttng_live_session_borrow_or_create_trace_by_id(session,
-               ctf_trace_id);
-       if (!trace) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Failed to borrow trace");
-               goto error;
-       }
-       trace->metadata = metadata;
-       return 0;
-
-error:
-       ctf_metadata_decoder_destroy(metadata->decoder);
-       g_free(metadata);
-       return -1;
-}
-
-BT_HIDDEN
-void lttng_live_metadata_fini(struct lttng_live_trace *trace)
-{
-       struct lttng_live_metadata *metadata = trace->metadata;
-
-       if (!metadata) {
-               return;
-       }
-       ctf_metadata_decoder_destroy(metadata->decoder);
-       trace->metadata = NULL;
-       g_free(metadata);
-}
diff --git a/src/plugins/ctf/lttng-live/metadata.cpp b/src/plugins/ctf/lttng-live/metadata.cpp
new file mode 100644 (file)
index 0000000..feaf0af
--- /dev/null
@@ -0,0 +1,360 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2019 Francis Deslauriers <francis.deslauriers@efficios.com>
+ * Copyright 2016 Philippe Proulx <pproulx@efficios.com>
+ * Copyright 2010-2011 EfficiOS Inc. and Linux Foundation
+ */
+
+#define BT_COMP_LOG_SELF_COMP self_comp
+#define BT_LOG_OUTPUT_LEVEL log_level
+#define BT_LOG_TAG "PLUGIN/SRC.CTF.LTTNG-LIVE/META"
+#include "logging/comp-logging.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <glib.h>
+#include "compat/memstream.h"
+#include <babeltrace2/babeltrace.h>
+
+#include "metadata.hpp"
+#include "../common/metadata/decoder.hpp"
+#include "../common/metadata/ctf-meta-configure-ir-trace.hpp"
+
+#define TSDL_MAGIC     0x75d11d57
+
+struct packet_header {
+       uint32_t magic;
+       uint8_t  uuid[16];
+       uint32_t checksum;
+       uint32_t content_size;
+       uint32_t packet_size;
+       uint8_t  compression_scheme;
+       uint8_t  encryption_scheme;
+       uint8_t  checksum_scheme;
+       uint8_t  major;
+       uint8_t  minor;
+} __attribute__((__packed__));
+
+
+static
+bool stream_classes_all_have_default_clock_class(bt_trace_class *tc,
+               bt_logging_level log_level,
+               bt_self_component *self_comp)
+{
+       uint64_t i, sc_count;
+       const bt_clock_class *cc = NULL;
+       const bt_stream_class *sc;
+       bool ret = true;
+
+       sc_count = bt_trace_class_get_stream_class_count(tc);
+       for (i = 0; i < sc_count; i++) {
+               sc = bt_trace_class_borrow_stream_class_by_index_const(tc, i);
+
+               BT_ASSERT(sc);
+
+               cc = bt_stream_class_borrow_default_clock_class_const(sc);
+               if (!cc) {
+                       ret = false;
+                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                               "Stream class doesn't have a default clock class: "
+                               "sc-id=%" PRIu64 ", sc-name=\"%s\"",
+                               bt_stream_class_get_id(sc),
+                               bt_stream_class_get_name(sc));
+                       goto end;
+               }
+       }
+
+end:
+       return ret;
+}
+/*
+ * Iterate over the stream classes and returns the first clock class
+ * encountered. This is useful to create message iterator inactivity message as
+ * we don't need a particular clock class.
+ */
+static
+const bt_clock_class *borrow_any_clock_class(bt_trace_class *tc)
+{
+       uint64_t i, sc_count;
+       const bt_clock_class *cc = NULL;
+       const bt_stream_class *sc;
+
+       sc_count = bt_trace_class_get_stream_class_count(tc);
+       for (i = 0; i < sc_count; i++) {
+               sc = bt_trace_class_borrow_stream_class_by_index_const(tc, i);
+               BT_ASSERT_DBG(sc);
+
+               cc = bt_stream_class_borrow_default_clock_class_const(sc);
+               if (cc) {
+                       goto end;
+               }
+       }
+end:
+       BT_ASSERT_DBG(cc);
+       return cc;
+}
+
+BT_HIDDEN
+enum lttng_live_iterator_status lttng_live_metadata_update(
+               struct lttng_live_trace *trace)
+{
+       struct lttng_live_session *session = trace->session;
+       struct lttng_live_metadata *metadata = trace->metadata;
+       size_t size, len_read = 0;
+       char *metadata_buf = NULL;
+       bool keep_receiving;
+       FILE *fp = NULL;
+       enum ctf_metadata_decoder_status decoder_status;
+       enum lttng_live_iterator_status status =
+               LTTNG_LIVE_ITERATOR_STATUS_OK;
+       bt_logging_level log_level = trace->log_level;
+       bt_self_component *self_comp = trace->self_comp;
+       enum lttng_live_get_one_metadata_status metadata_status;
+
+       BT_COMP_LOGD("Updating metadata for trace: session-id=%" PRIu64
+               ", trace-id=%" PRIu64, session->id, trace->id);
+
+       /* No metadata stream yet. */
+       if (!metadata) {
+               if (session->new_streams_needed) {
+                       status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN;
+               } else {
+                       session->new_streams_needed = true;
+                       status = LTTNG_LIVE_ITERATOR_STATUS_CONTINUE;
+               }
+               goto end;
+       }
+
+       if (trace->metadata_stream_state != LTTNG_LIVE_METADATA_STREAM_STATE_NEEDED) {
+               goto end;
+       }
+
+       /*
+        * Open a new write only file handle to populate the `metadata_buf`
+        * memory buffer so we can write in loop in it easily.
+        */
+       fp = bt_open_memstream(&metadata_buf, &size);
+       if (!fp) {
+               if (errno == EINTR &&
+                               lttng_live_graph_is_canceled(session->lttng_live_msg_iter)) {
+                       session->lttng_live_msg_iter->was_interrupted = true;
+                       status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN;
+               } else {
+                       BT_COMP_LOGE_APPEND_CAUSE_ERRNO(self_comp,
+                               "Metadata open_memstream", ".");
+                       status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
+               }
+               goto end;
+       }
+
+       keep_receiving = true;
+       /* Grab all available metadata. */
+       while (keep_receiving) {
+               size_t reply_len = 0;
+               /*
+                * lttng_live_get_one_metadata_packet() asks the Relay Daemon
+                * for new metadata. If new metadata is received, the function
+                * writes it to the provided file handle and updates the
+                * reply_len output parameter. We call this function in loop
+                * until it returns _END meaning that no new metadata is
+                * available.
+                * We may receive a _CLOSED status if the metadata stream we
+                * are requesting is no longer available on the relay.
+                * If we receive an _ERROR status, it means there was a
+                * networking, allocating, or some other unrecoverable error.
+                */
+               metadata_status = lttng_live_get_one_metadata_packet(trace, fp,
+                       &reply_len);
+
+               switch (metadata_status) {
+               case LTTNG_LIVE_GET_ONE_METADATA_STATUS_OK:
+                       len_read += reply_len;
+                       break;
+               case LTTNG_LIVE_GET_ONE_METADATA_STATUS_END:
+                       keep_receiving = false;
+                       break;
+               case LTTNG_LIVE_GET_ONE_METADATA_STATUS_CLOSED:
+                       BT_COMP_LOGD("Metadata stream was closed by the Relay, the trace is no longer active: "
+                               "trace-id=%" PRIu64 ", metadata-stream-id=%" PRIu64,
+                               trace->id, metadata->stream_id);
+                       /*
+                        * The stream was closed and we received everything
+                        * there was to receive for this metadata stream.
+                        * We go on with the decoding of what we received. So
+                        * that data stream can be decoded.
+                        */
+                       keep_receiving = false;
+                       trace->metadata_stream_state = LTTNG_LIVE_METADATA_STREAM_STATE_CLOSED;
+                       break;
+               case LTTNG_LIVE_GET_ONE_METADATA_STATUS_ERROR:
+                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                               "Error getting one trace metadata packet: "
+                               "trace-id=%" PRIu64, trace->id);
+                       goto error;
+               default:
+                       bt_common_abort();
+               }
+       }
+
+       /* The memory buffer `metadata_buf` contains all the metadata. */
+       if (bt_close_memstream(&metadata_buf, &size, fp)) {
+               BT_COMP_LOGW_ERRNO("Metadata bt_close_memstream", ".");
+       }
+
+       fp = NULL;
+
+       if (len_read == 0) {
+               if (!trace->trace) {
+                       status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN;
+                       goto end;
+               }
+
+               /* The relay sent zero bytes of metdata. */
+               trace->metadata_stream_state = LTTNG_LIVE_METADATA_STREAM_STATE_NOT_NEEDED;
+               goto end;
+       }
+
+       /*
+        * Open a new reading file handle on the `metadata_buf` and pass it to
+        * the metadata decoder.
+        */
+       fp = bt_fmemopen(metadata_buf, len_read, "rb");
+       if (!fp) {
+               if (errno == EINTR &&
+                               lttng_live_graph_is_canceled(session->lttng_live_msg_iter)) {
+                       session->lttng_live_msg_iter->was_interrupted = true;
+                       status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN;
+               } else {
+                       BT_COMP_LOGE_APPEND_CAUSE_ERRNO(self_comp,
+                               "Cannot memory-open metadata buffer", ".");
+                       status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
+               }
+               goto end;
+       }
+
+       /*
+        * The call to ctf_metadata_decoder_append_content() will append
+        * new metadata to our current trace class.
+        */
+       BT_COMP_LOGD("Appending new metadata to the ctf_trace class");
+       decoder_status = ctf_metadata_decoder_append_content(
+               metadata->decoder, fp);
+       switch (decoder_status) {
+       case CTF_METADATA_DECODER_STATUS_OK:
+               if (!trace->trace_class) {
+                       struct ctf_trace_class *tc =
+                               ctf_metadata_decoder_borrow_ctf_trace_class(
+                                       metadata->decoder);
+
+                       trace->trace_class =
+                               ctf_metadata_decoder_get_ir_trace_class(
+                                               metadata->decoder);
+                       trace->trace = bt_trace_create(trace->trace_class);
+                       if (!trace->trace) {
+                               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                                       "Failed to create bt_trace");
+                               goto error;
+                       }
+                       if (ctf_trace_class_configure_ir_trace(tc,
+                                       trace->trace)) {
+                               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                                       "Failed to configure ctf trace class");
+                               goto error;
+                       }
+                       if (!stream_classes_all_have_default_clock_class(
+                                       trace->trace_class, log_level,
+                                       self_comp)) {
+                               /* Error logged in function. */
+                               goto error;
+                       }
+                       trace->clock_class =
+                               borrow_any_clock_class(trace->trace_class);
+               }
+
+               /* The metadata was updated succesfully. */
+               trace->metadata_stream_state = LTTNG_LIVE_METADATA_STREAM_STATE_NOT_NEEDED;
+
+               break;
+       default:
+               goto error;
+       }
+
+       goto end;
+
+error:
+       status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
+end:
+       if (fp) {
+               int closeret;
+
+               closeret = fclose(fp);
+               if (closeret) {
+                       BT_COMP_LOGW_ERRNO("Error on fclose", ".");
+               }
+       }
+       free(metadata_buf);
+       return status;
+}
+
+BT_HIDDEN
+int lttng_live_metadata_create_stream(struct lttng_live_session *session,
+               uint64_t ctf_trace_id, uint64_t stream_id,
+               const char *trace_name)
+{
+       bt_self_component *self_comp = session->self_comp;
+       bt_logging_level log_level = session->log_level;
+       struct lttng_live_metadata *metadata = NULL;
+       struct lttng_live_trace *trace;
+
+       ctf_metadata_decoder_config cfg{};
+       cfg.log_level = session->log_level;
+       cfg.self_comp = session->self_comp;
+       cfg.clock_class_offset_s = 0;
+       cfg.clock_class_offset_ns = 0;
+       cfg.create_trace_class = true;
+
+       metadata = g_new0(struct lttng_live_metadata, 1);
+       if (!metadata) {
+               return -1;
+       }
+       metadata->log_level = session->log_level;
+       metadata->self_comp = session->self_comp;
+       metadata->stream_id = stream_id;
+
+       metadata->decoder = ctf_metadata_decoder_create(&cfg);
+       if (!metadata->decoder) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Failed to create CTF metadata decoder");
+               goto error;
+       }
+       trace = lttng_live_session_borrow_or_create_trace_by_id(session,
+               ctf_trace_id);
+       if (!trace) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Failed to borrow trace");
+               goto error;
+       }
+       trace->metadata = metadata;
+       return 0;
+
+error:
+       ctf_metadata_decoder_destroy(metadata->decoder);
+       g_free(metadata);
+       return -1;
+}
+
+BT_HIDDEN
+void lttng_live_metadata_fini(struct lttng_live_trace *trace)
+{
+       struct lttng_live_metadata *metadata = trace->metadata;
+
+       if (!metadata) {
+               return;
+       }
+       ctf_metadata_decoder_destroy(metadata->decoder);
+       trace->metadata = NULL;
+       g_free(metadata);
+}
diff --git a/src/plugins/ctf/lttng-live/metadata.h b/src/plugins/ctf/lttng-live/metadata.h
deleted file mode 100644 (file)
index 47de805..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2016 Philippe Proulx <pproulx@efficios.com>
- */
-
-#ifndef LTTNG_LIVE_METADATA_H
-#define LTTNG_LIVE_METADATA_H
-
-#include <stdint.h>
-#include <stdio.h>
-
-#include "lttng-live.h"
-
-int lttng_live_metadata_create_stream(struct lttng_live_session *session,
-               uint64_t ctf_trace_id, uint64_t stream_id,
-               const char *trace_name);
-
-enum lttng_live_iterator_status lttng_live_metadata_update(
-               struct lttng_live_trace *trace);
-
-void lttng_live_metadata_fini(struct lttng_live_trace *trace);
-
-#endif /* LTTNG_LIVE_METADATA_H */
diff --git a/src/plugins/ctf/lttng-live/metadata.hpp b/src/plugins/ctf/lttng-live/metadata.hpp
new file mode 100644 (file)
index 0000000..08a1fb6
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2016 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#ifndef LTTNG_LIVE_METADATA_H
+#define LTTNG_LIVE_METADATA_H
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include "lttng-live.hpp"
+
+int lttng_live_metadata_create_stream(struct lttng_live_session *session,
+               uint64_t ctf_trace_id, uint64_t stream_id,
+               const char *trace_name);
+
+enum lttng_live_iterator_status lttng_live_metadata_update(
+               struct lttng_live_trace *trace);
+
+void lttng_live_metadata_fini(struct lttng_live_trace *trace);
+
+#endif /* LTTNG_LIVE_METADATA_H */
diff --git a/src/plugins/ctf/lttng-live/viewer-connection.c b/src/plugins/ctf/lttng-live/viewer-connection.c
deleted file mode 100644 (file)
index 3548b63..0000000
+++ /dev/null
@@ -1,1961 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2019 Francis Deslauriers <francis.deslauriers@efficios.com>
- * Copyright 2016 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- */
-
-#define BT_COMP_LOG_SELF_COMP (viewer_connection->self_comp)
-#define BT_LOG_OUTPUT_LEVEL (viewer_connection->log_level)
-#define BT_LOG_TAG "PLUGIN/SRC.CTF.LTTNG-LIVE/VIEWER"
-#include "logging/comp-logging.h"
-
-#include <fcntl.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <glib.h>
-
-#include "compat/socket.h"
-#include "compat/endian.h"
-#include "compat/compiler.h"
-#include "common/common.h"
-#include <babeltrace2/babeltrace.h>
-
-#include "lttng-live.h"
-#include "viewer-connection.h"
-#include "lttng-viewer-abi.h"
-#include "data-stream.h"
-#include "metadata.h"
-
-#define viewer_handle_send_recv_status(_self_comp, _self_comp_class,   \
-               _status, _action, _msg_str)                             \
-do {                                                                   \
-       switch (_status) {                                              \
-       case LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED:                      \
-               break;                                                  \
-       case LTTNG_LIVE_VIEWER_STATUS_ERROR:                            \
-               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(_self_comp,     \
-                       _self_comp_class, "Error " _action " " _msg_str); \
-               break;                                                  \
-       default:                                                        \
-                bt_common_abort();                                     \
-       }                                                               \
-} while (0)
-
-#define viewer_handle_send_status(_self_comp, _self_comp_class, _status, _msg_str) \
-       viewer_handle_send_recv_status(_self_comp, _self_comp_class, _status, \
-               "sending", _msg_str)
-
-#define viewer_handle_recv_status(_self_comp, _self_comp_class, _status, _msg_str) \
-       viewer_handle_send_recv_status(_self_comp, _self_comp_class, _status, \
-               "receiving", _msg_str)
-
-#define LTTNG_LIVE_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE_ERRNO(_self_comp, \
-               _self_comp_class, _msg, _fmt, ...) \
-       do {                                                                            \
-               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(_self_comp, _self_comp_class,   \
-                       _msg ": %s" _fmt, bt_socket_errormsg(), ##__VA_ARGS__);         \
-       } while (0)
-
-static
-const char *lttng_viewer_command_string(enum lttng_viewer_command cmd)
-{
-       switch (cmd){
-       case LTTNG_VIEWER_CONNECT:
-               return "CONNECT";
-       case LTTNG_VIEWER_LIST_SESSIONS:
-               return "LIST_SESSIONS";
-       case LTTNG_VIEWER_ATTACH_SESSION:
-               return "ATTACH_SESSION";
-       case LTTNG_VIEWER_GET_NEXT_INDEX:
-               return "GET_NEXT_INDEX";
-       case LTTNG_VIEWER_GET_PACKET:
-               return "GET_PACKET";
-       case LTTNG_VIEWER_GET_METADATA:
-               return "GET_METADATA";
-       case LTTNG_VIEWER_GET_NEW_STREAMS:
-               return "GET_NEW_STREAMS";
-       case LTTNG_VIEWER_CREATE_SESSION:
-               return "CREATE_SESSION";
-       case LTTNG_VIEWER_DETACH_SESSION:
-               return "DETACH_SESSION";
-       }
-
-       bt_common_abort();
-}
-
-static
-const char *lttng_viewer_next_index_return_code_string(
-               enum lttng_viewer_next_index_return_code code)
-{
-       switch (code) {
-       case LTTNG_VIEWER_INDEX_OK:
-               return "INDEX_OK";
-       case LTTNG_VIEWER_INDEX_RETRY:
-               return "INDEX_RETRY";
-       case LTTNG_VIEWER_INDEX_HUP:
-               return "INDEX_HUP";
-       case LTTNG_VIEWER_INDEX_ERR:
-               return "INDEX_ERR";
-       case LTTNG_VIEWER_INDEX_INACTIVE:
-               return "INDEX_INACTIVE";
-       case LTTNG_VIEWER_INDEX_EOF:
-               return "INDEX_EOF";
-       }
-
-       bt_common_abort();
-}
-
-static
-const char *lttng_viewer_get_packet_return_code_string(
-               enum lttng_viewer_get_packet_return_code code)
-{
-       switch (code) {
-       case LTTNG_VIEWER_GET_PACKET_OK:
-               return "GET_PACKET_OK";
-       case LTTNG_VIEWER_GET_PACKET_RETRY:
-               return "GET_PACKET_RETRY";
-       case LTTNG_VIEWER_GET_PACKET_ERR:
-               return "GET_PACKET_ERR";
-       case LTTNG_VIEWER_GET_PACKET_EOF:
-               return "GET_PACKET_EOF";
-       }
-
-       bt_common_abort();
-};
-
-static
-const char *lttng_viewer_seek_string(enum lttng_viewer_seek seek)
-{
-       switch (seek) {
-       case LTTNG_VIEWER_SEEK_BEGINNING:
-               return "SEEK_BEGINNING";
-       case LTTNG_VIEWER_SEEK_LAST:
-               return "SEEK_LAST";
-       }
-
-       bt_common_abort();
-}
-
-static inline
-enum lttng_live_iterator_status viewer_status_to_live_iterator_status(
-               enum lttng_live_viewer_status viewer_status)
-{
-       switch (viewer_status) {
-       case LTTNG_LIVE_VIEWER_STATUS_OK:
-               return LTTNG_LIVE_ITERATOR_STATUS_OK;
-       case LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED:
-               return LTTNG_LIVE_ITERATOR_STATUS_AGAIN;
-       case LTTNG_LIVE_VIEWER_STATUS_ERROR:
-               return LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-       }
-
-       bt_common_abort();
-}
-
-static inline
-enum ctf_msg_iter_medium_status viewer_status_to_ctf_msg_iter_medium_status(
-               enum lttng_live_viewer_status viewer_status)
-{
-       switch (viewer_status) {
-       case LTTNG_LIVE_VIEWER_STATUS_OK:
-               return CTF_MSG_ITER_MEDIUM_STATUS_OK;
-       case LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED:
-               return CTF_MSG_ITER_MEDIUM_STATUS_AGAIN;
-       case LTTNG_LIVE_VIEWER_STATUS_ERROR:
-               return CTF_MSG_ITER_MEDIUM_STATUS_ERROR;
-       }
-
-       bt_common_abort();
-}
-
-static inline
-void viewer_connection_close_socket(
-               struct live_viewer_connection *viewer_connection)
-{
-       bt_self_component_class *self_comp_class =
-               viewer_connection->self_comp_class;
-       bt_self_component *self_comp =
-               viewer_connection->self_comp;
-       int ret = bt_socket_close(viewer_connection->control_sock);
-       if (ret == -1) {
-               BT_COMP_OR_COMP_CLASS_LOGW_ERRNO(
-                       self_comp, self_comp_class,
-                       "Error closing viewer connection socket: ", ".");
-       }
-
-       viewer_connection->control_sock = BT_INVALID_SOCKET;
-}
-
-/*
- * This function receives a message from the Relay daemon.
- * If it received the entire message, it returns _OK,
- * If it's interrupted, it returns _INTERRUPTED,
- * otherwise, it returns _ERROR.
- */
-static
-enum lttng_live_viewer_status lttng_live_recv(
-               struct live_viewer_connection *viewer_connection,
-               void *buf, size_t len)
-{
-       ssize_t received;
-       bt_self_component_class *self_comp_class =
-               viewer_connection->self_comp_class;
-       bt_self_component *self_comp =
-               viewer_connection->self_comp;
-       size_t total_received = 0, to_receive = len;
-       struct lttng_live_msg_iter *lttng_live_msg_iter =
-               viewer_connection->lttng_live_msg_iter;
-       enum lttng_live_viewer_status status;
-       BT_SOCKET sock = viewer_connection->control_sock;
-
-       /*
-        * Receive a message from the Relay.
-        */
-       do {
-               received = bt_socket_recv(sock, buf + total_received, to_receive, 0);
-               if (received == BT_SOCKET_ERROR) {
-                       if (bt_socket_interrupted()) {
-                               if (lttng_live_graph_is_canceled(lttng_live_msg_iter)) {
-                                       /*
-                                        * This interruption was due to a
-                                        * SIGINT and the graph is being torn
-                                        * down.
-                                        */
-                                       status = LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED;
-                                       lttng_live_msg_iter->was_interrupted = true;
-                                       goto end;
-                               } else {
-                                       /*
-                                        * A signal was received, but the graph
-                                        * is not being torn down. Carry on.
-                                        */
-                                       continue;
-                               }
-                       } else {
-                               /*
-                                * For any other types of socket error, close
-                                * the socket and return an error.
-                                */
-                               LTTNG_LIVE_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE_ERRNO(
-                                       self_comp, self_comp_class,
-                                       "Error receiving from Relay", ".");
-
-                               viewer_connection_close_socket(viewer_connection);
-                               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-                               goto end;
-                       }
-               } else if (received == 0) {
-                       /*
-                        * The recv() call returned 0. This means the
-                        * connection was orderly shutdown from the other peer.
-                        * If that happens when we are trying to receive
-                        * a message from it, it means something when wrong.
-                        * Close the socket and return an error.
-                        */
-                       BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp,
-                               self_comp_class, "Remote side has closed connection");
-                       viewer_connection_close_socket(viewer_connection);
-                       status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-                       goto end;
-               }
-
-               BT_ASSERT(received <= to_receive);
-               total_received += received;
-               to_receive -= received;
-
-       } while (to_receive > 0);
-
-       BT_ASSERT(total_received == len);
-       status = LTTNG_LIVE_VIEWER_STATUS_OK;
-
-end:
-       return status;
-}
-
-/*
- * This function sends a message to the Relay daemon.
- * If it send the message, it returns _OK,
- * If it's interrupted, it returns _INTERRUPTED,
- * otherwise, it returns _ERROR.
- */
-static
-enum lttng_live_viewer_status lttng_live_send(
-               struct live_viewer_connection *viewer_connection,
-               const void *buf, size_t len)
-{
-       enum lttng_live_viewer_status status;
-       bt_self_component_class *self_comp_class =
-               viewer_connection->self_comp_class;
-       bt_self_component *self_comp =
-               viewer_connection->self_comp;
-       struct lttng_live_msg_iter *lttng_live_msg_iter =
-               viewer_connection->lttng_live_msg_iter;
-       BT_SOCKET sock = viewer_connection->control_sock;
-       size_t to_send = len;
-       ssize_t total_sent = 0;
-
-       do {
-               ssize_t sent = bt_socket_send_nosigpipe(sock, buf + total_sent,
-                       to_send);
-               if (sent == BT_SOCKET_ERROR) {
-                       if (bt_socket_interrupted()) {
-                               if (lttng_live_graph_is_canceled(lttng_live_msg_iter)) {
-                                       /*
-                                        * This interruption was a SIGINT and
-                                        * the graph is being teared down.
-                                        */
-                                       status = LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED;
-                                       lttng_live_msg_iter->was_interrupted = true;
-                                       goto end;
-                               } else {
-                                       /*
-                                        * A signal was received, but the graph
-                                        * is not being teared down. Carry on.
-                                        */
-                                       continue;
-                               }
-                       } else {
-                               /*
-                                * For any other types of socket error, close
-                                * the socket and return an error.
-                                */
-                               LTTNG_LIVE_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE_ERRNO(
-                                       self_comp, self_comp_class,
-                                       "Error sending to Relay", ".");
-
-                               viewer_connection_close_socket(viewer_connection);
-                               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-                               goto end;
-                       }
-               }
-
-               BT_ASSERT(sent <= to_send);
-               total_sent += sent;
-               to_send -= sent;
-
-       } while (to_send > 0);
-
-       BT_ASSERT(total_sent == len);
-       status = LTTNG_LIVE_VIEWER_STATUS_OK;
-
-end:
-       return status;
-}
-
-static
-int parse_url(struct live_viewer_connection *viewer_connection)
-{
-       char error_buf[256] = { 0 };
-       bt_self_component *self_comp = viewer_connection->self_comp;
-       bt_self_component_class *self_comp_class = viewer_connection->self_comp_class;
-       struct bt_common_lttng_live_url_parts lttng_live_url_parts = { 0 };
-       int ret = -1;
-       const char *path = viewer_connection->url->str;
-
-       if (!path) {
-               goto end;
-       }
-
-       lttng_live_url_parts = bt_common_parse_lttng_live_url(path, error_buf,
-               sizeof(error_buf));
-       if (!lttng_live_url_parts.proto) {
-               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp,
-                       self_comp_class,"Invalid LTTng live URL format: %s",
-                       error_buf);
-               goto end;
-       }
-       viewer_connection->proto = lttng_live_url_parts.proto;
-       lttng_live_url_parts.proto = NULL;
-
-       viewer_connection->relay_hostname = lttng_live_url_parts.hostname;
-       lttng_live_url_parts.hostname = NULL;
-
-       if (lttng_live_url_parts.port >= 0) {
-               viewer_connection->port = lttng_live_url_parts.port;
-       } else {
-               viewer_connection->port = LTTNG_DEFAULT_NETWORK_VIEWER_PORT;
-       }
-
-       viewer_connection->target_hostname = lttng_live_url_parts.target_hostname;
-       lttng_live_url_parts.target_hostname = NULL;
-
-       if (lttng_live_url_parts.session_name) {
-               viewer_connection->session_name = lttng_live_url_parts.session_name;
-               lttng_live_url_parts.session_name = NULL;
-       }
-
-       ret = 0;
-
-end:
-       bt_common_destroy_lttng_live_url_parts(&lttng_live_url_parts);
-       return ret;
-}
-
-static
-enum lttng_live_viewer_status lttng_live_handshake(
-               struct live_viewer_connection *viewer_connection)
-{
-       struct lttng_viewer_cmd cmd;
-       struct lttng_viewer_connect connect;
-       enum lttng_live_viewer_status status;
-       bt_self_component_class *self_comp_class = viewer_connection->self_comp_class;
-       bt_self_component *self_comp = viewer_connection->self_comp;
-       const size_t cmd_buf_len = sizeof(cmd) + sizeof(connect);
-       char cmd_buf[cmd_buf_len];
-
-       BT_COMP_OR_COMP_CLASS_LOGD(self_comp, self_comp_class,
-               "Handshaking with the relay daemon: cmd=%s, major-version=%u, minor-version=%u",
-               lttng_viewer_command_string(LTTNG_VIEWER_CONNECT), LTTNG_LIVE_MAJOR,
-               LTTNG_LIVE_MINOR);
-
-       cmd.cmd = htobe32(LTTNG_VIEWER_CONNECT);
-       cmd.data_size = htobe64((uint64_t) sizeof(connect));
-       cmd.cmd_version = htobe32(0);
-
-       connect.viewer_session_id = -1ULL;      /* will be set on recv */
-       connect.major = htobe32(LTTNG_LIVE_MAJOR);
-       connect.minor = htobe32(LTTNG_LIVE_MINOR);
-       connect.type = htobe32(LTTNG_VIEWER_CLIENT_COMMAND);
-
-       /*
-        * Merge the cmd and connection request to prevent a write-write
-        * sequence on the TCP socket. Otherwise, a delayed ACK will prevent the
-        * second write to be performed quickly in presence of Nagle's algorithm
-        */
-       memcpy(cmd_buf, &cmd, sizeof(cmd));
-       memcpy(cmd_buf + sizeof(cmd), &connect, sizeof(connect));
-
-       status = lttng_live_send(viewer_connection, &cmd_buf, cmd_buf_len);
-       if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-               viewer_handle_send_status(self_comp, self_comp_class,
-                       status, "viewer connect command");
-               goto end;
-       }
-
-       status = lttng_live_recv(viewer_connection, &connect, sizeof(connect));
-       if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-               viewer_handle_recv_status(self_comp, self_comp_class,
-                       status, "viewer connect reply");
-               goto end;
-       }
-
-       BT_COMP_OR_COMP_CLASS_LOGI(self_comp, self_comp_class,
-               "Received viewer session ID : %" PRIu64,
-               (uint64_t) be64toh(connect.viewer_session_id));
-       BT_COMP_OR_COMP_CLASS_LOGI(self_comp, self_comp_class,
-               "Relayd version : %u.%u", be32toh(connect.major),
-               be32toh(connect.minor));
-
-       if (LTTNG_LIVE_MAJOR != be32toh(connect.major)) {
-               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp,
-                       self_comp_class, "Incompatible lttng-relayd protocol");
-               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-               goto end;
-       }
-       /* Use the smallest protocol version implemented. */
-       if (LTTNG_LIVE_MINOR > be32toh(connect.minor)) {
-               viewer_connection->minor =  be32toh(connect.minor);
-       } else {
-               viewer_connection->minor =  LTTNG_LIVE_MINOR;
-       }
-       viewer_connection->major = LTTNG_LIVE_MAJOR;
-
-       status = LTTNG_LIVE_VIEWER_STATUS_OK;
-
-       goto end;
-
-end:
-       return status;
-}
-
-static
-enum lttng_live_viewer_status lttng_live_connect_viewer(
-               struct live_viewer_connection *viewer_connection)
-{
-       struct hostent *host;
-       struct sockaddr_in server_addr;
-       enum lttng_live_viewer_status status;
-       bt_self_component_class *self_comp_class = viewer_connection->self_comp_class;
-       bt_self_component *self_comp = viewer_connection->self_comp;
-
-       if (parse_url(viewer_connection)) {
-               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp,
-                       self_comp_class, "Failed to parse URL");
-               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-               goto error;
-       }
-
-       BT_COMP_OR_COMP_CLASS_LOGD(self_comp, self_comp_class,
-               "Connecting to hostname : %s, port : %d, "
-               "target hostname : %s, session name : %s, proto : %s",
-               viewer_connection->relay_hostname->str,
-               viewer_connection->port,
-               !viewer_connection->target_hostname ?
-               "<none>" : viewer_connection->target_hostname->str,
-               !viewer_connection->session_name ?
-               "<none>" : viewer_connection->session_name->str,
-               viewer_connection->proto->str);
-
-       host = gethostbyname(viewer_connection->relay_hostname->str);
-       if (!host) {
-               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp,
-                       self_comp_class, "Cannot lookup hostname: hostname=\"%s\"",
-                       viewer_connection->relay_hostname->str);
-               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-               goto error;
-       }
-
-       if ((viewer_connection->control_sock = socket(AF_INET, SOCK_STREAM, 0)) == BT_INVALID_SOCKET) {
-               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp,
-                       self_comp_class, "Socket creation failed: %s", bt_socket_errormsg());
-               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-               goto error;
-       }
-
-       server_addr.sin_family = AF_INET;
-       server_addr.sin_port = htons(viewer_connection->port);
-       server_addr.sin_addr = *((struct in_addr *) host->h_addr);
-       memset(&(server_addr.sin_zero), 0, 8);
-
-       if (connect(viewer_connection->control_sock, (struct sockaddr *) &server_addr,
-                               sizeof(struct sockaddr)) == BT_SOCKET_ERROR) {
-               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp,
-                       self_comp_class, "Connection failed: %s",
-                       bt_socket_errormsg());
-               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-               goto error;
-       }
-
-       status = lttng_live_handshake(viewer_connection);
-
-       /*
-        * Only print error and append cause in case of error. not in case of
-        * interruption.
-        */
-       if (status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
-               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp,
-                       self_comp_class, "Viewer handshake failed");
-               goto error;
-       } else if (status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
-               goto end;
-       }
-
-       goto end;
-
-error:
-       if (viewer_connection->control_sock != BT_INVALID_SOCKET) {
-               if (bt_socket_close(viewer_connection->control_sock) == BT_SOCKET_ERROR) {
-                       BT_COMP_OR_COMP_CLASS_LOGW(self_comp, self_comp_class,
-                               "Error closing socket: %s.", bt_socket_errormsg());
-               }
-       }
-       viewer_connection->control_sock = BT_INVALID_SOCKET;
-end:
-       return status;
-}
-
-static
-void lttng_live_disconnect_viewer(
-               struct live_viewer_connection *viewer_connection)
-{
-       bt_self_component_class *self_comp_class = viewer_connection->self_comp_class;
-       bt_self_component *self_comp = viewer_connection->self_comp;
-
-       if (viewer_connection->control_sock == BT_INVALID_SOCKET) {
-               return;
-       }
-       if (bt_socket_close(viewer_connection->control_sock) == BT_SOCKET_ERROR) {
-               BT_COMP_OR_COMP_CLASS_LOGW(self_comp, self_comp_class,
-                       "Error closing socket: %s", bt_socket_errormsg());
-               viewer_connection->control_sock = BT_INVALID_SOCKET;
-       }
-}
-
-static
-int list_update_session(bt_value *results,
-               const struct lttng_viewer_session *session,
-               bool *_found, struct live_viewer_connection *viewer_connection)
-{
-       bt_self_component_class *self_comp_class = viewer_connection->self_comp_class;
-       bt_self_component *self_comp = viewer_connection->self_comp;
-       int ret = 0;
-       uint64_t i, len;
-       bt_value *map = NULL;
-       bt_value *hostname = NULL;
-       bt_value *session_name = NULL;
-       bt_value *btval = NULL;
-       bool found = false;
-
-       len = bt_value_array_get_length(results);
-       for (i = 0; i < len; i++) {
-               const char *hostname_str = NULL;
-               const char *session_name_str = NULL;
-
-               map = bt_value_array_borrow_element_by_index(results, i);
-               hostname = bt_value_map_borrow_entry_value(map, "target-hostname");
-               if (!hostname) {
-                       BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp,
-                               self_comp_class,
-                               "Error borrowing \"target-hostname\" entry.");
-                       ret = -1;
-                       goto end;
-               }
-               session_name = bt_value_map_borrow_entry_value(map, "session-name");
-               if (!session_name) {
-                       BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp,
-                               self_comp_class,
-                               "Error borrowing \"session-name\" entry.");
-                       ret = -1;
-                       goto end;
-               }
-               hostname_str = bt_value_string_get(hostname);
-               session_name_str = bt_value_string_get(session_name);
-
-               if (strcmp(session->hostname, hostname_str) == 0
-                               && strcmp(session->session_name, session_name_str) == 0) {
-                       int64_t val;
-                       uint32_t streams = be32toh(session->streams);
-                       uint32_t clients = be32toh(session->clients);
-
-                       found = true;
-
-                       btval = bt_value_map_borrow_entry_value(map, "stream-count");
-                       if (!btval) {
-                               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                                       self_comp, self_comp_class,
-                                       "Error borrowing \"stream-count\" entry.");
-                               ret = -1;
-                               goto end;
-                       }
-                       val = bt_value_integer_unsigned_get(btval);
-                       /* sum */
-                       val += streams;
-                       bt_value_integer_unsigned_set(btval, val);
-
-                       btval = bt_value_map_borrow_entry_value(map, "client-count");
-                       if (!btval) {
-                               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
-                                       self_comp, self_comp_class,
-                                       "Error borrowing \"client-count\" entry.");
-                               ret = -1;
-                               goto end;
-                       }
-                       val = bt_value_integer_unsigned_get(btval);
-                       /* max */
-                       val = bt_max_t(int64_t, clients, val);
-                       bt_value_integer_unsigned_set(btval, val);
-               }
-
-               if (found) {
-                       break;
-               }
-       }
-end:
-       *_found = found;
-       return ret;
-}
-
-static
-int list_append_session(bt_value *results,
-               GString *base_url,
-               const struct lttng_viewer_session *session,
-               struct live_viewer_connection *viewer_connection)
-{
-       int ret = 0;
-       bt_self_component_class *self_comp_class = viewer_connection->self_comp_class;
-       bt_value_map_insert_entry_status insert_status;
-       bt_value_array_append_element_status append_status;
-       bt_value *map = NULL;
-       GString *url = NULL;
-       bool found = false;
-
-       /*
-        * If the session already exists, add the stream count to it,
-        * and do max of client counts.
-        */
-       ret = list_update_session(results, session, &found, viewer_connection);
-       if (ret || found) {
-               goto end;
-       }
-
-       map = bt_value_map_create();
-       if (!map) {
-               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                       "Error creating map value.");
-               ret = -1;
-               goto end;
-       }
-
-       if (base_url->len < 1) {
-               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                       "Error: base_url length smaller than 1.");
-               ret = -1;
-               goto end;
-       }
-       /*
-        * key = "url",
-        * value = <string>,
-        */
-       url = g_string_new(base_url->str);
-       g_string_append(url, "/host/");
-       g_string_append(url, session->hostname);
-       g_string_append_c(url, '/');
-       g_string_append(url, session->session_name);
-
-       insert_status = bt_value_map_insert_string_entry(map, "url", url->str);
-       if (insert_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
-               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                       "Error inserting \"url\" entry.");
-               ret = -1;
-               goto end;
-       }
-
-       /*
-        * key = "target-hostname",
-        * value = <string>,
-        */
-       insert_status = bt_value_map_insert_string_entry(map, "target-hostname",
-               session->hostname);
-       if (insert_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
-               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                       "Error inserting \"target-hostname\" entry.");
-               ret = -1;
-               goto end;
-       }
-
-       /*
-        * key = "session-name",
-        * value = <string>,
-        */
-       insert_status = bt_value_map_insert_string_entry(map, "session-name",
-               session->session_name);
-       if (insert_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
-               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                       "Error inserting \"session-name\" entry.");
-               ret = -1;
-               goto end;
-       }
-
-       /*
-        * key = "timer-us",
-        * value = <integer>,
-        */
-       {
-               uint32_t live_timer = be32toh(session->live_timer);
-
-               insert_status = bt_value_map_insert_unsigned_integer_entry(
-                       map, "timer-us", live_timer);
-               if (insert_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
-                       BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                               "Error inserting \"timer-us\" entry.");
-                       ret = -1;
-                       goto end;
-               }
-       }
-
-       /*
-        * key = "stream-count",
-        * value = <integer>,
-        */
-       {
-               uint32_t streams = be32toh(session->streams);
-
-               insert_status = bt_value_map_insert_unsigned_integer_entry(map,
-                       "stream-count", streams);
-               if (insert_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
-                       BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                               "Error inserting \"stream-count\" entry.");
-                       ret = -1;
-                       goto end;
-               }
-       }
-
-       /*
-        * key = "client-count",
-        * value = <integer>,
-        */
-       {
-               uint32_t clients = be32toh(session->clients);
-
-               insert_status = bt_value_map_insert_unsigned_integer_entry(map,
-                       "client-count", clients);
-               if (insert_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
-                       BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                               "Error inserting \"client-count\" entry.");
-                       ret = -1;
-                       goto end;
-               }
-       }
-
-       append_status = bt_value_array_append_element(results, map);
-       if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
-               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                       "Error appending map to results.");
-               ret = -1;
-       }
-
-end:
-       if (url) {
-               g_string_free(url, true);
-       }
-       BT_VALUE_PUT_REF_AND_RESET(map);
-       return ret;
-}
-
-/*
- * Data structure returned:
- *
- * {
- *   <array> = {
- *     [n] = {
- *       <map> = {
- *         {
- *           key = "url",
- *           value = <string>,
- *         },
- *         {
- *           key = "target-hostname",
- *           value = <string>,
- *         },
- *         {
- *           key = "session-name",
- *           value = <string>,
- *         },
- *         {
- *           key = "timer-us",
- *           value = <integer>,
- *         },
- *         {
- *           key = "stream-count",
- *           value = <integer>,
- *         },
- *         {
- *           key = "client-count",
- *           value = <integer>,
- *         },
- *       },
- *     }
- *   }
- */
-
-BT_HIDDEN
-bt_component_class_query_method_status live_viewer_connection_list_sessions(
-               struct live_viewer_connection *viewer_connection,
-               const bt_value **user_result)
-{
-       bt_self_component_class *self_comp_class = viewer_connection->self_comp_class;
-       bt_component_class_query_method_status status =
-               BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_OK;
-       bt_value *result = NULL;
-       enum lttng_live_viewer_status viewer_status;
-       struct lttng_viewer_cmd cmd;
-       struct lttng_viewer_list_sessions list;
-       uint32_t i, sessions_count;
-
-       result = bt_value_array_create();
-       if (!result) {
-               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                       "Error creating array");
-               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_MEMORY_ERROR;
-               goto error;
-       }
-
-       BT_LOGD("Requesting list of sessions: cmd=%s",
-               lttng_viewer_command_string(LTTNG_VIEWER_LIST_SESSIONS));
-
-       cmd.cmd = htobe32(LTTNG_VIEWER_LIST_SESSIONS);
-       cmd.data_size = htobe64((uint64_t) 0);
-       cmd.cmd_version = htobe32(0);
-
-       viewer_status = lttng_live_send(viewer_connection, &cmd, sizeof(cmd));
-       if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
-               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                       "Error sending list sessions command");
-               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
-               goto error;
-       } else if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
-               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_AGAIN;
-               goto error;
-       }
-
-       viewer_status = lttng_live_recv(viewer_connection, &list, sizeof(list));
-       if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
-               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                       "Error receiving session list");
-               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
-               goto error;
-       } else if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
-               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_AGAIN;
-               goto error;
-       }
-
-       sessions_count = be32toh(list.sessions_count);
-       for (i = 0; i < sessions_count; i++) {
-               struct lttng_viewer_session lsession;
-
-               viewer_status = lttng_live_recv(viewer_connection, &lsession,
-                       sizeof(lsession));
-               if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
-                       BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                               "Error receiving session:");
-                       status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
-                       goto error;
-               } else if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
-                       status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_AGAIN;
-                       goto error;
-               }
-
-               lsession.hostname[LTTNG_VIEWER_HOST_NAME_MAX - 1] = '\0';
-               lsession.session_name[LTTNG_VIEWER_NAME_MAX - 1] = '\0';
-               if (list_append_session(result, viewer_connection->url,
-                               &lsession, viewer_connection)) {
-                       BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
-                               "Error appending session");
-                       status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
-                       goto error;
-               }
-       }
-
-       *user_result = result;
-       goto end;
-error:
-       BT_VALUE_PUT_REF_AND_RESET(result);
-end:
-       return status;
-}
-
-static
-enum lttng_live_viewer_status lttng_live_query_session_ids(
-               struct lttng_live_msg_iter *lttng_live_msg_iter)
-{
-       struct lttng_viewer_cmd cmd;
-       struct lttng_viewer_list_sessions list;
-       struct lttng_viewer_session lsession;
-       uint32_t i, sessions_count;
-       uint64_t session_id;
-       enum lttng_live_viewer_status status;
-       struct live_viewer_connection *viewer_connection =
-               lttng_live_msg_iter->viewer_connection;
-       bt_self_component *self_comp = viewer_connection->self_comp;
-       bt_self_component_class *self_comp_class =
-               viewer_connection->self_comp_class;
-
-       BT_COMP_LOGD("Asking the relay daemon for the list of sessions: cmd=%s",
-               lttng_viewer_command_string(LTTNG_VIEWER_LIST_SESSIONS));
-
-       cmd.cmd = htobe32(LTTNG_VIEWER_LIST_SESSIONS);
-       cmd.data_size = htobe64((uint64_t) 0);
-       cmd.cmd_version = htobe32(0);
-
-       status = lttng_live_send(viewer_connection, &cmd, sizeof(cmd));
-       if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-               viewer_handle_send_status(self_comp, self_comp_class,
-                       status, "list sessions command");
-               goto end;
-       }
-
-       status = lttng_live_recv(viewer_connection, &list, sizeof(list));
-       if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-               viewer_handle_recv_status(self_comp, self_comp_class,
-                       status, "session list reply");
-               goto end;
-       }
-
-       sessions_count = be32toh(list.sessions_count);
-       for (i = 0; i < sessions_count; i++) {
-               status = lttng_live_recv(viewer_connection, &lsession,
-                       sizeof(lsession));
-               if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-                       viewer_handle_recv_status(self_comp, self_comp_class,
-                               status, "session reply");
-                       goto end;
-               }
-               lsession.hostname[LTTNG_VIEWER_HOST_NAME_MAX - 1] = '\0';
-               lsession.session_name[LTTNG_VIEWER_NAME_MAX - 1] = '\0';
-               session_id = be64toh(lsession.id);
-
-               BT_COMP_LOGI("Adding session to internal list: "
-                       "session-id=%" PRIu64 ", hostname=\"%s\", session-name=\"%s\"",
-                       session_id, lsession.hostname, lsession.session_name);
-
-               if ((strncmp(lsession.session_name,
-                       viewer_connection->session_name->str,
-                       LTTNG_VIEWER_NAME_MAX) == 0) && (strncmp(lsession.hostname,
-                               viewer_connection->target_hostname->str,
-                               LTTNG_VIEWER_HOST_NAME_MAX) == 0)) {
-
-                       if (lttng_live_add_session(lttng_live_msg_iter, session_id,
-                                       lsession.hostname,
-                                       lsession.session_name)) {
-                               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                       "Failed to add live session");
-                               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-                               goto end;
-                       }
-               }
-       }
-
-       status = LTTNG_LIVE_VIEWER_STATUS_OK;
-
-end:
-       return status;
-}
-
-BT_HIDDEN
-enum lttng_live_viewer_status lttng_live_create_viewer_session(
-               struct lttng_live_msg_iter *lttng_live_msg_iter)
-{
-       struct lttng_viewer_cmd cmd;
-       struct lttng_viewer_create_session_response resp;
-       enum lttng_live_viewer_status status;
-       struct live_viewer_connection *viewer_connection =
-               lttng_live_msg_iter->viewer_connection;
-       bt_self_component *self_comp = viewer_connection->self_comp;
-       bt_self_component_class *self_comp_class =
-               viewer_connection->self_comp_class;
-
-       BT_COMP_OR_COMP_CLASS_LOGD(self_comp, self_comp_class,
-               "Creating a viewer session: cmd=%s",
-               lttng_viewer_command_string(LTTNG_VIEWER_CREATE_SESSION));
-
-       cmd.cmd = htobe32(LTTNG_VIEWER_CREATE_SESSION);
-       cmd.data_size = htobe64((uint64_t) 0);
-       cmd.cmd_version = htobe32(0);
-
-       status = lttng_live_send(viewer_connection, &cmd, sizeof(cmd));
-       if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-               viewer_handle_send_status(self_comp, self_comp_class,
-                       status, "create session command");
-               goto end;
-       }
-
-       status = lttng_live_recv(viewer_connection, &resp, sizeof(resp));
-       if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-               viewer_handle_recv_status(self_comp, self_comp_class,
-                       status, "create session reply");
-               goto end;
-       }
-
-       if (be32toh(resp.status) != LTTNG_VIEWER_CREATE_SESSION_OK) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Error creating viewer session");
-               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-               goto end;
-       }
-
-       status = lttng_live_query_session_ids(lttng_live_msg_iter);
-       if (status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Failed to query live viewer session ids");
-               goto end;
-       } else if (status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
-               goto end;
-       }
-
-end:
-       return status;
-}
-
-static
-enum lttng_live_viewer_status receive_streams(struct lttng_live_session *session,
-               uint32_t stream_count,
-               bt_self_message_iterator *self_msg_iter)
-{
-       uint32_t i;
-       struct lttng_live_msg_iter *lttng_live_msg_iter =
-               session->lttng_live_msg_iter;
-       enum lttng_live_viewer_status status;
-       struct live_viewer_connection *viewer_connection =
-               lttng_live_msg_iter->viewer_connection;
-       bt_self_component *self_comp = viewer_connection->self_comp;
-
-       BT_COMP_LOGI("Getting %" PRIu32 " new streams", stream_count);
-       for (i = 0; i < stream_count; i++) {
-               struct lttng_viewer_stream stream;
-               struct lttng_live_stream_iterator *live_stream;
-               uint64_t stream_id;
-               uint64_t ctf_trace_id;
-
-               status = lttng_live_recv(viewer_connection, &stream,
-                       sizeof(stream));
-               if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-                       viewer_handle_recv_status(self_comp, NULL,
-                               status, "stream reply");
-                       goto end;
-               }
-               stream.path_name[LTTNG_VIEWER_PATH_MAX - 1] = '\0';
-               stream.channel_name[LTTNG_VIEWER_NAME_MAX - 1] = '\0';
-               stream_id = be64toh(stream.id);
-               ctf_trace_id = be64toh(stream.ctf_trace_id);
-
-               if (stream.metadata_flag) {
-                       BT_COMP_LOGI("    metadata stream %" PRIu64 " : %s/%s",
-                               stream_id, stream.path_name, stream.channel_name);
-                       if (lttng_live_metadata_create_stream(session,
-                                       ctf_trace_id, stream_id,
-                                       stream.path_name)) {
-                               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                       "Error creating metadata stream");
-                               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-                               goto end;
-                       }
-                       session->lazy_stream_msg_init = true;
-               } else {
-                       BT_COMP_LOGI("    stream %" PRIu64 " : %s/%s",
-                               stream_id, stream.path_name, stream.channel_name);
-                       live_stream = lttng_live_stream_iterator_create(session,
-                               ctf_trace_id, stream_id, self_msg_iter);
-                       if (!live_stream) {
-                               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                                       "Error creating stream");
-                               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-                               goto end;
-                       }
-               }
-       }
-       status = LTTNG_LIVE_VIEWER_STATUS_OK;
-
-end:
-       return status;
-}
-
-BT_HIDDEN
-enum lttng_live_viewer_status lttng_live_session_attach(
-               struct lttng_live_session *session,
-               bt_self_message_iterator *self_msg_iter)
-{
-       struct lttng_viewer_cmd cmd;
-       enum lttng_live_viewer_status status;
-       struct lttng_viewer_attach_session_request rq;
-       struct lttng_viewer_attach_session_response rp;
-       struct lttng_live_msg_iter *lttng_live_msg_iter =
-               session->lttng_live_msg_iter;
-       struct live_viewer_connection *viewer_connection =
-               lttng_live_msg_iter->viewer_connection;
-       bt_self_component *self_comp = viewer_connection->self_comp;
-       uint64_t session_id = session->id;
-       uint32_t streams_count;
-       const size_t cmd_buf_len = sizeof(cmd) + sizeof(rq);
-       char cmd_buf[cmd_buf_len];
-
-       BT_COMP_LOGD("Attaching to session: cmd=%s, session-id=%" PRIu64
-               ", seek=%s",
-               lttng_viewer_command_string(LTTNG_VIEWER_ATTACH_SESSION),
-               session_id,
-               lttng_viewer_seek_string(LTTNG_VIEWER_SEEK_LAST));
-
-       cmd.cmd = htobe32(LTTNG_VIEWER_ATTACH_SESSION);
-       cmd.data_size = htobe64((uint64_t) sizeof(rq));
-       cmd.cmd_version = htobe32(0);
-
-       memset(&rq, 0, sizeof(rq));
-       rq.session_id = htobe64(session_id);
-       // TODO: add cmd line parameter to select seek beginning
-       // rq.seek = htobe32(LTTNG_VIEWER_SEEK_BEGINNING);
-       rq.seek = htobe32(LTTNG_VIEWER_SEEK_LAST);
-
-       /*
-        * Merge the cmd and connection request to prevent a write-write
-        * sequence on the TCP socket. Otherwise, a delayed ACK will prevent the
-        * second write to be performed quickly in presence of Nagle's algorithm.
-        */
-       memcpy(cmd_buf, &cmd, sizeof(cmd));
-       memcpy(cmd_buf + sizeof(cmd), &rq, sizeof(rq));
-       status = lttng_live_send(viewer_connection, &cmd_buf, cmd_buf_len);
-       if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-               viewer_handle_send_status(self_comp, NULL,
-                       status, "attach session command");
-               goto end;
-       }
-
-       status = lttng_live_recv(viewer_connection, &rp, sizeof(rp));
-       if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-               viewer_handle_recv_status(self_comp, NULL,
-                       status, "attach session reply");
-               goto end;
-       }
-
-       streams_count = be32toh(rp.streams_count);
-       switch(be32toh(rp.status)) {
-       case LTTNG_VIEWER_ATTACH_OK:
-               break;
-       case LTTNG_VIEWER_ATTACH_UNK:
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Session id %" PRIu64 " is unknown", session_id);
-               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-               goto end;
-       case LTTNG_VIEWER_ATTACH_ALREADY:
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "There is already a viewer attached to this session");
-               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-               goto end;
-       case LTTNG_VIEWER_ATTACH_NOT_LIVE:
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Not a live session");
-               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-               goto end;
-       case LTTNG_VIEWER_ATTACH_SEEK_ERR:
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Wrong seek parameter");
-               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-               goto end;
-       default:
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Unknown attach return code %u", be32toh(rp.status));
-               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-               goto end;
-       }
-
-       /* We receive the initial list of streams. */
-       status = receive_streams(session, streams_count, self_msg_iter);
-       switch (status) {
-       case LTTNG_LIVE_VIEWER_STATUS_OK:
-               break;
-       case LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED:
-               goto end;
-       case LTTNG_LIVE_VIEWER_STATUS_ERROR:
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Error receiving streams");
-               goto end;
-       default:
-               bt_common_abort();
-       }
-
-       session->attached = true;
-       session->new_streams_needed = false;
-
-end:
-       return status;
-}
-
-BT_HIDDEN
-enum lttng_live_viewer_status lttng_live_session_detach(
-               struct lttng_live_session *session)
-{
-       struct lttng_viewer_cmd cmd;
-       enum lttng_live_viewer_status status;
-       struct lttng_viewer_detach_session_request rq;
-       struct lttng_viewer_detach_session_response rp;
-       struct lttng_live_msg_iter *lttng_live_msg_iter =
-               session->lttng_live_msg_iter;
-       bt_self_component *self_comp = session->self_comp;
-       struct live_viewer_connection *viewer_connection =
-               lttng_live_msg_iter->viewer_connection;
-       uint64_t session_id = session->id;
-       const size_t cmd_buf_len = sizeof(cmd) + sizeof(rq);
-       char cmd_buf[cmd_buf_len];
-
-       /*
-        * The session might already be detached and the viewer socket might
-        * already been closed. This happens when calling this function when
-        * tearing down the graph after an error.
-        */
-       if (!session->attached || viewer_connection->control_sock == BT_INVALID_SOCKET) {
-               return 0;
-       }
-
-       BT_COMP_LOGD("Detaching from session: cmd=%s, session-id=%" PRIu64,
-               lttng_viewer_command_string(LTTNG_VIEWER_DETACH_SESSION),
-               session_id);
-
-       cmd.cmd = htobe32(LTTNG_VIEWER_DETACH_SESSION);
-       cmd.data_size = htobe64((uint64_t) sizeof(rq));
-       cmd.cmd_version = htobe32(0);
-
-       memset(&rq, 0, sizeof(rq));
-       rq.session_id = htobe64(session_id);
-
-       /*
-        * Merge the cmd and connection request to prevent a write-write
-        * sequence on the TCP socket. Otherwise, a delayed ACK will prevent the
-        * second write to be performed quickly in presence of Nagle's algorithm.
-        */
-       memcpy(cmd_buf, &cmd, sizeof(cmd));
-       memcpy(cmd_buf + sizeof(cmd), &rq, sizeof(rq));
-       status = lttng_live_send(viewer_connection, &cmd_buf, cmd_buf_len);
-       if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-               viewer_handle_send_status(self_comp, NULL,
-                       status, "detach session command");
-               goto end;
-       }
-
-       status = lttng_live_recv(viewer_connection, &rp, sizeof(rp));
-       if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-               viewer_handle_recv_status(self_comp, NULL,
-                       status, "detach session reply");
-               goto end;
-       }
-
-       switch(be32toh(rp.status)) {
-       case LTTNG_VIEWER_DETACH_SESSION_OK:
-               break;
-       case LTTNG_VIEWER_DETACH_SESSION_UNK:
-               BT_COMP_LOGW("Session id %" PRIu64 " is unknown", session_id);
-               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-               goto end;
-       case LTTNG_VIEWER_DETACH_SESSION_ERR:
-               BT_COMP_LOGW("Error detaching session id %" PRIu64 "", session_id);
-               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-               goto end;
-       default:
-               BT_COMP_LOGE("Unknown detach return code %u", be32toh(rp.status));
-               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-               goto end;
-       }
-
-       session->attached = false;
-
-       status = LTTNG_LIVE_VIEWER_STATUS_OK;
-
-end:
-       return status;
-}
-
-BT_HIDDEN
-enum lttng_live_get_one_metadata_status lttng_live_get_one_metadata_packet(
-               struct lttng_live_trace *trace, FILE *fp, size_t *reply_len)
-{
-       uint64_t len = 0;
-       enum lttng_live_get_one_metadata_status status;
-       enum lttng_live_viewer_status viewer_status;
-       struct lttng_viewer_cmd cmd;
-       struct lttng_viewer_get_metadata rq;
-       struct lttng_viewer_metadata_packet rp;
-       gchar *data = NULL;
-       ssize_t writelen;
-       struct lttng_live_session *session = trace->session;
-       struct lttng_live_msg_iter *lttng_live_msg_iter =
-               session->lttng_live_msg_iter;
-       struct lttng_live_metadata *metadata = trace->metadata;
-       struct live_viewer_connection *viewer_connection =
-               lttng_live_msg_iter->viewer_connection;
-       bt_self_component *self_comp = viewer_connection->self_comp;
-       const size_t cmd_buf_len = sizeof(cmd) + sizeof(rq);
-       char cmd_buf[cmd_buf_len];
-
-       BT_COMP_LOGD("Requesting new metadata for trace:"
-               "cmd=%s, trace-id=%" PRIu64 ", metadata-stream-id=%" PRIu64,
-               lttng_viewer_command_string(LTTNG_VIEWER_GET_METADATA),
-               trace->id, metadata->stream_id);
-
-       rq.stream_id = htobe64(metadata->stream_id);
-       cmd.cmd = htobe32(LTTNG_VIEWER_GET_METADATA);
-       cmd.data_size = htobe64((uint64_t) sizeof(rq));
-       cmd.cmd_version = htobe32(0);
-
-       /*
-        * Merge the cmd and connection request to prevent a write-write
-        * sequence on the TCP socket. Otherwise, a delayed ACK will prevent the
-        * second write to be performed quickly in presence of Nagle's algorithm.
-        */
-       memcpy(cmd_buf, &cmd, sizeof(cmd));
-       memcpy(cmd_buf + sizeof(cmd), &rq, sizeof(rq));
-       viewer_status = lttng_live_send(viewer_connection, &cmd_buf, cmd_buf_len);
-       if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-               viewer_handle_send_status(self_comp, NULL,
-                       viewer_status, "get metadata command");
-               status = (enum lttng_live_get_one_metadata_status) viewer_status;
-               goto end;
-       }
-
-       viewer_status = lttng_live_recv(viewer_connection, &rp, sizeof(rp));
-       if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-               viewer_handle_recv_status(self_comp, NULL,
-                       viewer_status, "get metadata reply");
-               status = (enum lttng_live_get_one_metadata_status) viewer_status;
-               goto end;
-       }
-
-       switch (be32toh(rp.status)) {
-               case LTTNG_VIEWER_METADATA_OK:
-                       BT_COMP_LOGD("Received get_metadata response: ok");
-                       break;
-               case LTTNG_VIEWER_NO_NEW_METADATA:
-                       BT_COMP_LOGD("Received get_metadata response: no new");
-                       status = LTTNG_LIVE_GET_ONE_METADATA_STATUS_END;
-                       goto end;
-               case LTTNG_VIEWER_METADATA_ERR:
-                       /*
-                        * The Relayd cannot find this stream id. Maybe its
-                        * gone already. This can happen in short lived UST app
-                        * in a per-pid session.
-                        */
-                       BT_COMP_LOGD("Received get_metadata response: error");
-                       status = LTTNG_LIVE_GET_ONE_METADATA_STATUS_CLOSED;
-                       goto end;
-               default:
-                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                               "Received get_metadata response: unknown");
-                       status = LTTNG_LIVE_GET_ONE_METADATA_STATUS_ERROR;
-                       goto end;
-       }
-
-       len = be64toh(rp.len);
-       BT_COMP_LOGD("Writing %" PRIu64" bytes to metadata", len);
-       if (len <= 0) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Erroneous response length");
-               status = LTTNG_LIVE_GET_ONE_METADATA_STATUS_ERROR;
-               goto end;
-       }
-
-       data = g_new0(gchar, len);
-       if (!data) {
-               BT_COMP_LOGE_APPEND_CAUSE_ERRNO(self_comp,
-                       "Failed to allocate data buffer", ".");
-               status = LTTNG_LIVE_GET_ONE_METADATA_STATUS_ERROR;
-               goto end;
-       }
-
-       viewer_status = lttng_live_recv(viewer_connection, data, len);
-       if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-               viewer_handle_recv_status(self_comp, NULL,
-                       viewer_status, "get metadata packet");
-               status = (enum lttng_live_get_one_metadata_status) viewer_status;
-               goto end;
-       }
-
-       /*
-        * Write the metadata to the file handle.
-        */
-       writelen = fwrite(data, sizeof(uint8_t), len, fp);
-       if (writelen != len) {
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Writing in the metadata file stream");
-               status = LTTNG_LIVE_GET_ONE_METADATA_STATUS_ERROR;
-               goto end;
-       }
-
-       *reply_len = len;
-       status = LTTNG_LIVE_GET_ONE_METADATA_STATUS_OK;
-
-end:
-       g_free(data);
-       return status;
-}
-
-/*
- * Assign the fields from a lttng_viewer_index to a packet_index.
- */
-static
-void lttng_index_to_packet_index(struct lttng_viewer_index *lindex,
-               struct packet_index *pindex)
-{
-       BT_ASSERT(lindex);
-       BT_ASSERT(pindex);
-
-       pindex->offset = be64toh(lindex->offset);
-       pindex->packet_size = be64toh(lindex->packet_size);
-       pindex->content_size = be64toh(lindex->content_size);
-       pindex->ts_cycles.timestamp_begin = be64toh(lindex->timestamp_begin);
-       pindex->ts_cycles.timestamp_end = be64toh(lindex->timestamp_end);
-       pindex->events_discarded = be64toh(lindex->events_discarded);
-}
-
-static
-void lttng_live_need_new_streams(struct lttng_live_msg_iter *lttng_live_msg_iter)
-{
-       uint64_t session_idx;
-       struct live_viewer_connection *viewer_connection =
-               lttng_live_msg_iter->viewer_connection;
-
-       for (session_idx = 0; session_idx < lttng_live_msg_iter->sessions->len;
-                       session_idx++) {
-               struct lttng_live_session *session =
-                               g_ptr_array_index(lttng_live_msg_iter->sessions, session_idx);
-               BT_COMP_LOGD("Marking session as needing new streams: "
-                               "session-id=%" PRIu64, session->id);
-               session->new_streams_needed = true;
-       }
-}
-
-BT_HIDDEN
-enum lttng_live_iterator_status lttng_live_get_next_index(
-               struct lttng_live_msg_iter *lttng_live_msg_iter,
-               struct lttng_live_stream_iterator *stream,
-               struct packet_index *index)
-{
-       struct lttng_viewer_cmd cmd;
-       struct lttng_viewer_get_next_index rq;
-       enum lttng_live_viewer_status viewer_status;
-       struct lttng_viewer_index rp;
-       enum lttng_live_iterator_status status;
-       struct live_viewer_connection *viewer_connection =
-               lttng_live_msg_iter->viewer_connection;
-       bt_self_component *self_comp = viewer_connection->self_comp;
-       struct lttng_live_trace *trace = stream->trace;
-       const size_t cmd_buf_len = sizeof(cmd) + sizeof(rq);
-       char cmd_buf[cmd_buf_len];
-       uint32_t flags, rp_status;
-
-       BT_COMP_LOGD("Requesting next index for stream: cmd=%s, "
-                       "viewer-stream-id=%" PRIu64,
-                       lttng_viewer_command_string(LTTNG_VIEWER_GET_NEXT_INDEX),
-                       stream->viewer_stream_id);
-       cmd.cmd = htobe32(LTTNG_VIEWER_GET_NEXT_INDEX);
-       cmd.data_size = htobe64((uint64_t) sizeof(rq));
-       cmd.cmd_version = htobe32(0);
-
-       memset(&rq, 0, sizeof(rq));
-       rq.stream_id = htobe64(stream->viewer_stream_id);
-
-       /*
-        * Merge the cmd and connection request to prevent a write-write
-        * sequence on the TCP socket. Otherwise, a delayed ACK will prevent the
-        * second write to be performed quickly in presence of Nagle's algorithm.
-        */
-       memcpy(cmd_buf, &cmd, sizeof(cmd));
-       memcpy(cmd_buf + sizeof(cmd), &rq, sizeof(rq));
-
-       viewer_status = lttng_live_send(viewer_connection, &cmd_buf, cmd_buf_len);
-       if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-               viewer_handle_send_status(self_comp, NULL,
-                       viewer_status, "get next index command");
-               goto error;
-       }
-
-       viewer_status = lttng_live_recv(viewer_connection, &rp, sizeof(rp));
-       if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-               viewer_handle_recv_status(self_comp, NULL,
-                       viewer_status, "get next index reply");
-               goto error;
-       }
-
-       flags = be32toh(rp.flags);
-       rp_status = be32toh(rp.status);
-
-       BT_COMP_LOGD("Received response from relay daemon: cmd=%s, response=%s",
-               lttng_viewer_command_string(LTTNG_VIEWER_GET_NEXT_INDEX),
-               lttng_viewer_next_index_return_code_string(rp_status));
-       switch (rp_status) {
-       case LTTNG_VIEWER_INDEX_INACTIVE:
-       {
-               uint64_t ctf_stream_class_id;
-
-               memset(index, 0, sizeof(struct packet_index));
-               index->ts_cycles.timestamp_end = be64toh(rp.timestamp_end);
-               stream->current_inactivity_ts = index->ts_cycles.timestamp_end;
-               ctf_stream_class_id = be64toh(rp.stream_id);
-               if (stream->ctf_stream_class_id.is_set) {
-                       BT_ASSERT(stream->ctf_stream_class_id.value==
-                               ctf_stream_class_id);
-               } else {
-                       stream->ctf_stream_class_id.value = ctf_stream_class_id;
-                       stream->ctf_stream_class_id.is_set = true;
-               }
-               lttng_live_stream_iterator_set_state(stream, LTTNG_LIVE_STREAM_QUIESCENT);
-               status = LTTNG_LIVE_ITERATOR_STATUS_OK;
-               break;
-       }
-       case LTTNG_VIEWER_INDEX_OK:
-       {
-               uint64_t ctf_stream_class_id;
-
-               lttng_index_to_packet_index(&rp, index);
-               ctf_stream_class_id = be64toh(rp.stream_id);
-               if (stream->ctf_stream_class_id.is_set) {
-                       BT_ASSERT(stream->ctf_stream_class_id.value==
-                               ctf_stream_class_id);
-               } else {
-                       stream->ctf_stream_class_id.value = ctf_stream_class_id;
-                       stream->ctf_stream_class_id.is_set = true;
-               }
-
-               lttng_live_stream_iterator_set_state(stream, LTTNG_LIVE_STREAM_ACTIVE_DATA);
-
-               if (flags & LTTNG_VIEWER_FLAG_NEW_METADATA) {
-                       BT_COMP_LOGD("Marking trace as needing new metadata: "
-                               "response=%s, response-flag=NEW_METADATA, trace-id=%" PRIu64,
-                               lttng_viewer_next_index_return_code_string(rp_status),
-                               trace->id);
-                       trace->metadata_stream_state = LTTNG_LIVE_METADATA_STREAM_STATE_NEEDED;
-               }
-               if (flags & LTTNG_VIEWER_FLAG_NEW_STREAM) {
-                       BT_COMP_LOGD("Marking all sessions as possibly needing new streams: "
-                               "response=%s, response-flag=NEW_STREAM",
-                               lttng_viewer_next_index_return_code_string(rp_status));
-                       lttng_live_need_new_streams(lttng_live_msg_iter);
-               }
-               status = LTTNG_LIVE_ITERATOR_STATUS_OK;
-               break;
-       }
-       case LTTNG_VIEWER_INDEX_RETRY:
-               memset(index, 0, sizeof(struct packet_index));
-               lttng_live_stream_iterator_set_state(stream, LTTNG_LIVE_STREAM_ACTIVE_NO_DATA);
-               status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN;
-               goto end;
-       case LTTNG_VIEWER_INDEX_HUP:
-               memset(index, 0, sizeof(struct packet_index));
-               index->offset = EOF;
-               lttng_live_stream_iterator_set_state(stream, LTTNG_LIVE_STREAM_EOF);
-               stream->has_stream_hung_up = true;
-               status = LTTNG_LIVE_ITERATOR_STATUS_END;
-               break;
-       case LTTNG_VIEWER_INDEX_ERR:
-               memset(index, 0, sizeof(struct packet_index));
-               lttng_live_stream_iterator_set_state(stream, LTTNG_LIVE_STREAM_ACTIVE_NO_DATA);
-               status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-               goto end;
-       default:
-               BT_COMP_LOGD("Received get_next_index response: unknown value");
-               memset(index, 0, sizeof(struct packet_index));
-               lttng_live_stream_iterator_set_state(stream, LTTNG_LIVE_STREAM_ACTIVE_NO_DATA);
-               status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-               goto end;
-       }
-       goto end;
-
-error:
-       status = viewer_status_to_live_iterator_status(viewer_status);
-end:
-       return status;
-}
-
-BT_HIDDEN
-enum ctf_msg_iter_medium_status lttng_live_get_stream_bytes(
-               struct lttng_live_msg_iter *lttng_live_msg_iter,
-               struct lttng_live_stream_iterator *stream, uint8_t *buf,
-               uint64_t offset, uint64_t req_len, uint64_t *recv_len)
-{
-       enum ctf_msg_iter_medium_status status;
-       enum lttng_live_viewer_status viewer_status;
-       struct lttng_viewer_trace_packet rp;
-       struct lttng_viewer_cmd cmd;
-       struct lttng_viewer_get_packet rq;
-       struct live_viewer_connection *viewer_connection =
-               lttng_live_msg_iter->viewer_connection;
-       bt_self_component *self_comp = viewer_connection->self_comp;
-       struct lttng_live_trace *trace = stream->trace;
-       const size_t cmd_buf_len = sizeof(cmd) + sizeof(rq);
-       char cmd_buf[cmd_buf_len];
-       uint32_t flags, rp_status;
-
-       BT_COMP_LOGD("Requesting data from stream: cmd=%s, "
-                       "offset=%" PRIu64 ", request-len=%" PRIu64,
-                       lttng_viewer_command_string(LTTNG_VIEWER_GET_PACKET),
-                       offset, req_len);
-
-       cmd.cmd = htobe32(LTTNG_VIEWER_GET_PACKET);
-       cmd.data_size = htobe64((uint64_t) sizeof(rq));
-       cmd.cmd_version = htobe32(0);
-
-       memset(&rq, 0, sizeof(rq));
-       rq.stream_id = htobe64(stream->viewer_stream_id);
-       rq.offset = htobe64(offset);
-       rq.len = htobe32(req_len);
-
-       /*
-        * Merge the cmd and connection request to prevent a write-write
-        * sequence on the TCP socket. Otherwise, a delayed ACK will prevent the
-        * second write to be performed quickly in presence of Nagle's algorithm.
-        */
-       memcpy(cmd_buf, &cmd, sizeof(cmd));
-       memcpy(cmd_buf + sizeof(cmd), &rq, sizeof(rq));
-
-       viewer_status = lttng_live_send(viewer_connection, &cmd_buf, cmd_buf_len);
-       if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-               viewer_handle_send_status(self_comp, NULL,
-                       viewer_status, "get data packet command");
-               goto error_convert_status;
-       }
-
-       viewer_status = lttng_live_recv(viewer_connection, &rp, sizeof(rp));
-       if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-               viewer_handle_recv_status(self_comp, NULL,
-                       viewer_status, "get data packet reply");
-               goto error_convert_status;
-       }
-
-       flags = be32toh(rp.flags);
-       rp_status = be32toh(rp.status);
-
-       BT_COMP_LOGD("Received response from relay daemon: cmd=%s, response=%s",
-               lttng_viewer_command_string(LTTNG_VIEWER_GET_PACKET),
-               lttng_viewer_get_packet_return_code_string(rp_status));
-       switch (rp_status) {
-       case LTTNG_VIEWER_GET_PACKET_OK:
-               req_len = be32toh(rp.len);
-               BT_COMP_LOGD("Got packet from relay daemon: response=%s, packet-len=%" PRIu64 "",
-                       lttng_viewer_get_packet_return_code_string(rp_status),
-                       req_len);
-               status = CTF_MSG_ITER_MEDIUM_STATUS_OK;
-               break;
-       case LTTNG_VIEWER_GET_PACKET_RETRY:
-               /* Unimplemented by relay daemon */
-               status = CTF_MSG_ITER_MEDIUM_STATUS_AGAIN;
-               goto end;
-       case LTTNG_VIEWER_GET_PACKET_ERR:
-               if (flags & LTTNG_VIEWER_FLAG_NEW_METADATA) {
-                       BT_COMP_LOGD("Marking trace as needing new metadata: "
-                               "response=%s, response-flag=NEW_METADATA, trace-id=%" PRIu64,
-                               lttng_viewer_next_index_return_code_string(rp_status),
-                               trace->id);
-                       trace->metadata_stream_state = LTTNG_LIVE_METADATA_STREAM_STATE_NEEDED;
-               }
-               if (flags & LTTNG_VIEWER_FLAG_NEW_STREAM) {
-                       BT_COMP_LOGD("Marking all sessions as possibly needing new streams: "
-                               "response=%s, response-flag=NEW_STREAM",
-                               lttng_viewer_next_index_return_code_string(rp_status));
-                       lttng_live_need_new_streams(lttng_live_msg_iter);
-               }
-               if (flags & (LTTNG_VIEWER_FLAG_NEW_METADATA
-                               | LTTNG_VIEWER_FLAG_NEW_STREAM)) {
-                       status = CTF_MSG_ITER_MEDIUM_STATUS_AGAIN;
-                       BT_COMP_LOGD("Reply with any one flags set means we should retry: response=%s",
-                               lttng_viewer_get_packet_return_code_string(rp_status));
-                       goto end;
-               }
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Received get_data_packet response: error");
-               status = CTF_MSG_ITER_MEDIUM_STATUS_ERROR;
-               goto end;
-       case LTTNG_VIEWER_GET_PACKET_EOF:
-               status = CTF_MSG_ITER_MEDIUM_STATUS_EOF;
-               goto end;
-       default:
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Received get_data_packet response: unknown (%d)", rp_status);
-               status = CTF_MSG_ITER_MEDIUM_STATUS_ERROR;
-               goto end;
-       }
-
-       if (req_len == 0) {
-               status = CTF_MSG_ITER_MEDIUM_STATUS_ERROR;
-               goto end;
-       }
-
-       viewer_status = lttng_live_recv(viewer_connection, buf, req_len);
-       if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-               viewer_handle_recv_status(self_comp, NULL,
-                       viewer_status, "get data packet");
-               goto error_convert_status;
-       }
-       *recv_len = req_len;
-
-       status = CTF_MSG_ITER_MEDIUM_STATUS_OK;
-       goto end;
-
-error_convert_status:
-       status = viewer_status_to_ctf_msg_iter_medium_status(viewer_status);
-end:
-       return status;
-}
-
-/*
- * Request new streams for a session.
- */
-BT_HIDDEN
-enum lttng_live_iterator_status lttng_live_session_get_new_streams(
-               struct lttng_live_session *session,
-               bt_self_message_iterator *self_msg_iter)
-{
-       enum lttng_live_iterator_status status =
-               LTTNG_LIVE_ITERATOR_STATUS_OK;
-       struct lttng_viewer_cmd cmd;
-       struct lttng_viewer_new_streams_request rq;
-       struct lttng_viewer_new_streams_response rp;
-       struct lttng_live_msg_iter *lttng_live_msg_iter =
-               session->lttng_live_msg_iter;
-       enum lttng_live_viewer_status viewer_status;
-       struct live_viewer_connection *viewer_connection =
-               lttng_live_msg_iter->viewer_connection;
-       bt_self_component *self_comp = viewer_connection->self_comp;
-       uint32_t streams_count;
-       const size_t cmd_buf_len = sizeof(cmd) + sizeof(rq);
-       char cmd_buf[cmd_buf_len];
-
-       if (!session->new_streams_needed) {
-               status = LTTNG_LIVE_ITERATOR_STATUS_OK;
-               goto end;
-       }
-
-       BT_COMP_LOGD("Requesting new streams for session: cmd=%s, "
-               "session-id=%" PRIu64,
-               lttng_viewer_command_string(LTTNG_VIEWER_GET_NEW_STREAMS),
-               session->id);
-
-       cmd.cmd = htobe32(LTTNG_VIEWER_GET_NEW_STREAMS);
-       cmd.data_size = htobe64((uint64_t) sizeof(rq));
-       cmd.cmd_version = htobe32(0);
-
-       memset(&rq, 0, sizeof(rq));
-       rq.session_id = htobe64(session->id);
-
-       /*
-        * Merge the cmd and connection request to prevent a write-write
-        * sequence on the TCP socket. Otherwise, a delayed ACK will prevent the
-        * second write to be performed quickly in presence of Nagle's algorithm.
-        */
-       memcpy(cmd_buf, &cmd, sizeof(cmd));
-       memcpy(cmd_buf + sizeof(cmd), &rq, sizeof(rq));
-
-       viewer_status = lttng_live_send(viewer_connection, &cmd_buf, cmd_buf_len);
-       if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-               viewer_handle_send_status(self_comp, NULL,
-                       viewer_status, "get new streams command");
-               status = viewer_status_to_live_iterator_status(viewer_status);
-               goto end;
-       }
-
-       viewer_status = lttng_live_recv(viewer_connection, &rp, sizeof(rp));
-       if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-               viewer_handle_recv_status(self_comp, NULL,
-                       viewer_status, "get new streams reply");
-               status = viewer_status_to_live_iterator_status(viewer_status);
-               goto end;
-       }
-
-       streams_count = be32toh(rp.streams_count);
-
-       switch(be32toh(rp.status)) {
-       case LTTNG_VIEWER_NEW_STREAMS_OK:
-               session->new_streams_needed = false;
-               break;
-       case LTTNG_VIEWER_NEW_STREAMS_NO_NEW:
-               session->new_streams_needed = false;
-               goto end;
-       case LTTNG_VIEWER_NEW_STREAMS_HUP:
-               session->new_streams_needed = false;
-               session->closed = true;
-               status = LTTNG_LIVE_ITERATOR_STATUS_END;
-               goto end;
-       case LTTNG_VIEWER_NEW_STREAMS_ERR:
-               BT_COMP_LOGD("Received get_new_streams response: error");
-               status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-               goto end;
-       default:
-               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
-                       "Received get_new_streams response: Unknown:"
-                       "return code %u", be32toh(rp.status));
-               status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
-               goto end;
-       }
-
-       viewer_status = receive_streams(session, streams_count, self_msg_iter);
-       if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
-               viewer_handle_recv_status(self_comp, NULL,
-                       viewer_status, "new streams");
-               status = viewer_status_to_live_iterator_status(viewer_status);
-               goto end;
-       }
-
-       status = LTTNG_LIVE_ITERATOR_STATUS_OK;
-end:
-       return status;
-}
-
-BT_HIDDEN
-enum lttng_live_viewer_status live_viewer_connection_create(
-               bt_self_component *self_comp,
-               bt_self_component_class *self_comp_class,
-               bt_logging_level log_level,
-               const char *url, bool in_query,
-               struct lttng_live_msg_iter *lttng_live_msg_iter,
-               struct live_viewer_connection **viewer)
-{
-       struct live_viewer_connection *viewer_connection;
-       enum lttng_live_viewer_status status;
-
-       viewer_connection = g_new0(struct live_viewer_connection, 1);
-
-       if (bt_socket_init(log_level) != 0) {
-               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp,
-                       self_comp_class, "Failed to init socket");
-               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-               goto error;
-       }
-
-       viewer_connection->log_level = log_level;
-
-       viewer_connection->self_comp = self_comp;
-       viewer_connection->self_comp_class = self_comp_class;
-
-       viewer_connection->control_sock = BT_INVALID_SOCKET;
-       viewer_connection->port = -1;
-       viewer_connection->in_query = in_query;
-       viewer_connection->lttng_live_msg_iter = lttng_live_msg_iter;
-       viewer_connection->url = g_string_new(url);
-       if (!viewer_connection->url) {
-               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp,
-                       self_comp_class, "Failed to allocate URL buffer");
-               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
-               goto error;
-       }
-
-       BT_COMP_OR_COMP_CLASS_LOGD(self_comp, self_comp_class,
-               "Establishing connection to url \"%s\"...", url);
-       status = lttng_live_connect_viewer(viewer_connection);
-       /*
-        * Only print error and append cause in case of error. not in case of
-        * interruption.
-        */
-       if (status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
-               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp,
-                       self_comp_class, "Failed to establish connection: "
-                       "url=\"%s\"", url);
-               goto error;
-       } else if (status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
-               goto error;
-       }
-       BT_COMP_OR_COMP_CLASS_LOGD(self_comp, self_comp_class,
-               "Connection to url \"%s\" is established", url);
-
-       *viewer = viewer_connection;
-       status = LTTNG_LIVE_VIEWER_STATUS_OK;
-       goto end;
-
-error:
-       if (viewer_connection) {
-               live_viewer_connection_destroy(viewer_connection);
-       }
-end:
-       return status;
-}
-
-BT_HIDDEN
-void live_viewer_connection_destroy(
-               struct live_viewer_connection *viewer_connection)
-{
-       bt_self_component *self_comp = viewer_connection->self_comp;
-       bt_self_component_class *self_comp_class = viewer_connection->self_comp_class;
-
-       if (!viewer_connection) {
-               goto end;
-       }
-
-       BT_COMP_OR_COMP_CLASS_LOGD(self_comp, self_comp_class,
-               "Closing connection to relay:"
-               "relay-url=\"%s\"", viewer_connection->url->str);
-
-       lttng_live_disconnect_viewer(viewer_connection);
-
-       if (viewer_connection->url) {
-               g_string_free(viewer_connection->url, true);
-       }
-
-       if (viewer_connection->relay_hostname) {
-               g_string_free(viewer_connection->relay_hostname, true);
-       }
-
-       if (viewer_connection->target_hostname) {
-               g_string_free(viewer_connection->target_hostname, true);
-       }
-
-       if (viewer_connection->session_name) {
-               g_string_free(viewer_connection->session_name, true);
-       }
-
-       if (viewer_connection->proto) {
-               g_string_free(viewer_connection->proto, true);
-       }
-
-       g_free(viewer_connection);
-
-       bt_socket_fini();
-
-end:
-       return;
-}
diff --git a/src/plugins/ctf/lttng-live/viewer-connection.cpp b/src/plugins/ctf/lttng-live/viewer-connection.cpp
new file mode 100644 (file)
index 0000000..73677fa
--- /dev/null
@@ -0,0 +1,1975 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2019 Francis Deslauriers <francis.deslauriers@efficios.com>
+ * Copyright 2016 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ */
+
+#define BT_COMP_LOG_SELF_COMP (viewer_connection->self_comp)
+#define BT_LOG_OUTPUT_LEVEL (viewer_connection->log_level)
+#define BT_LOG_TAG "PLUGIN/SRC.CTF.LTTNG-LIVE/VIEWER"
+#include "logging/comp-logging.h"
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#include "compat/socket.h"
+#include "compat/endian.h"
+#include "compat/compiler.h"
+#include "common/common.h"
+#include <babeltrace2/babeltrace.h>
+
+#include "lttng-live.hpp"
+#include "viewer-connection.hpp"
+#include "lttng-viewer-abi.hpp"
+#include "data-stream.hpp"
+#include "metadata.hpp"
+
+#define viewer_handle_send_recv_status(_self_comp, _self_comp_class,   \
+               _status, _action, _msg_str)                             \
+do {                                                                   \
+       switch (_status) {                                              \
+       case LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED:                      \
+               break;                                                  \
+       case LTTNG_LIVE_VIEWER_STATUS_ERROR:                            \
+               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(_self_comp,     \
+                       _self_comp_class, "Error " _action " " _msg_str); \
+               break;                                                  \
+       default:                                                        \
+                bt_common_abort();                                     \
+       }                                                               \
+} while (0)
+
+#define viewer_handle_send_status(_self_comp, _self_comp_class, _status, _msg_str) \
+       viewer_handle_send_recv_status(_self_comp, _self_comp_class, _status, \
+               "sending", _msg_str)
+
+#define viewer_handle_recv_status(_self_comp, _self_comp_class, _status, _msg_str) \
+       viewer_handle_send_recv_status(_self_comp, _self_comp_class, _status, \
+               "receiving", _msg_str)
+
+#define LTTNG_LIVE_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE_ERRNO(_self_comp, \
+               _self_comp_class, _msg, _fmt, ...) \
+       do {                                                                            \
+               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(_self_comp, _self_comp_class,   \
+                       _msg ": %s" _fmt, bt_socket_errormsg(), ##__VA_ARGS__);         \
+       } while (0)
+
+static
+const char *lttng_viewer_command_string(enum lttng_viewer_command cmd)
+{
+       switch (cmd){
+       case LTTNG_VIEWER_CONNECT:
+               return "CONNECT";
+       case LTTNG_VIEWER_LIST_SESSIONS:
+               return "LIST_SESSIONS";
+       case LTTNG_VIEWER_ATTACH_SESSION:
+               return "ATTACH_SESSION";
+       case LTTNG_VIEWER_GET_NEXT_INDEX:
+               return "GET_NEXT_INDEX";
+       case LTTNG_VIEWER_GET_PACKET:
+               return "GET_PACKET";
+       case LTTNG_VIEWER_GET_METADATA:
+               return "GET_METADATA";
+       case LTTNG_VIEWER_GET_NEW_STREAMS:
+               return "GET_NEW_STREAMS";
+       case LTTNG_VIEWER_CREATE_SESSION:
+               return "CREATE_SESSION";
+       case LTTNG_VIEWER_DETACH_SESSION:
+               return "DETACH_SESSION";
+       }
+
+       bt_common_abort();
+}
+
+static
+const char *lttng_viewer_next_index_return_code_string(
+               enum lttng_viewer_next_index_return_code code)
+{
+       switch (code) {
+       case LTTNG_VIEWER_INDEX_OK:
+               return "INDEX_OK";
+       case LTTNG_VIEWER_INDEX_RETRY:
+               return "INDEX_RETRY";
+       case LTTNG_VIEWER_INDEX_HUP:
+               return "INDEX_HUP";
+       case LTTNG_VIEWER_INDEX_ERR:
+               return "INDEX_ERR";
+       case LTTNG_VIEWER_INDEX_INACTIVE:
+               return "INDEX_INACTIVE";
+       case LTTNG_VIEWER_INDEX_EOF:
+               return "INDEX_EOF";
+       }
+
+       bt_common_abort();
+}
+
+static
+const char *lttng_viewer_next_index_return_code_string(uint32_t code)
+{
+       return lttng_viewer_next_index_return_code_string(
+               (lttng_viewer_next_index_return_code) code);
+}
+
+static
+const char *lttng_viewer_get_packet_return_code_string(
+               enum lttng_viewer_get_packet_return_code code)
+{
+       switch (code) {
+       case LTTNG_VIEWER_GET_PACKET_OK:
+               return "GET_PACKET_OK";
+       case LTTNG_VIEWER_GET_PACKET_RETRY:
+               return "GET_PACKET_RETRY";
+       case LTTNG_VIEWER_GET_PACKET_ERR:
+               return "GET_PACKET_ERR";
+       case LTTNG_VIEWER_GET_PACKET_EOF:
+               return "GET_PACKET_EOF";
+       }
+
+       bt_common_abort();
+};
+
+static
+const char *lttng_viewer_get_packet_return_code_string(uint32_t code)
+{
+       return lttng_viewer_get_packet_return_code_string(
+               (lttng_viewer_get_packet_return_code) code);
+}
+
+static
+const char *lttng_viewer_seek_string(enum lttng_viewer_seek seek)
+{
+       switch (seek) {
+       case LTTNG_VIEWER_SEEK_BEGINNING:
+               return "SEEK_BEGINNING";
+       case LTTNG_VIEWER_SEEK_LAST:
+               return "SEEK_LAST";
+       }
+
+       bt_common_abort();
+}
+
+static inline
+enum lttng_live_iterator_status viewer_status_to_live_iterator_status(
+               enum lttng_live_viewer_status viewer_status)
+{
+       switch (viewer_status) {
+       case LTTNG_LIVE_VIEWER_STATUS_OK:
+               return LTTNG_LIVE_ITERATOR_STATUS_OK;
+       case LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED:
+               return LTTNG_LIVE_ITERATOR_STATUS_AGAIN;
+       case LTTNG_LIVE_VIEWER_STATUS_ERROR:
+               return LTTNG_LIVE_ITERATOR_STATUS_ERROR;
+       }
+
+       bt_common_abort();
+}
+
+static inline
+enum ctf_msg_iter_medium_status viewer_status_to_ctf_msg_iter_medium_status(
+               enum lttng_live_viewer_status viewer_status)
+{
+       switch (viewer_status) {
+       case LTTNG_LIVE_VIEWER_STATUS_OK:
+               return CTF_MSG_ITER_MEDIUM_STATUS_OK;
+       case LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED:
+               return CTF_MSG_ITER_MEDIUM_STATUS_AGAIN;
+       case LTTNG_LIVE_VIEWER_STATUS_ERROR:
+               return CTF_MSG_ITER_MEDIUM_STATUS_ERROR;
+       }
+
+       bt_common_abort();
+}
+
+static inline
+void viewer_connection_close_socket(
+               struct live_viewer_connection *viewer_connection)
+{
+       bt_self_component_class *self_comp_class =
+               viewer_connection->self_comp_class;
+       bt_self_component *self_comp =
+               viewer_connection->self_comp;
+       int ret = bt_socket_close(viewer_connection->control_sock);
+       if (ret == -1) {
+               BT_COMP_OR_COMP_CLASS_LOGW_ERRNO(
+                       self_comp, self_comp_class,
+                       "Error closing viewer connection socket: ", ".");
+       }
+
+       viewer_connection->control_sock = BT_INVALID_SOCKET;
+}
+
+/*
+ * This function receives a message from the Relay daemon.
+ * If it received the entire message, it returns _OK,
+ * If it's interrupted, it returns _INTERRUPTED,
+ * otherwise, it returns _ERROR.
+ */
+static
+enum lttng_live_viewer_status lttng_live_recv(
+               struct live_viewer_connection *viewer_connection,
+               void *buf, size_t len)
+{
+       ssize_t received;
+       bt_self_component_class *self_comp_class =
+               viewer_connection->self_comp_class;
+       bt_self_component *self_comp =
+               viewer_connection->self_comp;
+       size_t total_received = 0, to_receive = len;
+       struct lttng_live_msg_iter *lttng_live_msg_iter =
+               viewer_connection->lttng_live_msg_iter;
+       enum lttng_live_viewer_status status;
+       BT_SOCKET sock = viewer_connection->control_sock;
+
+       /*
+        * Receive a message from the Relay.
+        */
+       do {
+               received = bt_socket_recv(sock, (char *) buf + total_received, to_receive, 0);
+               if (received == BT_SOCKET_ERROR) {
+                       if (bt_socket_interrupted()) {
+                               if (lttng_live_graph_is_canceled(lttng_live_msg_iter)) {
+                                       /*
+                                        * This interruption was due to a
+                                        * SIGINT and the graph is being torn
+                                        * down.
+                                        */
+                                       status = LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED;
+                                       lttng_live_msg_iter->was_interrupted = true;
+                                       goto end;
+                               } else {
+                                       /*
+                                        * A signal was received, but the graph
+                                        * is not being torn down. Carry on.
+                                        */
+                                       continue;
+                               }
+                       } else {
+                               /*
+                                * For any other types of socket error, close
+                                * the socket and return an error.
+                                */
+                               LTTNG_LIVE_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE_ERRNO(
+                                       self_comp, self_comp_class,
+                                       "Error receiving from Relay", ".");
+
+                               viewer_connection_close_socket(viewer_connection);
+                               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
+                               goto end;
+                       }
+               } else if (received == 0) {
+                       /*
+                        * The recv() call returned 0. This means the
+                        * connection was orderly shutdown from the other peer.
+                        * If that happens when we are trying to receive
+                        * a message from it, it means something when wrong.
+                        * Close the socket and return an error.
+                        */
+                       BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp,
+                               self_comp_class, "Remote side has closed connection");
+                       viewer_connection_close_socket(viewer_connection);
+                       status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
+                       goto end;
+               }
+
+               BT_ASSERT(received <= to_receive);
+               total_received += received;
+               to_receive -= received;
+
+       } while (to_receive > 0);
+
+       BT_ASSERT(total_received == len);
+       status = LTTNG_LIVE_VIEWER_STATUS_OK;
+
+end:
+       return status;
+}
+
+/*
+ * This function sends a message to the Relay daemon.
+ * If it send the message, it returns _OK,
+ * If it's interrupted, it returns _INTERRUPTED,
+ * otherwise, it returns _ERROR.
+ */
+static
+enum lttng_live_viewer_status lttng_live_send(
+               struct live_viewer_connection *viewer_connection,
+               const void *buf, size_t len)
+{
+       enum lttng_live_viewer_status status;
+       bt_self_component_class *self_comp_class =
+               viewer_connection->self_comp_class;
+       bt_self_component *self_comp =
+               viewer_connection->self_comp;
+       struct lttng_live_msg_iter *lttng_live_msg_iter =
+               viewer_connection->lttng_live_msg_iter;
+       BT_SOCKET sock = viewer_connection->control_sock;
+       size_t to_send = len;
+       ssize_t total_sent = 0;
+
+       do {
+               ssize_t sent = bt_socket_send_nosigpipe(sock, (char *) buf + total_sent,
+                       to_send);
+               if (sent == BT_SOCKET_ERROR) {
+                       if (bt_socket_interrupted()) {
+                               if (lttng_live_graph_is_canceled(lttng_live_msg_iter)) {
+                                       /*
+                                        * This interruption was a SIGINT and
+                                        * the graph is being teared down.
+                                        */
+                                       status = LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED;
+                                       lttng_live_msg_iter->was_interrupted = true;
+                                       goto end;
+                               } else {
+                                       /*
+                                        * A signal was received, but the graph
+                                        * is not being teared down. Carry on.
+                                        */
+                                       continue;
+                               }
+                       } else {
+                               /*
+                                * For any other types of socket error, close
+                                * the socket and return an error.
+                                */
+                               LTTNG_LIVE_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE_ERRNO(
+                                       self_comp, self_comp_class,
+                                       "Error sending to Relay", ".");
+
+                               viewer_connection_close_socket(viewer_connection);
+                               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
+                               goto end;
+                       }
+               }
+
+               BT_ASSERT(sent <= to_send);
+               total_sent += sent;
+               to_send -= sent;
+
+       } while (to_send > 0);
+
+       BT_ASSERT(total_sent == len);
+       status = LTTNG_LIVE_VIEWER_STATUS_OK;
+
+end:
+       return status;
+}
+
+static
+int parse_url(struct live_viewer_connection *viewer_connection)
+{
+       char error_buf[256] = { 0 };
+       bt_self_component *self_comp = viewer_connection->self_comp;
+       bt_self_component_class *self_comp_class = viewer_connection->self_comp_class;
+       struct bt_common_lttng_live_url_parts lttng_live_url_parts = { 0 };
+       int ret = -1;
+       const char *path = viewer_connection->url->str;
+
+       if (!path) {
+               goto end;
+       }
+
+       lttng_live_url_parts = bt_common_parse_lttng_live_url(path, error_buf,
+               sizeof(error_buf));
+       if (!lttng_live_url_parts.proto) {
+               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp,
+                       self_comp_class,"Invalid LTTng live URL format: %s",
+                       error_buf);
+               goto end;
+       }
+       viewer_connection->proto = lttng_live_url_parts.proto;
+       lttng_live_url_parts.proto = NULL;
+
+       viewer_connection->relay_hostname = lttng_live_url_parts.hostname;
+       lttng_live_url_parts.hostname = NULL;
+
+       if (lttng_live_url_parts.port >= 0) {
+               viewer_connection->port = lttng_live_url_parts.port;
+       } else {
+               viewer_connection->port = LTTNG_DEFAULT_NETWORK_VIEWER_PORT;
+       }
+
+       viewer_connection->target_hostname = lttng_live_url_parts.target_hostname;
+       lttng_live_url_parts.target_hostname = NULL;
+
+       if (lttng_live_url_parts.session_name) {
+               viewer_connection->session_name = lttng_live_url_parts.session_name;
+               lttng_live_url_parts.session_name = NULL;
+       }
+
+       ret = 0;
+
+end:
+       bt_common_destroy_lttng_live_url_parts(&lttng_live_url_parts);
+       return ret;
+}
+
+static
+enum lttng_live_viewer_status lttng_live_handshake(
+               struct live_viewer_connection *viewer_connection)
+{
+       struct lttng_viewer_cmd cmd;
+       struct lttng_viewer_connect connect;
+       enum lttng_live_viewer_status status;
+       bt_self_component_class *self_comp_class = viewer_connection->self_comp_class;
+       bt_self_component *self_comp = viewer_connection->self_comp;
+       const size_t cmd_buf_len = sizeof(cmd) + sizeof(connect);
+       char cmd_buf[cmd_buf_len];
+
+       BT_COMP_OR_COMP_CLASS_LOGD(self_comp, self_comp_class,
+               "Handshaking with the relay daemon: cmd=%s, major-version=%u, minor-version=%u",
+               lttng_viewer_command_string(LTTNG_VIEWER_CONNECT), LTTNG_LIVE_MAJOR,
+               LTTNG_LIVE_MINOR);
+
+       cmd.cmd = htobe32(LTTNG_VIEWER_CONNECT);
+       cmd.data_size = htobe64((uint64_t) sizeof(connect));
+       cmd.cmd_version = htobe32(0);
+
+       connect.viewer_session_id = -1ULL;      /* will be set on recv */
+       connect.major = htobe32(LTTNG_LIVE_MAJOR);
+       connect.minor = htobe32(LTTNG_LIVE_MINOR);
+       connect.type = htobe32(LTTNG_VIEWER_CLIENT_COMMAND);
+
+       /*
+        * Merge the cmd and connection request to prevent a write-write
+        * sequence on the TCP socket. Otherwise, a delayed ACK will prevent the
+        * second write to be performed quickly in presence of Nagle's algorithm
+        */
+       memcpy(cmd_buf, &cmd, sizeof(cmd));
+       memcpy(cmd_buf + sizeof(cmd), &connect, sizeof(connect));
+
+       status = lttng_live_send(viewer_connection, &cmd_buf, cmd_buf_len);
+       if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
+               viewer_handle_send_status(self_comp, self_comp_class,
+                       status, "viewer connect command");
+               goto end;
+       }
+
+       status = lttng_live_recv(viewer_connection, &connect, sizeof(connect));
+       if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
+               viewer_handle_recv_status(self_comp, self_comp_class,
+                       status, "viewer connect reply");
+               goto end;
+       }
+
+       BT_COMP_OR_COMP_CLASS_LOGI(self_comp, self_comp_class,
+               "Received viewer session ID : %" PRIu64,
+               (uint64_t) be64toh(connect.viewer_session_id));
+       BT_COMP_OR_COMP_CLASS_LOGI(self_comp, self_comp_class,
+               "Relayd version : %u.%u", be32toh(connect.major),
+               be32toh(connect.minor));
+
+       if (LTTNG_LIVE_MAJOR != be32toh(connect.major)) {
+               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp,
+                       self_comp_class, "Incompatible lttng-relayd protocol");
+               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
+               goto end;
+       }
+       /* Use the smallest protocol version implemented. */
+       if (LTTNG_LIVE_MINOR > be32toh(connect.minor)) {
+               viewer_connection->minor =  be32toh(connect.minor);
+       } else {
+               viewer_connection->minor =  LTTNG_LIVE_MINOR;
+       }
+       viewer_connection->major = LTTNG_LIVE_MAJOR;
+
+       status = LTTNG_LIVE_VIEWER_STATUS_OK;
+
+       goto end;
+
+end:
+       return status;
+}
+
+static
+enum lttng_live_viewer_status lttng_live_connect_viewer(
+               struct live_viewer_connection *viewer_connection)
+{
+       struct hostent *host;
+       struct sockaddr_in server_addr;
+       enum lttng_live_viewer_status status;
+       bt_self_component_class *self_comp_class = viewer_connection->self_comp_class;
+       bt_self_component *self_comp = viewer_connection->self_comp;
+
+       if (parse_url(viewer_connection)) {
+               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp,
+                       self_comp_class, "Failed to parse URL");
+               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
+               goto error;
+       }
+
+       BT_COMP_OR_COMP_CLASS_LOGD(self_comp, self_comp_class,
+               "Connecting to hostname : %s, port : %d, "
+               "target hostname : %s, session name : %s, proto : %s",
+               viewer_connection->relay_hostname->str,
+               viewer_connection->port,
+               !viewer_connection->target_hostname ?
+               "<none>" : viewer_connection->target_hostname->str,
+               !viewer_connection->session_name ?
+               "<none>" : viewer_connection->session_name->str,
+               viewer_connection->proto->str);
+
+       host = gethostbyname(viewer_connection->relay_hostname->str);
+       if (!host) {
+               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp,
+                       self_comp_class, "Cannot lookup hostname: hostname=\"%s\"",
+                       viewer_connection->relay_hostname->str);
+               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
+               goto error;
+       }
+
+       if ((viewer_connection->control_sock = socket(AF_INET, SOCK_STREAM, 0)) == BT_INVALID_SOCKET) {
+               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp,
+                       self_comp_class, "Socket creation failed: %s", bt_socket_errormsg());
+               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
+               goto error;
+       }
+
+       server_addr.sin_family = AF_INET;
+       server_addr.sin_port = htons(viewer_connection->port);
+       server_addr.sin_addr = *((struct in_addr *) host->h_addr);
+       memset(&(server_addr.sin_zero), 0, 8);
+
+       if (connect(viewer_connection->control_sock, (struct sockaddr *) &server_addr,
+                               sizeof(struct sockaddr)) == BT_SOCKET_ERROR) {
+               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp,
+                       self_comp_class, "Connection failed: %s",
+                       bt_socket_errormsg());
+               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
+               goto error;
+       }
+
+       status = lttng_live_handshake(viewer_connection);
+
+       /*
+        * Only print error and append cause in case of error. not in case of
+        * interruption.
+        */
+       if (status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
+               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp,
+                       self_comp_class, "Viewer handshake failed");
+               goto error;
+       } else if (status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
+               goto end;
+       }
+
+       goto end;
+
+error:
+       if (viewer_connection->control_sock != BT_INVALID_SOCKET) {
+               if (bt_socket_close(viewer_connection->control_sock) == BT_SOCKET_ERROR) {
+                       BT_COMP_OR_COMP_CLASS_LOGW(self_comp, self_comp_class,
+                               "Error closing socket: %s.", bt_socket_errormsg());
+               }
+       }
+       viewer_connection->control_sock = BT_INVALID_SOCKET;
+end:
+       return status;
+}
+
+static
+void lttng_live_disconnect_viewer(
+               struct live_viewer_connection *viewer_connection)
+{
+       bt_self_component_class *self_comp_class = viewer_connection->self_comp_class;
+       bt_self_component *self_comp = viewer_connection->self_comp;
+
+       if (viewer_connection->control_sock == BT_INVALID_SOCKET) {
+               return;
+       }
+       if (bt_socket_close(viewer_connection->control_sock) == BT_SOCKET_ERROR) {
+               BT_COMP_OR_COMP_CLASS_LOGW(self_comp, self_comp_class,
+                       "Error closing socket: %s", bt_socket_errormsg());
+               viewer_connection->control_sock = BT_INVALID_SOCKET;
+       }
+}
+
+static
+int list_update_session(bt_value *results,
+               const struct lttng_viewer_session *session,
+               bool *_found, struct live_viewer_connection *viewer_connection)
+{
+       bt_self_component_class *self_comp_class = viewer_connection->self_comp_class;
+       bt_self_component *self_comp = viewer_connection->self_comp;
+       int ret = 0;
+       uint64_t i, len;
+       bt_value *map = NULL;
+       bt_value *hostname = NULL;
+       bt_value *session_name = NULL;
+       bt_value *btval = NULL;
+       bool found = false;
+
+       len = bt_value_array_get_length(results);
+       for (i = 0; i < len; i++) {
+               const char *hostname_str = NULL;
+               const char *session_name_str = NULL;
+
+               map = bt_value_array_borrow_element_by_index(results, i);
+               hostname = bt_value_map_borrow_entry_value(map, "target-hostname");
+               if (!hostname) {
+                       BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp,
+                               self_comp_class,
+                               "Error borrowing \"target-hostname\" entry.");
+                       ret = -1;
+                       goto end;
+               }
+               session_name = bt_value_map_borrow_entry_value(map, "session-name");
+               if (!session_name) {
+                       BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp,
+                               self_comp_class,
+                               "Error borrowing \"session-name\" entry.");
+                       ret = -1;
+                       goto end;
+               }
+               hostname_str = bt_value_string_get(hostname);
+               session_name_str = bt_value_string_get(session_name);
+
+               if (strcmp(session->hostname, hostname_str) == 0
+                               && strcmp(session->session_name, session_name_str) == 0) {
+                       int64_t val;
+                       uint32_t streams = be32toh(session->streams);
+                       uint32_t clients = be32toh(session->clients);
+
+                       found = true;
+
+                       btval = bt_value_map_borrow_entry_value(map, "stream-count");
+                       if (!btval) {
+                               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
+                                       self_comp, self_comp_class,
+                                       "Error borrowing \"stream-count\" entry.");
+                               ret = -1;
+                               goto end;
+                       }
+                       val = bt_value_integer_unsigned_get(btval);
+                       /* sum */
+                       val += streams;
+                       bt_value_integer_unsigned_set(btval, val);
+
+                       btval = bt_value_map_borrow_entry_value(map, "client-count");
+                       if (!btval) {
+                               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(
+                                       self_comp, self_comp_class,
+                                       "Error borrowing \"client-count\" entry.");
+                               ret = -1;
+                               goto end;
+                       }
+                       val = bt_value_integer_unsigned_get(btval);
+                       /* max */
+                       val = bt_max_t(int64_t, clients, val);
+                       bt_value_integer_unsigned_set(btval, val);
+               }
+
+               if (found) {
+                       break;
+               }
+       }
+end:
+       *_found = found;
+       return ret;
+}
+
+static
+int list_append_session(bt_value *results,
+               GString *base_url,
+               const struct lttng_viewer_session *session,
+               struct live_viewer_connection *viewer_connection)
+{
+       int ret = 0;
+       bt_self_component_class *self_comp_class = viewer_connection->self_comp_class;
+       bt_value_map_insert_entry_status insert_status;
+       bt_value_array_append_element_status append_status;
+       bt_value *map = NULL;
+       GString *url = NULL;
+       bool found = false;
+
+       /*
+        * If the session already exists, add the stream count to it,
+        * and do max of client counts.
+        */
+       ret = list_update_session(results, session, &found, viewer_connection);
+       if (ret || found) {
+               goto end;
+       }
+
+       map = bt_value_map_create();
+       if (!map) {
+               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
+                       "Error creating map value.");
+               ret = -1;
+               goto end;
+       }
+
+       if (base_url->len < 1) {
+               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
+                       "Error: base_url length smaller than 1.");
+               ret = -1;
+               goto end;
+       }
+       /*
+        * key = "url",
+        * value = <string>,
+        */
+       url = g_string_new(base_url->str);
+       g_string_append(url, "/host/");
+       g_string_append(url, session->hostname);
+       g_string_append_c(url, '/');
+       g_string_append(url, session->session_name);
+
+       insert_status = bt_value_map_insert_string_entry(map, "url", url->str);
+       if (insert_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
+               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
+                       "Error inserting \"url\" entry.");
+               ret = -1;
+               goto end;
+       }
+
+       /*
+        * key = "target-hostname",
+        * value = <string>,
+        */
+       insert_status = bt_value_map_insert_string_entry(map, "target-hostname",
+               session->hostname);
+       if (insert_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
+               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
+                       "Error inserting \"target-hostname\" entry.");
+               ret = -1;
+               goto end;
+       }
+
+       /*
+        * key = "session-name",
+        * value = <string>,
+        */
+       insert_status = bt_value_map_insert_string_entry(map, "session-name",
+               session->session_name);
+       if (insert_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
+               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
+                       "Error inserting \"session-name\" entry.");
+               ret = -1;
+               goto end;
+       }
+
+       /*
+        * key = "timer-us",
+        * value = <integer>,
+        */
+       {
+               uint32_t live_timer = be32toh(session->live_timer);
+
+               insert_status = bt_value_map_insert_unsigned_integer_entry(
+                       map, "timer-us", live_timer);
+               if (insert_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
+                       BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
+                               "Error inserting \"timer-us\" entry.");
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       /*
+        * key = "stream-count",
+        * value = <integer>,
+        */
+       {
+               uint32_t streams = be32toh(session->streams);
+
+               insert_status = bt_value_map_insert_unsigned_integer_entry(map,
+                       "stream-count", streams);
+               if (insert_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
+                       BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
+                               "Error inserting \"stream-count\" entry.");
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       /*
+        * key = "client-count",
+        * value = <integer>,
+        */
+       {
+               uint32_t clients = be32toh(session->clients);
+
+               insert_status = bt_value_map_insert_unsigned_integer_entry(map,
+                       "client-count", clients);
+               if (insert_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
+                       BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
+                               "Error inserting \"client-count\" entry.");
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+       append_status = bt_value_array_append_element(results, map);
+       if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
+               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
+                       "Error appending map to results.");
+               ret = -1;
+       }
+
+end:
+       if (url) {
+               g_string_free(url, true);
+       }
+       BT_VALUE_PUT_REF_AND_RESET(map);
+       return ret;
+}
+
+/*
+ * Data structure returned:
+ *
+ * {
+ *   <array> = {
+ *     [n] = {
+ *       <map> = {
+ *         {
+ *           key = "url",
+ *           value = <string>,
+ *         },
+ *         {
+ *           key = "target-hostname",
+ *           value = <string>,
+ *         },
+ *         {
+ *           key = "session-name",
+ *           value = <string>,
+ *         },
+ *         {
+ *           key = "timer-us",
+ *           value = <integer>,
+ *         },
+ *         {
+ *           key = "stream-count",
+ *           value = <integer>,
+ *         },
+ *         {
+ *           key = "client-count",
+ *           value = <integer>,
+ *         },
+ *       },
+ *     }
+ *   }
+ */
+
+BT_HIDDEN
+bt_component_class_query_method_status live_viewer_connection_list_sessions(
+               struct live_viewer_connection *viewer_connection,
+               const bt_value **user_result)
+{
+       bt_self_component_class *self_comp_class = viewer_connection->self_comp_class;
+       bt_component_class_query_method_status status =
+               BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_OK;
+       bt_value *result = NULL;
+       enum lttng_live_viewer_status viewer_status;
+       struct lttng_viewer_cmd cmd;
+       struct lttng_viewer_list_sessions list;
+       uint32_t i, sessions_count;
+
+       result = bt_value_array_create();
+       if (!result) {
+               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
+                       "Error creating array");
+               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_MEMORY_ERROR;
+               goto error;
+       }
+
+       BT_LOGD("Requesting list of sessions: cmd=%s",
+               lttng_viewer_command_string(LTTNG_VIEWER_LIST_SESSIONS));
+
+       cmd.cmd = htobe32(LTTNG_VIEWER_LIST_SESSIONS);
+       cmd.data_size = htobe64((uint64_t) 0);
+       cmd.cmd_version = htobe32(0);
+
+       viewer_status = lttng_live_send(viewer_connection, &cmd, sizeof(cmd));
+       if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
+               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
+                       "Error sending list sessions command");
+               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
+               goto error;
+       } else if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
+               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_AGAIN;
+               goto error;
+       }
+
+       viewer_status = lttng_live_recv(viewer_connection, &list, sizeof(list));
+       if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
+               BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
+                       "Error receiving session list");
+               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
+               goto error;
+       } else if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
+               status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_AGAIN;
+               goto error;
+       }
+
+       sessions_count = be32toh(list.sessions_count);
+       for (i = 0; i < sessions_count; i++) {
+               struct lttng_viewer_session lsession;
+
+               viewer_status = lttng_live_recv(viewer_connection, &lsession,
+                       sizeof(lsession));
+               if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
+                       BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
+                               "Error receiving session:");
+                       status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
+                       goto error;
+               } else if (viewer_status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
+                       status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_AGAIN;
+                       goto error;
+               }
+
+               lsession.hostname[LTTNG_VIEWER_HOST_NAME_MAX - 1] = '\0';
+               lsession.session_name[LTTNG_VIEWER_NAME_MAX - 1] = '\0';
+               if (list_append_session(result, viewer_connection->url,
+                               &lsession, viewer_connection)) {
+                       BT_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp_class,
+                               "Error appending session");
+                       status = BT_COMPONENT_CLASS_QUERY_METHOD_STATUS_ERROR;
+                       goto error;
+               }
+       }
+
+       *user_result = result;
+       goto end;
+error:
+       BT_VALUE_PUT_REF_AND_RESET(result);
+end:
+       return status;
+}
+
+static
+enum lttng_live_viewer_status lttng_live_query_session_ids(
+               struct lttng_live_msg_iter *lttng_live_msg_iter)
+{
+       struct lttng_viewer_cmd cmd;
+       struct lttng_viewer_list_sessions list;
+       struct lttng_viewer_session lsession;
+       uint32_t i, sessions_count;
+       uint64_t session_id;
+       enum lttng_live_viewer_status status;
+       struct live_viewer_connection *viewer_connection =
+               lttng_live_msg_iter->viewer_connection;
+       bt_self_component *self_comp = viewer_connection->self_comp;
+       bt_self_component_class *self_comp_class =
+               viewer_connection->self_comp_class;
+
+       BT_COMP_LOGD("Asking the relay daemon for the list of sessions: cmd=%s",
+               lttng_viewer_command_string(LTTNG_VIEWER_LIST_SESSIONS));
+
+       cmd.cmd = htobe32(LTTNG_VIEWER_LIST_SESSIONS);
+       cmd.data_size = htobe64((uint64_t) 0);
+       cmd.cmd_version = htobe32(0);
+
+       status = lttng_live_send(viewer_connection, &cmd, sizeof(cmd));
+       if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
+               viewer_handle_send_status(self_comp, self_comp_class,
+                       status, "list sessions command");
+               goto end;
+       }
+
+       status = lttng_live_recv(viewer_connection, &list, sizeof(list));
+       if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
+               viewer_handle_recv_status(self_comp, self_comp_class,
+                       status, "session list reply");
+               goto end;
+       }
+
+       sessions_count = be32toh(list.sessions_count);
+       for (i = 0; i < sessions_count; i++) {
+               status = lttng_live_recv(viewer_connection, &lsession,
+                       sizeof(lsession));
+               if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
+                       viewer_handle_recv_status(self_comp, self_comp_class,
+                               status, "session reply");
+                       goto end;
+               }
+               lsession.hostname[LTTNG_VIEWER_HOST_NAME_MAX - 1] = '\0';
+               lsession.session_name[LTTNG_VIEWER_NAME_MAX - 1] = '\0';
+               session_id = be64toh(lsession.id);
+
+               BT_COMP_LOGI("Adding session to internal list: "
+                       "session-id=%" PRIu64 ", hostname=\"%s\", session-name=\"%s\"",
+                       session_id, lsession.hostname, lsession.session_name);
+
+               if ((strncmp(lsession.session_name,
+                       viewer_connection->session_name->str,
+                       LTTNG_VIEWER_NAME_MAX) == 0) && (strncmp(lsession.hostname,
+                               viewer_connection->target_hostname->str,
+                               LTTNG_VIEWER_HOST_NAME_MAX) == 0)) {
+
+                       if (lttng_live_add_session(lttng_live_msg_iter, session_id,
+                                       lsession.hostname,
+                                       lsession.session_name)) {
+                               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                                       "Failed to add live session");
+                               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
+                               goto end;
+                       }
+               }
+       }
+
+       status = LTTNG_LIVE_VIEWER_STATUS_OK;
+
+end:
+       return status;
+}
+
+BT_HIDDEN
+enum lttng_live_viewer_status lttng_live_create_viewer_session(
+               struct lttng_live_msg_iter *lttng_live_msg_iter)
+{
+       struct lttng_viewer_cmd cmd;
+       struct lttng_viewer_create_session_response resp;
+       enum lttng_live_viewer_status status;
+       struct live_viewer_connection *viewer_connection =
+               lttng_live_msg_iter->viewer_connection;
+       bt_self_component *self_comp = viewer_connection->self_comp;
+       bt_self_component_class *self_comp_class =
+               viewer_connection->self_comp_class;
+
+       BT_COMP_OR_COMP_CLASS_LOGD(self_comp, self_comp_class,
+               "Creating a viewer session: cmd=%s",
+               lttng_viewer_command_string(LTTNG_VIEWER_CREATE_SESSION));
+
+       cmd.cmd = htobe32(LTTNG_VIEWER_CREATE_SESSION);
+       cmd.data_size = htobe64((uint64_t) 0);
+       cmd.cmd_version = htobe32(0);
+
+       status = lttng_live_send(viewer_connection, &cmd, sizeof(cmd));
+       if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
+               viewer_handle_send_status(self_comp, self_comp_class,
+                       status, "create session command");
+               goto end;
+       }
+
+       status = lttng_live_recv(viewer_connection, &resp, sizeof(resp));
+       if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
+               viewer_handle_recv_status(self_comp, self_comp_class,
+                       status, "create session reply");
+               goto end;
+       }
+
+       if (be32toh(resp.status) != LTTNG_VIEWER_CREATE_SESSION_OK) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Error creating viewer session");
+               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
+               goto end;
+       }
+
+       status = lttng_live_query_session_ids(lttng_live_msg_iter);
+       if (status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Failed to query live viewer session ids");
+               goto end;
+       } else if (status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
+               goto end;
+       }
+
+end:
+       return status;
+}
+
+static
+enum lttng_live_viewer_status receive_streams(struct lttng_live_session *session,
+               uint32_t stream_count,
+               bt_self_message_iterator *self_msg_iter)
+{
+       uint32_t i;
+       struct lttng_live_msg_iter *lttng_live_msg_iter =
+               session->lttng_live_msg_iter;
+       enum lttng_live_viewer_status status;
+       struct live_viewer_connection *viewer_connection =
+               lttng_live_msg_iter->viewer_connection;
+       bt_self_component *self_comp = viewer_connection->self_comp;
+
+       BT_COMP_LOGI("Getting %" PRIu32 " new streams", stream_count);
+       for (i = 0; i < stream_count; i++) {
+               struct lttng_viewer_stream stream;
+               struct lttng_live_stream_iterator *live_stream;
+               uint64_t stream_id;
+               uint64_t ctf_trace_id;
+
+               status = lttng_live_recv(viewer_connection, &stream,
+                       sizeof(stream));
+               if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
+                       viewer_handle_recv_status(self_comp, NULL,
+                               status, "stream reply");
+                       goto end;
+               }
+               stream.path_name[LTTNG_VIEWER_PATH_MAX - 1] = '\0';
+               stream.channel_name[LTTNG_VIEWER_NAME_MAX - 1] = '\0';
+               stream_id = be64toh(stream.id);
+               ctf_trace_id = be64toh(stream.ctf_trace_id);
+
+               if (stream.metadata_flag) {
+                       BT_COMP_LOGI("    metadata stream %" PRIu64 " : %s/%s",
+                               stream_id, stream.path_name, stream.channel_name);
+                       if (lttng_live_metadata_create_stream(session,
+                                       ctf_trace_id, stream_id,
+                                       stream.path_name)) {
+                               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                                       "Error creating metadata stream");
+                               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
+                               goto end;
+                       }
+                       session->lazy_stream_msg_init = true;
+               } else {
+                       BT_COMP_LOGI("    stream %" PRIu64 " : %s/%s",
+                               stream_id, stream.path_name, stream.channel_name);
+                       live_stream = lttng_live_stream_iterator_create(session,
+                               ctf_trace_id, stream_id, self_msg_iter);
+                       if (!live_stream) {
+                               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                                       "Error creating stream");
+                               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
+                               goto end;
+                       }
+               }
+       }
+       status = LTTNG_LIVE_VIEWER_STATUS_OK;
+
+end:
+       return status;
+}
+
+BT_HIDDEN
+enum lttng_live_viewer_status lttng_live_session_attach(
+               struct lttng_live_session *session,
+               bt_self_message_iterator *self_msg_iter)
+{
+       struct lttng_viewer_cmd cmd;
+       enum lttng_live_viewer_status status;
+       struct lttng_viewer_attach_session_request rq;
+       struct lttng_viewer_attach_session_response rp;
+       struct lttng_live_msg_iter *lttng_live_msg_iter =
+               session->lttng_live_msg_iter;
+       struct live_viewer_connection *viewer_connection =
+               lttng_live_msg_iter->viewer_connection;
+       bt_self_component *self_comp = viewer_connection->self_comp;
+       uint64_t session_id = session->id;
+       uint32_t streams_count;
+       const size_t cmd_buf_len = sizeof(cmd) + sizeof(rq);
+       char cmd_buf[cmd_buf_len];
+
+       BT_COMP_LOGD("Attaching to session: cmd=%s, session-id=%" PRIu64
+               ", seek=%s",
+               lttng_viewer_command_string(LTTNG_VIEWER_ATTACH_SESSION),
+               session_id,
+               lttng_viewer_seek_string(LTTNG_VIEWER_SEEK_LAST));
+
+       cmd.cmd = htobe32(LTTNG_VIEWER_ATTACH_SESSION);
+       cmd.data_size = htobe64((uint64_t) sizeof(rq));
+       cmd.cmd_version = htobe32(0);
+
+       memset(&rq, 0, sizeof(rq));
+       rq.session_id = htobe64(session_id);
+       // TODO: add cmd line parameter to select seek beginning
+       // rq.seek = htobe32(LTTNG_VIEWER_SEEK_BEGINNING);
+       rq.seek = htobe32(LTTNG_VIEWER_SEEK_LAST);
+
+       /*
+        * Merge the cmd and connection request to prevent a write-write
+        * sequence on the TCP socket. Otherwise, a delayed ACK will prevent the
+        * second write to be performed quickly in presence of Nagle's algorithm.
+        */
+       memcpy(cmd_buf, &cmd, sizeof(cmd));
+       memcpy(cmd_buf + sizeof(cmd), &rq, sizeof(rq));
+       status = lttng_live_send(viewer_connection, &cmd_buf, cmd_buf_len);
+       if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
+               viewer_handle_send_status(self_comp, NULL,
+                       status, "attach session command");
+               goto end;
+       }
+
+       status = lttng_live_recv(viewer_connection, &rp, sizeof(rp));
+       if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
+               viewer_handle_recv_status(self_comp, NULL,
+                       status, "attach session reply");
+               goto end;
+       }
+
+       streams_count = be32toh(rp.streams_count);
+       switch(be32toh(rp.status)) {
+       case LTTNG_VIEWER_ATTACH_OK:
+               break;
+       case LTTNG_VIEWER_ATTACH_UNK:
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Session id %" PRIu64 " is unknown", session_id);
+               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
+               goto end;
+       case LTTNG_VIEWER_ATTACH_ALREADY:
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "There is already a viewer attached to this session");
+               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
+               goto end;
+       case LTTNG_VIEWER_ATTACH_NOT_LIVE:
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Not a live session");
+               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
+               goto end;
+       case LTTNG_VIEWER_ATTACH_SEEK_ERR:
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Wrong seek parameter");
+               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
+               goto end;
+       default:
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Unknown attach return code %u", be32toh(rp.status));
+               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
+               goto end;
+       }
+
+       /* We receive the initial list of streams. */
+       status = receive_streams(session, streams_count, self_msg_iter);
+       switch (status) {
+       case LTTNG_LIVE_VIEWER_STATUS_OK:
+               break;
+       case LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED:
+               goto end;
+       case LTTNG_LIVE_VIEWER_STATUS_ERROR:
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp, "Error receiving streams");
+               goto end;
+       default:
+               bt_common_abort();
+       }
+
+       session->attached = true;
+       session->new_streams_needed = false;
+
+end:
+       return status;
+}
+
+BT_HIDDEN
+enum lttng_live_viewer_status lttng_live_session_detach(
+               struct lttng_live_session *session)
+{
+       struct lttng_viewer_cmd cmd;
+       enum lttng_live_viewer_status status;
+       struct lttng_viewer_detach_session_request rq;
+       struct lttng_viewer_detach_session_response rp;
+       struct lttng_live_msg_iter *lttng_live_msg_iter =
+               session->lttng_live_msg_iter;
+       bt_self_component *self_comp = session->self_comp;
+       struct live_viewer_connection *viewer_connection =
+               lttng_live_msg_iter->viewer_connection;
+       uint64_t session_id = session->id;
+       const size_t cmd_buf_len = sizeof(cmd) + sizeof(rq);
+       char cmd_buf[cmd_buf_len];
+
+       /*
+        * The session might already be detached and the viewer socket might
+        * already been closed. This happens when calling this function when
+        * tearing down the graph after an error.
+        */
+       if (!session->attached || viewer_connection->control_sock == BT_INVALID_SOCKET) {
+               return LTTNG_LIVE_VIEWER_STATUS_OK;
+       }
+
+       BT_COMP_LOGD("Detaching from session: cmd=%s, session-id=%" PRIu64,
+               lttng_viewer_command_string(LTTNG_VIEWER_DETACH_SESSION),
+               session_id);
+
+       cmd.cmd = htobe32(LTTNG_VIEWER_DETACH_SESSION);
+       cmd.data_size = htobe64((uint64_t) sizeof(rq));
+       cmd.cmd_version = htobe32(0);
+
+       memset(&rq, 0, sizeof(rq));
+       rq.session_id = htobe64(session_id);
+
+       /*
+        * Merge the cmd and connection request to prevent a write-write
+        * sequence on the TCP socket. Otherwise, a delayed ACK will prevent the
+        * second write to be performed quickly in presence of Nagle's algorithm.
+        */
+       memcpy(cmd_buf, &cmd, sizeof(cmd));
+       memcpy(cmd_buf + sizeof(cmd), &rq, sizeof(rq));
+       status = lttng_live_send(viewer_connection, &cmd_buf, cmd_buf_len);
+       if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
+               viewer_handle_send_status(self_comp, NULL,
+                       status, "detach session command");
+               goto end;
+       }
+
+       status = lttng_live_recv(viewer_connection, &rp, sizeof(rp));
+       if (status != LTTNG_LIVE_VIEWER_STATUS_OK) {
+               viewer_handle_recv_status(self_comp, NULL,
+                       status, "detach session reply");
+               goto end;
+       }
+
+       switch(be32toh(rp.status)) {
+       case LTTNG_VIEWER_DETACH_SESSION_OK:
+               break;
+       case LTTNG_VIEWER_DETACH_SESSION_UNK:
+               BT_COMP_LOGW("Session id %" PRIu64 " is unknown", session_id);
+               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
+               goto end;
+       case LTTNG_VIEWER_DETACH_SESSION_ERR:
+               BT_COMP_LOGW("Error detaching session id %" PRIu64 "", session_id);
+               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
+               goto end;
+       default:
+               BT_COMP_LOGE("Unknown detach return code %u", be32toh(rp.status));
+               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
+               goto end;
+       }
+
+       session->attached = false;
+
+       status = LTTNG_LIVE_VIEWER_STATUS_OK;
+
+end:
+       return status;
+}
+
+BT_HIDDEN
+enum lttng_live_get_one_metadata_status lttng_live_get_one_metadata_packet(
+               struct lttng_live_trace *trace, FILE *fp, size_t *reply_len)
+{
+       uint64_t len = 0;
+       enum lttng_live_get_one_metadata_status status;
+       enum lttng_live_viewer_status viewer_status;
+       struct lttng_viewer_cmd cmd;
+       struct lttng_viewer_get_metadata rq;
+       struct lttng_viewer_metadata_packet rp;
+       gchar *data = NULL;
+       ssize_t writelen;
+       struct lttng_live_session *session = trace->session;
+       struct lttng_live_msg_iter *lttng_live_msg_iter =
+               session->lttng_live_msg_iter;
+       struct lttng_live_metadata *metadata = trace->metadata;
+       struct live_viewer_connection *viewer_connection =
+               lttng_live_msg_iter->viewer_connection;
+       bt_self_component *self_comp = viewer_connection->self_comp;
+       const size_t cmd_buf_len = sizeof(cmd) + sizeof(rq);
+       char cmd_buf[cmd_buf_len];
+
+       BT_COMP_LOGD("Requesting new metadata for trace:"
+               "cmd=%s, trace-id=%" PRIu64 ", metadata-stream-id=%" PRIu64,
+               lttng_viewer_command_string(LTTNG_VIEWER_GET_METADATA),
+               trace->id, metadata->stream_id);
+
+       rq.stream_id = htobe64(metadata->stream_id);
+       cmd.cmd = htobe32(LTTNG_VIEWER_GET_METADATA);
+       cmd.data_size = htobe64((uint64_t) sizeof(rq));
+       cmd.cmd_version = htobe32(0);
+
+       /*
+        * Merge the cmd and connection request to prevent a write-write
+        * sequence on the TCP socket. Otherwise, a delayed ACK will prevent the
+        * second write to be performed quickly in presence of Nagle's algorithm.
+        */
+       memcpy(cmd_buf, &cmd, sizeof(cmd));
+       memcpy(cmd_buf + sizeof(cmd), &rq, sizeof(rq));
+       viewer_status = lttng_live_send(viewer_connection, &cmd_buf, cmd_buf_len);
+       if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
+               viewer_handle_send_status(self_comp, NULL,
+                       viewer_status, "get metadata command");
+               status = (enum lttng_live_get_one_metadata_status) viewer_status;
+               goto end;
+       }
+
+       viewer_status = lttng_live_recv(viewer_connection, &rp, sizeof(rp));
+       if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
+               viewer_handle_recv_status(self_comp, NULL,
+                       viewer_status, "get metadata reply");
+               status = (enum lttng_live_get_one_metadata_status) viewer_status;
+               goto end;
+       }
+
+       switch (be32toh(rp.status)) {
+               case LTTNG_VIEWER_METADATA_OK:
+                       BT_COMP_LOGD("Received get_metadata response: ok");
+                       break;
+               case LTTNG_VIEWER_NO_NEW_METADATA:
+                       BT_COMP_LOGD("Received get_metadata response: no new");
+                       status = LTTNG_LIVE_GET_ONE_METADATA_STATUS_END;
+                       goto end;
+               case LTTNG_VIEWER_METADATA_ERR:
+                       /*
+                        * The Relayd cannot find this stream id. Maybe its
+                        * gone already. This can happen in short lived UST app
+                        * in a per-pid session.
+                        */
+                       BT_COMP_LOGD("Received get_metadata response: error");
+                       status = LTTNG_LIVE_GET_ONE_METADATA_STATUS_CLOSED;
+                       goto end;
+               default:
+                       BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                               "Received get_metadata response: unknown");
+                       status = LTTNG_LIVE_GET_ONE_METADATA_STATUS_ERROR;
+                       goto end;
+       }
+
+       len = be64toh(rp.len);
+       BT_COMP_LOGD("Writing %" PRIu64" bytes to metadata", len);
+       if (len <= 0) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Erroneous response length");
+               status = LTTNG_LIVE_GET_ONE_METADATA_STATUS_ERROR;
+               goto end;
+       }
+
+       data = g_new0(gchar, len);
+       if (!data) {
+               BT_COMP_LOGE_APPEND_CAUSE_ERRNO(self_comp,
+                       "Failed to allocate data buffer", ".");
+               status = LTTNG_LIVE_GET_ONE_METADATA_STATUS_ERROR;
+               goto end;
+       }
+
+       viewer_status = lttng_live_recv(viewer_connection, data, len);
+       if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
+               viewer_handle_recv_status(self_comp, NULL,
+                       viewer_status, "get metadata packet");
+               status = (enum lttng_live_get_one_metadata_status) viewer_status;
+               goto end;
+       }
+
+       /*
+        * Write the metadata to the file handle.
+        */
+       writelen = fwrite(data, sizeof(uint8_t), len, fp);
+       if (writelen != len) {
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Writing in the metadata file stream");
+               status = LTTNG_LIVE_GET_ONE_METADATA_STATUS_ERROR;
+               goto end;
+       }
+
+       *reply_len = len;
+       status = LTTNG_LIVE_GET_ONE_METADATA_STATUS_OK;
+
+end:
+       g_free(data);
+       return status;
+}
+
+/*
+ * Assign the fields from a lttng_viewer_index to a packet_index.
+ */
+static
+void lttng_index_to_packet_index(struct lttng_viewer_index *lindex,
+               struct packet_index *pindex)
+{
+       BT_ASSERT(lindex);
+       BT_ASSERT(pindex);
+
+       pindex->offset = be64toh(lindex->offset);
+       pindex->packet_size = be64toh(lindex->packet_size);
+       pindex->content_size = be64toh(lindex->content_size);
+       pindex->ts_cycles.timestamp_begin = be64toh(lindex->timestamp_begin);
+       pindex->ts_cycles.timestamp_end = be64toh(lindex->timestamp_end);
+       pindex->events_discarded = be64toh(lindex->events_discarded);
+}
+
+static
+void lttng_live_need_new_streams(struct lttng_live_msg_iter *lttng_live_msg_iter)
+{
+       uint64_t session_idx;
+       struct live_viewer_connection *viewer_connection =
+               lttng_live_msg_iter->viewer_connection;
+
+       for (session_idx = 0; session_idx < lttng_live_msg_iter->sessions->len;
+                       session_idx++) {
+               struct lttng_live_session *session =
+                               (lttng_live_session *) g_ptr_array_index(lttng_live_msg_iter->sessions, session_idx);
+               BT_COMP_LOGD("Marking session as needing new streams: "
+                               "session-id=%" PRIu64, session->id);
+               session->new_streams_needed = true;
+       }
+}
+
+BT_HIDDEN
+enum lttng_live_iterator_status lttng_live_get_next_index(
+               struct lttng_live_msg_iter *lttng_live_msg_iter,
+               struct lttng_live_stream_iterator *stream,
+               struct packet_index *index)
+{
+       struct lttng_viewer_cmd cmd;
+       struct lttng_viewer_get_next_index rq;
+       enum lttng_live_viewer_status viewer_status;
+       struct lttng_viewer_index rp;
+       enum lttng_live_iterator_status status;
+       struct live_viewer_connection *viewer_connection =
+               lttng_live_msg_iter->viewer_connection;
+       bt_self_component *self_comp = viewer_connection->self_comp;
+       struct lttng_live_trace *trace = stream->trace;
+       const size_t cmd_buf_len = sizeof(cmd) + sizeof(rq);
+       char cmd_buf[cmd_buf_len];
+       uint32_t flags, rp_status;
+
+       BT_COMP_LOGD("Requesting next index for stream: cmd=%s, "
+                       "viewer-stream-id=%" PRIu64,
+                       lttng_viewer_command_string(LTTNG_VIEWER_GET_NEXT_INDEX),
+                       stream->viewer_stream_id);
+       cmd.cmd = htobe32(LTTNG_VIEWER_GET_NEXT_INDEX);
+       cmd.data_size = htobe64((uint64_t) sizeof(rq));
+       cmd.cmd_version = htobe32(0);
+
+       memset(&rq, 0, sizeof(rq));
+       rq.stream_id = htobe64(stream->viewer_stream_id);
+
+       /*
+        * Merge the cmd and connection request to prevent a write-write
+        * sequence on the TCP socket. Otherwise, a delayed ACK will prevent the
+        * second write to be performed quickly in presence of Nagle's algorithm.
+        */
+       memcpy(cmd_buf, &cmd, sizeof(cmd));
+       memcpy(cmd_buf + sizeof(cmd), &rq, sizeof(rq));
+
+       viewer_status = lttng_live_send(viewer_connection, &cmd_buf, cmd_buf_len);
+       if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
+               viewer_handle_send_status(self_comp, NULL,
+                       viewer_status, "get next index command");
+               goto error;
+       }
+
+       viewer_status = lttng_live_recv(viewer_connection, &rp, sizeof(rp));
+       if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
+               viewer_handle_recv_status(self_comp, NULL,
+                       viewer_status, "get next index reply");
+               goto error;
+       }
+
+       flags = be32toh(rp.flags);
+       rp_status = be32toh(rp.status);
+
+       BT_COMP_LOGD("Received response from relay daemon: cmd=%s, response=%s",
+               lttng_viewer_command_string(LTTNG_VIEWER_GET_NEXT_INDEX),
+               lttng_viewer_next_index_return_code_string(rp_status));
+       switch (rp_status) {
+       case LTTNG_VIEWER_INDEX_INACTIVE:
+       {
+               uint64_t ctf_stream_class_id;
+
+               memset(index, 0, sizeof(struct packet_index));
+               index->ts_cycles.timestamp_end = be64toh(rp.timestamp_end);
+               stream->current_inactivity_ts = index->ts_cycles.timestamp_end;
+               ctf_stream_class_id = be64toh(rp.stream_id);
+               if (stream->ctf_stream_class_id.is_set) {
+                       BT_ASSERT(stream->ctf_stream_class_id.value==
+                               ctf_stream_class_id);
+               } else {
+                       stream->ctf_stream_class_id.value = ctf_stream_class_id;
+                       stream->ctf_stream_class_id.is_set = true;
+               }
+               lttng_live_stream_iterator_set_state(stream, LTTNG_LIVE_STREAM_QUIESCENT);
+               status = LTTNG_LIVE_ITERATOR_STATUS_OK;
+               break;
+       }
+       case LTTNG_VIEWER_INDEX_OK:
+       {
+               uint64_t ctf_stream_class_id;
+
+               lttng_index_to_packet_index(&rp, index);
+               ctf_stream_class_id = be64toh(rp.stream_id);
+               if (stream->ctf_stream_class_id.is_set) {
+                       BT_ASSERT(stream->ctf_stream_class_id.value==
+                               ctf_stream_class_id);
+               } else {
+                       stream->ctf_stream_class_id.value = ctf_stream_class_id;
+                       stream->ctf_stream_class_id.is_set = true;
+               }
+
+               lttng_live_stream_iterator_set_state(stream, LTTNG_LIVE_STREAM_ACTIVE_DATA);
+
+               if (flags & LTTNG_VIEWER_FLAG_NEW_METADATA) {
+                       BT_COMP_LOGD("Marking trace as needing new metadata: "
+                               "response=%s, response-flag=NEW_METADATA, trace-id=%" PRIu64,
+                               lttng_viewer_next_index_return_code_string(rp_status),
+                               trace->id);
+                       trace->metadata_stream_state = LTTNG_LIVE_METADATA_STREAM_STATE_NEEDED;
+               }
+               if (flags & LTTNG_VIEWER_FLAG_NEW_STREAM) {
+                       BT_COMP_LOGD("Marking all sessions as possibly needing new streams: "
+                               "response=%s, response-flag=NEW_STREAM",
+                               lttng_viewer_next_index_return_code_string(rp_status));
+                       lttng_live_need_new_streams(lttng_live_msg_iter);
+               }
+               status = LTTNG_LIVE_ITERATOR_STATUS_OK;
+               break;
+       }
+       case LTTNG_VIEWER_INDEX_RETRY:
+               memset(index, 0, sizeof(struct packet_index));
+               lttng_live_stream_iterator_set_state(stream, LTTNG_LIVE_STREAM_ACTIVE_NO_DATA);
+               status = LTTNG_LIVE_ITERATOR_STATUS_AGAIN;
+               goto end;
+       case LTTNG_VIEWER_INDEX_HUP:
+               memset(index, 0, sizeof(struct packet_index));
+               index->offset = EOF;
+               lttng_live_stream_iterator_set_state(stream, LTTNG_LIVE_STREAM_EOF);
+               stream->has_stream_hung_up = true;
+               status = LTTNG_LIVE_ITERATOR_STATUS_END;
+               break;
+       case LTTNG_VIEWER_INDEX_ERR:
+               memset(index, 0, sizeof(struct packet_index));
+               lttng_live_stream_iterator_set_state(stream, LTTNG_LIVE_STREAM_ACTIVE_NO_DATA);
+               status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
+               goto end;
+       default:
+               BT_COMP_LOGD("Received get_next_index response: unknown value");
+               memset(index, 0, sizeof(struct packet_index));
+               lttng_live_stream_iterator_set_state(stream, LTTNG_LIVE_STREAM_ACTIVE_NO_DATA);
+               status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
+               goto end;
+       }
+       goto end;
+
+error:
+       status = viewer_status_to_live_iterator_status(viewer_status);
+end:
+       return status;
+}
+
+BT_HIDDEN
+enum ctf_msg_iter_medium_status lttng_live_get_stream_bytes(
+               struct lttng_live_msg_iter *lttng_live_msg_iter,
+               struct lttng_live_stream_iterator *stream, uint8_t *buf,
+               uint64_t offset, uint64_t req_len, uint64_t *recv_len)
+{
+       enum ctf_msg_iter_medium_status status;
+       enum lttng_live_viewer_status viewer_status;
+       struct lttng_viewer_trace_packet rp;
+       struct lttng_viewer_cmd cmd;
+       struct lttng_viewer_get_packet rq;
+       struct live_viewer_connection *viewer_connection =
+               lttng_live_msg_iter->viewer_connection;
+       bt_self_component *self_comp = viewer_connection->self_comp;
+       struct lttng_live_trace *trace = stream->trace;
+       const size_t cmd_buf_len = sizeof(cmd) + sizeof(rq);
+       char cmd_buf[cmd_buf_len];
+       uint32_t flags, rp_status;
+
+       BT_COMP_LOGD("Requesting data from stream: cmd=%s, "
+                       "offset=%" PRIu64 ", request-len=%" PRIu64,
+                       lttng_viewer_command_string(LTTNG_VIEWER_GET_PACKET),
+                       offset, req_len);
+
+       cmd.cmd = htobe32(LTTNG_VIEWER_GET_PACKET);
+       cmd.data_size = htobe64((uint64_t) sizeof(rq));
+       cmd.cmd_version = htobe32(0);
+
+       memset(&rq, 0, sizeof(rq));
+       rq.stream_id = htobe64(stream->viewer_stream_id);
+       rq.offset = htobe64(offset);
+       rq.len = htobe32(req_len);
+
+       /*
+        * Merge the cmd and connection request to prevent a write-write
+        * sequence on the TCP socket. Otherwise, a delayed ACK will prevent the
+        * second write to be performed quickly in presence of Nagle's algorithm.
+        */
+       memcpy(cmd_buf, &cmd, sizeof(cmd));
+       memcpy(cmd_buf + sizeof(cmd), &rq, sizeof(rq));
+
+       viewer_status = lttng_live_send(viewer_connection, &cmd_buf, cmd_buf_len);
+       if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
+               viewer_handle_send_status(self_comp, NULL,
+                       viewer_status, "get data packet command");
+               goto error_convert_status;
+       }
+
+       viewer_status = lttng_live_recv(viewer_connection, &rp, sizeof(rp));
+       if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
+               viewer_handle_recv_status(self_comp, NULL,
+                       viewer_status, "get data packet reply");
+               goto error_convert_status;
+       }
+
+       flags = be32toh(rp.flags);
+       rp_status = be32toh(rp.status);
+
+       BT_COMP_LOGD("Received response from relay daemon: cmd=%s, response=%s",
+               lttng_viewer_command_string(LTTNG_VIEWER_GET_PACKET),
+               lttng_viewer_get_packet_return_code_string(rp_status));
+       switch (rp_status) {
+       case LTTNG_VIEWER_GET_PACKET_OK:
+               req_len = be32toh(rp.len);
+               BT_COMP_LOGD("Got packet from relay daemon: response=%s, packet-len=%" PRIu64 "",
+                       lttng_viewer_get_packet_return_code_string(rp_status),
+                       req_len);
+               status = CTF_MSG_ITER_MEDIUM_STATUS_OK;
+               break;
+       case LTTNG_VIEWER_GET_PACKET_RETRY:
+               /* Unimplemented by relay daemon */
+               status = CTF_MSG_ITER_MEDIUM_STATUS_AGAIN;
+               goto end;
+       case LTTNG_VIEWER_GET_PACKET_ERR:
+               if (flags & LTTNG_VIEWER_FLAG_NEW_METADATA) {
+                       BT_COMP_LOGD("Marking trace as needing new metadata: "
+                               "response=%s, response-flag=NEW_METADATA, trace-id=%" PRIu64,
+                               lttng_viewer_next_index_return_code_string(rp_status),
+                               trace->id);
+                       trace->metadata_stream_state = LTTNG_LIVE_METADATA_STREAM_STATE_NEEDED;
+               }
+               if (flags & LTTNG_VIEWER_FLAG_NEW_STREAM) {
+                       BT_COMP_LOGD("Marking all sessions as possibly needing new streams: "
+                               "response=%s, response-flag=NEW_STREAM",
+                               lttng_viewer_next_index_return_code_string(rp_status));
+                       lttng_live_need_new_streams(lttng_live_msg_iter);
+               }
+               if (flags & (LTTNG_VIEWER_FLAG_NEW_METADATA
+                               | LTTNG_VIEWER_FLAG_NEW_STREAM)) {
+                       status = CTF_MSG_ITER_MEDIUM_STATUS_AGAIN;
+                       BT_COMP_LOGD("Reply with any one flags set means we should retry: response=%s",
+                               lttng_viewer_get_packet_return_code_string(rp_status));
+                       goto end;
+               }
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Received get_data_packet response: error");
+               status = CTF_MSG_ITER_MEDIUM_STATUS_ERROR;
+               goto end;
+       case LTTNG_VIEWER_GET_PACKET_EOF:
+               status = CTF_MSG_ITER_MEDIUM_STATUS_EOF;
+               goto end;
+       default:
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Received get_data_packet response: unknown (%d)", rp_status);
+               status = CTF_MSG_ITER_MEDIUM_STATUS_ERROR;
+               goto end;
+       }
+
+       if (req_len == 0) {
+               status = CTF_MSG_ITER_MEDIUM_STATUS_ERROR;
+               goto end;
+       }
+
+       viewer_status = lttng_live_recv(viewer_connection, buf, req_len);
+       if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
+               viewer_handle_recv_status(self_comp, NULL,
+                       viewer_status, "get data packet");
+               goto error_convert_status;
+       }
+       *recv_len = req_len;
+
+       status = CTF_MSG_ITER_MEDIUM_STATUS_OK;
+       goto end;
+
+error_convert_status:
+       status = viewer_status_to_ctf_msg_iter_medium_status(viewer_status);
+end:
+       return status;
+}
+
+/*
+ * Request new streams for a session.
+ */
+BT_HIDDEN
+enum lttng_live_iterator_status lttng_live_session_get_new_streams(
+               struct lttng_live_session *session,
+               bt_self_message_iterator *self_msg_iter)
+{
+       enum lttng_live_iterator_status status =
+               LTTNG_LIVE_ITERATOR_STATUS_OK;
+       struct lttng_viewer_cmd cmd;
+       struct lttng_viewer_new_streams_request rq;
+       struct lttng_viewer_new_streams_response rp;
+       struct lttng_live_msg_iter *lttng_live_msg_iter =
+               session->lttng_live_msg_iter;
+       enum lttng_live_viewer_status viewer_status;
+       struct live_viewer_connection *viewer_connection =
+               lttng_live_msg_iter->viewer_connection;
+       bt_self_component *self_comp = viewer_connection->self_comp;
+       uint32_t streams_count;
+       const size_t cmd_buf_len = sizeof(cmd) + sizeof(rq);
+       char cmd_buf[cmd_buf_len];
+
+       if (!session->new_streams_needed) {
+               status = LTTNG_LIVE_ITERATOR_STATUS_OK;
+               goto end;
+       }
+
+       BT_COMP_LOGD("Requesting new streams for session: cmd=%s, "
+               "session-id=%" PRIu64,
+               lttng_viewer_command_string(LTTNG_VIEWER_GET_NEW_STREAMS),
+               session->id);
+
+       cmd.cmd = htobe32(LTTNG_VIEWER_GET_NEW_STREAMS);
+       cmd.data_size = htobe64((uint64_t) sizeof(rq));
+       cmd.cmd_version = htobe32(0);
+
+       memset(&rq, 0, sizeof(rq));
+       rq.session_id = htobe64(session->id);
+
+       /*
+        * Merge the cmd and connection request to prevent a write-write
+        * sequence on the TCP socket. Otherwise, a delayed ACK will prevent the
+        * second write to be performed quickly in presence of Nagle's algorithm.
+        */
+       memcpy(cmd_buf, &cmd, sizeof(cmd));
+       memcpy(cmd_buf + sizeof(cmd), &rq, sizeof(rq));
+
+       viewer_status = lttng_live_send(viewer_connection, &cmd_buf, cmd_buf_len);
+       if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
+               viewer_handle_send_status(self_comp, NULL,
+                       viewer_status, "get new streams command");
+               status = viewer_status_to_live_iterator_status(viewer_status);
+               goto end;
+       }
+
+       viewer_status = lttng_live_recv(viewer_connection, &rp, sizeof(rp));
+       if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
+               viewer_handle_recv_status(self_comp, NULL,
+                       viewer_status, "get new streams reply");
+               status = viewer_status_to_live_iterator_status(viewer_status);
+               goto end;
+       }
+
+       streams_count = be32toh(rp.streams_count);
+
+       switch(be32toh(rp.status)) {
+       case LTTNG_VIEWER_NEW_STREAMS_OK:
+               session->new_streams_needed = false;
+               break;
+       case LTTNG_VIEWER_NEW_STREAMS_NO_NEW:
+               session->new_streams_needed = false;
+               goto end;
+       case LTTNG_VIEWER_NEW_STREAMS_HUP:
+               session->new_streams_needed = false;
+               session->closed = true;
+               status = LTTNG_LIVE_ITERATOR_STATUS_END;
+               goto end;
+       case LTTNG_VIEWER_NEW_STREAMS_ERR:
+               BT_COMP_LOGD("Received get_new_streams response: error");
+               status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
+               goto end;
+       default:
+               BT_COMP_LOGE_APPEND_CAUSE(self_comp,
+                       "Received get_new_streams response: Unknown:"
+                       "return code %u", be32toh(rp.status));
+               status = LTTNG_LIVE_ITERATOR_STATUS_ERROR;
+               goto end;
+       }
+
+       viewer_status = receive_streams(session, streams_count, self_msg_iter);
+       if (viewer_status != LTTNG_LIVE_VIEWER_STATUS_OK) {
+               viewer_handle_recv_status(self_comp, NULL,
+                       viewer_status, "new streams");
+               status = viewer_status_to_live_iterator_status(viewer_status);
+               goto end;
+       }
+
+       status = LTTNG_LIVE_ITERATOR_STATUS_OK;
+end:
+       return status;
+}
+
+BT_HIDDEN
+enum lttng_live_viewer_status live_viewer_connection_create(
+               bt_self_component *self_comp,
+               bt_self_component_class *self_comp_class,
+               bt_logging_level log_level,
+               const char *url, bool in_query,
+               struct lttng_live_msg_iter *lttng_live_msg_iter,
+               struct live_viewer_connection **viewer)
+{
+       struct live_viewer_connection *viewer_connection;
+       enum lttng_live_viewer_status status;
+
+       viewer_connection = g_new0(struct live_viewer_connection, 1);
+
+       if (bt_socket_init(log_level) != 0) {
+               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp,
+                       self_comp_class, "Failed to init socket");
+               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
+               goto error;
+       }
+
+       viewer_connection->log_level = log_level;
+
+       viewer_connection->self_comp = self_comp;
+       viewer_connection->self_comp_class = self_comp_class;
+
+       viewer_connection->control_sock = BT_INVALID_SOCKET;
+       viewer_connection->port = -1;
+       viewer_connection->in_query = in_query;
+       viewer_connection->lttng_live_msg_iter = lttng_live_msg_iter;
+       viewer_connection->url = g_string_new(url);
+       if (!viewer_connection->url) {
+               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp,
+                       self_comp_class, "Failed to allocate URL buffer");
+               status = LTTNG_LIVE_VIEWER_STATUS_ERROR;
+               goto error;
+       }
+
+       BT_COMP_OR_COMP_CLASS_LOGD(self_comp, self_comp_class,
+               "Establishing connection to url \"%s\"...", url);
+       status = lttng_live_connect_viewer(viewer_connection);
+       /*
+        * Only print error and append cause in case of error. not in case of
+        * interruption.
+        */
+       if (status == LTTNG_LIVE_VIEWER_STATUS_ERROR) {
+               BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE(self_comp,
+                       self_comp_class, "Failed to establish connection: "
+                       "url=\"%s\"", url);
+               goto error;
+       } else if (status == LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED) {
+               goto error;
+       }
+       BT_COMP_OR_COMP_CLASS_LOGD(self_comp, self_comp_class,
+               "Connection to url \"%s\" is established", url);
+
+       *viewer = viewer_connection;
+       status = LTTNG_LIVE_VIEWER_STATUS_OK;
+       goto end;
+
+error:
+       if (viewer_connection) {
+               live_viewer_connection_destroy(viewer_connection);
+       }
+end:
+       return status;
+}
+
+BT_HIDDEN
+void live_viewer_connection_destroy(
+               struct live_viewer_connection *viewer_connection)
+{
+       bt_self_component *self_comp = viewer_connection->self_comp;
+       bt_self_component_class *self_comp_class = viewer_connection->self_comp_class;
+
+       if (!viewer_connection) {
+               goto end;
+       }
+
+       BT_COMP_OR_COMP_CLASS_LOGD(self_comp, self_comp_class,
+               "Closing connection to relay:"
+               "relay-url=\"%s\"", viewer_connection->url->str);
+
+       lttng_live_disconnect_viewer(viewer_connection);
+
+       if (viewer_connection->url) {
+               g_string_free(viewer_connection->url, true);
+       }
+
+       if (viewer_connection->relay_hostname) {
+               g_string_free(viewer_connection->relay_hostname, true);
+       }
+
+       if (viewer_connection->target_hostname) {
+               g_string_free(viewer_connection->target_hostname, true);
+       }
+
+       if (viewer_connection->session_name) {
+               g_string_free(viewer_connection->session_name, true);
+       }
+
+       if (viewer_connection->proto) {
+               g_string_free(viewer_connection->proto, true);
+       }
+
+       g_free(viewer_connection);
+
+       bt_socket_fini();
+
+end:
+       return;
+}
diff --git a/src/plugins/ctf/lttng-live/viewer-connection.h b/src/plugins/ctf/lttng-live/viewer-connection.h
deleted file mode 100644 (file)
index 45cc8e2..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2016 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- */
-
-#ifndef LTTNG_LIVE_VIEWER_CONNECTION_H
-#define LTTNG_LIVE_VIEWER_CONNECTION_H
-
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-
-#include <glib.h>
-
-#include <babeltrace2/babeltrace.h>
-
-#include "common/macros.h"
-#include "compat/socket.h"
-
-#define LTTNG_DEFAULT_NETWORK_VIEWER_PORT      5344
-
-#define LTTNG_LIVE_MAJOR                       2
-#define LTTNG_LIVE_MINOR                       4
-
-enum lttng_live_viewer_status {
-       LTTNG_LIVE_VIEWER_STATUS_OK             = 0,
-       LTTNG_LIVE_VIEWER_STATUS_ERROR          = -1,
-       LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED    = -2,
-};
-
-enum lttng_live_get_one_metadata_status {
-       /* The end of the metadata stream was reached. */
-       LTTNG_LIVE_GET_ONE_METADATA_STATUS_END          = 1,
-       /* One metadata packet was received and written to file. */
-       LTTNG_LIVE_GET_ONE_METADATA_STATUS_OK           = LTTNG_LIVE_VIEWER_STATUS_OK,
-       /*
-        * A critical error occurred when contacting the relay or while
-        * handling its response.
-        */
-       LTTNG_LIVE_GET_ONE_METADATA_STATUS_ERROR        = LTTNG_LIVE_VIEWER_STATUS_ERROR,
-
-       LTTNG_LIVE_GET_ONE_METADATA_STATUS_INTERRUPTED  = LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED,
-
-       /* The metadata stream was not found on the relay. */
-       LTTNG_LIVE_GET_ONE_METADATA_STATUS_CLOSED       = -3,
-};
-
-struct lttng_live_component;
-
-struct live_viewer_connection {
-       bt_logging_level log_level;
-       bt_self_component *self_comp;
-       bt_self_component_class *self_comp_class;
-
-       GString *url;
-
-       GString *relay_hostname;
-       GString *target_hostname;
-       GString *session_name;
-       GString *proto;
-
-       BT_SOCKET control_sock;
-       int port;
-
-       int32_t major;
-       int32_t minor;
-
-       bool in_query;
-       struct lttng_live_msg_iter *lttng_live_msg_iter;
-};
-
-struct packet_index_time {
-       uint64_t timestamp_begin;
-       uint64_t timestamp_end;
-};
-
-struct packet_index {
-       off_t offset;           /* offset of the packet in the file, in bytes */
-       int64_t data_offset;    /* offset of data within the packet, in bits */
-       uint64_t packet_size;   /* packet size, in bits */
-       uint64_t content_size;  /* content size, in bits */
-       uint64_t events_discarded;
-       uint64_t events_discarded_len;  /* length of the field, in bits */
-       struct packet_index_time ts_cycles;     /* timestamp in cycles */
-       struct packet_index_time ts_real;       /* realtime timestamp */
-       /* CTF_INDEX 1.0 limit */
-       uint64_t stream_instance_id;    /* ID of the channel instance */
-       uint64_t packet_seq_num;        /* packet sequence number */
-};
-
-enum lttng_live_viewer_status live_viewer_connection_create(
-               bt_self_component *self_comp,
-               bt_self_component_class *self_comp_class,
-               bt_logging_level log_level,
-               const char *url, bool in_query,
-               struct lttng_live_msg_iter *lttng_live_msg_iter,
-               struct live_viewer_connection **viewer_connection);
-
-void live_viewer_connection_destroy(
-               struct live_viewer_connection *conn);
-
-enum lttng_live_viewer_status lttng_live_create_viewer_session(
-               struct lttng_live_msg_iter *lttng_live_msg_iter);
-
-bt_component_class_query_method_status live_viewer_connection_list_sessions(
-               struct live_viewer_connection *viewer_connection,
-               const bt_value **user_result);
-
-#endif /* LTTNG_LIVE_VIEWER_CONNECTION_H */
diff --git a/src/plugins/ctf/lttng-live/viewer-connection.hpp b/src/plugins/ctf/lttng-live/viewer-connection.hpp
new file mode 100644 (file)
index 0000000..45cc8e2
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2016 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ */
+
+#ifndef LTTNG_LIVE_VIEWER_CONNECTION_H
+#define LTTNG_LIVE_VIEWER_CONNECTION_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include <babeltrace2/babeltrace.h>
+
+#include "common/macros.h"
+#include "compat/socket.h"
+
+#define LTTNG_DEFAULT_NETWORK_VIEWER_PORT      5344
+
+#define LTTNG_LIVE_MAJOR                       2
+#define LTTNG_LIVE_MINOR                       4
+
+enum lttng_live_viewer_status {
+       LTTNG_LIVE_VIEWER_STATUS_OK             = 0,
+       LTTNG_LIVE_VIEWER_STATUS_ERROR          = -1,
+       LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED    = -2,
+};
+
+enum lttng_live_get_one_metadata_status {
+       /* The end of the metadata stream was reached. */
+       LTTNG_LIVE_GET_ONE_METADATA_STATUS_END          = 1,
+       /* One metadata packet was received and written to file. */
+       LTTNG_LIVE_GET_ONE_METADATA_STATUS_OK           = LTTNG_LIVE_VIEWER_STATUS_OK,
+       /*
+        * A critical error occurred when contacting the relay or while
+        * handling its response.
+        */
+       LTTNG_LIVE_GET_ONE_METADATA_STATUS_ERROR        = LTTNG_LIVE_VIEWER_STATUS_ERROR,
+
+       LTTNG_LIVE_GET_ONE_METADATA_STATUS_INTERRUPTED  = LTTNG_LIVE_VIEWER_STATUS_INTERRUPTED,
+
+       /* The metadata stream was not found on the relay. */
+       LTTNG_LIVE_GET_ONE_METADATA_STATUS_CLOSED       = -3,
+};
+
+struct lttng_live_component;
+
+struct live_viewer_connection {
+       bt_logging_level log_level;
+       bt_self_component *self_comp;
+       bt_self_component_class *self_comp_class;
+
+       GString *url;
+
+       GString *relay_hostname;
+       GString *target_hostname;
+       GString *session_name;
+       GString *proto;
+
+       BT_SOCKET control_sock;
+       int port;
+
+       int32_t major;
+       int32_t minor;
+
+       bool in_query;
+       struct lttng_live_msg_iter *lttng_live_msg_iter;
+};
+
+struct packet_index_time {
+       uint64_t timestamp_begin;
+       uint64_t timestamp_end;
+};
+
+struct packet_index {
+       off_t offset;           /* offset of the packet in the file, in bytes */
+       int64_t data_offset;    /* offset of data within the packet, in bits */
+       uint64_t packet_size;   /* packet size, in bits */
+       uint64_t content_size;  /* content size, in bits */
+       uint64_t events_discarded;
+       uint64_t events_discarded_len;  /* length of the field, in bits */
+       struct packet_index_time ts_cycles;     /* timestamp in cycles */
+       struct packet_index_time ts_real;       /* realtime timestamp */
+       /* CTF_INDEX 1.0 limit */
+       uint64_t stream_instance_id;    /* ID of the channel instance */
+       uint64_t packet_seq_num;        /* packet sequence number */
+};
+
+enum lttng_live_viewer_status live_viewer_connection_create(
+               bt_self_component *self_comp,
+               bt_self_component_class *self_comp_class,
+               bt_logging_level log_level,
+               const char *url, bool in_query,
+               struct lttng_live_msg_iter *lttng_live_msg_iter,
+               struct live_viewer_connection **viewer_connection);
+
+void live_viewer_connection_destroy(
+               struct live_viewer_connection *conn);
+
+enum lttng_live_viewer_status lttng_live_create_viewer_session(
+               struct lttng_live_msg_iter *lttng_live_msg_iter);
+
+bt_component_class_query_method_status live_viewer_connection_list_sessions(
+               struct live_viewer_connection *viewer_connection,
+               const bt_value **user_result);
+
+#endif /* LTTNG_LIVE_VIEWER_CONNECTION_H */
diff --git a/src/plugins/ctf/plugin.c b/src/plugins/ctf/plugin.c
deleted file mode 100644 (file)
index 925b0f9..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * SPDX-License-Identifier: MIT
- *
- * Copyright 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
- *
- * Babeltrace CTF Plug-in Registration Symbols
- */
-
-#include <babeltrace2/babeltrace.h>
-
-#include "fs-src/fs.h"
-#include "fs-sink/fs-sink.h"
-#include "lttng-live/lttng-live.h"
-
-#ifndef BT_BUILT_IN_PLUGINS
-BT_PLUGIN_MODULE();
-#endif
-
-/* Initialize plug-in description. */
-BT_PLUGIN(ctf);
-BT_PLUGIN_DESCRIPTION("CTF input and output");
-BT_PLUGIN_AUTHOR("EfficiOS <https://www.efficios.com/>");
-BT_PLUGIN_LICENSE("MIT");
-
-/* ctf.fs source */
-BT_PLUGIN_SOURCE_COMPONENT_CLASS(fs, ctf_fs_iterator_next);
-BT_PLUGIN_SOURCE_COMPONENT_CLASS_DESCRIPTION(fs,
-       "Read CTF traces from the file system.");
-BT_PLUGIN_SOURCE_COMPONENT_CLASS_HELP(fs,
-       "See the babeltrace2-source.ctf.fs(7) manual page.");
-BT_PLUGIN_SOURCE_COMPONENT_CLASS_INITIALIZE_METHOD(fs, ctf_fs_init);
-BT_PLUGIN_SOURCE_COMPONENT_CLASS_QUERY_METHOD(fs, ctf_fs_query);
-BT_PLUGIN_SOURCE_COMPONENT_CLASS_FINALIZE_METHOD(fs, ctf_fs_finalize);
-BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD(fs,
-       ctf_fs_iterator_init);
-BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_CLASS_FINALIZE_METHOD(fs,
-       ctf_fs_iterator_finalize);
-BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_CLASS_SEEK_BEGINNING_METHODS(fs,
-       ctf_fs_iterator_seek_beginning, NULL);
-
-/* ctf.fs sink */
-BT_PLUGIN_SINK_COMPONENT_CLASS(fs, ctf_fs_sink_consume);
-BT_PLUGIN_SINK_COMPONENT_CLASS_INITIALIZE_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.");
-BT_PLUGIN_SINK_COMPONENT_CLASS_HELP(fs,
-       "See the babeltrace2-sink.ctf.fs(7) manual page.");
-
-/* ctf.lttng-live source */
-BT_PLUGIN_SOURCE_COMPONENT_CLASS_WITH_ID(auto, lttng_live, "lttng-live",
-       lttng_live_msg_iter_next);
-BT_PLUGIN_SOURCE_COMPONENT_CLASS_DESCRIPTION_WITH_ID(auto, lttng_live,
-       "Connect to an LTTng relay daemon and receive CTF streams.");
-BT_PLUGIN_SOURCE_COMPONENT_CLASS_HELP_WITH_ID(auto, lttng_live,
-       "See the babeltrace2-source.ctf.lttng-live(7) manual page.");
-BT_PLUGIN_SOURCE_COMPONENT_CLASS_INITIALIZE_METHOD_WITH_ID(auto, lttng_live,
-       lttng_live_component_init);
-BT_PLUGIN_SOURCE_COMPONENT_CLASS_QUERY_METHOD_WITH_ID(auto, lttng_live,
-       lttng_live_query);
-BT_PLUGIN_SOURCE_COMPONENT_CLASS_FINALIZE_METHOD_WITH_ID(auto, lttng_live,
-       lttng_live_component_finalize);
-BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_WITH_ID(auto,
-       lttng_live, lttng_live_msg_iter_init);
-BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_CLASS_FINALIZE_METHOD_WITH_ID(auto,
-       lttng_live, lttng_live_msg_iter_finalize);
diff --git a/src/plugins/ctf/plugin.cpp b/src/plugins/ctf/plugin.cpp
new file mode 100644 (file)
index 0000000..eeab90e
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com>
+ *
+ * Babeltrace CTF Plug-in Registration Symbols
+ */
+
+#include <babeltrace2/babeltrace.h>
+
+#include "fs-src/fs.hpp"
+#include "fs-sink/fs-sink.hpp"
+#include "lttng-live/lttng-live.hpp"
+
+#ifndef BT_BUILT_IN_PLUGINS
+BT_PLUGIN_MODULE();
+#endif
+
+/* Initialize plug-in description. */
+BT_PLUGIN(ctf);
+BT_PLUGIN_DESCRIPTION("CTF input and output");
+BT_PLUGIN_AUTHOR("EfficiOS <https://www.efficios.com/>");
+BT_PLUGIN_LICENSE("MIT");
+
+/* ctf.fs source */
+BT_PLUGIN_SOURCE_COMPONENT_CLASS(fs, ctf_fs_iterator_next);
+BT_PLUGIN_SOURCE_COMPONENT_CLASS_DESCRIPTION(fs,
+       "Read CTF traces from the file system.");
+BT_PLUGIN_SOURCE_COMPONENT_CLASS_HELP(fs,
+       "See the babeltrace2-source.ctf.fs(7) manual page.");
+BT_PLUGIN_SOURCE_COMPONENT_CLASS_INITIALIZE_METHOD(fs, ctf_fs_init);
+BT_PLUGIN_SOURCE_COMPONENT_CLASS_QUERY_METHOD(fs, ctf_fs_query);
+BT_PLUGIN_SOURCE_COMPONENT_CLASS_FINALIZE_METHOD(fs, ctf_fs_finalize);
+BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD(fs,
+       ctf_fs_iterator_init);
+BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_CLASS_FINALIZE_METHOD(fs,
+       ctf_fs_iterator_finalize);
+BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_CLASS_SEEK_BEGINNING_METHODS(fs,
+       ctf_fs_iterator_seek_beginning, NULL);
+
+/* ctf.fs sink */
+BT_PLUGIN_SINK_COMPONENT_CLASS(fs, ctf_fs_sink_consume);
+BT_PLUGIN_SINK_COMPONENT_CLASS_INITIALIZE_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.");
+BT_PLUGIN_SINK_COMPONENT_CLASS_HELP(fs,
+       "See the babeltrace2-sink.ctf.fs(7) manual page.");
+
+/* ctf.lttng-live source */
+BT_PLUGIN_SOURCE_COMPONENT_CLASS_WITH_ID(auto, lttng_live, "lttng-live",
+       lttng_live_msg_iter_next);
+BT_PLUGIN_SOURCE_COMPONENT_CLASS_DESCRIPTION_WITH_ID(auto, lttng_live,
+       "Connect to an LTTng relay daemon and receive CTF streams.");
+BT_PLUGIN_SOURCE_COMPONENT_CLASS_HELP_WITH_ID(auto, lttng_live,
+       "See the babeltrace2-source.ctf.lttng-live(7) manual page.");
+BT_PLUGIN_SOURCE_COMPONENT_CLASS_INITIALIZE_METHOD_WITH_ID(auto, lttng_live,
+       lttng_live_component_init);
+BT_PLUGIN_SOURCE_COMPONENT_CLASS_QUERY_METHOD_WITH_ID(auto, lttng_live,
+       lttng_live_query);
+BT_PLUGIN_SOURCE_COMPONENT_CLASS_FINALIZE_METHOD_WITH_ID(auto, lttng_live,
+       lttng_live_component_finalize);
+BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_CLASS_INITIALIZE_METHOD_WITH_ID(auto,
+       lttng_live, lttng_live_msg_iter_init);
+BT_PLUGIN_SOURCE_COMPONENT_CLASS_MESSAGE_ITERATOR_CLASS_FINALIZE_METHOD_WITH_ID(auto,
+       lttng_live, lttng_live_msg_iter_finalize);
This page took 0.917225 seconds and 4 git commands to generate.