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))
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);
/*
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;
#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 */ \
#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 */ \
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 */ \
}
*size = pos;
/* add final \0 */
- *buf = calloc(pos + 1, sizeof(char));
+ *buf = (char *) calloc(pos + 1, sizeof(char));
if (!*buf) {
return -ENOMEM;
}
* 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__ */
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
*
* 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);
* 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);
/*
*
* 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
}
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);
}
}
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);
}
#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);
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;
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. */
* `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, ...);
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) \
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 \
noinst_LTLIBRARIES = libctf-bfcr.la
libctf_bfcr_la_SOURCES = \
- bfcr.c \
- bfcr.h
+ bfcr.cpp \
+ bfcr.hpp
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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 */
--- /dev/null
+/*
+ * 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 */
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
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
+++ /dev/null
-/*
- * 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 */
--- /dev/null
+/*
+ * 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 */
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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 */
--- /dev/null
+/*
+ * 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 */
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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 */
--- /dev/null
+/*
+ * 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 */
+++ /dev/null
-/*
- * 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);
- }
- }
-}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+}
+++ /dev/null
-/*
- * 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(©_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(©_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 */
--- /dev/null
+/*
+ * 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(©_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(©_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(©_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(©_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(©_fc->base, &fc->base);
+ ctf_field_path_copy_content(©_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 */
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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 */
--- /dev/null
+/*
+ * 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 */
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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 */
--- /dev/null
+/*
+ * 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 */
+++ /dev/null
-%{
-/*
- * 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;
-%%
--- /dev/null
+%{
+/*
+ * 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;
+%%
+++ /dev/null
-/*
- * 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");
--- /dev/null
+/*
+ * 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");
+++ /dev/null
-/*
- * 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 */
--- /dev/null
+/*
+ * 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 */
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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 */
--- /dev/null
+/*
+ * 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 */
+++ /dev/null
-/*
- * 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
--- /dev/null
+/*
+ * 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
+++ /dev/null
-%{
-/*
- * 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);
- }
- ;
--- /dev/null
+%{
+/*
+ * 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);
+ }
+ ;
+++ /dev/null
-/*
- * 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 */
--- /dev/null
+/*
+ * 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 */
+++ /dev/null
-/*
- * 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 */
--- /dev/null
+/*
+ * 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 */
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
noinst_LTLIBRARIES = libctf-msg-iter.la
libctf_msg_iter_la_SOURCES = \
- msg-iter.c \
- msg-iter.h
+ msg-iter.cpp \
+ msg-iter.hpp
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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 */
--- /dev/null
+/*
+ * 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 */
+++ /dev/null
-/*
- * 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 */
--- /dev/null
+/*
+ * 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 */
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
+++ /dev/null
-/*
- * 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 */
--- /dev/null
+/*
+ * 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 */
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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 */
--- /dev/null
+/*
+ * 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 */
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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 */
--- /dev/null
+/*
+ * 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 */
+++ /dev/null
-/*
- * 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);
-}
--- /dev/null
+/*
+ * 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);
+}
+++ /dev/null
-/*
- * 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 */
--- /dev/null
+/*
+ * 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 */
+++ /dev/null
-/*
- * 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]);
- }
-}
--- /dev/null
+/*
+ * 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]);
+ }
+}
+++ /dev/null
-/*
- * 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 */
--- /dev/null
+/*
+ * 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 */
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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 */
--- /dev/null
+/*
+ * 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 */
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
+++ /dev/null
-/*
- * 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);
-}
--- /dev/null
+/*
+ * 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);
+}
+++ /dev/null
-/*
- * 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 */
--- /dev/null
+/*
+ * 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 */
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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 */
--- /dev/null
+/*
+ * 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 */
+++ /dev/null
-/*
- * 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, ¤t_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(
- ¤t_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(
- ¤t_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(
- ¤t_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;
-}
--- /dev/null
+/*
+ * 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, ¤t_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(
+ ¤t_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(
+ ¤t_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(
+ ¤t_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;
+}
+++ /dev/null
-/*
- * 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 */
--- /dev/null
+/*
+ * 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 */
+++ /dev/null
-/*
- * 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 */
--- /dev/null
+/*
+ * 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 */
+++ /dev/null
-/*
- * 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);
- }
-}
--- /dev/null
+/*
+ * 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);
+ }
+}
+++ /dev/null
-/*
- * 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 */
--- /dev/null
+/*
+ * 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 */
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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 */
--- /dev/null
+/*
+ * 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 */
# 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 =
+++ /dev/null
-/*
- * 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);
-}
--- /dev/null
+/*
+ * 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);
+}
+++ /dev/null
-/*
- * 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 */
--- /dev/null
+/*
+ * 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 */
+++ /dev/null
-/*
- * 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, <tng_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, <tng_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;
-}
--- /dev/null
+/*
+ * 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, <tng_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, <tng_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;
+}
+++ /dev/null
-/*
- * 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 */
--- /dev/null
+/*
+ * 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 */
+++ /dev/null
-/*
- * 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 */
--- /dev/null
+/*
+ * 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 */
+++ /dev/null
-/*
- * 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);
-}
--- /dev/null
+/*
+ * 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);
+}
+++ /dev/null
-/*
- * 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 */
--- /dev/null
+/*
+ * 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 */
+++ /dev/null
-/*
- * 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(<tng_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;
-}
--- /dev/null
+/*
+ * 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(<tng_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;
+}
+++ /dev/null
-/*
- * 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 */
--- /dev/null
+/*
+ * 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 */
+++ /dev/null
-/*
- * 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);
--- /dev/null
+/*
+ * 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);