From: Jérémie Galarneau Date: Tue, 30 Aug 2016 03:30:35 +0000 (-0400) Subject: Integrate ctf proto into the plugin build system X-Git-Tag: v2.0.0-pre1~784 X-Git-Url: http://git.efficios.com/?p=babeltrace.git;a=commitdiff_plain;h=06a626b83f4426ea5b8d3199d1edf5a07594026e Integrate ctf proto into the plugin build system Signed-off-by: Jérémie Galarneau --- diff --git a/Makefile.am b/Makefile.am index fab48f19..0544bd16 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,7 +2,7 @@ AM_CFLAGS = $(PACKAGE_CFLAGS) -I$(top_srcdir)/include ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = include types compat lib formats plugins converter bindings tests doc extras ctf-reader-proto +SUBDIRS = include types compat lib formats plugins converter bindings tests doc extras dist_doc_DATA = ChangeLog LICENSE mit-license.txt gpl-2.0.txt \ std-ext-lib.txt README diff --git a/configure.ac b/configure.ac index c3809bee..01043fbd 100644 --- a/configure.ac +++ b/configure.ac @@ -343,10 +343,6 @@ AC_CONFIG_FILES([ Makefile types/Makefile compat/Makefile - ctf-reader-proto/Makefile - ctf-reader-proto/metadata-parsing/Makefile - ctf-reader-proto/ctf-notif-iter/Makefile - ctf-reader-proto/ctf-btr/Makefile formats/Makefile formats/ctf/Makefile formats/ctf/types/Makefile @@ -380,6 +376,10 @@ AC_CONFIG_FILES([ extras/valgrind/Makefile plugins/Makefile plugins/ctf/Makefile + plugins/ctf/common/Makefile + plugins/ctf/common/btr/Makefile + plugins/ctf/common/metadata/Makefile + plugins/ctf/common/notif-iter/Makefile plugins/ctf/fs/Makefile plugins/ctf/lttng-live/Makefile plugins/text/Makefile diff --git a/ctf-reader-proto/ctf-btr.gdb b/ctf-reader-proto/ctf-btr.gdb deleted file mode 100644 index 9b42b1cd..00000000 --- a/ctf-reader-proto/ctf-btr.gdb +++ /dev/null @@ -1,24 +0,0 @@ -define ctf-btr-show-stack - if (stack_empty($arg0)) - printf "stack is empty!\n" - else - set $stack_size = stack_size($arg0) - set $stack_at = (int) ($stack_size - 1) - printf "%3s %10s %4s %3s\n", "pos", "base addr", "blen", "idx" - - while ($stack_at >= 0) - set $stack_entry = (struct stack_entry *) g_ptr_array_index($arg0->entries, $stack_at) - - if ($stack_at == $stack_size - 1) - printf "%3d %10p %3d %3d <-- top\n", $stack_at, \ - $stack_entry->base_type, $stack_entry->base_len, \ - $stack_entry->index - else - printf "%3d %10p %3d %3d\n", $stack_at, \ - $stack_entry->base_type, $stack_entry->base_len, \ - $stack_entry->index - end - set $stack_at = $stack_at - 1 - end - end -end diff --git a/ctf-reader-proto/ctf-btr/Makefile.am b/ctf-reader-proto/ctf-btr/Makefile.am deleted file mode 100644 index 93dd221e..00000000 --- a/ctf-reader-proto/ctf-btr/Makefile.am +++ /dev/null @@ -1,6 +0,0 @@ -AM_CFLAGS = $(PACKAGE_CFLAGS) -AM_CPPFLAGS = -I$(top_srcdir)/include - -noinst_LTLIBRARIES = libctf-btr.la - -libctf_btr_la_SOURCES = ctf-btr.c diff --git a/ctf-reader-proto/ctf-btr/ctf-btr.c b/ctf-reader-proto/ctf-btr/ctf-btr.c deleted file mode 100644 index cb250f9f..00000000 --- a/ctf-reader-proto/ctf-btr/ctf-btr.c +++ /dev/null @@ -1,1388 +0,0 @@ -/* - * Babeltrace - CTF binary type reader (BTR) - * - * Copyright (c) 2015-2016 EfficiOS Inc. and Linux Foundation - * Copyright (c) 2015-2016 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ctf-btr.h" - -#define PRINT_ERR_STREAM btr->err_stream -#define PRINT_PREFIX "ctf-btr" -#include "print.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 type of base field, one of: - * - * * Structure - * * Array - * * Sequence - * * Variant - * - * Owned by this. - */ - struct bt_ctf_field_type *base_type; - - /* Length of base field (always 1 for variant types) */ - int64_t base_len; - - /* Lndex of next field to read */ - int64_t index; -}; - -/* Visit stack */ -struct stack { - /* Entries (struct stack_entry *) (top is last element) */ - GPtrArray *entries; -}; - -/* Reading states */ -enum btr_state { - BTR_STATE_NEXT_FIELD, - BTR_STATE_ALIGN_BASIC, - BTR_STATE_ALIGN_COMPOUND, - BTR_STATE_READ_BASIC_BEGIN, - BTR_STATE_READ_BASIC_CONTINUE, - BTR_STATE_DONE, -}; - -/* Binary type reader */ -struct bt_ctf_btr { - /* Bisit stack */ - struct stack *stack; - - /* Error stream */ - FILE *err_stream; - - /* Current basic field type */ - struct bt_ctf_field_type *cur_basic_field_type; - - /* Current state */ - enum btr_state state; - - /* - * Last basic field type's byte order. - * - * This is used to detect errors since two contiguous basic - * types for which the common boundary is not the boundary of - * a byte cannot have different byte orders. - * - * This is set to BT_CTF_BYTE_ORDER_UNKNOWN on reset and when - * the last basic field type was a string type. - */ - enum bt_ctf_byte_order last_bo; - - /* Current byte order (copied to last_bo after a successful read) */ - enum bt_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_ctf_btr_cbs cbs; - - /* Private data */ - void *data; - } user; -}; - -static -void stack_entry_free_func(gpointer data) -{ - struct stack_entry *entry = data; - - BT_PUT(entry->base_type); - g_free(entry); -} - -static -struct stack *stack_new(void) -{ - struct stack *stack = NULL; - - stack = g_new0(struct stack, 1); - if (!stack) { - goto error; - } - - stack->entries = g_ptr_array_new_with_free_func(stack_entry_free_func); - if (!stack->entries) { - goto error; - } - - return stack; - -error: - g_free(stack); - - return NULL; -} - -static -void stack_destroy(struct stack *stack) -{ - if (!stack) { - return; - } - - g_ptr_array_free(stack->entries, TRUE); - g_free(stack); -} - -static inline -int64_t get_compound_field_type_length(struct bt_ctf_btr *btr, - struct bt_ctf_field_type *field_type) -{ - int64_t length; - - switch (bt_ctf_field_type_get_type_id(field_type)) { - case BT_CTF_TYPE_ID_STRUCT: - length = (int64_t) bt_ctf_field_type_structure_get_field_count( - field_type); - break; - case BT_CTF_TYPE_ID_VARIANT: - /* Variant field types always "contain" a single type */ - length = 1; - break; - case BT_CTF_TYPE_ID_ARRAY: - length = bt_ctf_field_type_array_get_length(field_type); - break; - case BT_CTF_TYPE_ID_SEQUENCE: - length = btr->user.cbs.query.get_sequence_length(field_type, - btr->user.data); - break; - default: - PERR("Cannot get length of field type with type ID %d\n", - bt_ctf_field_type_get_type_id(field_type)); - length = BT_CTF_BTR_STATUS_ERROR; - } - - return length; -} - -static -int stack_push(struct stack *stack, struct bt_ctf_field_type *base_type, - size_t base_len) -{ - int ret = 0; - struct stack_entry *entry; - - assert(stack); - assert(base_type); - - entry = g_new0(struct stack_entry, 1); - if (!entry) { - ret = BT_CTF_BTR_STATUS_ERROR; - goto end; - } - - entry->base_type = base_type; - bt_get(entry->base_type); - entry->base_len = base_len; - g_ptr_array_add(stack->entries, entry); - -end: - return ret; -} - -static -int stack_push_with_len(struct bt_ctf_btr *btr, - struct bt_ctf_field_type *base_type) -{ - int ret = 0; - int64_t base_len = get_compound_field_type_length(btr, base_type); - - if (base_len < 0) { - PERR("Failed to get compound field type's length\n"); - ret = BT_CTF_BTR_STATUS_ERROR; - goto end; - } - - ret = stack_push(btr->stack, base_type, (size_t) base_len); - -end: - return ret; -} - -static inline -unsigned int stack_size(struct stack *stack) -{ - assert(stack); - - return stack->entries->len; -} - -static -void stack_pop(struct stack *stack) -{ - assert(stack); - assert(stack_size(stack)); - g_ptr_array_remove_index(stack->entries, stack->entries->len - 1); -} - -static inline -bool stack_empty(struct stack *stack) -{ - return stack_size(stack) == 0; -} - -static -void stack_clear(struct stack *stack) -{ - assert(stack); - - if (!stack_empty(stack)) { - g_ptr_array_remove_range(stack->entries, 0, stack_size(stack)); - } - - assert(stack_empty(stack)); -} - -static inline -struct stack_entry *stack_top(struct stack *stack) -{ - assert(stack); - assert(stack_size(stack)); - - return g_ptr_array_index(stack->entries, stack->entries->len - 1); -} - -static inline -size_t available_bits(struct bt_ctf_btr *btr) -{ - return btr->buf.sz - btr->buf.at; -} - -static inline -void consume_bits(struct bt_ctf_btr *btr, size_t incr) -{ - btr->buf.at += incr; -} - -static inline -bool has_enough_bits(struct bt_ctf_btr *btr, size_t sz) -{ - return available_bits(btr) >= sz; -} - -static inline -bool at_least_one_bit_left(struct bt_ctf_btr *btr) -{ - return has_enough_bits(btr, 1); -} - -static inline -size_t packet_at(struct bt_ctf_btr *btr) -{ - return btr->buf.packet_offset + btr->buf.at; -} - -static inline -size_t buf_at_from_addr(struct bt_ctf_btr *btr) -{ - /* - * Considering this: - * - * ====== offset ===== (17) - * - * xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx - * ^ - * addr (0) ==== at ==== (12) - * - * We want this: - * - * =============================== (29) - */ - return btr->buf.offset + btr->buf.at; -} - -static inline -int get_basic_field_type_size(struct bt_ctf_btr *btr, - struct bt_ctf_field_type *field_type) -{ - int size; - - switch (bt_ctf_field_type_get_type_id(field_type)) { - case BT_CTF_TYPE_ID_INTEGER: - size = bt_ctf_field_type_integer_get_size(field_type); - break; - case BT_CTF_TYPE_ID_FLOAT: - { - int exp_dig, mant_dig; - - exp_dig = - bt_ctf_field_type_floating_point_get_exponent_digits( - field_type); - mant_dig = - bt_ctf_field_type_floating_point_get_mantissa_digits( - field_type); - if (exp_dig < 0 || mant_dig < 0) { - PERR("Failed to get floating point number type's sizes\n"); - size = BT_CTF_BTR_STATUS_ERROR; - } - - size = exp_dig + mant_dig; - break; - } - case BT_CTF_TYPE_ID_ENUM: - { - struct bt_ctf_field_type *int_type; - - int_type = bt_ctf_field_type_enumeration_get_container_type( - field_type); - if (!int_type) { - PERR("Failed to get enumeration type's container type\n"); - size = BT_CTF_BTR_STATUS_ERROR; - goto end; - } - - size = get_basic_field_type_size(btr, int_type); - BT_PUT(int_type); - break; - } - default: - size = BT_CTF_BTR_STATUS_ERROR; - break; - } - -end: - return size; -} - -static -void stitch_reset(struct bt_ctf_btr *btr) -{ - btr->stitch.offset = 0; - btr->stitch.at = 0; -} - -static inline -size_t stitch_at_from_addr(struct bt_ctf_btr *btr) -{ - return btr->stitch.offset + btr->stitch.at; -} - -static -void stitch_append_from_buf(struct bt_ctf_btr *btr, 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(btr)); - buf_byte_at = BITS_TO_BYTES_FLOOR(buf_at_from_addr(btr)); - nb_bytes = BITS_TO_BYTES_CEIL(sz); - assert(nb_bytes > 0); - memcpy(&btr->stitch.buf[stitch_byte_at], &btr->buf.addr[buf_byte_at], - nb_bytes); - btr->stitch.at += sz; - consume_bits(btr, sz); -} - -static -void stitch_append_from_remaining_buf(struct bt_ctf_btr *btr) -{ - stitch_append_from_buf(btr, available_bits(btr)); -} - -static -void stitch_set_from_remaining_buf(struct bt_ctf_btr *btr) -{ - stitch_reset(btr); - btr->stitch.offset = IN_BYTE_OFFSET(buf_at_from_addr(btr)); - stitch_append_from_remaining_buf(btr); -} - -static inline -enum bt_ctf_btr_status read_unsigned_bitfield(const uint8_t *buf, size_t at, - int64_t field_size, enum bt_ctf_byte_order bo, uint64_t *v) -{ - enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; - - switch (bo) { - case BT_CTF_BYTE_ORDER_BIG_ENDIAN: - case BT_CTF_BYTE_ORDER_NETWORK: - bt_bitfield_read_be(buf, uint8_t, at, field_size, v); - break; - case BT_CTF_BYTE_ORDER_LITTLE_ENDIAN: - bt_bitfield_read_le(buf, uint8_t, at, field_size, v); - break; - default: - status = BT_CTF_BTR_STATUS_ERROR; - } - - return status; -} - -static inline -enum bt_ctf_btr_status read_signed_bitfield(const uint8_t *buf, size_t at, - int64_t field_size, enum bt_ctf_byte_order bo, int64_t *v) -{ - enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; - - switch (bo) { - case BT_CTF_BYTE_ORDER_BIG_ENDIAN: - case BT_CTF_BYTE_ORDER_NETWORK: - bt_bitfield_read_be(buf, uint8_t, at, field_size, v); - break; - case BT_CTF_BYTE_ORDER_LITTLE_ENDIAN: - bt_bitfield_read_le(buf, uint8_t, at, field_size, v); - break; - default: - status = BT_CTF_BTR_STATUS_ERROR; - } - - return status; -} - -typedef enum bt_ctf_btr_status (* read_basic_and_call_cb_t)(struct bt_ctf_btr *, - const uint8_t *, size_t); - -static inline -enum bt_ctf_btr_status validate_contiguous_bo(struct bt_ctf_btr *btr, - enum bt_ctf_byte_order next_bo) -{ - enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; - - /* Always valid when at a byte boundary */ - if (packet_at(btr) % 8 == 0) { - goto end; - } - - /* Always valid if last byte order is unknown */ - if (btr->last_bo == BT_CTF_BYTE_ORDER_UNKNOWN) { - goto end; - } - - /* Always valid if next byte order is unknown */ - if (next_bo == BT_CTF_BYTE_ORDER_UNKNOWN) { - goto end; - } - - /* Make sure last byte order is compatible with the next byte order */ - switch (btr->last_bo) { - case BT_CTF_BYTE_ORDER_BIG_ENDIAN: - case BT_CTF_BYTE_ORDER_NETWORK: - if (next_bo != BT_CTF_BYTE_ORDER_BIG_ENDIAN && - next_bo != BT_CTF_BYTE_ORDER_NETWORK) { - status = BT_CTF_BTR_STATUS_ERROR; - } - break; - case BT_CTF_BYTE_ORDER_LITTLE_ENDIAN: - if (next_bo != BT_CTF_BYTE_ORDER_LITTLE_ENDIAN) { - status = BT_CTF_BTR_STATUS_ERROR; - } - break; - default: - status = BT_CTF_BTR_STATUS_ERROR; - } - -end: - return status; -} - -static -enum bt_ctf_btr_status read_basic_float_and_call_cb(struct bt_ctf_btr *btr, - const uint8_t *buf, size_t at) -{ - int ret; - double dblval; - int64_t field_size; - enum bt_ctf_byte_order bo; - enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; - - field_size = get_basic_field_type_size(btr, btr->cur_basic_field_type); - bo = bt_ctf_field_type_get_byte_order(btr->cur_basic_field_type); - btr->cur_bo = bo; - - switch (field_size) { - case 32: - { - uint64_t v; - union { - uint32_t u; - float f; - } f32; - - ret = bt_ctf_field_type_floating_point_get_mantissa_digits( - btr->cur_basic_field_type); - assert(ret == 24); - ret = bt_ctf_field_type_floating_point_get_exponent_digits( - btr->cur_basic_field_type); - assert(ret == 8); - status = read_unsigned_bitfield(buf, at, field_size, bo, &v); - if (status != BT_CTF_BTR_STATUS_OK) { - PERR("Failed to reader unsigned bitfield\n"); - goto end; - } - - f32.u = (uint32_t) v; - dblval = (double) f32.f; - break; - } - case 64: - { - union { - uint64_t u; - double d; - } f64; - - ret = bt_ctf_field_type_floating_point_get_mantissa_digits( - btr->cur_basic_field_type); - assert(ret == 53); - ret = bt_ctf_field_type_floating_point_get_exponent_digits( - btr->cur_basic_field_type); - assert(ret == 11); - status = read_unsigned_bitfield(buf, at, field_size, bo, - &f64.u); - if (status != BT_CTF_BTR_STATUS_OK) { - PERR("Failed to reader unsigned bitfield\n"); - goto end; - } - - dblval = f64.d; - break; - } - default: - /* Only 32-bit and 64-bit fields are supported currently */ - status = BT_CTF_BTR_STATUS_ERROR; - goto end; - } - - if (btr->user.cbs.types.floating_point) { - status = btr->user.cbs.types.floating_point(dblval, - btr->cur_basic_field_type, btr->user.data); - } - -end: - return status; -} - -static inline -enum bt_ctf_btr_status read_basic_int_and_call(struct bt_ctf_btr *btr, - const uint8_t *buf, size_t at, struct bt_ctf_field_type *int_type, - struct bt_ctf_field_type *orig_type) -{ - int signd; - int64_t field_size; - enum bt_ctf_byte_order bo; - enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; - - signd = bt_ctf_field_type_integer_get_signed(int_type); - field_size = get_basic_field_type_size(btr, int_type); - if (field_size < 1) { - PERR("Failed to get basic field type's size\n"); - status = BT_CTF_BTR_STATUS_ERROR; - goto end; - } - - bo = bt_ctf_field_type_get_byte_order(int_type); - - /* - * Update current byte order now because we could be reading - * the integer value of an enumeration type, and thus we know - * here the actual supporting integer type's byte order. - */ - btr->cur_bo = bo; - - if (signd) { - int64_t v; - - status = read_signed_bitfield(buf, at, field_size, bo, &v); - if (status != BT_CTF_BTR_STATUS_OK) { - PERR("Failed to reader signed bitfield\n"); - goto end; - } - - if (btr->user.cbs.types.signed_int) { - status = btr->user.cbs.types.signed_int(v, - btr->cur_basic_field_type, btr->user.data); - } - } else { - uint64_t v; - - status = read_unsigned_bitfield(buf, at, field_size, bo, &v); - if (status != BT_CTF_BTR_STATUS_OK) { - PERR("Failed to reader unsigned bitfield\n"); - goto end; - } - - if (btr->user.cbs.types.unsigned_int) { - status = btr->user.cbs.types.unsigned_int(v, - btr->cur_basic_field_type, btr->user.data); - } - } - -end: - return status; -} - -static -enum bt_ctf_btr_status read_basic_int_and_call_cb(struct bt_ctf_btr *btr, - const uint8_t *buf, size_t at) -{ - return read_basic_int_and_call(btr, buf, at, btr->cur_basic_field_type, - btr->cur_basic_field_type); -} - -static -enum bt_ctf_btr_status read_basic_enum_and_call_cb(struct bt_ctf_btr *btr, - const uint8_t *buf, size_t at) -{ - struct bt_ctf_field_type *int_field_type; - enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; - - int_field_type = bt_ctf_field_type_enumeration_get_container_type( - btr->cur_basic_field_type); - if (!int_field_type) { - PERR("Failed to get enumeration type's container type\n"); - status = BT_CTF_BTR_STATUS_ERROR; - goto end; - } - - status = read_basic_int_and_call(btr, buf, at, - int_field_type, btr->cur_basic_field_type); - -end: - BT_PUT(int_field_type); - - return status; -} - -static inline -enum bt_ctf_btr_status read_basic_type_and_call_continue(struct bt_ctf_btr *btr, - read_basic_and_call_cb_t read_basic_and_call_cb) -{ - size_t available; - int64_t field_size; - int64_t needed_bits; - enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; - - if (!at_least_one_bit_left(btr)) { - status = BT_CTF_BTR_STATUS_EOF; - goto end; - } - - field_size = get_basic_field_type_size(btr, btr->cur_basic_field_type); - if (field_size < 1) { - PERR("Failed to get basic field type's size\n"); - status = BT_CTF_BTR_STATUS_ERROR; - goto end; - } - - available = available_bits(btr); - needed_bits = field_size - btr->stitch.at; - if (needed_bits <= available) { - /* We have all the bits; append to stitch, then decode */ - stitch_append_from_buf(btr, needed_bits); - status = read_basic_and_call_cb(btr, btr->stitch.buf, - btr->stitch.offset); - if (status != BT_CTF_BTR_STATUS_OK) { - PERR("Failed to read basic field\n"); - goto end; - } - - if (stack_empty(btr->stack)) { - /* Root is a basic type */ - btr->state = BTR_STATE_DONE; - } else { - /* Go to next field */ - stack_top(btr->stack)->index++; - btr->state = BTR_STATE_NEXT_FIELD; - btr->last_bo = btr->cur_bo; - } - goto end; - } - - /* We are here; it means we don't have enough data to decode this */ - stitch_append_from_remaining_buf(btr); - status = BT_CTF_BTR_STATUS_EOF; - -end: - return status; -} - -static inline -enum bt_ctf_btr_status read_basic_type_and_call_begin(struct bt_ctf_btr *btr, - read_basic_and_call_cb_t read_basic_and_call_cb) -{ - size_t available; - int64_t field_size; - enum bt_ctf_byte_order bo; - enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; - - if (!at_least_one_bit_left(btr)) { - status = BT_CTF_BTR_STATUS_EOF; - goto end; - } - - field_size = get_basic_field_type_size(btr, btr->cur_basic_field_type); - - if (field_size < 1) { - PERR("Failed to get basic field type's size\n"); - status = BT_CTF_BTR_STATUS_ERROR; - goto end; - } - - bo = bt_ctf_field_type_get_byte_order(btr->cur_basic_field_type); - status = validate_contiguous_bo(btr, bo); - - if (status != BT_CTF_BTR_STATUS_OK) { - PERR("Invalid contiguous byte orders\n"); - goto end; - } - - available = available_bits(btr); - - if (field_size <= available) { - /* We have all the bits; decode and set now */ - status = read_basic_and_call_cb(btr, btr->buf.addr, - buf_at_from_addr(btr)); - - if (status != BT_CTF_BTR_STATUS_OK) { - PERR("Failed to read basic type\n"); - goto end; - } - - consume_bits(btr, field_size); - - if (stack_empty(btr->stack)) { - /* Root is a basic type */ - btr->state = BTR_STATE_DONE; - } else { - /* Go to next field */ - stack_top(btr->stack)->index++; - btr->state = BTR_STATE_NEXT_FIELD; - btr->last_bo = btr->cur_bo; - } - - goto end; - } - - /* We are here; it means we don't have enough data to decode this */ - stitch_set_from_remaining_buf(btr); - btr->state = BTR_STATE_READ_BASIC_CONTINUE; - status = BT_CTF_BTR_STATUS_EOF; - -end: - return status; -} - -static inline -enum bt_ctf_btr_status read_basic_int_type_and_call_begin( - struct bt_ctf_btr *btr) -{ - return read_basic_type_and_call_begin(btr, read_basic_int_and_call_cb); -} - -static inline -enum bt_ctf_btr_status read_basic_int_type_and_call_continue( - struct bt_ctf_btr *btr) -{ - return read_basic_type_and_call_continue(btr, - read_basic_int_and_call_cb); -} - -static inline -enum bt_ctf_btr_status read_basic_float_type_and_call_begin( - struct bt_ctf_btr *btr) -{ - return read_basic_type_and_call_begin(btr, - read_basic_float_and_call_cb); -} - -static inline -enum bt_ctf_btr_status read_basic_float_type_and_call_continue( - struct bt_ctf_btr *btr) -{ - return read_basic_type_and_call_continue(btr, - read_basic_float_and_call_cb); -} - -static inline -enum bt_ctf_btr_status read_basic_enum_type_and_call_begin( - struct bt_ctf_btr *btr) -{ - return read_basic_type_and_call_begin(btr, - read_basic_enum_and_call_cb); -} - -static inline -enum bt_ctf_btr_status read_basic_enum_type_and_call_continue( - struct bt_ctf_btr *btr) -{ - return read_basic_type_and_call_continue(btr, - read_basic_enum_and_call_cb); -} - -static inline -enum bt_ctf_btr_status read_basic_string_type_and_call( - struct bt_ctf_btr *btr, bool begin) -{ - size_t buf_at_bytes; - const uint8_t *result; - size_t available_bytes; - const uint8_t *first_chr; - enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; - - if (!at_least_one_bit_left(btr)) { - status = BT_CTF_BTR_STATUS_EOF; - goto end; - } - - assert(buf_at_from_addr(btr) % 8 == 0); - available_bytes = BITS_TO_BYTES_FLOOR(available_bits(btr)); - buf_at_bytes = BITS_TO_BYTES_FLOOR(buf_at_from_addr(btr)); - first_chr = &btr->buf.addr[buf_at_bytes]; - result = memchr(first_chr, '\0', available_bytes); - - if (begin && btr->user.cbs.types.string_begin) { - status = btr->user.cbs.types.string_begin( - btr->cur_basic_field_type, btr->user.data); - - if (status != BT_CTF_BTR_STATUS_OK) { - PERR("string_begin() user callback function failed\n"); - goto end; - } - } - - if (!result) { - /* No null character yet */ - if (btr->user.cbs.types.string) { - status = btr->user.cbs.types.string( - (const char *) first_chr, - available_bytes, btr->cur_basic_field_type, - btr->user.data); - if (status != BT_CTF_BTR_STATUS_OK) { - PERR("string() user callback function failed\n"); - goto end; - } - } - - consume_bits(btr, BYTES_TO_BITS(available_bytes)); - btr->state = BTR_STATE_READ_BASIC_CONTINUE; - status = BT_CTF_BTR_STATUS_EOF; - } else { - /* Found the null character */ - size_t result_len = (size_t) (result - first_chr); - - if (btr->user.cbs.types.string && result_len) { - status = btr->user.cbs.types.string( - (const char *) first_chr, - result_len, btr->cur_basic_field_type, - btr->user.data); - if (status != BT_CTF_BTR_STATUS_OK) { - PERR("string() user callback function failed\n"); - goto end; - } - } - - if (btr->user.cbs.types.string_end) { - status = btr->user.cbs.types.string_end( - btr->cur_basic_field_type, btr->user.data); - if (status != BT_CTF_BTR_STATUS_OK) { - PERR("string_end() user callback function failed\n"); - goto end; - } - } - - consume_bits(btr, BYTES_TO_BITS(result_len + 1)); - - if (stack_empty(btr->stack)) { - /* Root is a basic type */ - btr->state = BTR_STATE_DONE; - } else { - /* Go to next field */ - stack_top(btr->stack)->index++; - btr->state = BTR_STATE_NEXT_FIELD; - btr->last_bo = btr->cur_bo; - } - } - -end: - return status; -} - -static inline -enum bt_ctf_btr_status read_basic_begin_state(struct bt_ctf_btr *btr) -{ - enum bt_ctf_btr_status status; - - assert(btr->cur_basic_field_type); - - switch (bt_ctf_field_type_get_type_id(btr->cur_basic_field_type)) { - case BT_CTF_TYPE_ID_INTEGER: - status = read_basic_int_type_and_call_begin(btr); - break; - case BT_CTF_TYPE_ID_FLOAT: - status = read_basic_float_type_and_call_begin(btr); - break; - case BT_CTF_TYPE_ID_ENUM: - status = read_basic_enum_type_and_call_begin(btr); - break; - case BT_CTF_TYPE_ID_STRING: - status = read_basic_string_type_and_call(btr, true); - break; - default: - assert(false); - } - - return status; -} - -static inline -enum bt_ctf_btr_status read_basic_continue_state(struct bt_ctf_btr *btr) -{ - enum bt_ctf_btr_status status; - - assert(btr->cur_basic_field_type); - - switch (bt_ctf_field_type_get_type_id(btr->cur_basic_field_type)) { - case BT_CTF_TYPE_ID_INTEGER: - status = read_basic_int_type_and_call_continue(btr); - break; - case BT_CTF_TYPE_ID_FLOAT: - status = read_basic_float_type_and_call_continue(btr); - break; - case BT_CTF_TYPE_ID_ENUM: - status = read_basic_enum_type_and_call_continue(btr); - break; - case BT_CTF_TYPE_ID_STRING: - status = read_basic_string_type_and_call(btr, false); - break; - default: - assert(false); - } - - return status; -} - -static inline -size_t bits_to_skip_to_align_to(struct bt_ctf_btr *btr, size_t align) -{ - size_t aligned_packet_at; - - aligned_packet_at = ALIGN(packet_at(btr), align); - - return aligned_packet_at - packet_at(btr); -} - -static inline -enum bt_ctf_btr_status align_type_state(struct bt_ctf_btr *btr, - struct bt_ctf_field_type *field_type, enum btr_state next_state) -{ - int field_alignment; - size_t skip_bits; - enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; - - /* Get field's alignment */ - field_alignment = bt_ctf_field_type_get_alignment(field_type); - if (field_alignment < 0) { - PERR("Failed to get type alignment\n"); - status = BT_CTF_BTR_STATUS_ERROR; - goto end; - } - - /* - * 0 means "undefined" for variants; what we really want is 1 - * (always aligned) - */ - if (field_alignment == 0) { - field_alignment = 1; - } - - /* Compute how many bits we need to skip */ - skip_bits = bits_to_skip_to_align_to(btr, field_alignment); - - /* Nothing to skip? aligned */ - if (skip_bits == 0) { - btr->state = next_state; - goto end; - } - - /* Make sure there's at least one bit left */ - if (!at_least_one_bit_left(btr)) { - status = BT_CTF_BTR_STATUS_EOF; - goto end; - } - - /* Consume as many bits as possible in what's left */ - consume_bits(btr, MIN(available_bits(btr), skip_bits)); - - /* Are we done now? */ - skip_bits = bits_to_skip_to_align_to(btr, field_alignment); - - if (skip_bits == 0) { - /* Yes: go to next state */ - btr->state = next_state; - goto end; - } else { - /* No: need more data */ - status = BT_CTF_BTR_STATUS_EOF; - } - -end: - return status; -} - -static inline -bool is_compound_type(struct bt_ctf_field_type *field_type) -{ - enum bt_ctf_type_id id = bt_ctf_field_type_get_type_id(field_type); - - return id == BT_CTF_TYPE_ID_STRUCT || id == BT_CTF_TYPE_ID_ARRAY || - id == BT_CTF_TYPE_ID_SEQUENCE || id == BT_CTF_TYPE_ID_VARIANT; -} - -static inline -enum bt_ctf_btr_status next_field_state(struct bt_ctf_btr *btr) -{ - int ret; - struct stack_entry *top; - struct bt_ctf_field_type *next_field_type = NULL; - enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; - - if (stack_empty(btr->stack)) { - goto end; - } - - top = stack_top(btr->stack); - - /* Are we done with this base type? */ - while (top->index == top->base_len) { - if (btr->user.cbs.types.compound_end) { - status = btr->user.cbs.types.compound_end( - top->base_type, btr->user.data); - if (status != BT_CTF_BTR_STATUS_OK) { - PERR("compound_end() user callback function failed\n"); - goto end; - } - } - - stack_pop(btr->stack); - - /* Are we done with the root type? */ - if (stack_empty(btr->stack)) { - btr->state = BTR_STATE_DONE; - goto end; - } - - top = stack_top(btr->stack); - top->index++; - } - - /* Get next field's type */ - switch (bt_ctf_field_type_get_type_id(top->base_type)) { - case BT_CTF_TYPE_ID_STRUCT: - ret = bt_ctf_field_type_structure_get_field( - top->base_type, NULL, &next_field_type, - top->index); - if (ret) { - next_field_type = NULL; - } - break; - case BT_CTF_TYPE_ID_ARRAY: - next_field_type = - bt_ctf_field_type_array_get_element_type( - top->base_type); - break; - case BT_CTF_TYPE_ID_SEQUENCE: - next_field_type = - bt_ctf_field_type_sequence_get_element_type( - top->base_type); - break; - case BT_CTF_TYPE_ID_VARIANT: - /* Variant types are dynamic: query the user, he should know! */ - next_field_type = - btr->user.cbs.query.get_variant_type( - top->base_type, btr->user.data); - break; - default: - break; - } - - if (!next_field_type) { - PERR("Failed to get next field's type\n"); - status = BT_CTF_BTR_STATUS_ERROR; - goto end; - } - - if (is_compound_type(next_field_type)) { - if (btr->user.cbs.types.compound_begin) { - status = btr->user.cbs.types.compound_begin( - next_field_type, btr->user.data); - if (status != BT_CTF_BTR_STATUS_OK) { - PERR("compound_begin() user callback function failed\n"); - goto end; - } - } - - ret = stack_push_with_len(btr, next_field_type); - if (ret) { - PERR("Failed to push compound type onto the stack\n"); - status = BT_CTF_BTR_STATUS_ERROR; - goto end; - } - - /* Next state: align a compound type */ - btr->state = BTR_STATE_ALIGN_COMPOUND; - } else { - /* Replace current basic field type */ - BT_MOVE(btr->cur_basic_field_type, next_field_type); - - /* Next state: align a basic type */ - btr->state = BTR_STATE_ALIGN_BASIC; - } - -end: - BT_PUT(next_field_type); - - return status; -} - -static inline -enum bt_ctf_btr_status handle_state(struct bt_ctf_btr *btr) -{ - enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; - - switch (btr->state) { - case BTR_STATE_NEXT_FIELD: - status = next_field_state(btr); - break; - case BTR_STATE_ALIGN_BASIC: - status = align_type_state(btr, btr->cur_basic_field_type, - BTR_STATE_READ_BASIC_BEGIN); - break; - case BTR_STATE_ALIGN_COMPOUND: - status = align_type_state(btr, stack_top(btr->stack)->base_type, - BTR_STATE_NEXT_FIELD); - break; - case BTR_STATE_READ_BASIC_BEGIN: - status = read_basic_begin_state(btr); - break; - case BTR_STATE_READ_BASIC_CONTINUE: - status = read_basic_continue_state(btr); - break; - case BTR_STATE_DONE: - break; - } - - return status; -} - -struct bt_ctf_btr *bt_ctf_btr_create(struct bt_ctf_btr_cbs cbs, void *data, - FILE *err_stream) -{ - struct bt_ctf_btr *btr; - - btr = g_new0(struct bt_ctf_btr, 1); - if (!btr) { - PERR("Failed to allocate memory for binary type reader\n"); - goto end; - } - - btr->stack = stack_new(); - if (!btr->stack) { - PERR("Failed to create stack\n"); - bt_ctf_btr_destroy(btr); - btr = NULL; - goto end; - } - - btr->state = BTR_STATE_NEXT_FIELD; - btr->user.cbs = cbs; - btr->user.data = data; - btr->err_stream = err_stream; - -end: - return btr; -} - -void bt_ctf_btr_destroy(struct bt_ctf_btr *btr) -{ - if (btr->stack) { - stack_destroy(btr->stack); - } - - BT_PUT(btr->cur_basic_field_type); - g_free(btr); -} - -static -void reset(struct bt_ctf_btr *btr) -{ - stack_clear(btr->stack); - BT_PUT(btr->cur_basic_field_type); - stitch_reset(btr); - btr->buf.addr = NULL; - btr->last_bo = BT_CTF_BYTE_ORDER_UNKNOWN; -} - -size_t bt_ctf_btr_start(struct bt_ctf_btr *btr, - struct bt_ctf_field_type *type, const uint8_t *buf, - size_t offset, size_t packet_offset, size_t sz, - enum bt_ctf_btr_status *status) -{ - assert(btr); - assert(buf); - assert(sz > 0); - assert(BYTES_TO_BITS(sz) > offset); - reset(btr); - btr->buf.addr = buf; - btr->buf.offset = offset; - btr->buf.at = 0; - btr->buf.packet_offset = packet_offset; - btr->buf.buf_sz = sz; - btr->buf.sz = BYTES_TO_BITS(sz) - offset; - *status = BT_CTF_BTR_STATUS_OK; - - /* Set root type */ - if (is_compound_type(type)) { - /* Compound type: push on visit stack */ - int stack_ret; - - if (btr->user.cbs.types.compound_begin) { - *status = btr->user.cbs.types.compound_begin( - type, btr->user.data); - if (*status != BT_CTF_BTR_STATUS_OK) { - PERR("compound_begin() user callback function failed\n"); - goto end; - } - } - - stack_ret = stack_push_with_len(btr, type); - if (stack_ret) { - PERR("Failed to push initial compound type onto the stack\n"); - *status = BT_CTF_BTR_STATUS_ERROR; - goto end; - } - - btr->state = BTR_STATE_ALIGN_COMPOUND; - } else { - /* Basic type: set as current basic type */ - btr->cur_basic_field_type = type; - bt_get(btr->cur_basic_field_type); - btr->state = BTR_STATE_ALIGN_BASIC; - } - - /* Run the machine! */ - while (true) { - *status = handle_state(btr); - if (*status != BT_CTF_BTR_STATUS_OK) { - break; - } else if (btr->state == BTR_STATE_DONE) { - break; - } - } - - /* Update packet offset for next time */ - btr->buf.packet_offset += btr->buf.at; - -end: - return btr->buf.at; -} - -size_t bt_ctf_btr_continue(struct bt_ctf_btr *btr, - const uint8_t *buf, size_t sz, - enum bt_ctf_btr_status *status) -{ - assert(btr); - assert(buf); - assert(sz > 0); - btr->buf.addr = buf; - btr->buf.offset = 0; - btr->buf.at = 0; - btr->buf.buf_sz = sz; - btr->buf.sz = BYTES_TO_BITS(sz); - *status = BT_CTF_BTR_STATUS_OK; - - /* Continue running the machine */ - while (true) { - *status = handle_state(btr); - if (*status != BT_CTF_BTR_STATUS_OK) { - break; - } else if (btr->state == BTR_STATE_DONE) { - break; - } - } - - /* Update packet offset for next time */ - btr->buf.packet_offset += btr->buf.at; - - return btr->buf.at; -} diff --git a/ctf-reader-proto/ctf-btr/ctf-btr.h b/ctf-reader-proto/ctf-btr/ctf-btr.h deleted file mode 100644 index 75484155..00000000 --- a/ctf-reader-proto/ctf-btr/ctf-btr.h +++ /dev/null @@ -1,352 +0,0 @@ -#ifndef CTF_BTR_H -#define CTF_BTR_H - -/* - * Babeltrace - CTF binary type reader (BTR) - * - * Copyright (c) 2015-2016 EfficiOS Inc. and Linux Foundation - * Copyright (c) 2015-2016 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include - -/** - * @file ctf-btr.h - * - * Event-driven CTF binary type reader (BTR). - * - * This is a common, internal API used by CTF source plugins. It allows - * a binary CTF IR field type to be decoded from user-provided buffers. - * As the type is decoded (and, possibly, its nested types), registered - * user callback functions are called. - * - * This API is only concerned with reading one CTF type 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 types are - * requested to the user when needed. - */ - -/** - * Binary type reader API status codes. - */ -enum bt_ctf_btr_status { - /** - * The binary stream reader reached the end of the user-provided - * buffer, but data is still needed to finish decoding the - * requested type. - * - * The user needs to call bt_ctf_btr_continue() as long as - * #BT_CTF_BTR_STATUS_EOF is returned to complete the decoding - * process of a given type. - */ - BT_CTF_BTR_STATUS_EOF = -4, - - /** Invalid argument. */ - BT_CTF_BTR_STATUS_INVAL = -3, - - /** General error. */ - BT_CTF_BTR_STATUS_ERROR = -1, - - /** Everything okay. */ - BT_CTF_BTR_STATUS_OK = 0, -}; - -/** Type reader. */ -struct bt_ctf_btr; - -/* - * Type reader user callback functions. - */ -struct bt_ctf_btr_cbs { - /** - * Type callback functions. - * - * This CTF binary type reader is event-driven. The following - * functions are called during the decoding process, either when - * a compound type begins/ends, or when a basic type is - * completely decoded (along with its value). - * - * Each function also receives the CTF IR field type associated - * with the call, and user data (registered to the type reader - * calling them). This field type is a weak reference; the - * callback function must use bt_ctf_field_type_get() to keep - * its own reference of it. - * - * Actual CTF IR fields are \em not created here; this would be - * the responsibility of a type reader's user (the provider of - * those callback functions). - * - * All the type callback functions return one of the following - * values: - * - * - #BT_CTF_BTR_STATUS_OK: Everything is okay; - * continue the decoding process. - * - #BT_CTF_BTR_STATUS_ERROR: General error (reported - * to type reader's user). - * - * Any member of this structure may be set to \c NULL, should - * a specific notification be not needed. - */ - struct { - /** - * Called when a signed integer type is completely - * decoded. This could also be the supporting signed - * integer type of an enumeration type (\p type will - * indicate this). - * - * @param value Signed integer value - * @param type Integer or enumeration type (weak - * reference) - * @param data User data - * @returns #BT_CTF_BTR_STATUS_OK or - * #BT_CTF_BTR_STATUS_ERROR - */ - enum bt_ctf_btr_status (* signed_int)(int64_t value, - struct bt_ctf_field_type *type, void *data); - - /** - * Called when an unsigned integer type is completely - * decoded. This could also be the supporting signed - * integer type of an enumeration type (\p type will - * indicate this). - * - * @param value Unsigned integer value - * @param type Integer or enumeration type (weak - * reference) - * @param data User data - * @returns #BT_CTF_BTR_STATUS_OK or - * #BT_CTF_BTR_STATUS_ERROR - */ - enum bt_ctf_btr_status (* unsigned_int)(uint64_t value, - struct bt_ctf_field_type *type, void *data); - - /** - * Called when a floating point number type is - * completely decoded. - * - * @param value Floating point number value - * @param type Floating point number type (weak - * reference) - * @param data User data - * @returns #BT_CTF_BTR_STATUS_OK or - * #BT_CTF_BTR_STATUS_ERROR - */ - enum bt_ctf_btr_status (* floating_point)(double value, - struct bt_ctf_field_type *type, void *data); - - /** - * Called when a string type begins. - * - * All the following user callback function calls will - * be made to bt_ctf_btr_cbs::types::string(), each of - * them providing one substring of the complete string - * type's value. - * - * @param type Beginning string type (weak reference) - * @param data User data - * @returns #BT_CTF_BTR_STATUS_OK or - * #BT_CTF_BTR_STATUS_ERROR - */ - enum bt_ctf_btr_status (* string_begin)( - struct bt_ctf_field_type *type, void *data); - - /** - * Called when a string type's substring is decoded - * (between a call to bt_ctf_btr_cbs::types::string_begin() - * and a call to bt_ctf_btr_cbs::types::string_end()). - * - * @param value String value (\em not null-terminated) - * @param len String value length - * @param type String type (weak reference) - * @param data User data - * @returns #BT_CTF_BTR_STATUS_OK or - * #BT_CTF_BTR_STATUS_ERROR - */ - enum bt_ctf_btr_status (* string)(const char *value, - size_t len, struct bt_ctf_field_type *type, - void *data); - - /** - * Called when a string type ends. - * - * @param type Ending string type (weak reference) - * @param data User data - * @returns #BT_CTF_BTR_STATUS_OK or - * #BT_CTF_BTR_STATUS_ERROR - */ - enum bt_ctf_btr_status (* string_end)( - struct bt_ctf_field_type *type, void *data); - - /** - * Called when a compound type begins. - * - * All the following type callback function calls will - * signal sequential elements of this compound type, - * until the next corresponding - * bt_ctf_btr_cbs::types::compound_end() is called. - * - * If \p type is a variant type, then only one type - * callback function call will follow before the call to - * bt_ctf_btr_cbs::types::compound_end(). This single - * call indicates the selected type of this variant - * type. - * - * @param type Beginning compound type (weak reference) - * @param data User data - * @returns #BT_CTF_BTR_STATUS_OK or - * #BT_CTF_BTR_STATUS_ERROR - */ - enum bt_ctf_btr_status (* compound_begin)( - struct bt_ctf_field_type *type, void *data); - - /** - * Called when a compound type ends. - * - * @param type Ending compound type (weak reference) - * @param data User data - * @returns #BT_CTF_BTR_STATUS_OK or - * #BT_CTF_BTR_STATUS_ERROR - */ - enum bt_ctf_btr_status (* compound_end)( - struct bt_ctf_field_type *type, void *data); - } types; - - /** - * Query callback functions are used when the type reader needs - * dynamic information, i.e. a sequence type's current length - * or a variant type's current selected type. - * - * 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 - * type. - * - * @param type Sequence type (weak reference) - * @param data User data - * @returns Sequence length or - * #BT_CTF_BTR_STATUS_ERROR on error - */ - int64_t (* get_sequence_length)(struct bt_ctf_field_type *type, - void *data); - - /** - * Called to query the current selected type of a given - * variant type. - * - * @param type Variant type (weak reference) - * @param data User data - * @returns Current selected type (owned by - * this) or \c NULL on error - */ - struct bt_ctf_field_type * (* get_variant_type)( - struct bt_ctf_field_type *type, void *data); - } query; -}; - -/** - * Creates a CTF binary type reader. - * - * @param cbs User callback functions - * @param data User data (passed to user callback functions) - * @param err_stream Error stream (can be \c NULL to disable) - * @returns New binary type reader on success, or \c NULL on error - */ -struct bt_ctf_btr *bt_ctf_btr_create(struct bt_ctf_btr_cbs cbs, void *data, - FILE *err_stream); - -/** - * Destroys a CTF binary type reader, freeing all internal resources. - * - * @param btr Binary type reader - */ -void bt_ctf_btr_destroy(struct bt_ctf_btr *btr); - -/** - * Decodes a given CTF type 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: - * - * - #BT_CTF_BTR_STATUS_OK: Decoding is done. - * - #BT_CTF_BTR_STATUS_EOF: The end of the buffer was reached, - * but more data is needed to finish the decoding process of the - * requested type. The user needs to call bt_ctf_btr_continue() - * as long as #BT_CTF_BTR_STATUS_EOF is returned to complete the - * decoding process of the original type. - * - #BT_CTF_BTR_STATUS_INVAL: Invalid argument. - * - #BT_CTF_BTR_STATUS_ERROR: General error. - * - * Calling this function resets the type reader's internal state. If - * #BT_CTF_BTR_STATUS_EOF is returned, bt_ctf_btr_continue() needs to - * be called next, \em not bt_ctf_btr_decode(). - * - * @param btr Binary type reader - * @param type Type to decode (weak reference) - * @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 type (bits) - * @param sz Size of buffer in bytes (from \p buf) - * @param status Returned status (see description above) - * @returns Number of consumed bits - */ -size_t bt_ctf_btr_start(struct bt_ctf_btr *btr, - struct bt_ctf_field_type *type, const uint8_t *buf, - size_t offset, size_t packet_offset, size_t sz, - enum bt_ctf_btr_status *status); - -/** - * Continues the decoding process a given CTF type. - * - * The number of bits consumed by this function is returned. - * - * The \p status output parameter is where a status is placed, amongst - * the following: - * - * - #BT_CTF_BTR_STATUS_OK: decoding is done. - * - #BT_CTF_BTR_STATUS_EOF: the end of the buffer was reached, - * but more data is needed to finish the decoding process of the - * requested type. The user needs to call bt_ctf_btr_continue() - * as long as #BT_CTF_BTR_STATUS_EOF is returned to complete the - * decoding process of the original type. - * - #BT_CTF_BTR_STATUS_INVAL: invalid argument. - * - #BT_CTF_BTR_STATUS_ERROR: general error. - * - * @param btr Binary type 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 - */ -size_t bt_ctf_btr_continue(struct bt_ctf_btr *btr, - const uint8_t *buf, size_t sz, - enum bt_ctf_btr_status *status); - -#endif /* CTF_BTR_H */ diff --git a/ctf-reader-proto/ctf-btr/print.h b/ctf-reader-proto/ctf-btr/print.h deleted file mode 100644 index 22381dc1..00000000 --- a/ctf-reader-proto/ctf-btr/print.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef CTF_BTR_PRINT_H -#define CTF_BTR_PRINT_H - -/* - * Define PRINT_PREFIX and PRINT_ERR_STREAM, then include this file. - * - * Copyright (c) 2016 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include - -#define PERR(fmt, ...) \ - do { \ - if (PRINT_ERR_STREAM) { \ - fprintf(PRINT_ERR_STREAM, \ - "Error: " PRINT_PREFIX ": " fmt, \ - ##__VA_ARGS__); \ - } \ - } while (0) - -#define PWARN(fmt, ...) \ - do { \ - if (PRINT_ERR_STREAM) { \ - fprintf(PRINT_ERR_STREAM, \ - "Warning: " PRINT_PREFIX ": " fmt, \ - ##__VA_ARGS__); \ - } \ - } while (0) - -#define PDBG(fmt, ...) \ - do { \ - if (babeltrace_debug) { \ - fprintf(stderr, \ - "Debug: " PRINT_PREFIX ": " fmt, \ - ##__VA_ARGS__); \ - } \ - } while (0) - -#endif /* CTF_BTR_PRINT_H */ diff --git a/ctf-reader-proto/ctf-notif-iter/Makefile.am b/ctf-reader-proto/ctf-notif-iter/Makefile.am deleted file mode 100644 index fe901365..00000000 --- a/ctf-reader-proto/ctf-notif-iter/Makefile.am +++ /dev/null @@ -1,6 +0,0 @@ -AM_CFLAGS = $(PACKAGE_CFLAGS) -AM_CPPFLAGS = -I$(top_srcdir)/include - -noinst_LTLIBRARIES = libctf-notif-iter.la - -libctf_notif_iter_la_SOURCES = ctf-notif-iter.c diff --git a/ctf-reader-proto/ctf-notif-iter/ctf-notif-iter.c b/ctf-reader-proto/ctf-notif-iter/ctf-notif-iter.c deleted file mode 100644 index 9fefbd8f..00000000 --- a/ctf-reader-proto/ctf-notif-iter/ctf-notif-iter.c +++ /dev/null @@ -1,1938 +0,0 @@ -/* - * Babeltrace - CTF notification iterator - * ¯¯¯¯¯ ¯¯¯¯ - * Copyright (c) 2015-2016 EfficiOS Inc. and Linux Foundation - * Copyright (c) 2015-2016 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PRINT_ERR_STREAM notit->err_stream -#define PRINT_PREFIX "ctf-notif-iter" -#include "print.h" - -#include "ctf-notif-iter.h" -#include "../ctf-btr/ctf-btr.h" - -#define BYTES_TO_BITS(x) ((x) * 8) - -struct bt_ctf_notif_iter; - -/* A visit stack entry */ -struct stack_entry { - /* - * Current base field, one of: - * - * * string - * * structure - * * array - * * sequence - * * variant - * - * Field is owned by this. - */ - struct bt_ctf_field *base; - - /* index of next field to set */ - size_t index; -}; - -/* Visit stack */ -struct stack { - /* Entries (struct stack_entry *) (top is last element) */ - GPtrArray *entries; - - /* Link to owner */ - struct bt_ctf_notif_iter *notit; -}; - -/* State */ -enum state { - STATE_INIT, - 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_NOTIF_NEW_PACKET, - STATE_DSCOPE_STREAM_EVENT_HEADER_BEGIN, - STATE_DSCOPE_STREAM_EVENT_HEADER_CONTINUE, - STATE_AFTER_STREAM_EVENT_HEADER, - STATE_DSCOPE_STREAM_EVENT_CONTEXT_BEGIN, - STATE_DSCOPE_STREAM_EVENT_CONTEXT_CONTINUE, - STATE_DSCOPE_EVENT_CONTEXT_BEGIN, - STATE_DSCOPE_EVENT_CONTEXT_CONTINUE, - STATE_DSCOPE_EVENT_PAYLOAD_BEGIN, - STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE, - STATE_EMIT_NOTIF_EVENT, - STATE_EMIT_NOTIF_END_OF_PACKET, - STATE_SKIP_PACKET_PADDING, -}; - -/* CTF notification iterator */ -struct bt_ctf_notif_iter { - /* Visit stack */ - struct stack *stack; - - /* Error stream (may be NULL) */ - FILE *err_stream; - - /* - * Current dynamic scope field pointer. - * - * This is set when a dynamic scope field is first created by - * btr_compound_begin_cb(). It points to one of the fields in - * dscopes below. - */ - struct bt_ctf_field **cur_dscope_field; - - /* Trace and classes (owned by this) */ - struct { - struct bt_ctf_trace *trace; - struct bt_ctf_stream_class *stream_class; - struct bt_ctf_event_class *event_class; - } meta; - - /* Current packet (NULL if not created yet) */ - struct bt_ctf_packet *packet; - - /* Database of current dynamic scopes (owned by this) */ - struct { - struct bt_ctf_field *trace_packet_header; - struct bt_ctf_field *stream_packet_context; - struct bt_ctf_field *stream_event_header; - struct bt_ctf_field *stream_event_context; - struct bt_ctf_field *event_context; - struct bt_ctf_field *event_payload; - } dscopes; - - /* Current state */ - enum state state; - - /* User buffer stuff */ - 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; - } buf; - - /* Binary type reader */ - struct bt_ctf_btr *btr; - - /* Medium stuff */ - struct { - struct bt_ctf_notif_iter_medium_ops medops; - size_t max_request_sz; - void *data; - } medium; - - /* Current packet size (bits) (-1 if unknown) */ - size_t cur_packet_size; - - /* Current content size (bits) (-1 if unknown) */ - size_t cur_content_size; -}; - -static -void stack_entry_free_func(gpointer data) -{ - struct stack_entry *entry = data; - - bt_put(entry->base); - g_free(entry); -} - -static -struct stack *stack_new(struct bt_ctf_notif_iter *notit) -{ - struct stack *stack = NULL; - - stack = g_new0(struct stack, 1); - if (!stack) { - goto error; - } - - stack->entries = g_ptr_array_new_with_free_func(stack_entry_free_func); - if (!stack->entries) { - goto error; - } - - stack->notit = notit; - - return stack; - -error: - g_free(stack); - - return NULL; -} - -static -void stack_destroy(struct stack *stack) -{ - assert(stack); - g_ptr_array_free(stack->entries, TRUE); - g_free(stack); -} - -static -int stack_push(struct stack *stack, struct bt_ctf_field *base) -{ - int ret = 0; - struct stack_entry *entry; - struct bt_ctf_notif_iter *notit; - - assert(stack); - assert(base); - notit = stack->notit; - entry = g_new0(struct stack_entry, 1); - if (!entry) { - PERR("Cannot create new stack entry\n"); - ret = -1; - goto end; - } - - entry->base = bt_get(base); - g_ptr_array_add(stack->entries, entry); - -end: - return ret; -} - -static inline -unsigned int stack_size(struct stack *stack) -{ - assert(stack); - - return stack->entries->len; -} - -static -void stack_pop(struct stack *stack) -{ - assert(stack); - assert(stack_size(stack)); - g_ptr_array_remove_index(stack->entries, stack->entries->len - 1); -} - -static inline -struct stack_entry *stack_top(struct stack *stack) -{ - assert(stack); - assert(stack_size(stack)); - - return g_ptr_array_index(stack->entries, stack->entries->len - 1); -} - -static inline -bool stack_empty(struct stack *stack) -{ - return stack_size(stack) == 0; -} - -static -void stack_clear(struct stack *stack) -{ - assert(stack); - - if (!stack_empty(stack)) { - g_ptr_array_remove_range(stack->entries, 0, stack_size(stack)); - } - - assert(stack_empty(stack)); -} - -static inline -enum bt_ctf_notif_iter_status notif_iter_status_from_m_status( - enum bt_ctf_notif_iter_medium_status m_status) -{ - return m_status; -} - -static inline -size_t buf_size_bits(struct bt_ctf_notif_iter *notit) -{ - return BYTES_TO_BITS(notit->buf.sz); -} - -static inline -size_t buf_available_bits(struct bt_ctf_notif_iter *notit) -{ - return buf_size_bits(notit) - notit->buf.at; -} - -static inline -size_t packet_at(struct bt_ctf_notif_iter *notit) -{ - return notit->buf.packet_offset + notit->buf.at; -} - -static inline -size_t remaining_content_bits(struct bt_ctf_notif_iter *notit) -{ - if (notit->cur_content_size == -1) { - return -1; - } - - return notit->cur_content_size - packet_at(notit); -} - -static inline -size_t remaining_packet_bits(struct bt_ctf_notif_iter *notit) -{ - if (notit->cur_packet_size == -1) { - return -1; - } - - return notit->cur_packet_size - packet_at(notit); -} - -static inline -void buf_consume_bits(struct bt_ctf_notif_iter *notit, size_t incr) -{ - notit->buf.at += incr; -} - -static inline -bool buf_has_enough_bits(struct bt_ctf_notif_iter *notit, size_t sz) -{ - return buf_available_bits(notit) >= sz; -} - -static -enum bt_ctf_notif_iter_status request_medium_bytes(struct bt_ctf_notif_iter *notit) -{ - uint8_t *buffer_addr; - size_t buffer_sz; - enum bt_ctf_notif_iter_medium_status m_status; - - m_status = notit->medium.medops.request_bytes( - notit->medium.max_request_sz, &buffer_addr, - &buffer_sz, notit->medium.data); - if (m_status == BT_CTF_NOTIF_ITER_MEDIUM_STATUS_OK) { - assert(buffer_sz != 0); - - /* New packet offset is old one + old size (in bits) */ - notit->buf.packet_offset += buf_size_bits(notit); - - /* Restart at the beginning of the new medium buffer */ - notit->buf.at = 0; - - /* New medium buffer size */ - notit->buf.sz = buffer_sz; - - /* New medium buffer address */ - notit->buf.addr = buffer_addr; - } - - return notif_iter_status_from_m_status(m_status); -} - -static inline -enum bt_ctf_notif_iter_status buf_ensure_available_bits( - struct bt_ctf_notif_iter *notit) -{ - enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK; - - if (buf_available_bits(notit) == 0) { - /* - * This _cannot_ return BT_CTF_NOTIF_ITER_STATUS_OK - * _and_ no bits. - */ - status = request_medium_bytes(notit); - } - - return status; -} - -static -enum bt_ctf_notif_iter_status read_dscope_begin_state( - struct bt_ctf_notif_iter *notit, - struct bt_ctf_field_type *dscope_field_type, - enum state done_state, enum state continue_state, - struct bt_ctf_field **dscope_field) -{ - enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK; - enum bt_ctf_btr_status btr_status; - size_t consumed_bits; - - status = buf_ensure_available_bits(notit); - if (status != BT_CTF_NOTIF_ITER_STATUS_OK) { - goto end; - } - - bt_put(*dscope_field); - notit->cur_dscope_field = dscope_field; - consumed_bits = bt_ctf_btr_start(notit->btr, dscope_field_type, - notit->buf.addr, notit->buf.at, packet_at(notit), - notit->buf.sz, &btr_status); - - switch (btr_status) { - case BT_CTF_BTR_STATUS_OK: - /* type was read completely */ - notit->state = done_state; - break; - case BT_CTF_BTR_STATUS_EOF: - notit->state = continue_state; - break; - default: - PERR("Binary type reader failed to start\n"); - status = BT_CTF_NOTIF_ITER_STATUS_ERROR; - goto end; - } - - /* Consume bits now since we know we're not in an error state */ - buf_consume_bits(notit, consumed_bits); - -end: - return status; -} - -static -enum bt_ctf_notif_iter_status read_dscope_continue_state( - struct bt_ctf_notif_iter *notit, enum state done_state) -{ - enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK; - enum bt_ctf_btr_status btr_status; - size_t consumed_bits; - - status = buf_ensure_available_bits(notit); - if (status != BT_CTF_NOTIF_ITER_STATUS_OK) { - goto end; - } - - consumed_bits = bt_ctf_btr_continue(notit->btr, notit->buf.addr, - notit->buf.sz, &btr_status); - - switch (btr_status) { - case BT_CTF_BTR_STATUS_OK: - /* Type was read completely */ - notit->state = done_state; - break; - case BT_CTF_BTR_STATUS_EOF: - /* Stay in this continue state */ - break; - default: - PERR("Binary type reader failed to continue\n"); - status = BT_CTF_NOTIF_ITER_STATUS_ERROR; - goto end; - } - - /* Consume bits now since we know we're not in an error state */ - buf_consume_bits(notit, consumed_bits); - -end: - return status; -} - -static -void put_event_dscopes(struct bt_ctf_notif_iter *notit) -{ - BT_PUT(notit->dscopes.stream_event_header); - BT_PUT(notit->dscopes.stream_event_context); - BT_PUT(notit->dscopes.event_context); - BT_PUT(notit->dscopes.event_payload); -} - -static -void put_all_dscopes(struct bt_ctf_notif_iter *notit) -{ - BT_PUT(notit->dscopes.trace_packet_header); - BT_PUT(notit->dscopes.stream_packet_context); - put_event_dscopes(notit); -} - -static -enum bt_ctf_notif_iter_status read_packet_header_begin_state( - struct bt_ctf_notif_iter *notit) -{ - enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK; - struct bt_ctf_field_type *packet_header_type; - - /* Reset all dynamic scopes since we're reading a new packet */ - put_all_dscopes(notit); - BT_PUT(notit->packet); - BT_PUT(notit->meta.stream_class); - BT_PUT(notit->meta.event_class); - - /* Packet header type is common to the whole trace */ - packet_header_type = bt_ctf_trace_get_packet_header_type( - notit->meta.trace); - if (!packet_header_type) { - PERR("Failed to retrieve trace's packet header type\n"); - status = BT_CTF_NOTIF_ITER_STATUS_ERROR; - goto end; - } - - status = read_dscope_begin_state(notit, packet_header_type, - STATE_AFTER_TRACE_PACKET_HEADER, - STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE, - ¬it->dscopes.trace_packet_header); - -end: - BT_PUT(packet_header_type); - - return status; -} - -static -enum bt_ctf_notif_iter_status read_packet_header_continue_state( - struct bt_ctf_notif_iter *notit) -{ - return read_dscope_continue_state(notit, - STATE_AFTER_TRACE_PACKET_HEADER); -} - -static inline -bool is_struct_type(struct bt_ctf_field_type *field_type) -{ - return bt_ctf_field_type_get_type_id(field_type) == BT_CTF_TYPE_ID_STRUCT; -} - -static inline -bool is_variant_type(struct bt_ctf_field_type *field_type) -{ - return bt_ctf_field_type_get_type_id(field_type) == BT_CTF_TYPE_ID_VARIANT; -} - -static inline -enum bt_ctf_notif_iter_status set_current_stream_class(struct bt_ctf_notif_iter *notit) -{ - enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK; - struct bt_ctf_field_type *packet_header_type; - struct bt_ctf_field_type *stream_id_field_type = NULL; - uint64_t stream_id; - - /* Is there any "stream_id" field in the packet header? */ - packet_header_type = bt_ctf_trace_get_packet_header_type( - notit->meta.trace); - if (!packet_header_type) { - PERR("Failed to retrieve trace's packet header type\n"); - status = BT_CTF_NOTIF_ITER_STATUS_ERROR; - goto end; - } - - assert(is_struct_type(packet_header_type)); - - // TODO: optimalize! - stream_id_field_type = - bt_ctf_field_type_structure_get_field_type_by_name( - packet_header_type, "stream_id"); - if (stream_id_field_type) { - /* Find appropriate stream class using current stream ID */ - struct bt_ctf_field *stream_id_field = NULL; - int ret; - - assert(notit->dscopes.trace_packet_header); - - // TODO: optimalize! - stream_id_field = bt_ctf_field_structure_get_field( - notit->dscopes.trace_packet_header, "stream_id"); - assert(stream_id_field); - ret = bt_ctf_field_unsigned_integer_get_value( - stream_id_field, &stream_id); - assert(!ret); - BT_PUT(stream_id_field); - } else { - /* Only one stream: pick the first stream class */ - assert(bt_ctf_trace_get_stream_class_count( - notit->meta.trace) == 1); - stream_id = 0; - } - - BT_PUT(notit->meta.stream_class); - - // TODO: get by ID - notit->meta.stream_class = bt_ctf_trace_get_stream_class( - notit->meta.trace, stream_id); - if (!notit->meta.stream_class) { - PERR("Cannot find stream class with ID %" PRIu64 "\n", - stream_id); - status = BT_CTF_NOTIF_ITER_STATUS_ERROR; - goto end; - } - -end: - BT_PUT(packet_header_type); - BT_PUT(stream_id_field_type); - - return status; -} - -static -enum bt_ctf_notif_iter_status after_packet_header_state( - struct bt_ctf_notif_iter *notit) -{ - enum bt_ctf_notif_iter_status status; - - status = set_current_stream_class(notit); - if (status == BT_CTF_NOTIF_ITER_STATUS_OK) { - notit->state = STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN; - } - - return status; -} - -static -enum bt_ctf_notif_iter_status read_packet_context_begin_state( - struct bt_ctf_notif_iter *notit) -{ - enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK; - struct bt_ctf_field_type *packet_context_type; - - assert(notit->meta.stream_class); - packet_context_type = bt_ctf_stream_class_get_packet_context_type( - notit->meta.stream_class); - if (!packet_context_type) { - PERR("Failed to retrieve stream class's packet context\n"); - status = BT_CTF_NOTIF_ITER_STATUS_ERROR; - goto end; - } - - status = read_dscope_begin_state(notit, packet_context_type, - STATE_AFTER_STREAM_PACKET_CONTEXT, - STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE, - ¬it->dscopes.stream_packet_context); - -end: - BT_PUT(packet_context_type); - - return status; -} - -static -enum bt_ctf_notif_iter_status read_packet_context_continue_state( - struct bt_ctf_notif_iter *notit) -{ - return read_dscope_continue_state(notit, - STATE_AFTER_STREAM_PACKET_CONTEXT); -} - -static inline -enum bt_ctf_notif_iter_status set_current_packet_content_sizes( - struct bt_ctf_notif_iter *notit) -{ - enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK; - struct bt_ctf_field *packet_size_field = NULL; - struct bt_ctf_field *content_size_field = NULL; - uint64_t content_size = -1, packet_size = -1; - - assert(notit->dscopes.stream_packet_context); - - // TODO: optimalize! - packet_size_field = bt_ctf_field_structure_get_field( - notit->dscopes.stream_packet_context, "packet_size"); - content_size_field = bt_ctf_field_structure_get_field( - notit->dscopes.stream_packet_context, "content_size"); - if (packet_size_field) { - int ret = bt_ctf_field_unsigned_integer_get_value( - packet_size_field, &packet_size); - assert(!ret); - - if (packet_size == 0) { - PERR("Decoded packet size is 0\n"); - status = BT_CTF_NOTIF_ITER_STATUS_ERROR; - goto end; - } else if ((packet_size % 8) != 0) { - PERR("Decoded packet size is not a multiple of 8\n"); - status = BT_CTF_NOTIF_ITER_STATUS_ERROR; - goto end; - } - } - if (content_size_field) { - int ret = bt_ctf_field_unsigned_integer_get_value( - content_size_field, &content_size); - assert(!ret); - } else { - content_size = packet_size; - } - - notit->cur_packet_size = packet_size; - notit->cur_content_size = content_size; - -end: - BT_PUT(packet_size_field); - BT_PUT(content_size_field); - - return status; -} - -static -enum bt_ctf_notif_iter_status after_packet_context_state( - struct bt_ctf_notif_iter *notit) -{ - enum bt_ctf_notif_iter_status status; - - status = set_current_packet_content_sizes(notit); - if (status == BT_CTF_NOTIF_ITER_STATUS_OK) { - notit->state = STATE_EMIT_NOTIF_NEW_PACKET; - } - - return status; -} - -static -enum bt_ctf_notif_iter_status read_event_header_begin_state( - struct bt_ctf_notif_iter *notit) -{ - enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK; - struct bt_ctf_field_type *event_header_type = NULL; - - /* Check if we have some content left */ - if (notit->cur_content_size >= 0) { - if (packet_at(notit) == notit->cur_content_size) { - /* No more events! */ - notit->state = STATE_EMIT_NOTIF_END_OF_PACKET; - goto end; - } else if (packet_at(notit) > notit->cur_content_size) { - /* That's not supposed to happen */ - PERR("Cursor passed packet's content size:\n"); - PERR(" Decoded content size: %zu\n", - notit->cur_content_size); - PERR(" Cursor position: %zu\n", packet_at(notit)); - status = BT_CTF_NOTIF_ITER_STATUS_ERROR; - goto end; - } - } - - event_header_type = bt_ctf_stream_class_get_event_header_type( - notit->meta.stream_class); - if (!event_header_type) { - PERR("Failed to retrieve stream class's event header type\n"); - status = BT_CTF_NOTIF_ITER_STATUS_ERROR; - goto end; - } - - put_event_dscopes(notit); - status = read_dscope_begin_state(notit, event_header_type, - STATE_AFTER_STREAM_EVENT_HEADER, - STATE_DSCOPE_STREAM_EVENT_HEADER_CONTINUE, - ¬it->dscopes.stream_event_header); - -end: - BT_PUT(event_header_type); - - return status; -} - -static -enum bt_ctf_notif_iter_status read_event_header_continue_state( - struct bt_ctf_notif_iter *notit) -{ - return read_dscope_continue_state(notit, - STATE_AFTER_STREAM_EVENT_HEADER); -} - -static inline -enum bt_ctf_notif_iter_status set_current_event_class(struct bt_ctf_notif_iter *notit) -{ - /* - * The assert() calls in this function are okay because it is - * assumed here that all the metadata objects have been - * validated for CTF correctness before decoding actual streams. - */ - - enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK; - struct bt_ctf_field_type *event_header_type; - struct bt_ctf_field_type *id_field_type = NULL; - struct bt_ctf_field_type *v_field_type = NULL; - uint64_t event_id = -1ULL; - int ret; - - event_header_type = bt_ctf_stream_class_get_event_header_type( - notit->meta.stream_class); - if (!event_header_type) { - PERR("Failed to retrieve stream class's event header type\n"); - status = BT_CTF_NOTIF_ITER_STATUS_ERROR; - goto end; - } - - /* Is there any "id"/"v" field in the event header? */ - assert(is_struct_type(event_header_type)); - id_field_type = bt_ctf_field_type_structure_get_field_type_by_name( - event_header_type, "id"); - v_field_type = bt_ctf_field_type_structure_get_field_type_by_name( - event_header_type, "v"); - assert(notit->dscopes.stream_event_header); - if (v_field_type) { - /* - * _ _____ _____ - * | | |_ _|_ _| __ __ _ - * | | | | | || '_ \ / _` | - * | |___| | | || | | | (_| | S P E C I A L - * |_____|_| |_||_| |_|\__, | C A S E ™ - * |___/ - */ - struct bt_ctf_field *v_field = NULL; - struct bt_ctf_field *v_struct_field = NULL; - struct bt_ctf_field *v_struct_id_field = NULL; - - // TODO: optimalize! - v_field = bt_ctf_field_structure_get_field( - notit->dscopes.stream_event_header, "v"); - assert(v_field); - - v_struct_field = - bt_ctf_field_variant_get_current_field(v_field); - if (!v_struct_field) { - goto end_v_field_type; - } - - // TODO: optimalize! - v_struct_id_field = - bt_ctf_field_structure_get_field(v_struct_field, "id"); - if (!v_struct_id_field) { - goto end_v_field_type; - } - - ret = bt_ctf_field_unsigned_integer_get_value( - v_struct_id_field, &event_id); - if (ret) { - event_id = -1ULL; - } - -end_v_field_type: - BT_PUT(v_field); - BT_PUT(v_struct_field); - BT_PUT(v_struct_id_field); - } - - if (id_field_type && event_id == -1ULL) { - /* Check "id" field */ - struct bt_ctf_field *id_field = NULL; - int ret; - - // TODO: optimalize! - id_field = bt_ctf_field_structure_get_field( - notit->dscopes.stream_event_header, "id"); - assert(id_field); - assert(bt_ctf_field_is_integer(id_field) || - bt_ctf_field_is_enumeration(id_field)); - - if (bt_ctf_field_is_integer(id_field)) { - ret = bt_ctf_field_unsigned_integer_get_value( - id_field, &event_id); - } else { - struct bt_ctf_field *container; - - container = bt_ctf_field_enumeration_get_container( - id_field); - assert(container); - ret = bt_ctf_field_unsigned_integer_get_value( - container, &event_id); - BT_PUT(container); - } - assert(!ret); - BT_PUT(id_field); - } - - if (event_id == -1ULL) { - /* Event ID not found: single event? */ - assert(bt_ctf_stream_class_get_event_class_count( - notit->meta.stream_class) == 1); - event_id = 0; - } - - BT_PUT(notit->meta.event_class); - notit->meta.event_class = bt_ctf_stream_class_get_event_class_by_id( - notit->meta.stream_class, event_id); - if (!notit->meta.event_class) { - PERR("Cannot find event class with ID %" PRIu64 "\n", event_id); - status = BT_CTF_NOTIF_ITER_STATUS_ERROR; - goto end; - } - -end: - BT_PUT(event_header_type); - BT_PUT(id_field_type); - BT_PUT(v_field_type); - - return status; -} - -static -enum bt_ctf_notif_iter_status after_event_header_state( - struct bt_ctf_notif_iter *notit) -{ - enum bt_ctf_notif_iter_status status; - - status = set_current_packet_content_sizes(notit); - if (status != BT_CTF_NOTIF_ITER_STATUS_OK) { - PERR("Failed to set current packet and content sizes\n"); - goto end; - } - - status = set_current_event_class(notit); - if (status != BT_CTF_NOTIF_ITER_STATUS_OK) { - PERR("Failed to set current event class\n"); - goto end; - } - - notit->state = STATE_DSCOPE_STREAM_EVENT_CONTEXT_BEGIN; - -end: - return status; -} - -static -enum bt_ctf_notif_iter_status read_stream_event_context_begin_state( - struct bt_ctf_notif_iter *notit) -{ - enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK; - struct bt_ctf_field_type *stream_event_context_type; - - stream_event_context_type = bt_ctf_stream_class_get_event_context_type( - notit->meta.stream_class); - if (!stream_event_context_type) { - PERR("Failed to retrieve stream class's event context type\n"); - status = BT_CTF_NOTIF_ITER_STATUS_ERROR; - goto end; - } - - status = read_dscope_begin_state(notit, stream_event_context_type, - STATE_DSCOPE_EVENT_CONTEXT_BEGIN, - STATE_DSCOPE_STREAM_EVENT_CONTEXT_CONTINUE, - ¬it->dscopes.stream_event_context); - -end: - BT_PUT(stream_event_context_type); - - return status; -} - -static -enum bt_ctf_notif_iter_status read_stream_event_context_continue_state( - struct bt_ctf_notif_iter *notit) -{ - return read_dscope_continue_state(notit, - STATE_DSCOPE_EVENT_CONTEXT_BEGIN); -} - -static -enum bt_ctf_notif_iter_status read_event_context_begin_state( - struct bt_ctf_notif_iter *notit) -{ - enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK; - struct bt_ctf_field_type *event_context_type; - - event_context_type = bt_ctf_event_class_get_context_type( - notit->meta.event_class); - if (!event_context_type) { - PERR("Failed to retrieve event class's context type\n"); - status = BT_CTF_NOTIF_ITER_STATUS_ERROR; - goto end; - } - - status = read_dscope_begin_state(notit, event_context_type, - STATE_DSCOPE_EVENT_PAYLOAD_BEGIN, - STATE_DSCOPE_EVENT_CONTEXT_CONTINUE, - ¬it->dscopes.event_context); - -end: - BT_PUT(event_context_type); - - return status; -} - -static -enum bt_ctf_notif_iter_status read_event_context_continue_state( - struct bt_ctf_notif_iter *notit) -{ - return read_dscope_continue_state(notit, - STATE_DSCOPE_EVENT_PAYLOAD_BEGIN); -} - -static -enum bt_ctf_notif_iter_status read_event_payload_begin_state( - struct bt_ctf_notif_iter *notit) -{ - enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK; - struct bt_ctf_field_type *event_payload_type; - - event_payload_type = bt_ctf_event_class_get_payload_type( - notit->meta.event_class); - if (!event_payload_type) { - PERR("Failed to retrieve event class's payload type\n"); - status = BT_CTF_NOTIF_ITER_STATUS_ERROR; - goto end; - } - - status = read_dscope_begin_state(notit, event_payload_type, - STATE_EMIT_NOTIF_EVENT, - STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE, - ¬it->dscopes.event_payload); - -end: - BT_PUT(event_payload_type); - - return status; -} - -static -enum bt_ctf_notif_iter_status read_event_payload_continue_state( - struct bt_ctf_notif_iter *notit) -{ - return read_dscope_continue_state(notit, STATE_EMIT_NOTIF_EVENT); -} - -static -enum bt_ctf_notif_iter_status skip_packet_padding_state( - struct bt_ctf_notif_iter *notit) -{ - enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK; - size_t bits_to_skip; - - assert(notit->cur_packet_size > 0); - bits_to_skip = notit->cur_packet_size - packet_at(notit); - if (bits_to_skip == 0) { - notit->state = STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN; - goto end; - } else { - size_t bits_to_consume; - status = buf_ensure_available_bits(notit); - if (status != BT_CTF_NOTIF_ITER_STATUS_OK) { - goto end; - } - - bits_to_consume = MIN(buf_available_bits(notit), bits_to_skip); - buf_consume_bits(notit, bits_to_consume); - bits_to_skip = notit->cur_packet_size - packet_at(notit); - if (bits_to_skip == 0) { - notit->state = STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN; - goto end; - } - } - -end: - return status; -} - -static inline -enum bt_ctf_notif_iter_status handle_state(struct bt_ctf_notif_iter *notit) -{ - enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK; - - PDBG("Handling state %d\n", notit->state); - - // TODO: optimalize! - switch (notit->state) { - case STATE_INIT: - notit->state = STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN; - break; - case STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN: - status = read_packet_header_begin_state(notit); - break; - case STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE: - status = read_packet_header_continue_state(notit); - break; - case STATE_AFTER_TRACE_PACKET_HEADER: - status = after_packet_header_state(notit); - break; - case STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN: - status = read_packet_context_begin_state(notit); - break; - case STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE: - status = read_packet_context_continue_state(notit); - break; - case STATE_AFTER_STREAM_PACKET_CONTEXT: - status = after_packet_context_state(notit); - break; - case STATE_EMIT_NOTIF_NEW_PACKET: - notit->state = STATE_DSCOPE_STREAM_EVENT_HEADER_BEGIN; - break; - case STATE_DSCOPE_STREAM_EVENT_HEADER_BEGIN: - status = read_event_header_begin_state(notit); - break; - case STATE_DSCOPE_STREAM_EVENT_HEADER_CONTINUE: - status = read_event_header_continue_state(notit); - break; - case STATE_AFTER_STREAM_EVENT_HEADER: - status = after_event_header_state(notit); - break; - case STATE_DSCOPE_STREAM_EVENT_CONTEXT_BEGIN: - status = read_stream_event_context_begin_state(notit); - break; - case STATE_DSCOPE_STREAM_EVENT_CONTEXT_CONTINUE: - status = read_stream_event_context_continue_state(notit); - break; - case STATE_DSCOPE_EVENT_CONTEXT_BEGIN: - status = read_event_context_begin_state(notit); - break; - case STATE_DSCOPE_EVENT_CONTEXT_CONTINUE: - status = read_event_context_continue_state(notit); - break; - case STATE_DSCOPE_EVENT_PAYLOAD_BEGIN: - status = read_event_payload_begin_state(notit); - break; - case STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE: - status = read_event_payload_continue_state(notit); - break; - case STATE_EMIT_NOTIF_EVENT: - notit->state = STATE_DSCOPE_STREAM_EVENT_HEADER_BEGIN; - break; - case STATE_SKIP_PACKET_PADDING: - status = skip_packet_padding_state(notit); - break; - case STATE_EMIT_NOTIF_END_OF_PACKET: - notit->state = STATE_SKIP_PACKET_PADDING; - break; - } - - return status; -} - -void bt_ctf_notif_iter_reset(struct bt_ctf_notif_iter *notit) -{ - assert(notit); - stack_clear(notit->stack); - BT_PUT(notit->meta.stream_class); - BT_PUT(notit->meta.event_class); - BT_PUT(notit->packet); - put_all_dscopes(notit); - notit->buf.addr = NULL; - notit->buf.sz = 0; - notit->buf.at = 0; - notit->buf.packet_offset = 0; - notit->state = STATE_INIT; - notit->cur_content_size = -1; - notit->cur_packet_size = -1; -} - -static -struct bt_ctf_field *get_next_field(struct bt_ctf_notif_iter *notit) -{ - struct bt_ctf_field *next_field = NULL; - struct bt_ctf_field *base_field; - struct bt_ctf_field_type *base_type; - size_t index; - - assert(!stack_empty(notit->stack)); - index = stack_top(notit->stack)->index; - base_field = stack_top(notit->stack)->base; - base_type = bt_ctf_field_get_type(base_field); - if (!base_type) { - PERR("Failed to get base field's type\n"); - goto end; - } - - switch (bt_ctf_field_type_get_type_id(base_type)) { - case BT_CTF_TYPE_ID_STRUCT: - next_field = bt_ctf_field_structure_get_field_by_index( - base_field, index); - break; - case BT_CTF_TYPE_ID_ARRAY: - next_field = bt_ctf_field_array_get_field(base_field, index); - break; - case BT_CTF_TYPE_ID_SEQUENCE: - next_field = bt_ctf_field_sequence_get_field(base_field, index); - break; - case BT_CTF_TYPE_ID_VARIANT: - next_field = bt_ctf_field_variant_get_current_field(base_field); - break; - default: - assert(false); - break; - } - -end: - BT_PUT(base_type); - - return next_field; -} - -static -enum bt_ctf_btr_status btr_signed_int_cb(int64_t value, - struct bt_ctf_field_type *type, void *data) -{ - enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; - struct bt_ctf_field *field = NULL; - struct bt_ctf_field *int_field = NULL; - struct bt_ctf_notif_iter *notit = data; - int ret; - - /* create next field */ - field = get_next_field(notit); - if (!field) { - PERR("Failed to get next field (signed int)\n"); - status = BT_CTF_BTR_STATUS_ERROR; - goto end; - } - - switch(bt_ctf_field_type_get_type_id(type)) { - case BT_CTF_TYPE_ID_INTEGER: - /* Integer field is created field */ - BT_MOVE(int_field, field); - break; - case BT_CTF_TYPE_ID_ENUM: - int_field = bt_ctf_field_enumeration_get_container(field); - break; - default: - break; - } - - if (!int_field) { - PERR("Failed to get integer field\n"); - status = BT_CTF_BTR_STATUS_ERROR; - goto end; - } - - ret = bt_ctf_field_signed_integer_set_value(int_field, value); - assert(!ret); - stack_top(notit->stack)->index++; - -end: - BT_PUT(field); - BT_PUT(int_field); - - return status; -} - -static -enum bt_ctf_btr_status btr_unsigned_int_cb(uint64_t value, - struct bt_ctf_field_type *type, void *data) -{ - enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; - struct bt_ctf_field *field = NULL; - struct bt_ctf_field *int_field = NULL; - struct bt_ctf_notif_iter *notit = data; - int ret; - - /* Create next field */ - field = get_next_field(notit); - if (!field) { - PERR("Failed to get next field (unsigned int)\n"); - status = BT_CTF_BTR_STATUS_ERROR; - goto end; - } - - switch(bt_ctf_field_type_get_type_id(type)) { - case BT_CTF_TYPE_ID_INTEGER: - /* Integer field is created field */ - BT_MOVE(int_field, field); - break; - case BT_CTF_TYPE_ID_ENUM: - int_field = bt_ctf_field_enumeration_get_container(field); - break; - default: - break; - } - - if (!int_field) { - PERR("Failed to get integer field\n"); - status = BT_CTF_BTR_STATUS_ERROR; - goto end; - } - - ret = bt_ctf_field_unsigned_integer_set_value(int_field, value); - assert(!ret); - stack_top(notit->stack)->index++; - -end: - BT_PUT(field); - BT_PUT(int_field); - - return status; -} - -static -enum bt_ctf_btr_status btr_floating_point_cb(double value, - struct bt_ctf_field_type *type, void *data) -{ - enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; - struct bt_ctf_field *field = NULL; - struct bt_ctf_notif_iter *notit = data; - int ret; - - /* Create next field */ - field = get_next_field(notit); - if (!field) { - PERR("Failed to get next field (floating point number)\n"); - status = BT_CTF_BTR_STATUS_ERROR; - goto end; - } - - ret = bt_ctf_field_floating_point_set_value(field, value); - assert(!ret); - stack_top(notit->stack)->index++; - -end: - BT_PUT(field); - - return status; -} - -static -enum bt_ctf_btr_status btr_string_begin_cb( - struct bt_ctf_field_type *type, void *data) -{ - enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; - struct bt_ctf_field *field = NULL; - struct bt_ctf_notif_iter *notit = data; - int ret; - - /* Create next field */ - field = get_next_field(notit); - if (!field) { - PERR("Failed to get next field (string)\n"); - status = BT_CTF_BTR_STATUS_ERROR; - goto end; - } - - /* - * Push on stack. Not a compound type per se, but we know that only - * btr_string_cb() may be called between this call and a subsequent - * call to btr_string_end_cb(). - */ - ret = stack_push(notit->stack, field); - if (ret) { - PERR("Failed to push string field onto the stack\n"); - status = BT_CTF_BTR_STATUS_ERROR; - goto end; - } - -end: - BT_PUT(field); - - return status; -} - -static -enum bt_ctf_btr_status btr_string_cb(const char *value, - size_t len, struct bt_ctf_field_type *type, void *data) -{ - enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; - struct bt_ctf_field *field = NULL; - struct bt_ctf_notif_iter *notit = data; - int ret; - - /* Get string field */ - field = stack_top(notit->stack)->base; - assert(field); - - /* Append current string */ - ret = bt_ctf_field_string_append_len(field, value, len); - if (ret) { - PERR("Failed to append a string to a string field\n"); - status = BT_CTF_BTR_STATUS_ERROR; - goto end; - } - -end: - return status; -} - -static -enum bt_ctf_btr_status btr_string_end_cb( - struct bt_ctf_field_type *type, void *data) -{ - struct bt_ctf_notif_iter *notit = data; - - /* Pop string field */ - stack_pop(notit->stack); - - /* Go to next field */ - stack_top(notit->stack)->index++; - - return BT_CTF_BTR_STATUS_OK; -} - -enum bt_ctf_btr_status btr_compound_begin_cb( - struct bt_ctf_field_type *type, void *data) -{ - enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; - struct bt_ctf_notif_iter *notit = data; - struct bt_ctf_field *field; - int ret; - - /* Create field */ - if (stack_empty(notit->stack)) { - /* Root: create dynamic scope field */ - *notit->cur_dscope_field = bt_ctf_field_create(type); - field = *notit->cur_dscope_field; - - /* - * Field will be put at the end of this function - * (stack_push() will take one reference, but this - * reference is lost upon the equivalent stack_pop() - * later), so also get it for our context to own it. - */ - bt_get(*notit->cur_dscope_field); - } else { - field = get_next_field(notit); - } - - if (!field) { - PERR("Failed to get next field or create dynamic scope field\n"); - status = BT_CTF_BTR_STATUS_ERROR; - goto end; - } - - /* Push field */ - ret = stack_push(notit->stack, field); - if (ret) { - PERR("Failed to push compound field onto the stack\n"); - status = BT_CTF_BTR_STATUS_ERROR; - goto end; - } - -end: - BT_PUT(field); - - return status; -} - -enum bt_ctf_btr_status btr_compound_end_cb( - struct bt_ctf_field_type *type, void *data) -{ - struct bt_ctf_notif_iter *notit = data; - - assert(!stack_empty(notit->stack)); - - /* Pop stack */ - stack_pop(notit->stack); - - /* If the stack is not empty, increment the base's index */ - if (!stack_empty(notit->stack)) { - stack_top(notit->stack)->index++; - } - - return BT_CTF_BTR_STATUS_OK; -} - -static -struct bt_ctf_field *resolve_field(struct bt_ctf_notif_iter *notit, - struct bt_ctf_field_path *path) -{ - struct bt_ctf_field *field = NULL; - unsigned int i; - - switch (bt_ctf_field_path_get_root_scope(path)) { - case BT_CTF_SCOPE_TRACE_PACKET_HEADER: - field = notit->dscopes.trace_packet_header; - break; - case BT_CTF_SCOPE_STREAM_PACKET_CONTEXT: - field = notit->dscopes.stream_packet_context; - break; - case BT_CTF_SCOPE_STREAM_EVENT_HEADER: - field = notit->dscopes.stream_event_header; - break; - case BT_CTF_SCOPE_STREAM_EVENT_CONTEXT: - field = notit->dscopes.stream_event_context; - break; - case BT_CTF_SCOPE_EVENT_CONTEXT: - field = notit->dscopes.event_context; - break; - case BT_CTF_SCOPE_EVENT_FIELDS: - field = notit->dscopes.event_payload; - break; - default: - break; - } - - if (!field) { - goto end; - } - - bt_get(field); - - for (i = 0; i < bt_ctf_field_path_get_index_count(path); ++i) { - struct bt_ctf_field *next_field = NULL; - struct bt_ctf_field_type *field_type; - int index = bt_ctf_field_path_get_index(path, i); - - field_type = bt_ctf_field_get_type(field); - if (!field_type) { - BT_PUT(field); - goto end; - } - - if (is_struct_type(field_type)) { - next_field = bt_ctf_field_structure_get_field_by_index( - field, index); - } else if (is_variant_type(field_type)) { - next_field = - bt_ctf_field_variant_get_current_field(field); - } - - BT_PUT(field); - BT_PUT(field_type); - - if (!next_field) { - goto end; - } - - /* Move next field -> field */ - BT_MOVE(field, next_field); - } - -end: - return field; -} - -static -int64_t btr_get_sequence_length_cb(struct bt_ctf_field_type *type, void *data) -{ - int64_t ret = -1; - int iret; - struct bt_ctf_field_path *field_path; - struct bt_ctf_notif_iter *notit = data; - struct bt_ctf_field *field = NULL; - uint64_t length; - - field_path = bt_ctf_field_type_sequence_get_length_field_path(type); - if (!field_path) { - goto end; - } - - field = resolve_field(notit, field_path); - if (!field) { - goto end; - } - - iret = bt_ctf_field_unsigned_integer_get_value(field, &length); - if (iret) { - goto end; - } - - ret = (int64_t) length; - -end: - BT_PUT(field); - BT_PUT(field_path); - - return ret; -} - -static -struct bt_ctf_field_type *btr_get_variant_type_cb( - struct bt_ctf_field_type *type, void *data) -{ - struct bt_ctf_field_path *path; - struct bt_ctf_notif_iter *notit = data; - struct bt_ctf_field *tag_field = NULL; - struct bt_ctf_field *selected_field = NULL; - struct bt_ctf_field_type *selected_field_type = NULL; - - path = bt_ctf_field_type_variant_get_tag_field_path(type); - if (!path) { - goto end; - } - - tag_field = resolve_field(notit, path); - if (!tag_field) { - goto end; - } - - /* - * We found the enumeration tag field instance which should be - * able to select a current field for this variant. This - * callback function we're in is called _after_ - * compound_begin(), so the current stack top's base field is - * the variant field in question. We get the selected field here - * thanks to this tag field (thus creating the selected field), - * which will also provide us with its type. Then, this field - * will remain the current selected one until the next callback - * function call which is used to fill the current selected - * field. - */ - selected_field = bt_ctf_field_variant_get_field( - stack_top(notit->stack)->base, tag_field); - if (!selected_field) { - goto end; - } - - selected_field_type = bt_ctf_field_get_type(selected_field); - -end: - BT_PUT(tag_field); - BT_PUT(selected_field); - - return selected_field_type; -} - -static struct bt_ctf_event *create_event(struct bt_ctf_notif_iter *notit) -{ - struct bt_ctf_event *event; - int ret; - - /* Create event object */ - event = bt_ctf_event_create(notit->meta.event_class); - if (!event) { - goto error; - } - - /* Set header, stream event context, context, and payload fields */ - ret = bt_ctf_event_set_header(event, - notit->dscopes.stream_event_header); - if (ret) { - goto error; - } - - ret = bt_ctf_event_set_stream_event_context(event, - notit->dscopes.stream_event_context); - if (ret) { - goto error; - } - - ret = bt_ctf_event_set_event_context(event, - notit->dscopes.event_context); - if (ret) { - goto error; - } - - ret = bt_ctf_event_set_payload_field(event, - notit->dscopes.event_payload); - if (ret) { - goto error; - } - - /* Associate with current packet */ - assert(notit->packet); - ret = bt_ctf_event_set_packet(event, notit->packet); - if (ret) { - goto error; - } - - goto end; - -error: - BT_PUT(event); - -end: - return event; -} - -static void create_packet(struct bt_ctf_notif_iter *notit) -{ - int ret; - struct bt_ctf_stream *stream = NULL; - struct bt_ctf_packet *packet = NULL; - - /* Ask the user for the stream */ - stream = notit->medium.medops.get_stream(notit->meta.stream_class, - notit->medium.data); - if (!stream) { - goto error; - } - - /* Create packet */ - packet = bt_ctf_packet_create(stream); - if (!packet) { - goto error; - } - - /* Set packet's context and header fields */ - if (notit->dscopes.trace_packet_header) { - ret = bt_ctf_packet_set_header(packet, - notit->dscopes.trace_packet_header); - if (ret) { - goto error; - } - } - - if (notit->dscopes.stream_packet_context) { - ret = bt_ctf_packet_set_context(packet, - notit->dscopes.stream_packet_context); - if (ret) { - goto error; - } - } - - goto end; - -error: - BT_PUT(packet); - -end: - BT_MOVE(notit->packet, packet); -} - -static void notify_new_packet(struct bt_ctf_notif_iter *notit, - struct bt_ctf_notif_iter_notif **notification) -{ - struct bt_ctf_notif_iter_notif_new_packet *rnotif; - - rnotif = g_new0(struct bt_ctf_notif_iter_notif_new_packet, 1); - if (!rnotif) { - goto error; - } - - rnotif->base.type = BT_CTF_NOTIF_ITER_NOTIF_NEW_PACKET; - - /* Create packet */ - create_packet(notit); - if (!notit->packet) { - goto error; - } - - rnotif->packet = bt_get(notit->packet); - *notification = (struct bt_ctf_notif_iter_notif *) rnotif; - return; - -error: - bt_ctf_notif_iter_notif_destroy(rnotif); -} - -static void notify_end_of_packet(struct bt_ctf_notif_iter *notit, - struct bt_ctf_notif_iter_notif **notification) -{ - struct bt_ctf_notif_iter_notif_end_of_packet *rnotif; - - rnotif = g_new0(struct bt_ctf_notif_iter_notif_end_of_packet, 1); - if (!rnotif) { - goto error; - } - - rnotif->base.type = BT_CTF_NOTIF_ITER_NOTIF_END_OF_PACKET; - - /* Create packet */ - create_packet(notit); - if (!notit->packet) { - goto error; - } - - rnotif->packet = bt_get(notit->packet); - *notification = (struct bt_ctf_notif_iter_notif *) rnotif; - return; - -error: - bt_ctf_notif_iter_notif_destroy(rnotif); -} - -static void notify_event(struct bt_ctf_notif_iter *notit, - struct bt_ctf_notif_iter_notif **notification) -{ - struct bt_ctf_notif_iter_notif_event *rnotif; - struct bt_ctf_event *event = NULL; - - rnotif = g_new0(struct bt_ctf_notif_iter_notif_event, 1); - if (!rnotif) { - goto error; - } - - rnotif->base.type = BT_CTF_NOTIF_ITER_NOTIF_EVENT; - - /* Create event */ - event = create_event(notit); - if (!event) { - goto error; - } - - BT_MOVE(rnotif->event, event); - *notification = (struct bt_ctf_notif_iter_notif *) rnotif; - return; - -error: - BT_PUT(event); - bt_ctf_notif_iter_notif_destroy(rnotif); -} - -void bt_ctf_notif_iter_notif_destroy(void *vnotif) -{ - struct bt_ctf_notif_iter_notif *notif = vnotif; - - switch (notif->type) { - case BT_CTF_NOTIF_ITER_NOTIF_NEW_PACKET: - { - struct bt_ctf_notif_iter_notif_new_packet *rnotif = - (struct bt_ctf_notif_iter_notif_new_packet *) notif; - - BT_PUT(rnotif->packet); - break; - } - case BT_CTF_NOTIF_ITER_NOTIF_END_OF_PACKET: - { - struct bt_ctf_notif_iter_notif_end_of_packet *rnotif = - (struct bt_ctf_notif_iter_notif_end_of_packet *) notif; - - BT_PUT(rnotif->packet); - break; - } - case BT_CTF_NOTIF_ITER_NOTIF_EVENT: - { - struct bt_ctf_notif_iter_notif_event *rnotif = - (struct bt_ctf_notif_iter_notif_event *) notif; - - BT_PUT(rnotif->event); - break; - } - default: - assert(false); - } - - g_free(notif); -} - -struct bt_ctf_notif_iter *bt_ctf_notif_iter_create(struct bt_ctf_trace *trace, - size_t max_request_sz, - struct bt_ctf_notif_iter_medium_ops medops, - void *data, FILE *err_stream) -{ - struct bt_ctf_notif_iter *notit = NULL; - struct bt_ctf_btr_cbs cbs = { - .types = { - .signed_int = btr_signed_int_cb, - .unsigned_int = btr_unsigned_int_cb, - .floating_point = btr_floating_point_cb, - .string_begin = btr_string_begin_cb, - .string = btr_string_cb, - .string_end = btr_string_end_cb, - .compound_begin = btr_compound_begin_cb, - .compound_end = btr_compound_end_cb, - }, - .query = { - .get_sequence_length = btr_get_sequence_length_cb, - .get_variant_type = btr_get_variant_type_cb, - }, - }; - - assert(trace); - assert(medops.request_bytes); - notit = g_new0(struct bt_ctf_notif_iter, 1); - if (!notit) { - PERR("Failed to allocate memory for CTF notification iterator\n"); - goto end; - } - - notit->meta.trace = trace; - bt_get(notit->meta.trace); - notit->medium.medops = medops; - notit->medium.max_request_sz = max_request_sz; - notit->medium.data = data; - notit->err_stream = err_stream; - notit->stack = stack_new(notit); - if (!notit->stack) { - PERR("Failed to create stack\n"); - bt_ctf_notif_iter_destroy(notit); - notit = NULL; - goto end; - } - - notit->btr = bt_ctf_btr_create(cbs, notit, err_stream); - if (!notit->btr) { - PERR("Failed to create binary type reader\n"); - bt_ctf_notif_iter_destroy(notit); - notit = NULL; - goto end; - } - - bt_ctf_notif_iter_reset(notit); - -end: - return notit; -} - -void bt_ctf_notif_iter_destroy(struct bt_ctf_notif_iter *notit) -{ - BT_PUT(notit->meta.trace); - BT_PUT(notit->meta.stream_class); - BT_PUT(notit->meta.event_class); - BT_PUT(notit->packet); - put_all_dscopes(notit); - - if (notit->stack) { - stack_destroy(notit->stack); - } - - if (notit->btr) { - bt_ctf_btr_destroy(notit->btr); - } - - g_free(notit); -} - -enum bt_ctf_notif_iter_status bt_ctf_notif_iter_get_next_notification( - struct bt_ctf_notif_iter *notit, - struct bt_ctf_notif_iter_notif **notification) -{ - enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK; - - assert(notit); - assert(notification); - - while (true) { - status = handle_state(notit); - if (status != BT_CTF_NOTIF_ITER_STATUS_OK) { - if (status == BT_CTF_NOTIF_ITER_STATUS_EOF) { - PDBG("Medium operation reported end of file\n"); - } else { - PERR("Failed to handle state:\n"); - PERR(" State: %d\n", notit->state); - } - goto end; - } - - switch (notit->state) { - case STATE_EMIT_NOTIF_NEW_PACKET: - PDBG("Emitting new packet notification\n"); - notify_new_packet(notit, notification); - if (!*notification) { - status = BT_CTF_NOTIF_ITER_STATUS_ERROR; - } - goto end; - case STATE_EMIT_NOTIF_EVENT: - PDBG("Emitting event notification\n"); - notify_event(notit, notification); - if (!*notification) { - status = BT_CTF_NOTIF_ITER_STATUS_ERROR; - } - goto end; - case STATE_EMIT_NOTIF_END_OF_PACKET: - PDBG("Emitting end of packet notification\n"); - notify_end_of_packet(notit, notification); - if (!*notification) { - status = BT_CTF_NOTIF_ITER_STATUS_ERROR; - } - goto end; - default: - /* Non-emitting state: continue */ - break; - } - } - -end: - return status; -} diff --git a/ctf-reader-proto/ctf-notif-iter/ctf-notif-iter.h b/ctf-reader-proto/ctf-notif-iter/ctf-notif-iter.h deleted file mode 100644 index ed1dbbcd..00000000 --- a/ctf-reader-proto/ctf-notif-iter/ctf-notif-iter.h +++ /dev/null @@ -1,295 +0,0 @@ -#ifndef CTF_NOTIF_ITER_H -#define CTF_NOTIF_ITER_H - -/* - * Babeltrace - CTF notification iterator - * ¯¯¯¯¯ ¯¯¯¯ - * Copyright (c) 2015-2016 EfficiOS Inc. and Linux Foundation - * Copyright (c) 2015-2016 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include - -/** - * @file ctf-notif-iter.h - * - * CTF notification iterator - * ¯¯¯¯¯ ¯¯¯¯ - * This is a common internal API used by CTF source plugins. It allows - * one to get notifications from a user-provided medium. - */ - -/** - * Medium operations status codes. - */ -enum bt_ctf_notif_iter_medium_status { - /** - * End of file. - * - * The medium function called by the notification iterator - * function reached the end of the file. - */ - BT_CTF_NOTIF_ITER_MEDIUM_STATUS_EOF = -4, - - /** - * There is no data available right now, try again later. - */ - BT_CTF_NOTIF_ITER_MEDIUM_STATUS_AGAIN = -3, - - /** Invalid argument. */ - BT_CTF_NOTIF_ITER_MEDIUM_STATUS_INVAL = -2, - - /** General error. */ - BT_CTF_NOTIF_ITER_MEDIUM_STATUS_ERROR = -1, - - /** Everything okay. */ - BT_CTF_NOTIF_ITER_MEDIUM_STATUS_OK = 0, -}; - -/** - * CTF notification iterator API status code. - */ -enum bt_ctf_notif_iter_status { - /** - * End of file. - * - * The medium function called by the notification iterator - * function reached the end of the file. - */ - BT_CTF_NOTIF_ITER_STATUS_EOF = -4, - - /** - * There is no data available right now, try again later. - * - * Some condition resulted in the - * bt_ctf_notif_iter_medium_ops::request_bytes() user function not - * having access to any data now. You should retry calling the - * last called notification iterator function once the situation - * is resolved. - */ - BT_CTF_NOTIF_ITER_STATUS_AGAIN = -3, - - /** Invalid argument. */ - BT_CTF_NOTIF_ITER_STATUS_INVAL = -2, - - /** General error. */ - BT_CTF_NOTIF_ITER_STATUS_ERROR = -1, - - /** Everything okay. */ - BT_CTF_NOTIF_ITER_STATUS_OK = 0, -}; - -/** - * Medium operations. - * - * Those user functions are called by the notification iterator - * functions to request medium actions. - */ -struct bt_ctf_notif_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 notification 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 notification iterator functions. The - * returned buffer won't be modified by the notification - * 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: - * - * - #BT_CTF_NOTIF_ITER_MEDIUM_STATUS_OK: 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. - * - #BT_CTF_NOTIF_ITER_MEDIUM_STATUS_AGAIN: No data is - * available right now. In this case, the notification - * iterator function called by the user returns - * #BT_CTF_NOTIF_ITER_STATUS_AGAIN, and it is the user's - * responsibility to make sure enough data becomes available - * before calling the \em same notification iterator - * function again to continue the decoding process. - * - #BT_CTF_NOTIF_ITER_MEDIUM_STATUS_EOF: The end of - * the file was reached, and no more data will ever be - * available for this file. In this case, the notification - * iterator function called by the user returns - * #BT_CTF_NOTIF_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 - * #BT_CTF_NOTIF_ITER_MEDIUM_STATUS_EOF on the \em following - * call. - * - #BT_CTF_NOTIF_ITER_MEDIUM_STATUS_ERROR: A fatal - * error occured during this operation. In this case, the - * notification iterator function called by the user returns - * #BT_CTF_NOTIF_ITER_STATUS_ERROR. - * - * If #BT_CTF_NOTIF_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 bt_ctf_notif_iter_medium_status (* request_bytes)( - size_t request_sz, uint8_t **buffer_addr, - size_t *buffer_sz, 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 notification - * iterator. - * - * @param stream_class Stream class associated to the stream - * @param data User data - * @returns Stream instance (weak reference) or - * \c NULL on error - */ - struct bt_ctf_stream * (* get_stream)( - struct bt_ctf_stream_class *stream_class, void *data); -}; - -/** CTF notification iterator. */ -struct bt_ctf_notif_iter; - -// TODO: Replace by the real thing -enum bt_ctf_notif_iter_notif_type { - BT_CTF_NOTIF_ITER_NOTIF_NEW_PACKET, - BT_CTF_NOTIF_ITER_NOTIF_END_OF_PACKET, - BT_CTF_NOTIF_ITER_NOTIF_EVENT, -}; - -struct bt_ctf_notif_iter_notif { - enum bt_ctf_notif_iter_notif_type type; -}; - -struct bt_ctf_notif_iter_notif_new_packet { - struct bt_ctf_notif_iter_notif base; - struct bt_ctf_packet *packet; -}; - -struct bt_ctf_notif_iter_notif_end_of_packet { - struct bt_ctf_notif_iter_notif base; - struct bt_ctf_packet *packet; -}; - -struct bt_ctf_notif_iter_notif_event { - struct bt_ctf_notif_iter_notif base; - struct bt_ctf_event *event; -}; - -void bt_ctf_notif_iter_notif_destroy(void *notif); - -/** - * Creates a CTF notification 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 - * bt_ctf_notif_iter_medium_ops::request_bytes() - * at a time - * @param medops Medium operations - * @param medops_data User data (passed to medium operations) - * @param err_stream Error stream (can be \c NULL to disable) - * @returns New CTF notification iterator on - * success, or \c NULL on error - */ -struct bt_ctf_notif_iter *bt_ctf_notif_iter_create(struct bt_ctf_trace *trace, - size_t max_request_sz, struct bt_ctf_notif_iter_medium_ops medops, - void *medops_data, FILE *err_stream); - -/** - * Destroys a CTF notification iterator, freeing all internal resources. - * - * The registered trace's reference count is decremented. - * - * @param notif_iter CTF notification iterator - */ -void bt_ctf_notif_iter_destroy(struct bt_ctf_notif_iter *notif_iter); - -/** - * Resets the internal state of a CTF notification iterator. - * - * This function can be used when it is desired to seek to the beginning - * of another packet. It is expected that the next call to - * bt_ctf_notif_iter_medium_ops::request_bytes() made by this - * notification iterator will return the \em first bytes of a \em - * packet. - * - * @param notif_iter CTF notification iterator - */ -void bt_ctf_notif_iter_reset(struct bt_ctf_notif_iter *notif_iter); - -/** - * Returns the next notification from a CTF notification iterator. - * - * Upon successful completion, #BT_CTF_NOTIF_ITER_STATUS_OK is - * returned, and the next notification is written to \p notif. - * In this case, the caller is responsible for calling - * bt_notification_put() on the returned notification. - * - * If this function returns #BT_CTF_NOTIF_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 notif_iter CTF notification iterator - * @param notification Returned notification if the function's - * return value is #BT_CTF_NOTIF_ITER_STATUS_OK - * @returns One of #bt_ctf_notif_iter_status values - */ -enum bt_ctf_notif_iter_status bt_ctf_notif_iter_get_next_notification( - struct bt_ctf_notif_iter *notif_iter, - struct bt_ctf_notif_iter_notif **notification); - -#endif /* CTF_NOTIF_ITER_H */ diff --git a/ctf-reader-proto/ctf-notif-iter/print.h b/ctf-reader-proto/ctf-notif-iter/print.h deleted file mode 100644 index bb5c62a3..00000000 --- a/ctf-reader-proto/ctf-notif-iter/print.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef CTF_NOTIF_ITER_PRINT_H -#define CTF_NOTIF_ITER_PRINT_H - -/* - * Define PRINT_PREFIX and PRINT_ERR_STREAM, then include this file. - * - * Copyright (c) 2016 Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include - -#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_NOTIF_ITER_PRINT_H */ diff --git a/ctf-reader-proto/metadata-parsing/Makefile.am b/ctf-reader-proto/metadata-parsing/Makefile.am deleted file mode 100644 index 2195aff3..00000000 --- a/ctf-reader-proto/metadata-parsing/Makefile.am +++ /dev/null @@ -1,38 +0,0 @@ -AM_CPPFLAGS = $(CPPFLAGS) -I$(top_srcdir)/include -I$(srcdir) -AM_CFLAGS = $(PACKAGE_CFLAGS) -BUILT_SOURCES = ctf-parser.h ctf-parser.c ctf-lexer.c -AM_YFLAGS = -t -d -v - -noinst_LTLIBRARIES = libctf-parser.la libctf-ast.la - -noinst_HEADERS = \ - ctf-scanner.h \ - ctf-ast.h \ - ctf-scanner-symbols.h - -libctf_parser_la_SOURCES = ctf-lexer.l ctf-parser.y objstack.c -# ctf-scanner-symbols.h is included to prefix generated yy_* symbols -# with bt_. -libctf_parser_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir) \ - -include $(srcdir)/ctf-scanner-symbols.h - -libctf_ast_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(builddir) -libctf_ast_la_SOURCES = \ - ctf-visitor-generate-ir.c \ - ctf-visitor-semantic-validator.c \ - ctf-visitor-parent-links.c -libctf_ast_la_LIBADD = $(top_builddir)/lib/libbabeltrace.la - -if BABELTRACE_BUILD_WITH_LIBUUID -libctf_ast_la_LIBADD += -luuid -endif - -if BABELTRACE_BUILD_WITH_LIBC_UUID -libctf_ast_la_LIBADD += -lc -endif - -if BABELTRACE_BUILD_WITH_MINGW -libctf_ast_la_LIBADD += -lrpcrt4 -lintl -liconv -lole32 -lpopt -endif - -CLEANFILES = $(BUILT_SOURCES) ctf-parser.output diff --git a/ctf-reader-proto/metadata-parsing/ctf-ast.h b/ctf-reader-proto/metadata-parsing/ctf-ast.h deleted file mode 100644 index c8843270..00000000 --- a/ctf-reader-proto/metadata-parsing/ctf-ast.h +++ /dev/null @@ -1,321 +0,0 @@ -#ifndef _CTF_AST_H -#define _CTF_AST_H - -/* - * ctf-ast.h - * - * Copyright 2011-2012 - Mathieu Desnoyers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - */ - -#include -#include -#include -#include -#include - -// 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; - -#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, typedef, - * typealias and type_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, typedef, - * typealias and type_specifier_list. - */ - struct bt_list_head declaration_list; - } event; - struct { - /* - * Children nodes are ctf_expression, typedef, - * typealias and type_specifier_list. - */ - struct bt_list_head declaration_list; - } stream; - struct { - /* - * Children nodes are ctf_expression, typedef, - * typealias and type_specifier_list. - */ - struct bt_list_head declaration_list; - } env; - struct { - /* - * Children nodes are ctf_expression, typedef, - * typealias and type_specifier_list. - */ - struct bt_list_head declaration_list; - } trace; - struct { - /* - * Children nodes are ctf_expression, typedef, - * typealias and type_specifier_list. - */ - struct bt_list_head declaration_list; - } clock; - struct { - /* - * Children nodes are ctf_expression, typedef, - * typealias and type_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 *type_specifier_list; - struct bt_list_head type_declarators; - } _typedef; - /* new type is "alias", existing type "target" */ - struct { - struct ctf_node *type_specifier_list; - struct bt_list_head type_declarators; - } typealias_target; - struct { - struct ctf_node *type_specifier_list; - struct bt_list_head type_declarators; - } typealias_alias; - struct { - struct ctf_node *target; - struct ctf_node *alias; - } typealias; - 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; - } type_specifier; - struct { - /* list of type_specifier */ - struct bt_list_head head; - } type_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 *type_declarator; - /* - * unary expression (value) or - * type_specifier_list. - */ - struct bt_list_head length; - /* for abstract type declarator */ - unsigned int abstract_array; - } nested; - } u; - struct ctf_node *bitfield_len; - } type_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 - * type_specifier_list. - */ - struct ctf_node *container_type; - struct bt_list_head enumerator_list; - int has_body; - } _enum; - struct { - struct ctf_node *type_specifier_list; - struct bt_list_head type_declarators; - } struct_or_variant_declaration; - struct { - char *name; - char *choice; - /* list of typedef, typealias and declarations */ - struct bt_list_head declaration_list; - int has_body; - } variant; - struct { - char *name; - /* list of typedef, typealias 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 ctf_trace; - -BT_HIDDEN -int ctf_visitor_generate_ir(FILE *efd, struct ctf_node *node, - struct bt_ctf_trace **trace); - -BT_HIDDEN -int ctf_visitor_semantic_check(FILE *fd, int depth, struct ctf_node *node); - -BT_HIDDEN -int ctf_visitor_parent_links(FILE *fd, int depth, struct ctf_node *node); - -BT_HIDDEN -int ctf_destroy_metadata(struct ctf_trace *trace); - -#endif /* _CTF_AST_H */ diff --git a/ctf-reader-proto/metadata-parsing/ctf-lexer.l b/ctf-reader-proto/metadata-parsing/ctf-lexer.l deleted file mode 100644 index 6b605a0f..00000000 --- a/ctf-reader-proto/metadata-parsing/ctf-lexer.l +++ /dev/null @@ -1,142 +0,0 @@ -%{ -/* - * ctf-lexer.l - * - * Common Trace Formal Lexer - * - * Copyright 2010 - Mathieu Desnoyers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include "ctf-scanner.h" -#include "ctf-parser.h" -#include "ctf-ast.h" - -#define PARSE_INTEGER_LITERAL(base) \ - do { \ - errno = 0; \ - yylval->ull = strtoull(yytext, NULL, base); \ - if (errno) { \ - printfl_perror(yylineno, "Integer literal"); \ - return ERROR; \ - } \ - } while (0) - -BT_HIDDEN -void setstring(struct ctf_scanner *scanner, YYSTYPE *lvalp, const char *src); - -static void yyunput (int c, register char * yy_bp , yyscan_t yyscanner) - __attribute__((unused)); -static int input (yyscan_t yyscanner) __attribute__((unused)); - -BT_HIDDEN -int import_string(struct ctf_scanner *scanner, YYSTYPE *lvalp, const char *src, char delim); - -%} - -%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); -[^*\n]* /* eat anything that's not a '*' */ -"*"+[^*/\n]* /* eat up '*'s not followed by '/'s */ -\n -"*"+"/" BEGIN(INITIAL); - -"//"[^\n]*\n /* skip comment */ - -L?\"(\\.|[^\\"])*\" { if (import_string(yyextra, yylval, yytext, '\"') < 0) return ERROR; else return STRING_LITERAL; } -L?\'(\\.|[^\\'])*\' { if (import_string(yyextra, yylval, yytext, '\'') < 0) return ERROR; else return CHARACTER_LITERAL; } - -"[" return LSBRAC; -"]" return RSBRAC; -"(" return LPAREN; -")" return RPAREN; -"{" return LBRAC; -"}" return RBRAC; -"->" return RARROW; -"*" return STAR; -"+" return PLUS; -"-" return MINUS; -"<" return LT; -">" return GT; -:= return TYPEASSIGN; -: return COLON; -; return SEMICOLON; -"..." return DOTDOTDOT; -"." return DOT; -= return EQUAL; -"," return COMMA; -align setstring(yyextra, yylval, yytext); return TOK_ALIGN; -const setstring(yyextra, yylval, yytext); return CONST; -char setstring(yyextra, yylval, yytext); return CHAR; -clock setstring(yyextra, yylval, yytext); return CLOCK; -double setstring(yyextra, yylval, yytext); return DOUBLE; -enum setstring(yyextra, yylval, yytext); return ENUM; -env setstring(yyextra, yylval, yytext); return ENV; -event setstring(yyextra, yylval, yytext); return EVENT; -floating_point setstring(yyextra, yylval, yytext); return FLOATING_POINT; -float setstring(yyextra, yylval, yytext); return FLOAT; -integer setstring(yyextra, yylval, yytext); return INTEGER; -int setstring(yyextra, yylval, yytext); return INT; -long setstring(yyextra, yylval, yytext); return LONG; -short setstring(yyextra, yylval, yytext); return SHORT; -signed setstring(yyextra, yylval, yytext); return SIGNED; -stream setstring(yyextra, yylval, yytext); return STREAM; -string setstring(yyextra, yylval, yytext); return STRING; -struct setstring(yyextra, yylval, yytext); return STRUCT; -trace setstring(yyextra, yylval, yytext); return TRACE; -callsite setstring(yyextra, yylval, yytext); return CALLSITE; -typealias setstring(yyextra, yylval, yytext); return TYPEALIAS; -typedef setstring(yyextra, yylval, yytext); return TYPEDEF; -unsigned setstring(yyextra, yylval, yytext); return UNSIGNED; -variant setstring(yyextra, yylval, yytext); return VARIANT; -void setstring(yyextra, yylval, yytext); return VOID; -_Bool setstring(yyextra, yylval, yytext); return _BOOL; -_Complex setstring(yyextra, yylval, yytext); return _COMPLEX; -_Imaginary setstring(yyextra, yylval, yytext); return _IMAGINARY; -[1-9]{DIGIT}*{INTEGER_SUFFIX}? PARSE_INTEGER_LITERAL(10); return INTEGER_LITERAL; -0{OCTALDIGIT}*{INTEGER_SUFFIX}? PARSE_INTEGER_LITERAL(8); return INTEGER_LITERAL; -0[xX]{HEXDIGIT}+{INTEGER_SUFFIX}? PARSE_INTEGER_LITERAL(16); return INTEGER_LITERAL; - -{IDENTIFIER} printf_debug("\n", yytext); setstring(yyextra, yylval, yytext); if (is_type(yyextra, yytext)) return ID_TYPE; else return IDENTIFIER; -[ \t\r\n] ; /* ignore */ -. printfl_error(yylineno, "invalid character '0x%02X'", yytext[0]); return ERROR; -%% diff --git a/ctf-reader-proto/metadata-parsing/ctf-parser.y b/ctf-reader-proto/metadata-parsing/ctf-parser.y deleted file mode 100644 index 821f0f74..00000000 --- a/ctf-reader-proto/metadata-parsing/ctf-parser.y +++ /dev/null @@ -1,2586 +0,0 @@ -%{ -/* - * ctf-parser.y - * - * Common Trace Format Metadata Grammar. - * - * Copyright 2010 - Mathieu Desnoyers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "ctf-scanner.h" -#include "ctf-parser.h" -#include "ctf-ast.h" -#include "objstack.h" - -BT_HIDDEN -int yydebug; - -/* 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 yyparse(struct ctf_scanner *scanner, yyscan_t yyscanner); -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; - } - } - assert(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 - printfl_error(yyget_lineno(scanner), - "Wide string not supported yet."); - 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->types = 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->types); -} - -static void push_scope(struct ctf_scanner *scanner) -{ - struct ctf_scanner_scope *ns; - - printf_debug("push scope\n"); - 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; - - printf_debug("pop scope\n"); - 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 = (int) (long) g_hash_table_lookup(s->types, id); - printf_debug("lookup %p %s %d\n", 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 != NULL; it = it->parent) { - if (lookup_type(it, id)) { - ret = 1; - break; - } - } - printf_debug("is type %s %d\n", id, ret); - return ret; -} - -static void add_type(struct ctf_scanner *scanner, char *id) -{ - printf_debug("add type %s\n", id); - if (lookup_type(scanner->cs, id)) - return; - g_hash_table_insert(scanner->cs->types, 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) { - printfl_fatal(yyget_lineno(scanner->scanner), "out of memory"); - 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; - printfn_fatal(node, "trying to create root node"); - 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._typedef.type_declarators); - break; - case NODE_TYPEALIAS_TARGET: - BT_INIT_LIST_HEAD(&node->u.typealias_target.type_declarators); - break; - case NODE_TYPEALIAS_ALIAS: - BT_INIT_LIST_HEAD(&node->u.typealias_alias.type_declarators); - break; - case NODE_TYPEALIAS: - break; - - case NODE_TYPE_SPECIFIER: - break; - case NODE_TYPE_SPECIFIER_LIST: - BT_INIT_LIST_HEAD(&node->u.type_specifier_list.head); - break; - case NODE_POINTER: - break; - case NODE_TYPE_DECLARATOR: - BT_INIT_LIST_HEAD(&node->u.type_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.type_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; - printfn_fatal(node, "unknown node type '%d'", (int) 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: - printfn_fatal(node, "unknown node type '%d'", (int) 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: - printfn_fatal(node, "unknown node type %d", parent->type); - return -EINVAL; - } - return 0; -} - -static int reparent_typealias(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: - printfn_fatal(node, "unknown node type '%d'", (int) parent->type); - return -EINVAL; - } - return 0; -} - -static int reparent_type_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.type_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: - printfn_fatal(node, "unknown node type '%d'", (int) parent->type); - return -EINVAL; - } - return 0; -} - -static int reparent_type_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._typedef.type_specifier_list = node; - break; - case NODE_TYPEALIAS_TARGET: - parent->u.typealias_target.type_specifier_list = node; - break; - case NODE_TYPEALIAS_ALIAS: - parent->u.typealias_alias.type_specifier_list = node; - break; - case NODE_ENUM: - parent->u._enum.container_type = node; - break; - case NODE_STRUCT_OR_VARIANT_DECLARATION: - parent->u.struct_or_variant_declaration.type_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: - printfn_fatal(node, "unknown node type '%d'", (int) parent->type); - return -EINVAL; - } - return 0; -} - -static int reparent_type_declarator(struct ctf_node *node, - struct ctf_node *parent) -{ - switch (parent->type) { - case NODE_TYPE_DECLARATOR: - parent->u.type_declarator.type = TYPEDEC_NESTED; - parent->u.type_declarator.u.nested.type_declarator = node; - break; - case NODE_STRUCT_OR_VARIANT_DECLARATION: - _bt_list_splice_tail(&node->tmp_head, &parent->u.struct_or_variant_declaration.type_declarators); - break; - case NODE_TYPEDEF: - _bt_list_splice_tail(&node->tmp_head, &parent->u._typedef.type_declarators); - break; - case NODE_TYPEALIAS_TARGET: - _bt_list_splice_tail(&node->tmp_head, &parent->u.typealias_target.type_declarators); - break; - case NODE_TYPEALIAS_ALIAS: - _bt_list_splice_tail(&node->tmp_head, &parent->u.typealias_alias.type_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: - printfn_fatal(node, "unknown node type '%d'", (int) 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: - printfn_fatal(node, "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.type_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.typealias.target = node; - else - return -EINVAL; - case NODE_TYPEALIAS_ALIAS: - if (parent->type == NODE_TYPEALIAS) - parent->u.typealias.alias = node; - else - return -EINVAL; - case NODE_TYPEALIAS: - return reparent_typealias(node, parent); - - case NODE_POINTER: - if (parent->type == NODE_TYPE_DECLARATOR) { - _bt_list_splice_tail(&node->tmp_head, &parent->u.type_declarator.pointers); - } else - return -EPERM; - break; - case NODE_TYPE_DECLARATOR: - return reparent_type_declarator(node, parent); - - case NODE_TYPE_SPECIFIER_LIST: - return reparent_type_specifier_list(node, parent); - - case NODE_TYPE_SPECIFIER: - return reparent_type_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: - printfn_fatal(node, "unknown node type '%d'", (int) parent->type); - return -EINVAL; - } - return 0; -} - -BT_HIDDEN -void yyerror(struct ctf_scanner *scanner, yyscan_t yyscanner, const char *str) -{ - printfl_error(yyget_lineno(scanner->scanner), - "token \"%s\": %s\n", - yyget_text(scanner->scanner), str); -} - -BT_HIDDEN -int yywrap(void) -{ - return 1; -} - -#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); - if (yydebug) - fprintf(stdout, "Scanner input is a%s.\n", - isatty(fileno(input)) ? "n interactive tty" : - " noninteractive file"); - return yyparse(scanner, scanner->scanner); -} - -struct ctf_scanner *ctf_scanner_alloc(void) -{ - struct ctf_scanner *scanner; - int ret; - - yydebug = babeltrace_debug; - - scanner = malloc(sizeof(*scanner)); - if (!scanner) - return NULL; - memset(scanner, 0, sizeof(*scanner)); - ret = yylex_init_extra(scanner, &scanner->scanner); - if (ret) { - printf_fatal("yylex_init error"); - 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) - printf_fatal("yylex_destroy error"); -cleanup_scanner: - free(scanner); - return NULL; -} - -void ctf_scanner_free(struct ctf_scanner *scanner) -{ - int ret; - - if (!scanner) - return; - finalize_scope(&scanner->root_scope); - objstack_destroy(scanner->objstack); - ret = yylex_destroy(scanner->scanner); - if (ret) - printf_error("yylex_destroy error"); - free(scanner); -} - -%} - -%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 INTEGER_LITERAL STRING_LITERAL CHARACTER_LITERAL LSBRAC RSBRAC LPAREN RPAREN LBRAC RBRAC RARROW STAR PLUS MINUS LT GT TYPEASSIGN COLON SEMICOLON DOTDOTDOT DOT EQUAL COMMA CONST CHAR DOUBLE ENUM ENV EVENT FLOATING_POINT FLOAT INTEGER INT LONG SHORT SIGNED STREAM STRING STRUCT TRACE CALLSITE CLOCK TYPEALIAS TYPEDEF UNSIGNED VARIANT VOID _BOOL _COMPLEX _IMAGINARY TOK_ALIGN -%token IDENTIFIER ID_TYPE -%token ERROR -%union -{ - long long ll; - unsigned long long ull; - char c; - char *s; - struct ctf_node *n; -} - -%type STRING_LITERAL CHARACTER_LITERAL - -%type keywords - -%type INTEGER_LITERAL -%type postfix_expression unary_expression unary_expression_or_range - -%type declaration -%type event_declaration -%type stream_declaration -%type env_declaration -%type trace_declaration -%type clock_declaration -%type callsite_declaration -%type integer_declaration_specifiers -%type declaration_specifiers -%type alias_declaration_specifiers - -%type type_declarator_list -%type integer_type_specifier -%type type_specifier -%type struct_type_specifier -%type variant_type_specifier -%type enum_type_specifier -%type struct_or_variant_declaration_list -%type struct_or_variant_declaration -%type struct_or_variant_declarator_list -%type struct_or_variant_declarator -%type enumerator_list -%type enumerator -%type abstract_declarator_list -%type abstract_declarator -%type direct_abstract_declarator -%type alias_abstract_declarator_list -%type alias_abstract_declarator -%type direct_alias_abstract_declarator -%type declarator -%type direct_declarator -%type type_declarator -%type direct_type_declarator -%type pointer -%type ctf_assignment_expression_list -%type 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: - VOID - { $$ = yylval.s; } - | CHAR - { $$ = yylval.s; } - | SHORT - { $$ = yylval.s; } - | INT - { $$ = yylval.s; } - | LONG - { $$ = yylval.s; } - | FLOAT - { $$ = yylval.s; } - | DOUBLE - { $$ = yylval.s; } - | SIGNED - { $$ = yylval.s; } - | UNSIGNED - { $$ = yylval.s; } - | _BOOL - { $$ = yylval.s; } - | _COMPLEX - { $$ = yylval.s; } - | _IMAGINARY - { $$ = yylval.s; } - | FLOATING_POINT - { $$ = yylval.s; } - | INTEGER - { $$ = yylval.s; } - | STRING - { $$ = yylval.s; } - | ENUM - { $$ = yylval.s; } - | VARIANT - { $$ = yylval.s; } - | STRUCT - { $$ = yylval.s; } - | CONST - { $$ = yylval.s; } - | TYPEDEF - { $$ = yylval.s; } - | EVENT - { $$ = yylval.s; } - | STREAM - { $$ = yylval.s; } - | ENV - { $$ = yylval.s; } - | TRACE - { $$ = yylval.s; } - | CLOCK - { $$ = yylval.s; } - | CALLSITE - { $$ = yylval.s; } - | 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; - } - | INTEGER_LITERAL - { - $$ = make_node(scanner, NODE_UNARY_EXPRESSION); - $$->u.unary_expression.type = UNARY_UNSIGNED_CONSTANT; - $$->u.unary_expression.u.unsigned_constant = $1; - } - | STRING_LITERAL - { - $$ = make_node(scanner, NODE_UNARY_EXPRESSION); - $$->u.unary_expression.type = UNARY_STRING; - $$->u.unary_expression.u.string = $1; - } - | CHARACTER_LITERAL - { - $$ = make_node(scanner, NODE_UNARY_EXPRESSION); - $$->u.unary_expression.type = UNARY_STRING; - $$->u.unary_expression.u.string = $1; - } - | LPAREN unary_expression RPAREN - { - $$ = $2; - } - | postfix_expression LSBRAC unary_expression 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 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 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 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 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 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; } - | 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"); - } - } - | 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_UNSIGNED_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 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 SEMICOLON - { $$ = $1; } - | event_declaration - { $$ = $1; } - | stream_declaration - { $$ = $1; } - | env_declaration - { $$ = $1; } - | trace_declaration - { $$ = $1; } - | clock_declaration - { $$ = $1; } - | callsite_declaration - { $$ = $1; } - | declaration_specifiers TYPEDEF declaration_specifiers type_declarator_list SEMICOLON - { - struct ctf_node *list; - - $$ = make_node(scanner, NODE_TYPEDEF); - list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - $$->u._typedef.type_specifier_list = list; - _bt_list_splice_tail(&($1)->u.type_specifier_list.head, &list->u.type_specifier_list.head); - _bt_list_splice_tail(&($3)->u.type_specifier_list.head, &list->u.type_specifier_list.head); - _bt_list_splice_tail(&($4)->tmp_head, &($$)->u._typedef.type_declarators); - } - | TYPEDEF declaration_specifiers type_declarator_list SEMICOLON - { - struct ctf_node *list; - - $$ = make_node(scanner, NODE_TYPEDEF); - list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - $$->u._typedef.type_specifier_list = list; - _bt_list_splice_tail(&($2)->u.type_specifier_list.head, &list->u.type_specifier_list.head); - _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._typedef.type_declarators); - } - | declaration_specifiers TYPEDEF type_declarator_list SEMICOLON - { - struct ctf_node *list; - - $$ = make_node(scanner, NODE_TYPEDEF); - list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - $$->u._typedef.type_specifier_list = list; - _bt_list_splice_tail(&($1)->u.type_specifier_list.head, &list->u.type_specifier_list.head); - _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._typedef.type_declarators); - } - | TYPEALIAS declaration_specifiers abstract_declarator_list TYPEASSIGN alias_declaration_specifiers alias_abstract_declarator_list SEMICOLON - { - struct ctf_node *list; - - $$ = make_node(scanner, NODE_TYPEALIAS); - $$->u.typealias.target = make_node(scanner, NODE_TYPEALIAS_TARGET); - $$->u.typealias.alias = make_node(scanner, NODE_TYPEALIAS_ALIAS); - - list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - $$->u.typealias.target->u.typealias_target.type_specifier_list = list; - _bt_list_splice_tail(&($2)->u.type_specifier_list.head, &list->u.type_specifier_list.head); - _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.typealias.target->u.typealias_target.type_declarators); - - list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - $$->u.typealias.alias->u.typealias_alias.type_specifier_list = list; - _bt_list_splice_tail(&($5)->u.type_specifier_list.head, &list->u.type_specifier_list.head); - _bt_list_splice_tail(&($6)->tmp_head, &($$)->u.typealias.alias->u.typealias_alias.type_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: - EVENT LBRAC - { push_scope(scanner); } - ; - -event_declaration_end: - RBRAC 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: - STREAM LBRAC - { push_scope(scanner); } - ; - -stream_declaration_end: - RBRAC 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: - ENV LBRAC - { push_scope(scanner); } - ; - -env_declaration_end: - RBRAC 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: - TRACE LBRAC - { push_scope(scanner); } - ; - -trace_declaration_end: - RBRAC SEMICOLON - { pop_scope(scanner); } - ; - -clock_declaration: - CLOCK clock_declaration_begin clock_declaration_end - { - $$ = make_node(scanner, NODE_CLOCK); - } - | 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: - LBRAC - { push_scope(scanner); } - ; - -clock_declaration_end: - RBRAC SEMICOLON - { pop_scope(scanner); } - ; - -callsite_declaration: - CALLSITE callsite_declaration_begin callsite_declaration_end - { - $$ = make_node(scanner, NODE_CALLSITE); - } - | 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: - LBRAC - { push_scope(scanner); } - ; - -callsite_declaration_end: - RBRAC SEMICOLON - { pop_scope(scanner); } - ; - -integer_declaration_specifiers: - CONST - { - struct ctf_node *node; - - $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - node = make_node(scanner, NODE_TYPE_SPECIFIER); - node->u.type_specifier.type = TYPESPEC_CONST; - bt_list_add_tail(&node->siblings, &($$)->u.type_specifier_list.head); - } - | integer_type_specifier - { - struct ctf_node *node; - - $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - node = $1; - bt_list_add_tail(&node->siblings, &($$)->u.type_specifier_list.head); - } - | integer_declaration_specifiers CONST - { - struct ctf_node *node; - - $$ = $1; - node = make_node(scanner, NODE_TYPE_SPECIFIER); - node->u.type_specifier.type = TYPESPEC_CONST; - bt_list_add_tail(&node->siblings, &($$)->u.type_specifier_list.head); - } - | integer_declaration_specifiers integer_type_specifier - { - $$ = $1; - bt_list_add_tail(&($2)->siblings, &($$)->u.type_specifier_list.head); - } - ; - -declaration_specifiers: - CONST - { - struct ctf_node *node; - - $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - node = make_node(scanner, NODE_TYPE_SPECIFIER); - node->u.type_specifier.type = TYPESPEC_CONST; - bt_list_add_tail(&node->siblings, &($$)->u.type_specifier_list.head); - } - | type_specifier - { - struct ctf_node *node; - - $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - node = $1; - bt_list_add_tail(&node->siblings, &($$)->u.type_specifier_list.head); - } - | declaration_specifiers CONST - { - struct ctf_node *node; - - $$ = $1; - node = make_node(scanner, NODE_TYPE_SPECIFIER); - node->u.type_specifier.type = TYPESPEC_CONST; - bt_list_add_tail(&node->siblings, &($$)->u.type_specifier_list.head); - } - | declaration_specifiers type_specifier - { - $$ = $1; - bt_list_add_tail(&($2)->siblings, &($$)->u.type_specifier_list.head); - } - ; - -type_declarator_list: - type_declarator - { $$ = $1; } - | type_declarator_list COMMA type_declarator - { - $$ = $1; - bt_list_add_tail(&($3)->siblings, &($$)->tmp_head); - } - ; - -integer_type_specifier: - CHAR - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.type_specifier.type = TYPESPEC_CHAR; - } - | SHORT - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.type_specifier.type = TYPESPEC_SHORT; - } - | INT - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.type_specifier.type = TYPESPEC_INT; - } - | LONG - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.type_specifier.type = TYPESPEC_LONG; - } - | SIGNED - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.type_specifier.type = TYPESPEC_SIGNED; - } - | UNSIGNED - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.type_specifier.type = TYPESPEC_UNSIGNED; - } - | _BOOL - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.type_specifier.type = TYPESPEC_BOOL; - } - | ID_TYPE - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.type_specifier.type = TYPESPEC_ID_TYPE; - $$->u.type_specifier.id_type = yylval.s; - } - | INTEGER LBRAC RBRAC - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.type_specifier.type = TYPESPEC_INTEGER; - $$->u.type_specifier.node = make_node(scanner, NODE_INTEGER); - } - | INTEGER LBRAC ctf_assignment_expression_list RBRAC - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.type_specifier.type = TYPESPEC_INTEGER; - $$->u.type_specifier.node = make_node(scanner, NODE_INTEGER); - if (set_parent_node($3, $$->u.type_specifier.node)) - reparent_error(scanner, "integer reparent error"); - } - ; - -type_specifier: - VOID - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.type_specifier.type = TYPESPEC_VOID; - } - | CHAR - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.type_specifier.type = TYPESPEC_CHAR; - } - | SHORT - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.type_specifier.type = TYPESPEC_SHORT; - } - | INT - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.type_specifier.type = TYPESPEC_INT; - } - | LONG - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.type_specifier.type = TYPESPEC_LONG; - } - | FLOAT - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.type_specifier.type = TYPESPEC_FLOAT; - } - | DOUBLE - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.type_specifier.type = TYPESPEC_DOUBLE; - } - | SIGNED - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.type_specifier.type = TYPESPEC_SIGNED; - } - | UNSIGNED - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.type_specifier.type = TYPESPEC_UNSIGNED; - } - | _BOOL - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.type_specifier.type = TYPESPEC_BOOL; - } - | _COMPLEX - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.type_specifier.type = TYPESPEC_COMPLEX; - } - | _IMAGINARY - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.type_specifier.type = TYPESPEC_IMAGINARY; - } - | ID_TYPE - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.type_specifier.type = TYPESPEC_ID_TYPE; - $$->u.type_specifier.id_type = yylval.s; - } - | FLOATING_POINT LBRAC RBRAC - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.type_specifier.type = TYPESPEC_FLOATING_POINT; - $$->u.type_specifier.node = make_node(scanner, NODE_FLOATING_POINT); - } - | FLOATING_POINT LBRAC ctf_assignment_expression_list RBRAC - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.type_specifier.type = TYPESPEC_FLOATING_POINT; - $$->u.type_specifier.node = make_node(scanner, NODE_FLOATING_POINT); - if (set_parent_node($3, $$->u.type_specifier.node)) - reparent_error(scanner, "floating point reparent error"); - } - | INTEGER LBRAC RBRAC - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.type_specifier.type = TYPESPEC_INTEGER; - $$->u.type_specifier.node = make_node(scanner, NODE_INTEGER); - } - | INTEGER LBRAC ctf_assignment_expression_list RBRAC - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.type_specifier.type = TYPESPEC_INTEGER; - $$->u.type_specifier.node = make_node(scanner, NODE_INTEGER); - if (set_parent_node($3, $$->u.type_specifier.node)) - reparent_error(scanner, "integer reparent error"); - } - | STRING - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.type_specifier.type = TYPESPEC_STRING; - $$->u.type_specifier.node = make_node(scanner, NODE_STRING); - } - | STRING LBRAC RBRAC - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.type_specifier.type = TYPESPEC_STRING; - $$->u.type_specifier.node = make_node(scanner, NODE_STRING); - } - | STRING LBRAC ctf_assignment_expression_list RBRAC - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.type_specifier.type = TYPESPEC_STRING; - $$->u.type_specifier.node = make_node(scanner, NODE_STRING); - if (set_parent_node($3, $$->u.type_specifier.node)) - reparent_error(scanner, "string reparent error"); - } - | ENUM enum_type_specifier - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.type_specifier.type = TYPESPEC_ENUM; - $$->u.type_specifier.node = $2; - } - | VARIANT variant_type_specifier - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.type_specifier.type = TYPESPEC_VARIANT; - $$->u.type_specifier.node = $2; - } - | STRUCT struct_type_specifier - { - $$ = make_node(scanner, NODE_TYPE_SPECIFIER); - $$->u.type_specifier.type = TYPESPEC_STRUCT; - $$->u.type_specifier.node = $2; - } - ; - -struct_type_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 TOK_ALIGN LPAREN unary_expression 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 TOK_ALIGN LPAREN unary_expression 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 TOK_ALIGN LPAREN unary_expression 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: - LBRAC - { push_scope(scanner); } - ; - -struct_declaration_end: - RBRAC - { pop_scope(scanner); } - ; - -variant_type_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"); - } - | LT IDENTIFIER 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"); - } - | LT ID_TYPE 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 LT IDENTIFIER 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 LT IDENTIFIER GT - { - $$ = make_node(scanner, NODE_VARIANT); - $$->u.variant.has_body = 0; - $$->u.variant.name = $1; - $$->u.variant.choice = $3; - } - | IDENTIFIER LT ID_TYPE 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 LT ID_TYPE 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 LT IDENTIFIER 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 LT IDENTIFIER GT - { - $$ = make_node(scanner, NODE_VARIANT); - $$->u.variant.has_body = 0; - $$->u.variant.name = $1; - $$->u.variant.choice = $3; - } - | ID_TYPE LT ID_TYPE 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 LT ID_TYPE GT - { - $$ = make_node(scanner, NODE_VARIANT); - $$->u.variant.has_body = 0; - $$->u.variant.name = $1; - $$->u.variant.choice = $3; - } - ; - -variant_declaration_begin: - LBRAC - { push_scope(scanner); } - ; - -variant_declaration_end: - RBRAC - { pop_scope(scanner); } - ; - -enum_type_specifier: - LBRAC enumerator_list RBRAC - { - $$ = make_node(scanner, NODE_ENUM); - $$->u._enum.has_body = 1; - _bt_list_splice_tail(&($2)->tmp_head, &($$)->u._enum.enumerator_list); - } - | COLON integer_declaration_specifiers LBRAC enumerator_list RBRAC - { - $$ = make_node(scanner, NODE_ENUM); - $$->u._enum.has_body = 1; - ($$)->u._enum.container_type = $2; - _bt_list_splice_tail(&($4)->tmp_head, &($$)->u._enum.enumerator_list); - } - | IDENTIFIER LBRAC enumerator_list 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 COLON integer_declaration_specifiers LBRAC enumerator_list RBRAC - { - $$ = make_node(scanner, NODE_ENUM); - $$->u._enum.has_body = 1; - $$->u._enum.enum_id = $1; - ($$)->u._enum.container_type = $3; - _bt_list_splice_tail(&($5)->tmp_head, &($$)->u._enum.enumerator_list); - } - | ID_TYPE LBRAC enumerator_list 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 COLON integer_declaration_specifiers LBRAC enumerator_list RBRAC - { - $$ = make_node(scanner, NODE_ENUM); - $$->u._enum.has_body = 1; - $$->u._enum.enum_id = $1; - ($$)->u._enum.container_type = $3; - _bt_list_splice_tail(&($5)->tmp_head, &($$)->u._enum.enumerator_list); - } - | LBRAC enumerator_list COMMA RBRAC - { - $$ = make_node(scanner, NODE_ENUM); - $$->u._enum.has_body = 1; - _bt_list_splice_tail(&($2)->tmp_head, &($$)->u._enum.enumerator_list); - } - | COLON integer_declaration_specifiers LBRAC enumerator_list COMMA RBRAC - { - $$ = make_node(scanner, NODE_ENUM); - $$->u._enum.has_body = 1; - ($$)->u._enum.container_type = $2; - _bt_list_splice_tail(&($4)->tmp_head, &($$)->u._enum.enumerator_list); - } - | IDENTIFIER LBRAC enumerator_list COMMA 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 COLON integer_declaration_specifiers LBRAC enumerator_list COMMA RBRAC - { - $$ = make_node(scanner, NODE_ENUM); - $$->u._enum.has_body = 1; - $$->u._enum.enum_id = $1; - ($$)->u._enum.container_type = $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 LBRAC enumerator_list COMMA 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 COLON integer_declaration_specifiers LBRAC enumerator_list COMMA RBRAC - { - $$ = make_node(scanner, NODE_ENUM); - $$->u._enum.has_body = 1; - $$->u._enum.enum_id = $1; - ($$)->u._enum.container_type = $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 SEMICOLON - { - struct ctf_node *list; - - list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - _bt_list_splice_tail(&($1)->u.type_specifier_list.head, &list->u.type_specifier_list.head); - $$ = make_node(scanner, NODE_STRUCT_OR_VARIANT_DECLARATION); - ($$)->u.struct_or_variant_declaration.type_specifier_list = list; - _bt_list_splice_tail(&($2)->tmp_head, &($$)->u.struct_or_variant_declaration.type_declarators); - } - | declaration_specifiers TYPEDEF declaration_specifiers type_declarator_list SEMICOLON - { - struct ctf_node *list; - - $$ = make_node(scanner, NODE_TYPEDEF); - list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - $$->u._typedef.type_specifier_list = list; - _bt_list_splice_tail(&($1)->u.type_specifier_list.head, &list->u.type_specifier_list.head); - _bt_list_splice_tail(&($3)->u.type_specifier_list.head, &list->u.type_specifier_list.head); - _bt_list_splice_tail(&($4)->tmp_head, &($$)->u._typedef.type_declarators); - } - | TYPEDEF declaration_specifiers type_declarator_list SEMICOLON - { - struct ctf_node *list; - - $$ = make_node(scanner, NODE_TYPEDEF); - list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - $$->u._typedef.type_specifier_list = list; - _bt_list_splice_tail(&($2)->u.type_specifier_list.head, &list->u.type_specifier_list.head); - _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._typedef.type_declarators); - } - | declaration_specifiers TYPEDEF type_declarator_list SEMICOLON - { - struct ctf_node *list; - - list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - _bt_list_splice_tail(&($1)->u.type_specifier_list.head, &list->u.type_specifier_list.head); - $$ = make_node(scanner, NODE_TYPEDEF); - ($$)->u.struct_or_variant_declaration.type_specifier_list = list; - _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._typedef.type_declarators); - } - | TYPEALIAS declaration_specifiers abstract_declarator_list TYPEASSIGN alias_declaration_specifiers alias_abstract_declarator_list SEMICOLON - { - struct ctf_node *list; - - $$ = make_node(scanner, NODE_TYPEALIAS); - $$->u.typealias.target = make_node(scanner, NODE_TYPEALIAS_TARGET); - $$->u.typealias.alias = make_node(scanner, NODE_TYPEALIAS_ALIAS); - - list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - $$->u.typealias.target->u.typealias_target.type_specifier_list = list; - _bt_list_splice_tail(&($2)->u.type_specifier_list.head, &list->u.type_specifier_list.head); - _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.typealias.target->u.typealias_target.type_declarators); - - list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - $$->u.typealias.alias->u.typealias_alias.type_specifier_list = list; - _bt_list_splice_tail(&($5)->u.type_specifier_list.head, &list->u.type_specifier_list.head); - _bt_list_splice_tail(&($6)->tmp_head, &($$)->u.typealias.alias->u.typealias_alias.type_declarators); - } - ; - -alias_declaration_specifiers: - CONST - { - struct ctf_node *node; - - $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - node = make_node(scanner, NODE_TYPE_SPECIFIER); - node->u.type_specifier.type = TYPESPEC_CONST; - bt_list_add_tail(&node->siblings, &($$)->u.type_specifier_list.head); - } - | type_specifier - { - struct ctf_node *node; - - $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - node = $1; - bt_list_add_tail(&node->siblings, &($$)->u.type_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.type_specifier.type = TYPESPEC_ID_TYPE; - node->u.type_specifier.id_type = yylval.s; - bt_list_add_tail(&node->siblings, &($$)->u.type_specifier_list.head); - } - | alias_declaration_specifiers CONST - { - struct ctf_node *node; - - $$ = $1; - node = make_node(scanner, NODE_TYPE_SPECIFIER); - node->u.type_specifier.type = TYPESPEC_CONST; - bt_list_add_tail(&node->siblings, &($$)->u.type_specifier_list.head); - } - | alias_declaration_specifiers type_specifier - { - $$ = $1; - bt_list_add_tail(&($2)->siblings, &($$)->u.type_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.type_specifier.type = TYPESPEC_ID_TYPE; - node->u.type_specifier.id_type = yylval.s; - bt_list_add_tail(&node->siblings, &($$)->u.type_specifier_list.head); - } - ; - -struct_or_variant_declarator_list: - struct_or_variant_declarator - { $$ = $1; } - | struct_or_variant_declarator_list COMMA struct_or_variant_declarator - { - $$ = $1; - bt_list_add_tail(&($3)->siblings, &($$)->tmp_head); - } - ; - -struct_or_variant_declarator: - declarator - { $$ = $1; } - | COLON unary_expression - { $$ = $2; } - | declarator COLON unary_expression - { - $$ = $1; - if (set_parent_node($3, $1)) - reparent_error(scanner, "struct_or_variant_declarator"); - } - ; - -enumerator_list: - enumerator - { $$ = $1; } - | enumerator_list 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; - } - | STRING_LITERAL - { - $$ = make_node(scanner, NODE_ENUMERATOR); - $$->u.enumerator.id = $1; - } - | IDENTIFIER 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 EQUAL unary_expression_or_range - { - $$ = make_node(scanner, NODE_ENUMERATOR); - $$->u.enumerator.id = $1; - bt_list_splice(&($3)->tmp_head, &($$)->u.enumerator.values); - } - | keywords EQUAL unary_expression_or_range - { - $$ = make_node(scanner, NODE_ENUMERATOR); - $$->u.enumerator.id = $1; - bt_list_splice(&($3)->tmp_head, &($$)->u.enumerator.values); - } - | STRING_LITERAL 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 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.type_declarator.pointers); - } - ; - -direct_abstract_declarator: - /* empty */ - { - $$ = make_node(scanner, NODE_TYPE_DECLARATOR); - $$->u.type_declarator.type = TYPEDEC_ID; - /* id is NULL */ - } - | IDENTIFIER - { - $$ = make_node(scanner, NODE_TYPE_DECLARATOR); - $$->u.type_declarator.type = TYPEDEC_ID; - $$->u.type_declarator.u.id = $1; - } - | LPAREN abstract_declarator RPAREN - { - $$ = make_node(scanner, NODE_TYPE_DECLARATOR); - $$->u.type_declarator.type = TYPEDEC_NESTED; - $$->u.type_declarator.u.nested.type_declarator = $2; - } - | direct_abstract_declarator LSBRAC unary_expression RSBRAC - { - $$ = make_node(scanner, NODE_TYPE_DECLARATOR); - $$->u.type_declarator.type = TYPEDEC_NESTED; - $$->u.type_declarator.u.nested.type_declarator = $1; - BT_INIT_LIST_HEAD(&($$)->u.type_declarator.u.nested.length); - _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.type_declarator.u.nested.length); - } - | direct_abstract_declarator LSBRAC RSBRAC - { - $$ = make_node(scanner, NODE_TYPE_DECLARATOR); - $$->u.type_declarator.type = TYPEDEC_NESTED; - $$->u.type_declarator.u.nested.type_declarator = $1; - $$->u.type_declarator.u.nested.abstract_array = 1; - } - ; - -alias_abstract_declarator_list: - alias_abstract_declarator - { $$ = $1; } - | alias_abstract_declarator_list 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.type_declarator.pointers); - } - ; - -direct_alias_abstract_declarator: - /* empty */ - { - $$ = make_node(scanner, NODE_TYPE_DECLARATOR); - $$->u.type_declarator.type = TYPEDEC_ID; - /* id is NULL */ - } - | LPAREN alias_abstract_declarator RPAREN - { - $$ = make_node(scanner, NODE_TYPE_DECLARATOR); - $$->u.type_declarator.type = TYPEDEC_NESTED; - $$->u.type_declarator.u.nested.type_declarator = $2; - } - | direct_alias_abstract_declarator LSBRAC unary_expression RSBRAC - { - $$ = make_node(scanner, NODE_TYPE_DECLARATOR); - $$->u.type_declarator.type = TYPEDEC_NESTED; - $$->u.type_declarator.u.nested.type_declarator = $1; - BT_INIT_LIST_HEAD(&($$)->u.type_declarator.u.nested.length); - _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.type_declarator.u.nested.length); - } - | direct_alias_abstract_declarator LSBRAC RSBRAC - { - $$ = make_node(scanner, NODE_TYPE_DECLARATOR); - $$->u.type_declarator.type = TYPEDEC_NESTED; - $$->u.type_declarator.u.nested.type_declarator = $1; - $$->u.type_declarator.u.nested.abstract_array = 1; - } - ; - -declarator: - direct_declarator - { $$ = $1; } - | pointer direct_declarator - { - $$ = $2; - bt_list_splice(&($1)->tmp_head, &($$)->u.type_declarator.pointers); - } - ; - -direct_declarator: - IDENTIFIER - { - $$ = make_node(scanner, NODE_TYPE_DECLARATOR); - $$->u.type_declarator.type = TYPEDEC_ID; - $$->u.type_declarator.u.id = $1; - } - | LPAREN declarator RPAREN - { - $$ = make_node(scanner, NODE_TYPE_DECLARATOR); - $$->u.type_declarator.type = TYPEDEC_NESTED; - $$->u.type_declarator.u.nested.type_declarator = $2; - } - | direct_declarator LSBRAC unary_expression RSBRAC - { - $$ = make_node(scanner, NODE_TYPE_DECLARATOR); - $$->u.type_declarator.type = TYPEDEC_NESTED; - $$->u.type_declarator.u.nested.type_declarator = $1; - BT_INIT_LIST_HEAD(&($$)->u.type_declarator.u.nested.length); - _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.type_declarator.u.nested.length); - } - ; - -type_declarator: - direct_type_declarator - { $$ = $1; } - | pointer direct_type_declarator - { - $$ = $2; - bt_list_splice(&($1)->tmp_head, &($$)->u.type_declarator.pointers); - } - ; - -direct_type_declarator: - IDENTIFIER - { - add_type(scanner, $1); - $$ = make_node(scanner, NODE_TYPE_DECLARATOR); - $$->u.type_declarator.type = TYPEDEC_ID; - $$->u.type_declarator.u.id = $1; - } - | LPAREN type_declarator RPAREN - { - $$ = make_node(scanner, NODE_TYPE_DECLARATOR); - $$->u.type_declarator.type = TYPEDEC_NESTED; - $$->u.type_declarator.u.nested.type_declarator = $2; - } - | direct_type_declarator LSBRAC unary_expression RSBRAC - { - $$ = make_node(scanner, NODE_TYPE_DECLARATOR); - $$->u.type_declarator.type = TYPEDEC_NESTED; - $$->u.type_declarator.u.nested.type_declarator = $1; - BT_INIT_LIST_HEAD(&($$)->u.type_declarator.u.nested.length); - _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.type_declarator.u.nested.length); - } - ; - -pointer: - STAR - { - $$ = make_node(scanner, NODE_POINTER); - } - | STAR pointer - { - $$ = make_node(scanner, NODE_POINTER); - bt_list_splice(&($2)->tmp_head, &($$)->tmp_head); - } - | 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 */ - CONST - | type_qualifier_list CONST - ; - -/* 2.3: CTF-specific declarations */ - -ctf_assignment_expression_list: - ctf_assignment_expression SEMICOLON - { $$ = $1; } - | ctf_assignment_expression_list ctf_assignment_expression SEMICOLON - { - $$ = $1; - bt_list_add_tail(&($2)->siblings, &($$)->tmp_head); - } - ; - -ctf_assignment_expression: - unary_expression 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 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 TYPEDEF declaration_specifiers type_declarator_list - { - struct ctf_node *list; - - list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - _bt_list_splice_tail(&($1)->u.type_specifier_list.head, &list->u.type_specifier_list.head); - _bt_list_splice_tail(&($3)->u.type_specifier_list.head, &list->u.type_specifier_list.head); - $$ = make_node(scanner, NODE_TYPEDEF); - ($$)->u.struct_or_variant_declaration.type_specifier_list = list; - _bt_list_splice_tail(&($4)->tmp_head, &($$)->u._typedef.type_declarators); - } - | TYPEDEF declaration_specifiers type_declarator_list - { - struct ctf_node *list; - - $$ = make_node(scanner, NODE_TYPEDEF); - list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - $$->u._typedef.type_specifier_list = list; - _bt_list_splice_tail(&($2)->u.type_specifier_list.head, &list->u.type_specifier_list.head); - _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._typedef.type_declarators); - } - | declaration_specifiers TYPEDEF type_declarator_list - { - struct ctf_node *list; - - list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - _bt_list_splice_tail(&($1)->u.type_specifier_list.head, &list->u.type_specifier_list.head); - $$ = make_node(scanner, NODE_TYPEDEF); - ($$)->u.struct_or_variant_declaration.type_specifier_list = list; - _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._typedef.type_declarators); - } - | TYPEALIAS declaration_specifiers abstract_declarator_list TYPEASSIGN alias_declaration_specifiers alias_abstract_declarator_list - { - struct ctf_node *list; - - $$ = make_node(scanner, NODE_TYPEALIAS); - $$->u.typealias.target = make_node(scanner, NODE_TYPEALIAS_TARGET); - $$->u.typealias.alias = make_node(scanner, NODE_TYPEALIAS_ALIAS); - - list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - $$->u.typealias.target->u.typealias_target.type_specifier_list = list; - _bt_list_splice_tail(&($2)->u.type_specifier_list.head, &list->u.type_specifier_list.head); - _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.typealias.target->u.typealias_target.type_declarators); - - list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); - $$->u.typealias.alias->u.typealias_alias.type_specifier_list = list; - _bt_list_splice_tail(&($5)->u.type_specifier_list.head, &list->u.type_specifier_list.head); - _bt_list_splice_tail(&($6)->tmp_head, &($$)->u.typealias.alias->u.typealias_alias.type_declarators); - } - ; diff --git a/ctf-reader-proto/metadata-parsing/ctf-scanner-symbols.h b/ctf-reader-proto/metadata-parsing/ctf-scanner-symbols.h deleted file mode 100644 index 9b9e3631..00000000 --- a/ctf-reader-proto/metadata-parsing/ctf-scanner-symbols.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef _CTF_SCANNER_SYMBOLS -#define _CTF_SCANNER_SYMBOLS - -/* - * ctf-scanner-symbols.h - * - * Copyright 2011-2012 - Mathieu Desnoyers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - */ - -#define yy_create_buffer bt_yy_create_buffer -#define yy_delete_buffer bt_yy_delete_buffer -#define yy_flush_buffer bt_yy_flush_buffer -#define yy_scan_buffer bt_yy_scan_buffer -#define yy_scan_bytes bt_yy_scan_bytes -#define yy_scan_string bt_yy_scan_string -#define yy_switch_to_buffer bt_yy_switch_to_buffer -#define yyalloc bt_yyalloc -#define yyfree bt_yyfree -#define yyget_column bt_yyget_column -#define yyget_debug bt_yyget_debug -#define yyget_extra bt_yyget_extra -#define yyget_in bt_yyget_in -#define yyget_leng bt_yyget_leng -#define yyget_lineno bt_yyget_lineno -#define yyget_lval bt_yyget_lval -#define yyget_out bt_yyget_out -#define yyget_text bt_yyget_text -#define yylex_init bt_yylex_init -#define yypop_buffer_state bt_yypop_buffer_state -#define yypush_buffer_state bt_yypush_buffer_state -#define yyrealloc bt_yyrealloc -#define yyset_column bt_yyset_column -#define yyset_debug bt_yyset_debug -#define yyset_extra bt_yyset_extra -#define yyset_in bt_yyset_in -#define yyset_lineno bt_yyset_lineno -#define yyset_lval bt_yyset_lval -#define yyset_out bt_yyset_out - -#endif /* _CTF_SCANNER_SYMBOLS */ diff --git a/ctf-reader-proto/metadata-parsing/ctf-scanner.h b/ctf-reader-proto/metadata-parsing/ctf-scanner.h deleted file mode 100644 index 51484a1d..00000000 --- a/ctf-reader-proto/metadata-parsing/ctf-scanner.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef _CTF_SCANNER_H -#define _CTF_SCANNER_H - -/* - * ctf-scanner.h - * - * Copyright 2011-2012 - Mathieu Desnoyers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - */ - -#include -#include "ctf-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 *types; -}; - -struct ctf_scanner { - yyscan_t scanner; - struct ctf_ast *ast; - struct ctf_scanner_scope root_scope; - struct ctf_scanner_scope *cs; - struct objstack *objstack; -}; - -struct ctf_scanner *ctf_scanner_alloc(void); -void ctf_scanner_free(struct ctf_scanner *scanner); -int ctf_scanner_append_ast(struct ctf_scanner *scanner, FILE *input); - -static inline -struct ctf_ast *ctf_scanner_get_ast(struct ctf_scanner *scanner) -{ - return scanner->ast; -} - -BT_HIDDEN -int is_type(struct ctf_scanner *scanner, const char *id); - -#endif /* _CTF_SCANNER_H */ diff --git a/ctf-reader-proto/metadata-parsing/ctf-visitor-generate-ir.c b/ctf-reader-proto/metadata-parsing/ctf-visitor-generate-ir.c deleted file mode 100644 index 3a28ca84..00000000 --- a/ctf-reader-proto/metadata-parsing/ctf-visitor-generate-ir.c +++ /dev/null @@ -1,4662 +0,0 @@ -/* - * ctf-visitor-generate-ir.c - * - * Common Trace Format metadata visitor (generates CTF IR objects). - * - * Based on older ctf-visitor-generate-io-struct.c. - * - * Copyright 2010 - Mathieu Desnoyers - * Copyright 2015-2016 - Philippe Proulx - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ctf-scanner.h" -#include "ctf-parser.h" -#include "ctf-ast.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)) - -/* 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_LOGLEVEL_SET = _BV(4), - _EVENT_CONTEXT_SET = _BV(5), - _EVENT_FIELDS_SET = _BV(6), -}; - -/* Prefixes of type 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, _type, _member) \ - bt_list_entry((_ptr)->next, _type, _member) - -#define _BT_CTF_FIELD_TYPE_INIT(_name) struct bt_ctf_field_type *_name = NULL; - -/* Error printing wrappers */ -#define _PERROR(_fmt, ...) \ - do { \ - fprintf(ctx->efd, "[error] %s: " _fmt "\n", \ - __func__, __VA_ARGS__); \ - } while (0) - -#define _PWARNING(_fmt, ...) \ - do { \ - fprintf(ctx->efd, "[warning] %s: " _fmt "\n", \ - __func__, __VA_ARGS__); \ - } while (0) - -#define _FPERROR(_stream, _fmt, ...) \ - do { \ - fprintf(_stream, "[error] %s: " _fmt "\n", \ - __func__, __VA_ARGS__); \ - } while (0) - -#define _FPWARNING(_stream, _fmt, ...) \ - do { \ - fprintf(_stream, "[warning] %s: " _fmt "\n", \ - __func__, __VA_ARGS__); \ - } while (0) - -#define _PERROR_DUP_ATTR(_attr, _entity) \ - do { \ - fprintf(ctx->efd, \ - "[error] %s: duplicate attribute \"" \ - _attr "\" in " _entity "\n", __func__); \ - } while (0) - -/* - * 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 type. - * - * GQuark -> struct bt_ctf_field_type * - */ - GHashTable *decl_map; - - /* Parent scope; NULL if this is the root declaration scope */ - struct ctx_decl_scope *parent_scope; -}; - -/* - * Visitor context. - */ -struct ctx { - /* Trace being filled (weak ref.) */ - struct bt_ctf_trace *trace; - - /* Error stream to use during visit */ - FILE *efd; - - /* Current declaration scope (top of the stack) */ - struct ctx_decl_scope *current_scope; - - /* 1 if trace declaration is visited */ - int is_trace_visited; - - /* Trace attributes */ - uint64_t trace_major; - uint64_t trace_minor; - unsigned char trace_uuid[BABELTRACE_UUID_LEN]; - - /* - * Stream IDs to stream classes. - * - * int64_t -> struct bt_ctf_stream_class * - */ - GHashTable *stream_classes; -}; - -/** - * 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 ctx_decl_scope *par_scope) -{ - struct ctx_decl_scope *scope; - - scope = g_new(struct ctx_decl_scope, 1); - if (!scope) { - goto end; - } - - scope->decl_map = g_hash_table_new_full(g_direct_hash, g_direct_equal, - NULL, (GDestroyNotify) bt_ctf_field_type_put); - 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(char prefix, const char *name) -{ - GQuark qname = 0; - - assert(name); - - /* Prefix character + original string + '\0' */ - char *prname = g_new(char, strlen(name) + 2); - if (!prname) { - goto end; - } - - sprintf(prname, "%c%s", prefix, name); - qname = g_quark_from_string(prname); - g_free(prname); - -end: - return qname; -} - -/** - * Looks up a prefixed type alias within a declaration scope. - * - * @param scope Declaration scope - * @param prefix Prefix character - * @param name Alias name - * @param level Number of levels to dig (-1 means infinite) - * @returns Declaration, or NULL if not found - */ -static -struct bt_ctf_field_type *ctx_decl_scope_lookup_prefix_alias( - struct ctx_decl_scope *scope, char prefix, - const char *name, int levels) -{ - GQuark qname = 0; - int cur_levels = 0; - _BT_CTF_FIELD_TYPE_INIT(decl); - struct ctx_decl_scope *cur_scope = scope; - - assert(scope); - assert(name); - qname = get_prefixed_named_quark(prefix, name); - if (!qname) { - goto error; - } - - if (levels < 0) { - levels = INT_MAX; - } - - while (cur_scope && cur_levels < levels) { - decl = g_hash_table_lookup(cur_scope->decl_map, - (gconstpointer) (unsigned long) qname); - if (decl) { - /* Caller's reference */ - bt_get(decl); - break; - } - - cur_scope = cur_scope->parent_scope; - cur_levels++; - } - - return decl; - -error: - return NULL; -} - -/** - * Looks up a type alias within a declaration scope. - * - * @param scope Declaration scope - * @param name Alias name - * @param level Number of levels to dig (-1 means infinite) - * @returns Declaration, or NULL if not found - */ -static -struct bt_ctf_field_type *ctx_decl_scope_lookup_alias( - struct ctx_decl_scope *scope, const char *name, int levels) -{ - return ctx_decl_scope_lookup_prefix_alias(scope, _PREFIX_ALIAS, - name, levels); -} - -/** - * Looks up an enumeration within a declaration scope. - * - * @param scope Declaration scope - * @param name Enumeration name - * @param level Number of levels to dig (-1 means infinite) - * @returns Declaration, or NULL if not found - */ -static -struct bt_ctf_field_type *ctx_decl_scope_lookup_enum( - struct ctx_decl_scope *scope, const char *name, int levels) -{ - return ctx_decl_scope_lookup_prefix_alias(scope, _PREFIX_ENUM, - name, levels); -} - -/** - * Looks up a structure within a declaration scope. - * - * @param scope Declaration scope - * @param name Structure name - * @param level Number of levels to dig (-1 means infinite) - * @returns Declaration, or NULL if not found - */ -static -struct bt_ctf_field_type *ctx_decl_scope_lookup_struct( - struct ctx_decl_scope *scope, const char *name, int levels) -{ - return ctx_decl_scope_lookup_prefix_alias(scope, _PREFIX_STRUCT, - name, levels); -} - -/** - * Looks up a variant within a declaration scope. - * - * @param scope Declaration scope - * @param name Variant name - * @param level Number of levels to dig (-1 means infinite) - * @returns Declaration, or NULL if not found - */ -static -struct bt_ctf_field_type *ctx_decl_scope_lookup_variant( - struct ctx_decl_scope *scope, const char *name, int levels) -{ - return ctx_decl_scope_lookup_prefix_alias(scope, _PREFIX_VARIANT, - name, levels); -} - -/** - * Registers a prefixed type alias within a declaration scope. - * - * @param scope Declaration scope - * @param prefix Prefix character - * @param name Alias name (non-NULL) - * @param decl Declaration to register - * @returns 0 if registration went okay, negative value otherwise - */ -static -int ctx_decl_scope_register_prefix_alias(struct ctx_decl_scope *scope, - char prefix, const char *name, struct bt_ctf_field_type *decl) -{ - int ret = 0; - GQuark qname = 0; - _BT_CTF_FIELD_TYPE_INIT(edecl); - - assert(scope); - assert(name); - assert(decl); - qname = get_prefixed_named_quark(prefix, name); - if (!qname) { - ret = -ENOMEM; - goto error; - } - - /* Make sure alias does not exist in local scope */ - edecl = ctx_decl_scope_lookup_prefix_alias(scope, prefix, name, 1); - if (edecl) { - BT_PUT(edecl); - ret = -EEXIST; - goto error; - } - - g_hash_table_insert(scope->decl_map, - (gpointer) (unsigned long) qname, decl); - - /* Hash table's reference */ - bt_get(decl); - - return 0; - -error: - return ret; -} - -/** - * Registers a type alias within a declaration scope. - * - * @param scope Declaration scope - * @param name Alias name (non-NULL) - * @param decl Declaration to register - * @returns 0 if registration went okay, negative value otherwise - */ -static -int ctx_decl_scope_register_alias(struct ctx_decl_scope *scope, - const char *name, struct bt_ctf_field_type *decl) -{ - return ctx_decl_scope_register_prefix_alias(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 declaration to register - * @returns 0 if registration went okay, negative value otherwise - */ -static -int ctx_decl_scope_register_enum(struct ctx_decl_scope *scope, - const char *name, struct bt_ctf_field_type *decl) -{ - return ctx_decl_scope_register_prefix_alias(scope, _PREFIX_ENUM, - name, decl); -} - -/** - * Registers a structure declaration within a declaration scope. - * - * @param scope Declaration scope - * @param name Structure name (non-NULL) - * @param decl Structure declaration to register - * @returns 0 if registration went okay, negative value otherwise - */ -static -int ctx_decl_scope_register_struct(struct ctx_decl_scope *scope, - const char *name, struct bt_ctf_field_type *decl) -{ - return ctx_decl_scope_register_prefix_alias(scope, _PREFIX_STRUCT, - name, decl); -} - -/** - * Registers a variant declaration within a declaration scope. - * - * @param scope Declaration scope - * @param name Variant name (non-NULL) - * @param decl Variant declaration to register - * @returns 0 if registration went okay, negative value otherwise - */ -static -int ctx_decl_scope_register_variant(struct ctx_decl_scope *scope, - const char *name, struct bt_ctf_field_type *decl) -{ - return ctx_decl_scope_register_prefix_alias(scope, _PREFIX_VARIANT, - name, decl); -} - -/** - * Creates a new visitor context. - * - * @param trace Associated trace - * @param efd Error stream - * @returns New visitor context, or NULL on error - */ -static -struct ctx *ctx_create(struct bt_ctf_trace *trace, FILE *efd) -{ - struct ctx *ctx = NULL; - struct ctx_decl_scope *scope = NULL; - - ctx = g_new(struct ctx, 1); - if (!ctx) { - goto error; - } - - /* Root declaration scope */ - scope = ctx_decl_scope_create(NULL); - if (!scope) { - goto error; - } - - ctx->stream_classes = g_hash_table_new_full(g_direct_hash, - g_direct_equal, NULL, (GDestroyNotify) bt_put); - if (!ctx->stream_classes) { - goto error; - } - - ctx->trace = trace; - ctx->efd = efd; - ctx->current_scope = scope; - ctx->is_trace_visited = FALSE; - - return ctx; - -error: - g_free(ctx); - ctx_decl_scope_destroy(scope); - - return NULL; -} - -/** - * Destroys a visitor context. - * - * @param ctx Visitor context to destroy - */ -static -void ctx_destroy(struct ctx *ctx) -{ - struct ctx_decl_scope *scope; - /* - * Destroy all scopes, from current one to the root scope. - */ - - if (!ctx) { - goto end; - } - - scope = ctx->current_scope; - - while (scope) { - struct ctx_decl_scope *parent_scope = scope->parent_scope; - - ctx_decl_scope_destroy(scope); - scope = parent_scope; - } - - g_hash_table_destroy(ctx->stream_classes); - g_free(ctx); - -end: - return; -} - -/** - * 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 ctx *ctx) -{ - int ret = 0; - struct ctx_decl_scope *new_scope; - - assert(ctx); - new_scope = ctx_decl_scope_create(ctx->current_scope); - if (!new_scope) { - ret = -ENOMEM; - goto end; - } - - ctx->current_scope = new_scope; - -end: - return ret; -} - -static -void ctx_pop_scope(struct ctx *ctx) -{ - struct ctx_decl_scope *parent_scope = NULL; - - 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_type_specifier_list(struct ctx *ctx, struct ctf_node *ts_list, - struct bt_ctf_field_type **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 -char *concatenate_unary_strings(struct bt_list_head *head) -{ - int i = 0; - GString *str; - struct ctf_node *node; - - str = g_string_new(NULL); - - 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 -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 bt_list_head *head, uint64_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_link != UNARY_LINK_UNKNOWN || i != 0; - if (cond) { - 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_UNSIGNED_CONSTANT && - uexpr_type != UNARY_SIGNED_CONSTANT) || - uexpr_link != UNARY_LINK_UNKNOWN || i != 0; - if (cond) { - ret = -EINVAL; - goto end; - } - - switch (node->u.unary_expression.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 bt_list_head *head, unsigned char *uuid) -{ - 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_parse(src_string, uuid); - if (ret) { - goto end; - } - } - -end: - return ret; -} - -static -int get_boolean(FILE *efd, struct ctf_node *unary_expr) -{ - int ret = 0; - - if (unary_expr->type != NODE_UNARY_EXPRESSION) { - _FPERROR(efd, "%s", "expecting unary expression"); - 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") || !strcmp(str, "TRUE")) { - ret = TRUE; - } else if (!strcmp(str, "false") || !strcmp(str, "FALSE")) { - ret = FALSE; - } else { - _FPERROR(efd, "unexpected string \"%s\"", str); - ret = -EINVAL; - goto end; - } - break; - } - default: - _FPERROR(efd, "%s", "unexpected unary expression type"); - ret = -EINVAL; - goto end; - } - -end: - return ret; -} - -static -enum bt_ctf_byte_order byte_order_from_unary_expr(FILE *efd, - struct ctf_node *unary_expr) -{ - const char *str; - enum bt_ctf_byte_order bo = BT_CTF_BYTE_ORDER_UNKNOWN; - - if (unary_expr->u.unary_expression.type != UNARY_STRING) { - _FPERROR(efd, "%s", - "\"byte_order\" attribute: expecting string"); - goto end; - } - - str = unary_expr->u.unary_expression.u.string; - - if (!strcmp(str, "be") || !strcmp(str, "network")) { - bo = BT_CTF_BYTE_ORDER_BIG_ENDIAN; - } else if (!strcmp(str, "le")) { - bo = BT_CTF_BYTE_ORDER_LITTLE_ENDIAN; - } else if (!strcmp(str, "native")) { - bo = BT_CTF_BYTE_ORDER_NATIVE; - } else { - _FPERROR(efd, "unexpected \"byte_order\" attribute value \"%s\"; should be \"be\", \"le\", \"network\", or \"native\"", - str); - goto end; - } - -end: - return bo; -} - -static -enum bt_ctf_byte_order get_real_byte_order(struct ctx *ctx, - struct ctf_node *uexpr) -{ - enum bt_ctf_byte_order bo = byte_order_from_unary_expr(ctx->efd, uexpr); - - if (bo == BT_CTF_BYTE_ORDER_NATIVE) { - bo = bt_ctf_trace_get_byte_order(ctx->trace); - } - - return bo; -} - -static -int is_align_valid(uint64_t align) -{ - return (align != 0) && !(align & (align - 1)); -} - -static -int get_type_specifier_name(struct ctx *ctx, struct ctf_node *type_specifier, - GString *str) -{ - int ret = 0; - - if (type_specifier->type != NODE_TYPE_SPECIFIER) { - ret = -EINVAL; - goto end; - } - - switch (type_specifier->u.type_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 (type_specifier->u.type_specifier.id_type) { - g_string_append(str, - type_specifier->u.type_specifier.id_type); - } - break; - case TYPESPEC_STRUCT: - { - struct ctf_node *node = type_specifier->u.type_specifier.node; - - if (!node->u._struct.name) { - _PERROR("%s", "unexpected empty structure 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 = type_specifier->u.type_specifier.node; - - if (!node->u.variant.name) { - _PERROR("%s", "unexpected empty variant 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 = type_specifier->u.type_specifier.node; - - if (!node->u._enum.enum_id) { - _PERROR("%s", "unexpected empty 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: - _PERROR("%s", "unknown specifier"); - ret = -EINVAL; - goto end; - } - -end: - return ret; -} - -static -int get_type_specifier_list_name(struct ctx *ctx, - struct ctf_node *type_specifier_list, GString *str) -{ - int ret; - struct ctf_node *iter; - int alias_item_nr = 0; - struct bt_list_head *head = - &type_specifier_list->u.type_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_type_specifier_name(ctx, iter, str); - if (ret) { - goto end; - } - } - -end: - return ret; -} - -static -GQuark create_typealias_identifier(struct ctx *ctx, - struct ctf_node *type_specifier_list, - struct ctf_node *node_type_declarator) -{ - int ret; - char *str_c; - GString *str; - GQuark qalias = 0; - struct ctf_node *iter; - struct bt_list_head *pointers = - &node_type_declarator->u.type_declarator.pointers; - - str = g_string_new(""); - ret = get_type_specifier_list_name(ctx, type_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_type_declarator(struct ctx *ctx, struct ctf_node *type_specifier_list, - GQuark *field_name, struct ctf_node *node_type_declarator, - struct bt_ctf_field_type **field_decl, - struct bt_ctf_field_type *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 type declarator node */ - if (node_type_declarator) { - if (node_type_declarator->u.type_declarator.type == - TYPEDEC_UNKNOWN) { - ret = -EINVAL; - goto error; - } - - /* TODO: GCC bitfields not supported yet */ - if (node_type_declarator->u.type_declarator.bitfield_len != - NULL) { - _PERROR("%s", "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_type_declarator->u.type_declarator.pointers; - - if (node_type_declarator && !bt_list_empty(pointers)) { - GQuark qalias; - _BT_CTF_FIELD_TYPE_INIT(nested_decl_copy); - - /* - * If we have a pointer declarator, it HAS to - * be present in the typealiases (else fail). - */ - qalias = create_typealias_identifier(ctx, - type_specifier_list, node_type_declarator); - nested_decl = - ctx_decl_scope_lookup_alias(ctx->current_scope, - g_quark_to_string(qalias), -1); - if (!nested_decl) { - _PERROR("cannot find typealias \"%s\"", - g_quark_to_string(qalias)); - ret = -EINVAL; - goto error; - } - - /* Make a copy of it */ - nested_decl_copy = bt_ctf_field_type_copy(nested_decl); - BT_PUT(nested_decl); - if (!nested_decl_copy) { - _PERROR("%s", "cannot copy nested declaration"); - ret = -EINVAL; - goto error; - } - - BT_MOVE(nested_decl, nested_decl_copy); - - /* Force integer's base to 16 since it's a pointer */ - if (bt_ctf_field_type_is_integer(nested_decl)) { - bt_ctf_field_type_integer_set_base(nested_decl, - BT_CTF_INTEGER_BASE_HEXADECIMAL); - } - } else { - ret = visit_type_specifier_list(ctx, - type_specifier_list, &nested_decl); - if (ret) { - assert(!nested_decl); - goto error; - } - } - } - - assert(nested_decl); - - if (!node_type_declarator) { - BT_MOVE(*field_decl, nested_decl); - goto end; - } - - if (node_type_declarator->u.type_declarator.type == TYPEDEC_ID) { - if (node_type_declarator->u.type_declarator.u.id) { - const char *id = - node_type_declarator->u.type_declarator.u.id; - - *field_name = g_quark_from_string(id); - } else { - *field_name = 0; - } - - BT_MOVE(*field_decl, nested_decl); - goto end; - } else { - struct ctf_node *first; - _BT_CTF_FIELD_TYPE_INIT(decl); - _BT_CTF_FIELD_TYPE_INIT(outer_field_decl); - struct bt_list_head *length = - &node_type_declarator-> - u.type_declarator.u.nested.length; - - /* Create array/sequence, pass nested_decl as child */ - if (bt_list_empty(length)) { - _PERROR("%s", - "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) { - ret = -EINVAL; - goto error; - } - - switch (first->u.unary_expression.type) { - case UNARY_UNSIGNED_CONSTANT: - { - size_t len; - _BT_CTF_FIELD_TYPE_INIT(array_decl); - - len = first->u.unary_expression.u.unsigned_constant; - array_decl = bt_ctf_field_type_array_create(nested_decl, - len); - BT_PUT(nested_decl); - if (!array_decl) { - _PERROR("%s", - "cannot create array declaration"); - ret = -ENOMEM; - goto error; - } - - BT_MOVE(decl, array_decl); - break; - } - case UNARY_STRING: - { - /* Lookup unsigned integer definition, create seq. */ - _BT_CTF_FIELD_TYPE_INIT(seq_decl); - char *length_name = concatenate_unary_strings(length); - - if (!length_name) { - ret = -EINVAL; - goto error; - } - - seq_decl = bt_ctf_field_type_sequence_create( - nested_decl, length_name); - g_free(length_name); - BT_PUT(nested_decl); - if (!seq_decl) { - _PERROR("%s", - "cannot create sequence declaration"); - ret = -ENOMEM; - goto error; - } - - BT_MOVE(decl, seq_decl); - break; - } - default: - ret = -EINVAL; - goto error; - } - - assert(!nested_decl); - assert(decl); - 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_type_declarator(ctx, type_specifier_list, - field_name, - node_type_declarator-> - u.type_declarator.u.nested.type_declarator, - &outer_field_decl, decl); - decl = NULL; - if (ret) { - assert(!outer_field_decl); - ret = -EINVAL; - goto error; - } - - assert(outer_field_decl); - BT_MOVE(*field_decl, outer_field_decl); - } - -end: - BT_PUT(nested_decl); - assert(*field_decl); - - return 0; - -error: - BT_PUT(nested_decl); - BT_PUT(*field_decl); - - return ret; -} - -static -int visit_struct_decl_field(struct ctx *ctx, - struct bt_ctf_field_type *struct_decl, - struct ctf_node *type_specifier_list, - struct bt_list_head *type_declarators) -{ - int ret = 0; - struct ctf_node *iter; - _BT_CTF_FIELD_TYPE_INIT(field_decl); - - bt_list_for_each_entry(iter, type_declarators, siblings) { - field_decl = NULL; - GQuark qfield_name; - const char *field_name; - _BT_CTF_FIELD_TYPE_INIT(efield_decl); - - ret = visit_type_declarator(ctx, type_specifier_list, - &qfield_name, iter, &field_decl, NULL); - if (ret) { - assert(!field_decl); - _PERROR("%s", "unable to find structure field declaration type"); - goto error; - } - - assert(field_decl); - field_name = g_quark_to_string(qfield_name); - - /* Check if field with same name already exists */ - efield_decl = - bt_ctf_field_type_structure_get_field_type_by_name( - struct_decl, field_name); - if (efield_decl) { - BT_PUT(efield_decl); - _PERROR("duplicate field \"%s\" in structure", - field_name); - ret = -EINVAL; - goto error; - } - - /* Add field to structure */ - ret = bt_ctf_field_type_structure_add_field(struct_decl, - field_decl, field_name); - BT_PUT(field_decl); - if (ret) { - _PERROR("cannot add field \"%s\" to structure", - g_quark_to_string(qfield_name)); - goto error; - } - } - - return 0; - -error: - BT_PUT(field_decl); - - return ret; -} - -static -int visit_variant_decl_field(struct ctx *ctx, - struct bt_ctf_field_type *variant_decl, - struct ctf_node *type_specifier_list, - struct bt_list_head *type_declarators) -{ - int ret = 0; - struct ctf_node *iter; - _BT_CTF_FIELD_TYPE_INIT(field_decl); - - bt_list_for_each_entry(iter, type_declarators, siblings) { - field_decl = NULL; - GQuark qfield_name; - const char *field_name; - _BT_CTF_FIELD_TYPE_INIT(efield_decl); - - ret = visit_type_declarator(ctx, type_specifier_list, - &qfield_name, iter, &field_decl, NULL); - if (ret) { - assert(!field_decl); - _PERROR("%s", - "unable to find variant field declaration type"); - goto error; - } - - assert(field_decl); - field_name = g_quark_to_string(qfield_name); - - /* Check if field with same name already exists */ - efield_decl = - bt_ctf_field_type_variant_get_field_type_by_name( - variant_decl, field_name); - if (efield_decl) { - BT_PUT(efield_decl); - _PERROR("duplicate field \"%s\" in variant", - field_name); - ret = -EINVAL; - goto error; - } - - /* Add field to structure */ - ret = bt_ctf_field_type_variant_add_field(variant_decl, - field_decl, field_name); - BT_PUT(field_decl); - if (ret) { - _PERROR("cannot add field \"%s\" to variant", - g_quark_to_string(qfield_name)); - goto error; - } - } - - return 0; - -error: - BT_PUT(field_decl); - - return ret; -} - -static -int visit_typedef(struct ctx *ctx, struct ctf_node *type_specifier_list, - struct bt_list_head *type_declarators) -{ - int ret = 0; - GQuark qidentifier; - struct ctf_node *iter; - _BT_CTF_FIELD_TYPE_INIT(type_decl); - - bt_list_for_each_entry(iter, type_declarators, siblings) { - ret = visit_type_declarator(ctx, type_specifier_list, - &qidentifier, iter, &type_decl, NULL); - if (ret) { - _PERROR("%s", "problem creating type declaration"); - ret = -EINVAL; - goto end; - } - - /* Do not allow typedef and typealias of untagged variants */ - if (bt_ctf_field_type_is_variant(type_decl)) { - if (bt_ctf_field_type_variant_get_tag_name(type_decl)) { - _PERROR("%s", "typedef of untagged variant is not allowed"); - ret = -EPERM; - goto end; - } - } - - ret = ctx_decl_scope_register_alias(ctx->current_scope, - g_quark_to_string(qidentifier), type_decl); - if (ret) { - _PERROR("cannot register typedef \"%s\"", - g_quark_to_string(qidentifier)); - goto end; - } - } - -end: - BT_PUT(type_decl); - - return ret; -} - -static -int visit_typealias(struct ctx *ctx, struct ctf_node *target, - struct ctf_node *alias) -{ - int ret = 0; - GQuark qalias; - struct ctf_node *node; - GQuark qdummy_field_name; - _BT_CTF_FIELD_TYPE_INIT(type_decl); - - /* Create target type declaration */ - if (bt_list_empty(&target->u.typealias_target.type_declarators)) { - node = NULL; - } else { - node = _BT_LIST_FIRST_ENTRY( - &target->u.typealias_target.type_declarators, - struct ctf_node, siblings); - } - - ret = visit_type_declarator(ctx, - target->u.typealias_target.type_specifier_list, - &qdummy_field_name, node, &type_decl, NULL); - if (ret) { - assert(!type_decl); - _PERROR("%s", "problem creating type declaration"); - goto end; - } - - /* Do not allow typedef and typealias of untagged variants */ - if (bt_ctf_field_type_is_variant(type_decl)) { - if (bt_ctf_field_type_variant_get_tag_name(type_decl)) { - _PERROR("%s", - "typealias of untagged variant 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) { - _PERROR("%s", "expecting empty identifier"); - ret = -EINVAL; - goto end; - } - - /* Create alias identifier */ - node = _BT_LIST_FIRST_ENTRY(&alias->u.typealias_alias.type_declarators, - struct ctf_node, siblings); - qalias = create_typealias_identifier(ctx, - alias->u.typealias_alias.type_specifier_list, node); - ret = ctx_decl_scope_register_alias(ctx->current_scope, - g_quark_to_string(qalias), type_decl); - if (ret) { - _PERROR("cannot register typealias \"%s\"", - g_quark_to_string(qalias)); - goto end; - } - -end: - BT_PUT(type_decl); - - return ret; -} - -static -int visit_struct_decl_entry(struct ctx *ctx, struct ctf_node *entry_node, - struct bt_ctf_field_type *struct_decl) -{ - int ret = 0; - - switch (entry_node->type) { - case NODE_TYPEDEF: - ret = visit_typedef(ctx, - entry_node->u._typedef.type_specifier_list, - &entry_node->u._typedef.type_declarators); - if (ret) { - _PERROR("%s", - "cannot add typedef in \"struct\" declaration"); - goto end; - } - break; - case NODE_TYPEALIAS: - ret = visit_typealias(ctx, entry_node->u.typealias.target, - entry_node->u.typealias.alias); - if (ret) { - _PERROR("%s", - "cannot add typealias in \"struct\" declaration"); - 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. - type_specifier_list, - &entry_node->u.struct_or_variant_declaration. - type_declarators); - if (ret) { - goto end; - } - break; - default: - _PERROR("unexpected node type: %d", (int) entry_node->type); - ret = -EINVAL; - goto end; - } - -end: - return ret; -} - -static -int visit_variant_decl_entry(struct ctx *ctx, struct ctf_node *entry_node, - struct bt_ctf_field_type *variant_decl) -{ - int ret = 0; - - switch (entry_node->type) { - case NODE_TYPEDEF: - ret = visit_typedef(ctx, - entry_node->u._typedef.type_specifier_list, - &entry_node->u._typedef.type_declarators); - if (ret) { - _PERROR("%s", - "cannot add typedef in \"variant\" declaration"); - goto end; - } - break; - case NODE_TYPEALIAS: - ret = visit_typealias(ctx, entry_node->u.typealias.target, - entry_node->u.typealias.alias); - if (ret) { - _PERROR("%s", - "cannot add typealias in \"variant\" declaration"); - 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. - type_specifier_list, - &entry_node->u.struct_or_variant_declaration. - type_declarators); - if (ret) { - goto end; - } - break; - default: - _PERROR("unexpected node type: %d", (int) entry_node->type); - ret = -EINVAL; - goto end; - } - -end: - return ret; -} - -static -int visit_struct_decl(struct ctx *ctx, const char *name, - struct bt_list_head *decl_list, int has_body, - struct bt_list_head *min_align, - struct bt_ctf_field_type **struct_decl) -{ - int ret = 0; - - *struct_decl = NULL; - - /* For named struct (without body), lookup in declaration scope */ - if (!has_body) { - _BT_CTF_FIELD_TYPE_INIT(struct_decl_copy); - - if (!name) { - ret = -EPERM; - goto error; - } - - *struct_decl = ctx_decl_scope_lookup_struct(ctx->current_scope, - name, -1); - if (!*struct_decl) { - _PERROR("cannot find \"struct %s\"", name); - ret = -EINVAL; - goto error; - } - - /* Make a copy of it */ - struct_decl_copy = bt_ctf_field_type_copy(*struct_decl); - if (!struct_decl_copy) { - _PERROR("%s", - "cannot create copy of structure declaration"); - ret = -EINVAL; - goto error; - } - - BT_MOVE(*struct_decl, struct_decl_copy); - } else { - struct ctf_node *entry_node; - uint64_t min_align_value = 0; - - if (name) { - _BT_CTF_FIELD_TYPE_INIT(estruct_decl); - - estruct_decl = ctx_decl_scope_lookup_struct( - ctx->current_scope, name, 1); - if (estruct_decl) { - BT_PUT(estruct_decl); - _PERROR("\"struct %s\" already declared in local scope", - name); - ret = -EINVAL; - goto error; - } - } - - if (!bt_list_empty(min_align)) { - ret = get_unary_unsigned(min_align, &min_align_value); - if (ret) { - _PERROR("%s", "unexpected unary expression for structure declaration's \"align\" attribute"); - goto error; - } - } - - *struct_decl = bt_ctf_field_type_structure_create(); - if (!*struct_decl) { - _PERROR("%s", "cannot create structure declaration"); - ret = -ENOMEM; - goto error; - } - - ret = ctx_push_scope(ctx); - if (ret) { - _PERROR("%s", "cannot push scope"); - goto error; - } - - bt_list_for_each_entry(entry_node, decl_list, siblings) { - ret = visit_struct_decl_entry(ctx, entry_node, - *struct_decl); - if (ret) { - ctx_pop_scope(ctx); - goto error; - } - } - - ctx_pop_scope(ctx); - - if (name) { - ret = ctx_decl_scope_register_struct(ctx->current_scope, - name, *struct_decl); - if (ret) { - _PERROR("cannot register \"struct %s\" in declaration scope", - name); - goto error; - } - } - } - - return 0; - -error: - BT_PUT(*struct_decl); - - return ret; -} - -static -int visit_variant_decl(struct ctx *ctx, const char *name, - const char *tag, struct bt_list_head *decl_list, - int has_body, struct bt_ctf_field_type **variant_decl) -{ - int ret = 0; - _BT_CTF_FIELD_TYPE_INIT(untagged_variant_decl); - - *variant_decl = NULL; - - /* For named variant (without body), lookup in declaration scope */ - if (!has_body) { - _BT_CTF_FIELD_TYPE_INIT(variant_decl_copy); - - if (!name) { - ret = -EPERM; - goto error; - } - - untagged_variant_decl = - ctx_decl_scope_lookup_variant(ctx->current_scope, - name, -1); - if (!untagged_variant_decl) { - _PERROR("cannot find \"variant %s\"", name); - ret = -EINVAL; - goto error; - } - - /* Make a copy of it */ - variant_decl_copy = bt_ctf_field_type_copy( - untagged_variant_decl); - if (!variant_decl_copy) { - _PERROR("%s", - "cannot create copy of structure declaration"); - ret = -EINVAL; - goto error; - } - - BT_MOVE(untagged_variant_decl, variant_decl_copy); - } else { - struct ctf_node *entry_node; - - if (name) { - struct bt_ctf_field_type *evariant_decl = - ctx_decl_scope_lookup_struct(ctx->current_scope, - name, 1); - - if (evariant_decl) { - BT_PUT(evariant_decl); - _PERROR("\"variant %s\" already declared in local scope", - name); - ret = -EINVAL; - goto error; - } - } - - untagged_variant_decl = bt_ctf_field_type_variant_create(NULL, - NULL); - if (!untagged_variant_decl) { - _PERROR("%s", "cannot create variant declaration"); - ret = -ENOMEM; - goto error; - } - - ret = ctx_push_scope(ctx); - if (ret) { - _PERROR("%s", "cannot push scope"); - 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) { - ctx_pop_scope(ctx); - goto error; - } - } - - ctx_pop_scope(ctx); - - if (name) { - ret = ctx_decl_scope_register_variant( - ctx->current_scope, name, - untagged_variant_decl); - if (ret) { - _PERROR("cannot register \"variant %s\" in declaration scope", - name); - goto error; - } - } - } - - /* - * If tagged, create tagged variant and return; otherwise - * return untagged variant. - */ - if (!tag) { - BT_MOVE(*variant_decl, untagged_variant_decl); - } else { - /* - * At this point, we have a fresh untagged variant; nobody - * else owns it. Set its tag now. - */ - ret = bt_ctf_field_type_variant_set_tag_name( - untagged_variant_decl, tag); - if (ret) { - goto error; - } - - BT_MOVE(*variant_decl, untagged_variant_decl); - } - - assert(!untagged_variant_decl); - assert(*variant_decl); - - return 0; - -error: - BT_PUT(untagged_variant_decl); - BT_PUT(*variant_decl); - - return ret; -} - -static -int visit_enum_decl_entry(struct ctx *ctx, struct ctf_node *enumerator, - struct bt_ctf_field_type *enum_decl, int64_t *last, int is_signed) -{ - int ret = 0; - int nr_vals = 0; - struct ctf_node *iter; - int64_t start = 0, end = 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) { - int64_t *target; - - if (iter->type != NODE_UNARY_EXPRESSION) { - _PERROR("wrong unary expression for enumeration label \"%s\"", - 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 = iter->u.unary_expression.u.signed_constant; - break; - case UNARY_UNSIGNED_CONSTANT: - *target = (int64_t) - iter->u.unary_expression.u.unsigned_constant; - break; - default: - _PERROR("invalid enumeration entry: \"%s\"", - label); - ret = -EINVAL; - goto error; - } - - if (nr_vals > 1) { - _PERROR("invalid enumeration entry: \"%s\"", - label); - ret = -EINVAL; - goto error; - } - - nr_vals++; - } - - if (nr_vals == 0) { - start = *last; - } - - if (nr_vals <= 1) { - end = start; - } - - *last = end + 1; - - if (is_signed) { - ret = bt_ctf_field_type_enumeration_add_mapping(enum_decl, label, - start, end); - } else { - ret = bt_ctf_field_type_enumeration_add_mapping_unsigned(enum_decl, - label, (uint64_t) start, (uint64_t) end); - } - if (ret) { - _PERROR("cannot add mapping to enumeration for label \"%s\"", - label); - goto error; - } - - return 0; - -error: - return ret; -} - -static -int visit_enum_decl(struct ctx *ctx, const char *name, - struct ctf_node *container_type, - struct bt_list_head *enumerator_list, - int has_body, - struct bt_ctf_field_type **enum_decl) -{ - int ret = 0; - GQuark qdummy_id; - _BT_CTF_FIELD_TYPE_INIT(integer_decl); - - *enum_decl = NULL; - - /* For named enum (without body), lookup in declaration scope */ - if (!has_body) { - _BT_CTF_FIELD_TYPE_INIT(enum_decl_copy); - - if (!name) { - ret = -EPERM; - goto error; - } - - *enum_decl = ctx_decl_scope_lookup_enum(ctx->current_scope, - name, -1); - if (!*enum_decl) { - _PERROR("cannot find \"enum %s\"", name); - ret = -EINVAL; - goto error; - } - - /* Make a copy of it */ - enum_decl_copy = bt_ctf_field_type_copy(*enum_decl); - if (!enum_decl_copy) { - _PERROR("%s", - "cannot create copy of enumeration declaration"); - ret = -EINVAL; - goto error; - } - - BT_PUT(*enum_decl); - BT_MOVE(*enum_decl, enum_decl_copy); - } else { - struct ctf_node *iter; - int64_t last_value = 0; - - if (name) { - _BT_CTF_FIELD_TYPE_INIT(eenum_decl); - - eenum_decl = ctx_decl_scope_lookup_enum( - ctx->current_scope, name, 1); - if (eenum_decl) { - BT_PUT(eenum_decl); - _PERROR("\"enum %s\" already declared in local scope", - name); - ret = -EINVAL; - goto error; - } - } - - if (!container_type) { - integer_decl = ctx_decl_scope_lookup_alias( - ctx->current_scope, "int", -1); - if (!integer_decl) { - _PERROR("%s", "cannot find \"int\" type for enumeration"); - ret = -EINVAL; - goto error; - } - } else { - ret = visit_type_declarator(ctx, container_type, - &qdummy_id, NULL, &integer_decl, NULL); - if (ret) { - assert(!integer_decl); - ret = -EINVAL; - goto error; - } - } - - assert(integer_decl); - - if (!bt_ctf_field_type_is_integer(integer_decl)) { - _PERROR("%s", "container type for enumeration is not an integer"); - ret = -EINVAL; - goto error; - } - - *enum_decl = bt_ctf_field_type_enumeration_create(integer_decl); - if (!*enum_decl) { - _PERROR("%s", "cannot create enumeration declaration"); - ret = -ENOMEM; - goto error; - } - - bt_list_for_each_entry(iter, enumerator_list, siblings) { - ret = visit_enum_decl_entry(ctx, iter, *enum_decl, - &last_value, - bt_ctf_field_type_integer_get_signed(integer_decl)); - if (ret) { - goto error; - } - } - - if (name) { - ret = ctx_decl_scope_register_enum(ctx->current_scope, - name, *enum_decl); - if (ret) { - goto error; - } - } - } - - BT_PUT(integer_decl); - - return 0; - -error: - BT_PUT(integer_decl); - BT_PUT(*enum_decl); - - return ret; -} - -static -int visit_type_specifier(struct ctx *ctx, - struct ctf_node *type_specifier_list, - struct bt_ctf_field_type **decl) -{ - int ret = 0; - GString *str = NULL; - _BT_CTF_FIELD_TYPE_INIT(decl_copy); - - *decl = NULL; - str = g_string_new(""); - ret = get_type_specifier_list_name(ctx, type_specifier_list, str); - if (ret) { - goto error; - } - - *decl = ctx_decl_scope_lookup_alias(ctx->current_scope, str->str, -1); - if (!*decl) { - _PERROR("cannot find type alias \"%s\"", str->str); - ret = -EINVAL; - goto error; - } - - /* Make a copy of the type declaration */ - decl_copy = bt_ctf_field_type_copy(*decl); - if (!decl_copy) { - _PERROR("%s", "cannot create copy of type declaration"); - ret = -EINVAL; - goto error; - } - - BT_MOVE(*decl, decl_copy); - (void) g_string_free(str, TRUE); - str = NULL; - - return 0; - -error: - if (str) { - (void) g_string_free(str, TRUE); - } - - BT_PUT(*decl); - - return ret; -} - -static -int visit_integer_decl(struct ctx *ctx, - struct bt_list_head *expressions, - struct bt_ctf_field_type **integer_decl) -{ - int set = 0; - int ret = 0; - int signedness = 0; - struct ctf_node *expression; - uint64_t alignment = 0, size = 0; - struct bt_ctf_clock *mapped_clock = NULL; - enum bt_ctf_string_encoding encoding = BT_CTF_STRING_ENCODING_NONE; - enum bt_ctf_integer_base base = BT_CTF_INTEGER_BASE_DECIMAL; - enum bt_ctf_byte_order byte_order = - bt_ctf_trace_get_byte_order(ctx->trace); - - *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) { - ret = -EINVAL; - goto error; - } - - if (!strcmp(left->u.unary_expression.u.string, "signed")) { - if (_IS_SET(&set, _INTEGER_SIGNED_SET)) { - _PERROR_DUP_ATTR("signed", - "integer declaration"); - ret = -EPERM; - goto error; - } - - signedness = get_boolean(ctx->efd, right); - if (signedness < 0) { - ret = -EINVAL; - goto error; - } - - _SET(&set, _INTEGER_SIGNED_SET); - } else if (!strcmp(left->u.unary_expression.u.string, - "byte_order")) { - if (_IS_SET(&set, _INTEGER_BYTE_ORDER_SET)) { - _PERROR_DUP_ATTR("byte_order", - "integer declaration"); - ret = -EPERM; - goto error; - } - - byte_order = get_real_byte_order(ctx, right); - if (byte_order == BT_CTF_BYTE_ORDER_UNKNOWN) { - _PERROR("%s", "invalid \"byte_order\" attribute in integer declaration"); - ret = -EINVAL; - goto error; - } - - _SET(&set, _INTEGER_BYTE_ORDER_SET); - } else if (!strcmp(left->u.unary_expression.u.string, "size")) { - if (_IS_SET(&set, _INTEGER_SIZE_SET)) { - _PERROR_DUP_ATTR("size", - "integer declaration"); - ret = -EPERM; - goto error; - } - - if (right->u.unary_expression.type != - UNARY_UNSIGNED_CONSTANT) { - _PERROR("%s", "invalid \"size\" attribute in integer declaration: expecting unsigned constant"); - ret = -EINVAL; - goto error; - } - - size = right->u.unary_expression.u.unsigned_constant; - if (size == 0) { - _PERROR("%s", "invalid \"size\" attribute in integer declaration: expecting positive constant"); - ret = -EINVAL; - goto error; - } else if (size > 64) { - _PERROR("%s", "invalid \"size\" attribute in integer declaration: integers over 64-bit are not supported as of this version"); - ret = -EINVAL; - goto error; - } - - _SET(&set, _INTEGER_SIZE_SET); - } else if (!strcmp(left->u.unary_expression.u.string, - "align")) { - if (_IS_SET(&set, _INTEGER_ALIGN_SET)) { - _PERROR_DUP_ATTR("align", - "integer declaration"); - ret = -EPERM; - goto error; - } - - if (right->u.unary_expression.type != - UNARY_UNSIGNED_CONSTANT) { - _PERROR("%s", "invalid \"align\" attribute in integer declaration: expecting unsigned constant"); - ret = -EINVAL; - goto error; - } - - alignment = - right->u.unary_expression.u.unsigned_constant; - if (!is_align_valid(alignment)) { - _PERROR("%s", "invalid \"align\" attribute in integer declaration: expecting power of two"); - ret = -EINVAL; - goto error; - } - - _SET(&set, _INTEGER_ALIGN_SET); - } else if (!strcmp(left->u.unary_expression.u.string, "base")) { - if (_IS_SET(&set, _INTEGER_BASE_SET)) { - _PERROR_DUP_ATTR("base", "integer declaration"); - 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_CTF_INTEGER_BASE_BINARY; - break; - case 8: - base = BT_CTF_INTEGER_BASE_OCTAL; - break; - case 10: - base = BT_CTF_INTEGER_BASE_DECIMAL; - break; - case 16: - base = BT_CTF_INTEGER_BASE_HEXADECIMAL; - break; - default: - _PERROR("invalid \"base\" attribute in integer declaration: %" PRIu64, - right->u.unary_expression.u.unsigned_constant); - ret = -EINVAL; - goto error; - } - break; - } - case UNARY_STRING: - { - char *s_right = concatenate_unary_strings( - &expression->u.ctf_expression.right); - if (!s_right) { - _PERROR("%s", "unexpected unary expression for integer declaration's \"base\" attribute"); - ret = -EINVAL; - goto error; - } - - if (!strcmp(s_right, "decimal") || - !strcmp(s_right, "dec") || - !strcmp(s_right, "d") || - !strcmp(s_right, "i") || - !strcmp(s_right, "u")) { - base = BT_CTF_INTEGER_BASE_DECIMAL; - } else if (!strcmp(s_right, "hexadecimal") || - !strcmp(s_right, "hex") || - !strcmp(s_right, "x") || - !strcmp(s_right, "X") || - !strcmp(s_right, "p")) { - base = BT_CTF_INTEGER_BASE_HEXADECIMAL; - } else if (!strcmp(s_right, "octal") || - !strcmp(s_right, "oct") || - !strcmp(s_right, "o")) { - base = BT_CTF_INTEGER_BASE_OCTAL; - } else if (!strcmp(s_right, "binary") || - !strcmp(s_right, "b")) { - base = BT_CTF_INTEGER_BASE_BINARY; - } else { - _PERROR("unexpected unary expression for integer declaration's \"base\" attribute: \"%s\"", - s_right); - g_free(s_right); - ret = -EINVAL; - goto error; - } - - g_free(s_right); - break; - } - default: - _PERROR("%s", "invalid \"base\" attribute in integer declaration: expecting unsigned constant or unary string"); - ret = -EINVAL; - goto error; - } - - _SET(&set, _INTEGER_BASE_SET); - } else if (!strcmp(left->u.unary_expression.u.string, - "encoding")) { - char *s_right; - - if (_IS_SET(&set, _INTEGER_ENCODING_SET)) { - _PERROR_DUP_ATTR("encoding", - "integer declaration"); - ret = -EPERM; - goto error; - } - - if (right->u.unary_expression.type != UNARY_STRING) { - _PERROR("%s", "invalid \"encoding\" attribute in integer declaration: expecting unary string"); - ret = -EINVAL; - goto error; - } - - s_right = concatenate_unary_strings( - &expression->u.ctf_expression.right); - if (!s_right) { - _PERROR("%s", "unexpected unary expression for integer declaration's \"encoding\" attribute"); - ret = -EINVAL; - goto error; - } - - if (!strcmp(s_right, "UTF8") || - !strcmp(s_right, "utf8") || - !strcmp(s_right, "utf-8") || - !strcmp(s_right, "UTF-8")) { - encoding = BT_CTF_STRING_ENCODING_UTF8; - } else if (!strcmp(s_right, "ASCII") || - !strcmp(s_right, "ascii")) { - encoding = BT_CTF_STRING_ENCODING_ASCII; - } else if (!strcmp(s_right, "none")) { - encoding = BT_CTF_STRING_ENCODING_NONE; - } else { - _PERROR("invalid \"encoding\" attribute in integer declaration: unknown 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")) { - const char *clock_name; - - if (_IS_SET(&set, _INTEGER_MAP_SET)) { - _PERROR_DUP_ATTR("map", "integer declaration"); - ret = -EPERM; - goto error; - } - - if (right->u.unary_expression.type != UNARY_STRING) { - _PERROR("%s", "invalid \"map\" attribute in integer declaration: 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 = concatenate_unary_strings( - &expression->u.ctf_expression.right); - - if (!s_right) { - _PERROR("%s", "unexpected unary expression for integer declaration's \"map\" attribute"); - ret = -EINVAL; - goto error; - } - - _PWARNING("invalid \"map\" attribute in integer declaration: unknown clock: \"%s\"", - s_right); - _SET(&set, _INTEGER_MAP_SET); - g_free(s_right); - continue; - } - - mapped_clock = bt_ctf_trace_get_clock_by_name( - ctx->trace, clock_name); - if (!mapped_clock) { - _PERROR("invalid \"map\" attribute in integer declaration: cannot find clock \"%s\"", - clock_name); - ret = -EINVAL; - goto error; - } - - _SET(&set, _INTEGER_MAP_SET); - } else { - _PWARNING("unknown attribute \"%s\" in integer declaration", - left->u.unary_expression.u.string); - } - } - - if (!_IS_SET(&set, _INTEGER_SIZE_SET)) { - _PERROR("%s", - "missing \"size\" attribute in integer declaration"); - 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 = bt_ctf_field_type_integer_create((unsigned int) size); - if (!*integer_decl) { - _PERROR("%s", "cannot create integer declaration"); - ret = -ENOMEM; - goto error; - } - - ret = bt_ctf_field_type_integer_set_signed(*integer_decl, signedness); - ret |= bt_ctf_field_type_integer_set_base(*integer_decl, base); - ret |= bt_ctf_field_type_integer_set_encoding(*integer_decl, encoding); - ret |= bt_ctf_field_type_set_alignment(*integer_decl, - (unsigned int) alignment); - ret |= bt_ctf_field_type_set_byte_order(*integer_decl, byte_order); - - if (mapped_clock) { - /* Move clock */ - ret |= bt_ctf_field_type_integer_set_mapped_clock( - *integer_decl, mapped_clock); - bt_put(mapped_clock); - mapped_clock = NULL; - } - - if (ret) { - _PERROR("%s", "cannot configure integer declaration"); - ret = -EINVAL; - goto error; - } - - return 0; - -error: - if (mapped_clock) { - bt_put(mapped_clock); - } - - BT_PUT(*integer_decl); - - return ret; -} - -static -int visit_floating_point_number_decl(struct ctx *ctx, - struct bt_list_head *expressions, - struct bt_ctf_field_type **float_decl) -{ - int set = 0; - int ret = 0; - struct ctf_node *expression; - uint64_t alignment = 1, exp_dig = 0, mant_dig = 0; - enum bt_ctf_byte_order byte_order = - bt_ctf_trace_get_byte_order(ctx->trace); - - *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) { - ret = -EINVAL; - goto error; - } - - if (!strcmp(left->u.unary_expression.u.string, "byte_order")) { - if (_IS_SET(&set, _FLOAT_BYTE_ORDER_SET)) { - _PERROR_DUP_ATTR("byte_order", - "floating point number declaration"); - ret = -EPERM; - goto error; - } - - byte_order = get_real_byte_order(ctx, right); - if (byte_order == BT_CTF_BYTE_ORDER_UNKNOWN) { - _PERROR("%s", "invalid \"byte_order\" attribute in floating point number declaration"); - ret = -EINVAL; - goto error; - } - - _SET(&set, _FLOAT_BYTE_ORDER_SET); - } else if (!strcmp(left->u.unary_expression.u.string, - "exp_dig")) { - if (_IS_SET(&set, _FLOAT_EXP_DIG_SET)) { - _PERROR_DUP_ATTR("exp_dig", - "floating point number declaration"); - ret = -EPERM; - goto error; - } - - if (right->u.unary_expression.type != - UNARY_UNSIGNED_CONSTANT) { - _PERROR("%s", "invalid \"exp_dig\" attribute in floating point number declaration: expecting unsigned constant"); - 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")) { - if (_IS_SET(&set, _FLOAT_MANT_DIG_SET)) { - _PERROR_DUP_ATTR("mant_dig", - "floating point number declaration"); - ret = -EPERM; - goto error; - } - - if (right->u.unary_expression.type != - UNARY_UNSIGNED_CONSTANT) { - _PERROR("%s", "invalid \"mant_dig\" attribute in floating point number declaration: expecting unsigned constant"); - 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")) { - if (_IS_SET(&set, _FLOAT_ALIGN_SET)) { - _PERROR_DUP_ATTR("align", - "floating point number declaration"); - ret = -EPERM; - goto error; - } - - if (right->u.unary_expression.type != - UNARY_UNSIGNED_CONSTANT) { - _PERROR("%s", "invalid \"align\" attribute in floating point number declaration: expecting unsigned constant"); - ret = -EINVAL; - goto error; - } - - alignment = right->u.unary_expression.u. - unsigned_constant; - - if (!is_align_valid(alignment)) { - _PERROR("%s", "invalid \"align\" attribute in floating point number declaration: expecting power of two"); - ret = -EINVAL; - goto error; - } - - _SET(&set, _FLOAT_ALIGN_SET); - } else { - _PWARNING("unknown attribute \"%s\" in floating point number declaration", - left->u.unary_expression.u.string); - } - } - - if (!_IS_SET(&set, _FLOAT_MANT_DIG_SET)) { - _PERROR("%s", "missing \"mant_dig\" attribute in floating point number declaration"); - ret = -EPERM; - goto error; - } - - if (!_IS_SET(&set, _FLOAT_EXP_DIG_SET)) { - _PERROR("%s", "missing \"exp_dig\" attribute in floating point number declaration"); - 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 = bt_ctf_field_type_floating_point_create(); - if (!*float_decl) { - _PERROR("%s", - "cannot create floating point number declaration"); - ret = -ENOMEM; - goto error; - } - - ret = bt_ctf_field_type_floating_point_set_exponent_digits( - *float_decl, exp_dig); - ret |= bt_ctf_field_type_floating_point_set_mantissa_digits( - *float_decl, mant_dig); - ret |= bt_ctf_field_type_set_byte_order(*float_decl, byte_order); - ret |= bt_ctf_field_type_set_alignment(*float_decl, alignment); - if (ret) { - _PERROR("%s", - "cannot configure floating point number declaration"); - ret = -EINVAL; - goto error; - } - - return 0; - -error: - BT_PUT(*float_decl); - - return ret; -} - -static -int visit_string_decl(struct ctx *ctx, - struct bt_list_head *expressions, - struct bt_ctf_field_type **string_decl) -{ - int set = 0; - int ret = 0; - struct ctf_node *expression; - enum bt_ctf_string_encoding encoding = BT_CTF_STRING_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) { - ret = -EINVAL; - goto error; - } - - if (!strcmp(left->u.unary_expression.u.string, "encoding")) { - char *s_right; - - if (_IS_SET(&set, _STRING_ENCODING_SET)) { - _PERROR_DUP_ATTR("encoding", - "string declaration"); - ret = -EPERM; - goto error; - } - - if (right->u.unary_expression.type != UNARY_STRING) { - _PERROR("%s", "invalid \"encoding\" attribute in string declaration: expecting unary string"); - ret = -EINVAL; - goto error; - } - - s_right = concatenate_unary_strings( - &expression->u.ctf_expression.right); - if (!s_right) { - _PERROR("%s", "unexpected unary expression for string declaration's \"encoding\" attribute"); - ret = -EINVAL; - goto error; - } - - if (!strcmp(s_right, "UTF8") || - !strcmp(s_right, "utf8") || - !strcmp(s_right, "utf-8") || - !strcmp(s_right, "UTF-8")) { - encoding = BT_CTF_STRING_ENCODING_UTF8; - } else if (!strcmp(s_right, "ASCII") || - !strcmp(s_right, "ascii")) { - encoding = BT_CTF_STRING_ENCODING_ASCII; - } else if (!strcmp(s_right, "none")) { - encoding = BT_CTF_STRING_ENCODING_NONE; - } else { - _PERROR("invalid \"encoding\" attribute in string declaration: unknown encoding \"%s\"", - s_right); - g_free(s_right); - ret = -EINVAL; - goto error; - } - - g_free(s_right); - _SET(&set, _STRING_ENCODING_SET); - } else { - _PWARNING("unknown attribute \"%s\" in string declaration", - left->u.unary_expression.u.string); - } - } - - *string_decl = bt_ctf_field_type_string_create(); - if (!*string_decl) { - _PERROR("%s", "cannot create string declaration"); - ret = -ENOMEM; - goto error; - } - - ret = bt_ctf_field_type_string_set_encoding(*string_decl, encoding); - if (ret) { - _PERROR("%s", "cannot configure string declaration"); - ret = -EINVAL; - goto error; - } - - return 0; - -error: - BT_PUT(*string_decl); - - return ret; -} - -static -int visit_type_specifier_list(struct ctx *ctx, - struct ctf_node *ts_list, - struct bt_ctf_field_type **decl) -{ - int ret = 0; - struct ctf_node *first, *node; - - *decl = NULL; - - if (ts_list->type != NODE_TYPE_SPECIFIER_LIST) { - ret = -EINVAL; - goto error; - } - - first = _BT_LIST_FIRST_ENTRY(&ts_list->u.type_specifier_list.head, - struct ctf_node, siblings); - if (first->type != NODE_TYPE_SPECIFIER) { - ret = -EINVAL; - goto error; - } - - node = first->u.type_specifier.node; - - switch (first->u.type_specifier.type) { - case TYPESPEC_INTEGER: - ret = visit_integer_decl(ctx, &node->u.integer.expressions, - decl); - if (ret) { - assert(!*decl); - goto error; - } - break; - case TYPESPEC_FLOATING_POINT: - ret = visit_floating_point_number_decl(ctx, - &node->u.floating_point.expressions, decl); - if (ret) { - assert(!*decl); - goto error; - } - break; - case TYPESPEC_STRING: - ret = visit_string_decl(ctx, - &node->u.string.expressions, decl); - if (ret) { - 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, decl); - if (ret) { - 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, decl); - if (ret) { - assert(!*decl); - goto error; - } - break; - case TYPESPEC_ENUM: - ret = visit_enum_decl(ctx, node->u._enum.enum_id, - node->u._enum.container_type, - &node->u._enum.enumerator_list, - node->u._enum.has_body, decl); - if (ret) { - 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_type_specifier(ctx, ts_list, decl); - if (ret) { - assert(!*decl); - goto error; - } - break; - default: - _PERROR("unexpected node type: %d", - (int) first->u.type_specifier.type); - ret = -EINVAL; - goto error; - } - - assert(*decl); - - return 0; - -error: - BT_PUT(*decl); - - return ret; -} - -static -int visit_event_decl_entry(struct ctx *ctx, struct ctf_node *node, - struct bt_ctf_event_class *event_class, int64_t *stream_id, - int *set) -{ - int ret = 0; - char *left = NULL; - _BT_CTF_FIELD_TYPE_INIT(decl); - - switch (node->type) { - case NODE_TYPEDEF: - ret = visit_typedef(ctx, node->u._typedef.type_specifier_list, - &node->u._typedef.type_declarators); - if (ret) { - _PERROR("%s", - "cannot add typedef in \"event\" declaration"); - goto error; - } - break; - case NODE_TYPEALIAS: - ret = visit_typealias(ctx, node->u.typealias.target, - node->u.typealias.alias); - if (ret) { - _PERROR("%s", "cannot add typealias in \"event\" declaration"); - goto error; - } - break; - case NODE_CTF_EXPRESSION: - { - left = concatenate_unary_strings(&node->u.ctf_expression.left); - if (!left) { - ret = -EINVAL; - goto error; - } - - if (!strcmp(left, "name")) { - /* This is already known at this stage */ - if (_IS_SET(set, _EVENT_NAME_SET)) { - _PERROR_DUP_ATTR("name", "event declaration"); - ret = -EPERM; - goto error; - } - - _SET(set, _EVENT_NAME_SET); - } else if (!strcmp(left, "id")) { - int64_t id; - - if (_IS_SET(set, _EVENT_ID_SET)) { - _PERROR_DUP_ATTR("id", "event declaration"); - ret = -EPERM; - goto error; - } - - ret = get_unary_unsigned(&node->u.ctf_expression.right, - (uint64_t *) &id); - if (ret || id < 0) { - _PERROR("%s", "unexpected unary expression for event declaration's \"id\" attribute"); - ret = -EINVAL; - goto error; - } - - ret = bt_ctf_event_class_set_id(event_class, id); - if (ret) { - _PERROR("%s", - "cannot set event declaration's ID"); - goto error; - } - - _SET(set, _EVENT_ID_SET); - } else if (!strcmp(left, "stream_id")) { - if (_IS_SET(set, _EVENT_STREAM_ID_SET)) { - _PERROR_DUP_ATTR("stream_id", - "event declaration"); - ret = -EPERM; - goto error; - } - - ret = get_unary_unsigned(&node->u.ctf_expression.right, - (uint64_t *) stream_id); - if (ret || *stream_id < 0) { - _PERROR("%s", "unexpected unary expression for event declaration's \"stream_id\" attribute"); - ret = -EINVAL; - goto error; - } - - _SET(set, _EVENT_STREAM_ID_SET); - } else if (!strcmp(left, "context")) { - if (_IS_SET(set, _EVENT_CONTEXT_SET)) { - _PERROR("%s", "duplicate \"context\" entry in event declaration"); - ret = -EPERM; - goto error; - } - - ret = visit_type_specifier_list(ctx, - _BT_LIST_FIRST_ENTRY( - &node->u.ctf_expression.right, - struct ctf_node, siblings), - &decl); - if (ret) { - _PERROR("%s", "cannot create event context declaration"); - goto error; - } - - assert(decl); - ret = bt_ctf_event_class_set_context_type( - event_class, decl); - BT_PUT(decl); - if (ret) { - _PERROR("%s", "cannot set event's context declaration"); - goto error; - } - - _SET(set, _EVENT_CONTEXT_SET); - } else if (!strcmp(left, "fields")) { - if (_IS_SET(set, _EVENT_FIELDS_SET)) { - _PERROR("%s", "duplicate \"fields\" entry in event declaration"); - ret = -EPERM; - goto error; - } - - ret = visit_type_specifier_list(ctx, - _BT_LIST_FIRST_ENTRY( - &node->u.ctf_expression.right, - struct ctf_node, siblings), - &decl); - if (ret) { - _PERROR("%s", "cannot create event payload declaration"); - goto error; - } - - assert(decl); - ret = bt_ctf_event_class_set_payload_type( - event_class, decl); - BT_PUT(decl); - if (ret) { - _PERROR("%s", "cannot set event's payload declaration"); - goto error; - } - - _SET(set, _EVENT_FIELDS_SET); - } else if (!strcmp(left, "loglevel")) { - uint64_t loglevel; - - if (_IS_SET(set, _EVENT_LOGLEVEL_SET)) { - _PERROR_DUP_ATTR("loglevel", - "event declaration"); - ret = -EPERM; - goto error; - } - - ret = get_unary_unsigned(&node->u.ctf_expression.right, - &loglevel); - if (ret) { - _PERROR("%s", "unexpected unary expression for event declaration's \"loglevel\" attribute"); - ret = -EINVAL; - goto error; - } - - // TODO: FIXME: set log level here - - _SET(set, _EVENT_LOGLEVEL_SET); - } else if (!strcmp(left, "model.emf.uri")) { - char *right; - - if (_IS_SET(set, _EVENT_MODEL_EMF_URI_SET)) { - _PERROR_DUP_ATTR("model.emf.uri", - "event declaration"); - ret = -EPERM; - goto error; - } - - right = concatenate_unary_strings( - &node->u.ctf_expression.right); - if (!right) { - _PERROR("%s", "unexpected unary expression for event declaration's \"model.emf.uri\" attribute"); - ret = -EINVAL; - goto error; - } - - // TODO: FIXME: set model EMF URI here - - g_free(right); - _SET(set, _EVENT_MODEL_EMF_URI_SET); - } else { - _PWARNING("unknown attribute \"%s\" in event declaration", - left); - } - - g_free(left); - left = NULL; - break; - } - default: - ret = -EPERM; - goto error; - } - - return 0; - -error: - if (left) { - g_free(left); - } - - BT_PUT(decl); - - return ret; -} - -static -char *get_event_decl_name(struct ctx *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 = concatenate_unary_strings(&iter->u.ctf_expression.left); - if (!left) { - goto error; - } - - if (!strcmp(left, "name")) { - name = concatenate_unary_strings( - &iter->u.ctf_expression.right); - if (!name) { - _PERROR("%s", "unexpected unary expression for event declaration's \"name\" attribute"); - goto error; - } - } - - g_free(left); - left = NULL; - - if (name) { - break; - } - } - - return name; - -error: - g_free(left); - - return NULL; -} - -static -int reset_event_decl_types(struct ctx *ctx, - struct bt_ctf_event_class *event_class) -{ - int ret = 0; - _BT_CTF_FIELD_TYPE_INIT(decl); - - /* Event context */ - decl = bt_ctf_field_type_structure_create(); - if (!decl) { - _PERROR("%s", "cannot create initial, empty event context structure"); - ret = -ENOMEM; - goto error; - } - - ret = bt_ctf_event_class_set_context_type(event_class, decl); - BT_PUT(decl); - if (ret) { - _PERROR("%s", "cannot set initial, empty event context structure"); - goto error; - } - - /* Event payload */ - decl = bt_ctf_field_type_structure_create(); - if (!decl) { - _PERROR("%s", "cannot create initial, empty event payload structure"); - ret = -ENOMEM; - goto error; - } - - ret = bt_ctf_event_class_set_payload_type(event_class, decl); - BT_PUT(decl); - if (ret) { - _PERROR("%s", "cannot set initial, empty event payload structure"); - goto error; - } - - return 0; - -error: - BT_PUT(decl); - - return ret; -} - -static -int reset_stream_decl_types(struct ctx *ctx, - struct bt_ctf_stream_class *stream_class) -{ - int ret = 0; - _BT_CTF_FIELD_TYPE_INIT(decl); - - /* Packet context */ - decl = bt_ctf_field_type_structure_create(); - if (!decl) { - _PERROR("%s", "cannot create initial, empty packet context structure"); - ret = -ENOMEM; - goto error; - } - - ret = bt_ctf_stream_class_set_packet_context_type(stream_class, decl); - BT_PUT(decl); - if (ret) { - _PERROR("%s", "cannot set initial, empty packet context structure"); - goto error; - } - - /* Event header */ - decl = bt_ctf_field_type_structure_create(); - if (!decl) { - _PERROR("%s", "cannot create initial, empty event header structure"); - ret = -ENOMEM; - goto error; - } - - ret = bt_ctf_stream_class_set_event_header_type(stream_class, decl); - BT_PUT(decl); - if (ret) { - _PERROR("%s", "cannot set initial, empty event header structure"); - goto error; - } - - /* Event context */ - decl = bt_ctf_field_type_structure_create(); - if (!decl) { - _PERROR("%s", "cannot create initial, empty stream event context structure"); - ret = -ENOMEM; - goto error; - } - - ret = bt_ctf_stream_class_set_event_context_type(stream_class, decl); - BT_PUT(decl); - if (ret) { - _PERROR("%s", "cannot set initial, empty stream event context structure"); - goto error; - } - - return 0; - -error: - BT_PUT(decl); - - return ret; -} - -static -struct bt_ctf_stream_class *create_reset_stream_class(struct ctx *ctx) -{ - int ret; - struct bt_ctf_stream_class *stream_class; - - stream_class = bt_ctf_stream_class_create(NULL); - if (!stream_class) { - _PERROR("%s", "cannot create stream class"); - goto error; - } - - /* - * Set packet context, event header, and event context to empty - * structures to override the default ones. - */ - ret = reset_stream_decl_types(ctx, stream_class); - if (ret) { - goto error; - } - - return stream_class; - -error: - BT_PUT(stream_class); - - return NULL; -} - -static -int visit_event_decl(struct ctx *ctx, struct ctf_node *node) -{ - int ret = 0; - int set = 0; - int64_t event_id; - struct ctf_node *iter; - int64_t stream_id = -1; - char *event_name = NULL; - struct bt_ctf_event_class *event_class = NULL; - struct bt_ctf_event_class *eevent_class; - struct bt_ctf_stream_class *stream_class; - struct bt_list_head *decl_list = &node->u.event.declaration_list; - - if (node->visited) { - goto end; - } - - node->visited = TRUE; - event_name = get_event_decl_name(ctx, node); - if (!event_name) { - _PERROR("%s", - "missing \"name\" attribute in event declaration"); - ret = -EPERM; - goto error; - } - - event_class = bt_ctf_event_class_create(event_name); - - /* - * Set context and fields to empty structures to override the - * default ones. - */ - ret = reset_event_decl_types(ctx, event_class); - if (ret) { - goto error; - } - - - ret = ctx_push_scope(ctx); - if (ret) { - _PERROR("%s", "cannot push scope"); - goto error; - } - - bt_list_for_each_entry(iter, decl_list, siblings) { - ret = visit_event_decl_entry(ctx, iter, event_class, - &stream_id, &set); - if (ret) { - goto error; - } - } - - if (!_IS_SET(&set, _EVENT_STREAM_ID_SET)) { - GList *keys = NULL; - struct bt_ctf_stream_class *new_stream_class; - - /* Allow missing stream_id if there is only a single stream */ - switch (g_hash_table_size(ctx->stream_classes)) { - case 0: - /* Create stream if there's none */ - new_stream_class = create_reset_stream_class(ctx); - if (!new_stream_class) { - ret = -EINVAL; - goto error; - } - - ret = bt_ctf_stream_class_set_id(new_stream_class, 0); - if (ret) { - _PERROR("%s", "cannot set stream's ID"); - BT_PUT(new_stream_class); - goto error; - } - - stream_id = 0; - - /* Move reference to visitor's context */ - g_hash_table_insert(ctx->stream_classes, - (gpointer) stream_id, new_stream_class); - new_stream_class = NULL; - - break; - case 1: - /* Single stream: get its ID */ - keys = g_hash_table_get_keys(ctx->stream_classes); - stream_id = (int64_t) keys->data; - g_list_free(keys); - keys = NULL; - break; - default: - _PERROR("%s", "missing \"stream_id\" attribute in event declaration"); - ret = -EPERM; - goto error; - } - } - - - - assert(stream_id >= 0); - - /* We have the stream ID now; borrow the stream class if found */ - stream_class = g_hash_table_lookup(ctx->stream_classes, - (gpointer) stream_id); - if (!stream_class) { - _PERROR("cannot find stream class with ID %" PRId64, - stream_id); - ret = -EINVAL; - goto error; - } - - if (!_IS_SET(&set, _EVENT_ID_SET)) { - /* Allow only one event without ID per stream */ - if (bt_ctf_stream_class_get_event_class_count(stream_class) != - 0) { - _PERROR("%s", - "missing \"id\" field in event declaration"); - ret = -EPERM; - goto error; - } - - /* Automatic ID */ - ret = bt_ctf_event_class_set_id(event_class, 0); - if (ret) { - _PERROR("%s", "cannot set event's ID"); - goto error; - } - } - - event_id = bt_ctf_event_class_get_id(event_class); - if (event_id < 0) { - _PERROR("%s", "cannot get event's ID"); - ret = -EINVAL; - goto error; - } - - eevent_class = bt_ctf_stream_class_get_event_class_by_id(stream_class, - event_id); - if (eevent_class) { - BT_PUT(eevent_class); - _PERROR("%s", "duplicate event with ID %" PRId64 " in same stream"); - ret = -EEXIST; - goto error; - } - - eevent_class = bt_ctf_stream_class_get_event_class_by_name(stream_class, - event_name); - if (eevent_class) { - BT_PUT(eevent_class); - eevent_class = NULL; - _PERROR("%s", - "duplicate event with name \"%s\" in same stream"); - ret = -EEXIST; - goto error; - } - - g_free(event_name); - ret = bt_ctf_stream_class_add_event_class(stream_class, event_class); - BT_PUT(event_class); - event_class = NULL; - - if (ret) { - _PERROR("%s", "cannot add event class to stream class"); - goto error; - } - -end: - return 0; - -error: - g_free(event_name); - BT_PUT(event_class); - - /* stream_class is borrowed; it still belongs to the hash table */ - - return ret; -} - -static -int visit_stream_decl_entry(struct ctx *ctx, struct ctf_node *node, - struct bt_ctf_stream_class *stream_class, int *set) -{ - int ret = 0; - char *left = NULL; - _BT_CTF_FIELD_TYPE_INIT(decl); - - switch (node->type) { - case NODE_TYPEDEF: - ret = visit_typedef(ctx, node->u._typedef.type_specifier_list, - &node->u._typedef.type_declarators); - if (ret) { - _PERROR("%s", - "cannot add typedef in \"stream\" declaration"); - goto error; - } - break; - case NODE_TYPEALIAS: - ret = visit_typealias(ctx, node->u.typealias.target, - node->u.typealias.alias); - if (ret) { - _PERROR("%s", "cannot add typealias in \"stream\" declaration"); - goto error; - } - break; - case NODE_CTF_EXPRESSION: - { - left = concatenate_unary_strings(&node->u.ctf_expression.left); - if (!left) { - ret = -EINVAL; - goto error; - } - - if (!strcmp(left, "id")) { - int64_t id; - gpointer ptr; - - if (_IS_SET(set, _STREAM_ID_SET)) { - _PERROR_DUP_ATTR("id", "stream declaration"); - ret = -EPERM; - goto error; - } - - ret = get_unary_unsigned(&node->u.ctf_expression.right, - (uint64_t *) &id); - if (ret || id < 0) { - _PERROR("%s", "unexpected unary expression for stream declaration's \"id\" attribute"); - ret = -EINVAL; - goto error; - } - - ptr = g_hash_table_lookup(ctx->stream_classes, - (gpointer) id); - if (ptr) { - _PERROR("duplicate stream with ID %" PRId64, - id); - ret = -EEXIST; - goto error; - } - - ret = bt_ctf_stream_class_set_id(stream_class, id); - if (ret) { - _PERROR("%s", - "cannot set stream declaration's ID"); - goto error; - } - - _SET(set, _STREAM_ID_SET); - } else if (!strcmp(left, "event.header")) { - if (_IS_SET(set, _STREAM_EVENT_HEADER_SET)) { - _PERROR("%s", "duplicate \"event.header\" entry in stream declaration"); - ret = -EPERM; - goto error; - } - - ret = visit_type_specifier_list(ctx, - _BT_LIST_FIRST_ENTRY( - &node->u.ctf_expression.right, - struct ctf_node, siblings), - &decl); - if (ret) { - _PERROR("%s", "cannot create event header declaration"); - goto error; - } - - assert(decl); - - ret = bt_ctf_stream_class_set_event_header_type( - stream_class, decl); - BT_PUT(decl); - if (ret) { - _PERROR("%s", "cannot set stream's event header declaration"); - goto error; - } - - _SET(set, _STREAM_EVENT_HEADER_SET); - } else if (!strcmp(left, "event.context")) { - if (_IS_SET(set, _STREAM_EVENT_CONTEXT_SET)) { - _PERROR("%s", "duplicate \"event.context\" entry in stream declaration"); - ret = -EPERM; - goto error; - } - - ret = visit_type_specifier_list(ctx, - _BT_LIST_FIRST_ENTRY( - &node->u.ctf_expression.right, - struct ctf_node, siblings), - &decl); - if (ret) { - _PERROR("%s", "cannot create stream event context declaration"); - goto error; - } - - assert(decl); - - ret = bt_ctf_stream_class_set_event_context_type( - stream_class, decl); - BT_PUT(decl); - if (ret) { - _PERROR("%s", "cannot set stream's event context declaration"); - goto error; - } - - _SET(set, _STREAM_EVENT_CONTEXT_SET); - } else if (!strcmp(left, "packet.context")) { - if (_IS_SET(set, _STREAM_PACKET_CONTEXT_SET)) { - _PERROR("%s", "duplicate \"packet.context\" entry in stream declaration"); - ret = -EPERM; - goto error; - } - - ret = visit_type_specifier_list(ctx, - _BT_LIST_FIRST_ENTRY( - &node->u.ctf_expression.right, - struct ctf_node, siblings), - &decl); - if (ret) { - _PERROR("%s", "cannot create packet context declaration"); - goto error; - } - - assert(decl); - - ret = bt_ctf_stream_class_set_packet_context_type( - stream_class, decl); - BT_PUT(decl); - if (ret) { - _PERROR("%s", "cannot set stream's packet context declaration"); - goto error; - } - - _SET(set, _STREAM_PACKET_CONTEXT_SET); - } else { - _PWARNING("unknown attribute \"%s\" in stream declaration", - left); - } - - g_free(left); - left = NULL; - break; - } - - default: - ret = -EPERM; - goto error; - } - - return 0; - -error: - g_free(left); - BT_PUT(decl); - - return ret; -} - -static -int visit_stream_decl(struct ctx *ctx, struct ctf_node *node) -{ - int64_t id; - int set = 0; - int ret = 0; - struct ctf_node *iter; - struct bt_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 = create_reset_stream_class(ctx); - if (!stream_class) { - ret = -EINVAL; - goto error; - } - - ret = ctx_push_scope(ctx); - if (ret) { - _PERROR("%s", "cannot push scope"); - goto error; - } - - bt_list_for_each_entry(iter, decl_list, siblings) { - ret = visit_stream_decl_entry(ctx, iter, stream_class, &set); - if (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 */ - _BT_CTF_FIELD_TYPE_INIT(stream_id_decl); - _BT_CTF_FIELD_TYPE_INIT(packet_header_decl); - - packet_header_decl = - bt_ctf_trace_get_packet_header_type(ctx->trace); - if (!packet_header_decl) { - _PERROR("%s", - "cannot get trace packet header declaration"); - goto error; - } - - stream_id_decl = - bt_ctf_field_type_structure_get_field_type_by_name( - packet_header_decl, "stream_id"); - BT_PUT(packet_header_decl); - if (!stream_id_decl) { - _PERROR("%s", "missing \"stream_id\" field in packet header declaration, but \"id\" attribute is declared for stream"); - goto error; - } - - if (!bt_ctf_field_type_is_integer(stream_id_decl)) { - BT_PUT(stream_id_decl); - _PERROR("%s", "\"stream_id\" field in packet header declaration is not an integer"); - goto error; - } - - BT_PUT(stream_id_decl); - } else { - /* Allow only _one_ ID-less stream */ - if (g_hash_table_size(ctx->stream_classes) != 0) { - _PERROR("%s", - "missing \"id\" field in stream declaration"); - ret = -EPERM; - goto error; - } - - /* Automatic ID: 0 */ - ret = bt_ctf_stream_class_set_id(stream_class, 0); - } - - id = bt_ctf_stream_class_get_id(stream_class); - if (id < 0) { - _PERROR("wrong stream ID: %" PRId64, id); - ret = -EINVAL; - goto error; - } - - /* Move reference to visitor's context */ - g_hash_table_insert(ctx->stream_classes, (gpointer) (int64_t) id, - stream_class); - stream_class = NULL; - -end: - return 0; - -error: - BT_PUT(stream_class); - - return ret; -} - -static -int visit_trace_decl_entry(struct ctx *ctx, struct ctf_node *node, int *set) -{ - int ret = 0; - char *left = NULL; - _BT_CTF_FIELD_TYPE_INIT(packet_header_decl); - - switch (node->type) { - case NODE_TYPEDEF: - ret = visit_typedef(ctx, node->u._typedef.type_specifier_list, - &node->u._typedef.type_declarators); - if (ret) { - _PERROR("%s", - "cannot add typedef in \"trace\" declaration"); - goto error; - } - break; - case NODE_TYPEALIAS: - ret = visit_typealias(ctx, node->u.typealias.target, - node->u.typealias.alias); - if (ret) { - _PERROR("%s", - "cannot add typealias in \"trace\" declaration"); - goto error; - } - break; - case NODE_CTF_EXPRESSION: - { - left = concatenate_unary_strings(&node->u.ctf_expression.left); - if (!left) { - ret = -EINVAL; - goto error; - } - - if (!strcmp(left, "major")) { - if (_IS_SET(set, _TRACE_MAJOR_SET)) { - _PERROR_DUP_ATTR("major", "trace declaration"); - ret = -EPERM; - goto error; - } - - ret = get_unary_unsigned(&node->u.ctf_expression.right, - &ctx->trace_major); - if (ret) { - _PERROR("%s", "unexpected unary expression for trace's \"major\" attribute"); - ret = -EINVAL; - goto error; - } - - _SET(set, _TRACE_MAJOR_SET); - } else if (!strcmp(left, "minor")) { - if (_IS_SET(set, _TRACE_MINOR_SET)) { - _PERROR_DUP_ATTR("minor", "trace declaration"); - ret = -EPERM; - goto error; - } - - ret = get_unary_unsigned(&node->u.ctf_expression.right, - &ctx->trace_minor); - if (ret) { - _PERROR("%s", "unexpected unary expression for trace's \"minor\" attribute"); - ret = -EINVAL; - goto error; - } - - _SET(set, _TRACE_MINOR_SET); - } else if (!strcmp(left, "uuid")) { - if (_IS_SET(set, _TRACE_UUID_SET)) { - _PERROR_DUP_ATTR("uuid", "trace declaration"); - ret = -EPERM; - goto error; - } - - ret = get_unary_uuid(&node->u.ctf_expression.right, - ctx->trace_uuid); - if (ret) { - _PERROR("%s", - "invalid trace declaration's UUID"); - goto error; - } - - _SET(set, _TRACE_UUID_SET); - } else if (!strcmp(left, "byte_order")) { - /* Native byte order is already known at this stage */ - if (_IS_SET(set, _TRACE_BYTE_ORDER_SET)) { - _PERROR_DUP_ATTR("byte_order", - "trace declaration"); - ret = -EPERM; - goto error; - } - - _SET(set, _TRACE_BYTE_ORDER_SET); - } else if (!strcmp(left, "packet.header")) { - if (_IS_SET(set, _TRACE_PACKET_HEADER_SET)) { - _PERROR("%s", "duplicate \"packet.header\" entry in trace declaration"); - ret = -EPERM; - goto error; - } - - ret = visit_type_specifier_list(ctx, - _BT_LIST_FIRST_ENTRY( - &node->u.ctf_expression.right, - struct ctf_node, siblings), - &packet_header_decl); - if (ret) { - _PERROR("%s", "cannot create packet header declaration"); - goto error; - } - - assert(packet_header_decl); - ret = bt_ctf_trace_set_packet_header_type(ctx->trace, - packet_header_decl); - BT_PUT(packet_header_decl); - if (ret) { - _PERROR("%s", "cannot set trace declaration's packet header declaration"); - goto error; - } - - _SET(set, _TRACE_PACKET_HEADER_SET); - } else { - _PWARNING("%s", "unknown attribute \"%s\" in trace declaration"); - } - - g_free(left); - left = NULL; - break; - } - default: - _PERROR("%s", "unknown expression in trace declaration"); - ret = -EINVAL; - goto error; - } - - return 0; - -error: - g_free(left); - BT_PUT(packet_header_decl); - - return ret; -} - -static -int visit_trace_decl(struct ctx *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) { - _PERROR("%s", "duplicate \"trace\" block"); - ret = -EEXIST; - goto error; - } - - ret = ctx_push_scope(ctx); - if (ret) { - _PERROR("%s", "cannot push scope"); - goto error; - } - - bt_list_for_each_entry(iter, decl_list, siblings) { - ret = visit_trace_decl_entry(ctx, iter, &set); - if (ret) { - ctx_pop_scope(ctx); - goto error; - } - } - - ctx_pop_scope(ctx); - - if (!_IS_SET(&set, _TRACE_MAJOR_SET)) { - _PERROR("%s", - "missing \"major\" attribute in trace declaration"); - ret = -EPERM; - goto error; - } - - if (!_IS_SET(&set, _TRACE_MINOR_SET)) { - _PERROR("%s", - "missing \"minor\" attribute in trace declaration"); - ret = -EPERM; - goto error; - } - - if (!_IS_SET(&set, _TRACE_BYTE_ORDER_SET)) { - _PERROR("%s", "missing \"byte_order\" attribute in trace declaration"); - ret = -EPERM; - goto error; - } - - ctx->is_trace_visited = TRUE; - -end: - return 0; - -error: - return ret; -} - -static -int visit_env(struct ctx *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) { - _PERROR("%s", "wrong expression in environment entry"); - ret = -EPERM; - goto error; - } - - left = concatenate_unary_strings( - &entry_node->u.ctf_expression.left); - if (!left) { - _PERROR("%s", "cannot get environment entry name"); - ret = -EINVAL; - goto error; - } - - if (is_unary_string(right_head)) { - char *right = concatenate_unary_strings(right_head); - - if (!right) { - _PERROR("unexpected unary expression for environment entry's value (\"%s\")", - left); - ret = -EINVAL; - goto error; - } - - printf_verbose("env.%s = \"%s\"\n", left, right); - ret = bt_ctf_trace_set_environment_field_string( - ctx->trace, left, right); - g_free(right); - - if (ret) { - _PERROR("environment: cannot add entry \"%s\" to trace", - left); - goto error; - } - } 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(right_head, - (uint64_t *) &v); - } else { - ret = get_unary_signed(right_head, &v); - } - if (ret) { - _PERROR("unexpected unary expression for environment entry's value (\"%s\")", - left); - ret = -EINVAL; - goto error; - } - - printf_verbose("env.%s = %" PRId64 "\n", left, v); - ret = bt_ctf_trace_set_environment_field_integer( - ctx->trace, left, v); - if (ret) { - _PERROR("environment: cannot add entry \"%s\" to trace", - left); - goto error; - } - } else { - printf_verbose("%s: environment entry \"%s\" has unknown type\n", - __func__, left); - } - - g_free(left); - left = NULL; - } - -end: - return 0; - -error: - g_free(left); - - return ret; -} - -static -int set_trace_byte_order(struct ctx *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 = concatenate_unary_strings( - &node->u.ctf_expression.left); - if (!left) { - ret = -EINVAL; - goto error; - } - - if (!strcmp(left, "byte_order")) { - enum bt_ctf_byte_order bo; - - if (_IS_SET(&set, _TRACE_BYTE_ORDER_SET)) { - _PERROR_DUP_ATTR("byte_order", - "trace declaration"); - 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->efd, - right_node); - if (bo == BT_CTF_BYTE_ORDER_UNKNOWN) { - _PERROR("%s", "unknown \"byte_order\" attribute in trace declaration"); - ret = -EINVAL; - goto error; - } else if (bo == BT_CTF_BYTE_ORDER_NATIVE) { - _PERROR("%s", "\"byte_order\" attribute cannot be set to \"native\" in trace declaration"); - ret = -EPERM; - goto error; - } - - ret = bt_ctf_trace_set_byte_order( - ctx->trace, bo); - if (ret) { - _PERROR("cannot set trace's byte order (%d)", - ret); - goto error; - } - } - - g_free(left); - left = NULL; - } - } - - if (!_IS_SET(&set, _TRACE_BYTE_ORDER_SET)) { - _PERROR("%s", "missing \"byte_order\" attribute in trace declaration"); - ret = -EINVAL; - goto error; - } - - return 0; - -error: - g_free(left); - - return ret; -} - -static -int visit_clock_decl_entry(struct ctx *ctx, struct ctf_node *entry_node, - struct bt_ctf_clock *clock, int *set) -{ - int ret = 0; - char *left = NULL; - - if (entry_node->type != NODE_CTF_EXPRESSION) { - ret = -EPERM; - goto error; - } - - left = concatenate_unary_strings(&entry_node->u.ctf_expression.left); - if (!left) { - ret = -EINVAL; - goto error; - } - - if (!strcmp(left, "name")) { - char *right; - - if (_IS_SET(set, _CLOCK_NAME_SET)) { - _PERROR_DUP_ATTR("name", "clock declaration"); - ret = -EPERM; - goto error; - } - - right = concatenate_unary_strings( - &entry_node->u.ctf_expression.right); - if (!right) { - _PERROR("%s", "unexpected unary expression for clock declaration's \"name\" attribute"); - ret = -EINVAL; - goto error; - } - - ret = bt_ctf_clock_set_name(clock, right); - if (ret) { - _PERROR("%s", "cannot set clock's name"); - g_free(right); - goto error; - } - - g_free(right); - _SET(set, _CLOCK_NAME_SET); - } else if (!strcmp(left, "uuid")) { - unsigned char uuid[BABELTRACE_UUID_LEN]; - - if (_IS_SET(set, _CLOCK_UUID_SET)) { - _PERROR_DUP_ATTR("uuid", "clock declaration"); - ret = -EPERM; - goto error; - } - - ret = get_unary_uuid(&entry_node->u.ctf_expression.right, uuid); - if (ret) { - _PERROR("%s", "invalid clock UUID"); - goto error; - } - - ret = bt_ctf_clock_set_uuid(clock, uuid); - if (ret) { - _PERROR("%s", "cannot set clock's UUID"); - goto error; - } - - _SET(set, _CLOCK_UUID_SET); - } else if (!strcmp(left, "description")) { - char *right; - - if (_IS_SET(set, _CLOCK_DESCRIPTION_SET)) { - _PERROR_DUP_ATTR("description", "clock declaration"); - ret = -EPERM; - goto error; - } - - right = concatenate_unary_strings( - &entry_node->u.ctf_expression.right); - if (!right) { - _PERROR("%s", "unexpected unary expression for clock's \"description\" attribute"); - ret = -EINVAL; - goto error; - } - - ret = bt_ctf_clock_set_description(clock, right); - if (ret) { - _PERROR("%s", "cannot set clock's description"); - g_free(right); - goto error; - } - - g_free(right); - _SET(set, _CLOCK_DESCRIPTION_SET); - } else if (!strcmp(left, "freq")) { - uint64_t freq; - - if (_IS_SET(set, _CLOCK_FREQ_SET)) { - _PERROR_DUP_ATTR("freq", "clock declaration"); - ret = -EPERM; - goto error; - } - - ret = get_unary_unsigned( - &entry_node->u.ctf_expression.right, &freq); - if (ret) { - _PERROR("%s", "unexpected unary expression for clock declaration's \"freq\" attribute"); - ret = -EINVAL; - goto error; - } - - ret = bt_ctf_clock_set_frequency(clock, freq); - if (ret) { - _PERROR("%s", "cannot set clock's frequency"); - goto error; - } - - _SET(set, _CLOCK_FREQ_SET); - } else if (!strcmp(left, "precision")) { - uint64_t precision; - - if (_IS_SET(set, _CLOCK_PRECISION_SET)) { - _PERROR_DUP_ATTR("precision", "clock declaration"); - ret = -EPERM; - goto error; - } - - ret = get_unary_unsigned( - &entry_node->u.ctf_expression.right, &precision); - if (ret) { - _PERROR("%s", "unexpected unary expression for clock declaration's \"precision\" attribute"); - ret = -EINVAL; - goto error; - } - - ret = bt_ctf_clock_set_precision(clock, precision); - if (ret) { - _PERROR("%s", "cannot set clock's precision"); - goto error; - } - - _SET(set, _CLOCK_PRECISION_SET); - } else if (!strcmp(left, "offset_s")) { - uint64_t offset_s; - - if (_IS_SET(set, _CLOCK_OFFSET_S_SET)) { - _PERROR_DUP_ATTR("offset_s", "clock declaration"); - ret = -EPERM; - goto error; - } - - ret = get_unary_unsigned( - &entry_node->u.ctf_expression.right, &offset_s); - if (ret) { - _PERROR("%s", "unexpected unary expression for clock declaration's \"offset_s\" attribute"); - ret = -EINVAL; - goto error; - } - - ret = bt_ctf_clock_set_offset_s(clock, offset_s); - if (ret) { - _PERROR("%s", "cannot set clock's offset in seconds"); - goto error; - } - - _SET(set, _CLOCK_OFFSET_S_SET); - } else if (!strcmp(left, "offset")) { - uint64_t offset; - - if (_IS_SET(set, _CLOCK_OFFSET_SET)) { - _PERROR_DUP_ATTR("offset", "clock declaration"); - ret = -EPERM; - goto error; - } - - ret = get_unary_unsigned( - &entry_node->u.ctf_expression.right, &offset); - if (ret) { - _PERROR("%s", "unexpected unary expression for clock declaration's \"offset\" attribute"); - ret = -EINVAL; - goto error; - } - - ret = bt_ctf_clock_set_offset(clock, offset); - if (ret) { - _PERROR("%s", "cannot set clock's offset in cycles"); - goto error; - } - - _SET(set, _CLOCK_OFFSET_SET); - } else if (!strcmp(left, "absolute")) { - struct ctf_node *right; - - if (_IS_SET(set, _CLOCK_ABSOLUTE_SET)) { - _PERROR_DUP_ATTR("absolute", "clock declaration"); - ret = -EPERM; - goto error; - } - - right = _BT_LIST_FIRST_ENTRY( - &entry_node->u.ctf_expression.right, - struct ctf_node, siblings); - ret = get_boolean(ctx->efd, right); - if (ret < 0) { - _PERROR("%s", "unexpected unary expression for clock declaration's \"absolute\" attribute"); - ret = -EINVAL; - goto error; - } - - ret = bt_ctf_clock_set_is_absolute(clock, ret); - if (ret) { - _PERROR("%s", "cannot set clock's absolute option"); - goto error; - } - - _SET(set, _CLOCK_ABSOLUTE_SET); - } else { - _PWARNING("unknown attribute \"%s\" in clock declaration", - left); - } - - g_free(left); - left = NULL; - - return 0; - -error: - g_free(left); - - return ret; -} - -static -int visit_clock_decl(struct ctx *ctx, struct ctf_node *clock_node) -{ - int ret = 0; - int set = 0; - struct bt_ctf_clock *clock; - struct ctf_node *entry_node; - struct bt_list_head *decl_list = &clock_node->u.clock.declaration_list; - - if (clock_node->visited) { - return 0; - } - - clock_node->visited = TRUE; - clock = bt_ctf_clock_create(NULL); - if (!clock) { - _PERROR("%s", "cannot create clock"); - ret = -ENOMEM; - goto error; - } - - bt_list_for_each_entry(entry_node, decl_list, siblings) { - ret = visit_clock_decl_entry(ctx, entry_node, clock, &set); - if (ret) { - goto error; - } - } - - if (!_IS_SET(&set, _CLOCK_NAME_SET)) { - _PERROR("%s", - "missing \"name\" attribute in clock declaration"); - ret = -EPERM; - goto error; - } - - if (bt_ctf_trace_get_clock_count(ctx->trace) != 0) { - _PERROR("%s", "only CTF traces with a single clock declaration are supported as of this version"); - ret = -EINVAL; - goto error; - } - - ret = bt_ctf_trace_add_clock(ctx->trace, clock); - if (ret) { - _PERROR("%s", "cannot add clock to trace"); - goto error; - } - -error: - BT_PUT(clock); - - return ret; -} - -static -int visit_root_decl(struct ctx *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_typedef(ctx, - root_decl_node->u._typedef.type_specifier_list, - &root_decl_node->u._typedef.type_declarators); - if (ret) { - _PERROR("%s", "cannot add typedef in root scope"); - goto end; - } - break; - case NODE_TYPEALIAS: - ret = visit_typealias(ctx, root_decl_node->u.typealias.target, - root_decl_node->u.typealias.alias); - if (ret) { - _PERROR("%s", "cannot add typealias in root scope"); - goto end; - } - break; - case NODE_TYPE_SPECIFIER_LIST: - { - _BT_CTF_FIELD_TYPE_INIT(decl); - - /* - * Just add the type specifier to the root - * declaration scope. Put local reference. - */ - ret = visit_type_specifier_list(ctx, root_decl_node, &decl); - if (ret) { - assert(!decl); - goto end; - } - - BT_PUT(decl); - break; - } - default: - ret = -EPERM; - goto end; - } - -end: - return ret; -} - -static -int add_stream_classes_to_trace(struct ctx *ctx) -{ - int ret; - GHashTableIter iter; - gpointer key, stream_class; - - g_hash_table_iter_init(&iter, ctx->stream_classes); - - while (g_hash_table_iter_next(&iter, &key, &stream_class)) { - ret = bt_ctf_trace_add_stream_class(ctx->trace, - stream_class); - if (ret) { - int64_t id = bt_ctf_stream_class_get_id(stream_class); - _PERROR("cannot add stream class %" PRId64 " to trace", - id); - goto end; - } - } - -end: - return ret; -} - -int ctf_visitor_generate_ir(FILE *efd, struct ctf_node *node, - struct bt_ctf_trace **trace) -{ - int ret = 0; - struct ctx *ctx = NULL; - _BT_CTF_FIELD_TYPE_INIT(packet_header_decl); - - printf_verbose("CTF visitor: AST -> CTF IR...\n"); - - *trace = bt_ctf_trace_create(); - if (!*trace) { - _FPERROR(efd, "%s", "cannot create trace"); - ret = -ENOMEM; - goto error; - } - - /* Set packet header to an empty struct tu override the default one */ - packet_header_decl = bt_ctf_field_type_structure_create(); - - if (!packet_header_decl) { - _FPERROR(efd, - "%s", - "cannot create initial, empty packet header structure"); - ret = -ENOMEM; - goto error; - } - - ret = bt_ctf_trace_set_packet_header_type(*trace, packet_header_decl); - BT_PUT(packet_header_decl); - if (ret) { - _FPERROR(efd, - "%s", - "cannot set initial, empty packet header structure"); - goto error; - } - - ctx = ctx_create(*trace, efd); - if (!ctx) { - _FPERROR(efd, "%s", "cannot create visitor context"); - ret = -ENOMEM; - goto error; - } - - switch (node->type) { - case NODE_ROOT: - { - struct ctf_node *iter; - int got_trace_decl = FALSE; - int found_callsite = FALSE; - - /* - * Find trace declaration's byte order first (for early - * type aliases). - */ - bt_list_for_each_entry(iter, &node->u.root.trace, siblings) { - if (got_trace_decl) { - _PERROR("%s", "duplicate trace declaration"); - goto error; - } - - ret = set_trace_byte_order(ctx, iter); - if (ret) { - _PERROR("cannot set trace's byte order (%d)", - ret); - goto error; - } - - got_trace_decl = TRUE; - } - - if (!got_trace_decl) { - _PERROR("no trace declaration found (%d)", ret); - ret = -EPERM; - goto error; - } - - /* - * Visit clocks first since any early integer can be mapped - * to one. - */ - bt_list_for_each_entry(iter, &node->u.root.clock, siblings) { - ret = visit_clock_decl(ctx, iter); - if (ret) { - _PERROR("error while visiting clock declaration (%d)", - ret); - goto error; - } - } - - /* - * 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) { - _PERROR("error while visiting root declaration (%d)", - ret); - goto error; - } - } - - /* Callsite are not supported */ - bt_list_for_each_entry(iter, &node->u.root.callsite, siblings) { - found_callsite = TRUE; - break; - } - - if (found_callsite) { - _PWARNING("%s", "\"callsite\" blocks are not supported as of this version"); - } - - /* Environment */ - bt_list_for_each_entry(iter, &node->u.root.env, siblings) { - ret = visit_env(ctx, iter); - if (ret) { - _PERROR("error while visiting environment block (%d)", - ret); - goto error; - } - } - - /* Trace */ - bt_list_for_each_entry(iter, &node->u.root.trace, siblings) { - ret = visit_trace_decl(ctx, iter); - if (ret) { - _PERROR("%s", "error while visiting trace declaration"); - goto error; - } - } - - /* Streams */ - bt_list_for_each_entry(iter, &node->u.root.stream, siblings) { - ret = visit_stream_decl(ctx, iter); - if (ret) { - _PERROR("%s", "error while visiting stream declaration"); - goto error; - } - } - - /* Events */ - bt_list_for_each_entry(iter, &node->u.root.event, siblings) { - ret = visit_event_decl(ctx, iter); - if (ret) { - _PERROR("%s", "error while visiting event declaration"); - goto error; - } - } - break; - } - case NODE_UNKNOWN: - default: - _PERROR("unknown node type: %d", (int) node->type); - ret = -EINVAL; - goto error; - } - - /* Add stream classes to trace now */ - ret = add_stream_classes_to_trace(ctx); - if (ret) { - _PERROR("%s", "cannot add stream classes to trace"); - } - - ctx_destroy(ctx); - printf_verbose("done!\n"); - - return ret; - -error: - BT_PUT(packet_header_decl); - ctx_destroy(ctx); - BT_PUT(*trace); - - return ret; -} diff --git a/ctf-reader-proto/metadata-parsing/ctf-visitor-parent-links.c b/ctf-reader-proto/metadata-parsing/ctf-visitor-parent-links.c deleted file mode 100644 index 037496af..00000000 --- a/ctf-reader-proto/metadata-parsing/ctf-visitor-parent-links.c +++ /dev/null @@ -1,461 +0,0 @@ -/* - * ctf-visitor-parent-links.c - * - * Common Trace Format Metadata Parent Link Creator. - * - * Copyright 2010 - Mathieu Desnoyers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "ctf-scanner.h" -#include "ctf-parser.h" -#include "ctf-ast.h" - -#define fprintf_dbg(fd, fmt, args...) fprintf(fd, "%s: " fmt, __func__, ## args) - -static -int ctf_visitor_unary_expression(FILE *fd, int depth, struct ctf_node *node) -{ - int ret = 0; - - switch (node->u.unary_expression.link) { - case UNARY_LINK_UNKNOWN: - case UNARY_DOTLINK: - case UNARY_ARROWLINK: - case UNARY_DOTDOTDOT: - break; - default: - fprintf(fd, "[error] %s: unknown expression link type %d\n", __func__, - (int) 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(fd, depth + 1, - node->u.unary_expression.u.sbrac_exp); - if (ret) - return ret; - break; - - case UNARY_UNKNOWN: - default: - fprintf(fd, "[error] %s: unknown expression type %d\n", __func__, - (int) node->u.unary_expression.type); - return -EINVAL; - } - return 0; -} - -static -int ctf_visitor_type_specifier(FILE *fd, int depth, struct ctf_node *node) -{ - int ret; - - switch (node->u.type_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.type_specifier.node->parent = node; - ret = ctf_visitor_parent_links(fd, depth + 1, node->u.type_specifier.node); - if (ret) - return ret; - break; - - case TYPESPEC_UNKNOWN: - default: - fprintf(fd, "[error] %s: unknown type specifier %d\n", __func__, - (int) node->u.type_specifier.type); - return -EINVAL; - } - return 0; -} - -static -int ctf_visitor_type_declarator(FILE *fd, int depth, struct ctf_node *node) -{ - int ret = 0; - struct ctf_node *iter; - - depth++; - - bt_list_for_each_entry(iter, &node->u.type_declarator.pointers, - siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(fd, depth + 1, iter); - if (ret) - return ret; - } - - switch (node->u.type_declarator.type) { - case TYPEDEC_ID: - break; - case TYPEDEC_NESTED: - if (node->u.type_declarator.u.nested.type_declarator) { - node->u.type_declarator.u.nested.type_declarator->parent = node; - ret = ctf_visitor_parent_links(fd, depth + 1, - node->u.type_declarator.u.nested.type_declarator); - if (ret) - return ret; - } - if (!node->u.type_declarator.u.nested.abstract_array) { - bt_list_for_each_entry(iter, &node->u.type_declarator.u.nested.length, - siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(fd, depth + 1, iter); - if (ret) - return ret; - } - } - if (node->u.type_declarator.bitfield_len) { - node->u.type_declarator.bitfield_len = node; - ret = ctf_visitor_parent_links(fd, depth + 1, - node->u.type_declarator.bitfield_len); - if (ret) - return ret; - } - break; - case TYPEDEC_UNKNOWN: - default: - fprintf(fd, "[error] %s: unknown type declarator %d\n", __func__, - (int) node->u.type_declarator.type); - return -EINVAL; - } - depth--; - return 0; -} - -int ctf_visitor_parent_links(FILE *fd, int depth, struct ctf_node *node) -{ - 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(fd, depth + 1, iter); - if (ret) - return ret; - } - bt_list_for_each_entry(iter, &node->u.root.trace, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(fd, depth + 1, iter); - if (ret) - return ret; - } - bt_list_for_each_entry(iter, &node->u.root.stream, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(fd, depth + 1, iter); - if (ret) - return ret; - } - bt_list_for_each_entry(iter, &node->u.root.event, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(fd, depth + 1, iter); - if (ret) - return ret; - } - bt_list_for_each_entry(iter, &node->u.root.clock, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(fd, depth + 1, iter); - if (ret) - return ret; - } - bt_list_for_each_entry(iter, &node->u.root.callsite, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(fd, depth + 1, iter); - 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(fd, depth + 1, iter); - 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(fd, depth + 1, iter); - 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(fd, depth + 1, iter); - 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(fd, depth + 1, iter); - 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(fd, depth + 1, iter); - 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(fd, depth + 1, iter); - 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(fd, depth + 1, iter); - if (ret) - return ret; - } - bt_list_for_each_entry(iter, &node->u.ctf_expression.right, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(fd, depth + 1, iter); - if (ret) - return ret; - } - depth--; - break; - case NODE_UNARY_EXPRESSION: - return ctf_visitor_unary_expression(fd, depth, node); - - case NODE_TYPEDEF: - depth++; - node->u._typedef.type_specifier_list->parent = node; - ret = ctf_visitor_parent_links(fd, depth + 1, node->u._typedef.type_specifier_list); - if (ret) - return ret; - bt_list_for_each_entry(iter, &node->u._typedef.type_declarators, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(fd, depth + 1, iter); - if (ret) - return ret; - } - depth--; - break; - case NODE_TYPEALIAS_TARGET: - depth++; - node->u.typealias_target.type_specifier_list->parent = node; - ret = ctf_visitor_parent_links(fd, depth + 1, node->u.typealias_target.type_specifier_list); - if (ret) - return ret; - bt_list_for_each_entry(iter, &node->u.typealias_target.type_declarators, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(fd, depth + 1, iter); - if (ret) - return ret; - } - depth--; - break; - case NODE_TYPEALIAS_ALIAS: - depth++; - node->u.typealias_alias.type_specifier_list->parent = node; - ret = ctf_visitor_parent_links(fd, depth + 1, node->u.typealias_alias.type_specifier_list); - if (ret) - return ret; - bt_list_for_each_entry(iter, &node->u.typealias_alias.type_declarators, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(fd, depth + 1, iter); - if (ret) - return ret; - } - depth--; - break; - case NODE_TYPEALIAS: - node->u.typealias.target->parent = node; - ret = ctf_visitor_parent_links(fd, depth + 1, node->u.typealias.target); - if (ret) - return ret; - node->u.typealias.alias->parent = node; - ret = ctf_visitor_parent_links(fd, depth + 1, node->u.typealias.alias); - if (ret) - return ret; - break; - - case NODE_TYPE_SPECIFIER_LIST: - bt_list_for_each_entry(iter, &node->u.type_specifier_list.head, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(fd, depth + 1, iter); - if (ret) - return ret; - } - break; - - case NODE_TYPE_SPECIFIER: - ret = ctf_visitor_type_specifier(fd, depth, node); - if (ret) - return ret; - break; - case NODE_POINTER: - break; - case NODE_TYPE_DECLARATOR: - ret = ctf_visitor_type_declarator(fd, depth, node); - 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(fd, depth + 1, iter); - 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(fd, depth + 1, iter); - 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(fd, depth + 1, iter); - 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(fd, depth + 1, iter); - if (ret) - return ret; - } - break; - case NODE_ENUM: - depth++; - if (node->u._enum.container_type) { - ret = ctf_visitor_parent_links(fd, depth + 1, node->u._enum.container_type); - if (ret) - return ret; - } - - bt_list_for_each_entry(iter, &node->u._enum.enumerator_list, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(fd, depth + 1, iter); - if (ret) - return ret; - } - depth--; - break; - case NODE_STRUCT_OR_VARIANT_DECLARATION: - node->u.struct_or_variant_declaration.type_specifier_list->parent = node; - ret = ctf_visitor_parent_links(fd, depth + 1, - node->u.struct_or_variant_declaration.type_specifier_list); - if (ret) - return ret; - bt_list_for_each_entry(iter, &node->u.struct_or_variant_declaration.type_declarators, siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(fd, depth + 1, iter); - 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(fd, depth + 1, iter); - 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(fd, depth + 1, iter); - if (ret) - return ret; - } - bt_list_for_each_entry(iter, &node->u._struct.min_align, - siblings) { - iter->parent = node; - ret = ctf_visitor_parent_links(fd, depth + 1, iter); - if (ret) - return ret; - } - break; - - case NODE_UNKNOWN: - default: - fprintf(fd, "[error] %s: unknown node type %d\n", __func__, - (int) node->type); - return -EINVAL; - } - return ret; -} diff --git a/ctf-reader-proto/metadata-parsing/ctf-visitor-semantic-validator.c b/ctf-reader-proto/metadata-parsing/ctf-visitor-semantic-validator.c deleted file mode 100644 index 96776a95..00000000 --- a/ctf-reader-proto/metadata-parsing/ctf-visitor-semantic-validator.c +++ /dev/null @@ -1,978 +0,0 @@ -/* - * ctf-visitor-semantic-validator.c - * - * Common Trace Format Metadata Semantic Validator. - * - * Copyright 2010 - Mathieu Desnoyers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "ctf-scanner.h" -#include "ctf-parser.h" -#include "ctf-ast.h" - -#define _bt_list_first_entry(ptr, type, member) \ - bt_list_entry((ptr)->next, type, member) - -#define fprintf_dbg(fd, fmt, args...) fprintf(fd, "%s: " fmt, __func__, ## args) - -static -int _ctf_visitor_semantic_check(FILE *fd, int depth, struct ctf_node *node); - -static -int ctf_visitor_unary_expression(FILE *fd, int depth, struct ctf_node *node) -{ - 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) { - fprintf(fd, "[error]: semantic error (left child of a ctf expression is only allowed to be a string)\n"); - - 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: - fprintf(fd, "[error]: semantic error (children of type declarator and enum can only be unsigned numeric constants or references to fields (a.b.c))\n"); - 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: - fprintf(fd, "[error]: semantic error (structure alignment attribute can only be unsigned numeric constants)\n"); - 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. - */ - fprintf(fd, "[error]: semantic error (nested unary expressions not allowed ( () and [] ))\n"); - 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) { - fprintf(fd, "[error]: semantic error (empty link not allowed except on first node of unary expression (need to separate nodes with \".\" or \"->\")\n"); - 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) { - fprintf(fd, "[error]: semantic error (links \".\" and \"->\" are only allowed as children of ctf expression)\n"); - goto errperm; - } - /* - * Only strings can be separated linked by . or ->. - * This includes "", '' and non-quoted identifiers. - */ - if (node->u.unary_expression.type != UNARY_STRING) { - fprintf(fd, "[error]: semantic error (links \".\" and \"->\" are only allowed to separate strings and identifiers)\n"); - 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) { - fprintf(fd, "[error]: semantic error (links \".\" and \"->\" are not allowed before first node of the unary expression list)\n"); - goto errperm; - } - break; - case UNARY_DOTDOTDOT: - /* We only allow ... link between children of enumerator. */ - if (node->parent->type != NODE_ENUMERATOR) { - fprintf(fd, "[error]: semantic error (link \"...\" is only allowed within enumerator)\n"); - 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) { - fprintf(fd, "[error]: semantic error (link \"...\" is not allowed on the first node of the unary expression list)\n"); - goto errperm; - } - break; - default: - fprintf(fd, "[error] %s: unknown expression link type %d\n", __func__, - (int) node->u.unary_expression.link); - return -EINVAL; - } - return 0; - -errinval: - fprintf(fd, "[error] %s: incoherent parent type %s for node type %s\n", __func__, - node_type(node->parent), node_type(node)); - return -EINVAL; /* Incoherent structure */ - -errperm: - fprintf(fd, "[error] %s: semantic error (parent type %s for node type %s)\n", __func__, - node_type(node->parent), node_type(node)); - return -EPERM; /* Structure not allowed */ -} - -static -int ctf_visitor_type_specifier_list(FILE *fd, int depth, struct ctf_node *node) -{ - 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: - fprintf(fd, "[error] %s: incoherent parent type %s for node type %s\n", __func__, - node_type(node->parent), node_type(node)); - return -EINVAL; /* Incoherent structure */ -} - -static -int ctf_visitor_type_specifier(FILE *fd, int depth, struct ctf_node *node) -{ - 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: - fprintf(fd, "[error] %s: incoherent parent type %s for node type %s\n", __func__, - node_type(node->parent), node_type(node)); - return -EINVAL; /* Incoherent structure */ -} - -static -int ctf_visitor_type_declarator(FILE *fd, int depth, struct ctf_node *node) -{ - int ret = 0; - struct ctf_node *iter; - - depth++; - - switch (node->parent->type) { - case NODE_TYPE_DECLARATOR: - /* - * A nested type declarator is not allowed to contain pointers. - */ - if (!bt_list_empty(&node->u.type_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 typealias). - * NOT accepting alias with identifier. The declarator should - * be either empty or contain pointer(s). - */ - if (node->u.type_declarator.type == TYPEDEC_NESTED) - goto errperm; - bt_list_for_each_entry(iter, &node->parent->u.typealias_alias.type_specifier_list->u.type_specifier_list.head, - siblings) { - switch (iter->u.type_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.type_declarator.pointers)) - goto errperm; - break; - default: - break; - } - } - if (node->u.type_declarator.type == TYPEDEC_ID && - node->u.type_declarator.u.id != NULL) - 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.type_declarator.pointers, - siblings) { - ret = _ctf_visitor_semantic_check(fd, depth + 1, iter); - if (ret) - return ret; - } - - switch (node->u.type_declarator.type) { - case TYPEDEC_ID: - break; - case TYPEDEC_NESTED: - { - if (node->u.type_declarator.u.nested.type_declarator) { - ret = _ctf_visitor_semantic_check(fd, depth + 1, - node->u.type_declarator.u.nested.type_declarator); - if (ret) - return ret; - } - if (!node->u.type_declarator.u.nested.abstract_array) { - bt_list_for_each_entry(iter, &node->u.type_declarator.u.nested.length, - siblings) { - if (iter->type != NODE_UNARY_EXPRESSION) { - fprintf(fd, "[error] %s: expecting unary expression as length\n", __func__); - return -EINVAL; - } - ret = _ctf_visitor_semantic_check(fd, depth + 1, iter); - if (ret) - return ret; - } - } else { - if (node->parent->type == NODE_TYPEALIAS_TARGET) { - fprintf(fd, "[error] %s: abstract array declarator not permitted as target of typealias\n", __func__); - return -EINVAL; - } - } - if (node->u.type_declarator.bitfield_len) { - ret = _ctf_visitor_semantic_check(fd, depth + 1, - node->u.type_declarator.bitfield_len); - if (ret) - return ret; - } - break; - } - case TYPEDEC_UNKNOWN: - default: - fprintf(fd, "[error] %s: unknown type declarator %d\n", __func__, - (int) node->u.type_declarator.type); - return -EINVAL; - } - depth--; - return 0; - -errinval: - fprintf(fd, "[error] %s: incoherent parent type %s for node type %s\n", __func__, - node_type(node->parent), node_type(node)); - return -EINVAL; /* Incoherent structure */ - -errperm: - fprintf(fd, "[error] %s: semantic error (parent type %s for node type %s)\n", __func__, - node_type(node->parent), node_type(node)); - return -EPERM; /* Structure not allowed */ -} - -static -int _ctf_visitor_semantic_check(FILE *fd, int depth, struct ctf_node *node) -{ - 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(fd, depth + 1, iter); - if (ret) - return ret; - } - bt_list_for_each_entry(iter, &node->u.root.trace, siblings) { - ret = _ctf_visitor_semantic_check(fd, depth + 1, iter); - if (ret) - return ret; - } - bt_list_for_each_entry(iter, &node->u.root.stream, siblings) { - ret = _ctf_visitor_semantic_check(fd, depth + 1, iter); - if (ret) - return ret; - } - bt_list_for_each_entry(iter, &node->u.root.event, siblings) { - ret = _ctf_visitor_semantic_check(fd, depth + 1, iter); - 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(fd, depth + 1, iter); - 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(fd, depth + 1, iter); - 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(fd, depth + 1, iter); - 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(fd, depth + 1, iter); - 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(fd, depth + 1, iter); - 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(fd, depth + 1, iter); - 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(fd, depth + 1, iter); - if (ret) - return ret; - } - bt_list_for_each_entry(iter, &node->u.ctf_expression.right, siblings) { - ret = _ctf_visitor_semantic_check(fd, depth + 1, iter); - if (ret) - return ret; - } - depth--; - break; - case NODE_UNARY_EXPRESSION: - return ctf_visitor_unary_expression(fd, depth, node); - - 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(fd, depth + 1, - node->u._typedef.type_specifier_list); - if (ret) - return ret; - bt_list_for_each_entry(iter, &node->u._typedef.type_declarators, siblings) { - ret = _ctf_visitor_semantic_check(fd, depth + 1, iter); - 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(fd, depth + 1, - node->u.typealias_target.type_specifier_list); - if (ret) - return ret; - nr_declarators = 0; - bt_list_for_each_entry(iter, &node->u.typealias_target.type_declarators, siblings) { - ret = _ctf_visitor_semantic_check(fd, depth + 1, iter); - if (ret) - return ret; - nr_declarators++; - } - if (nr_declarators > 1) { - fprintf(fd, "[error] %s: Too many declarators in typealias alias (%d, max is 1)\n", __func__, 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(fd, depth + 1, - node->u.typealias_alias.type_specifier_list); - if (ret) - return ret; - nr_declarators = 0; - bt_list_for_each_entry(iter, &node->u.typealias_alias.type_declarators, siblings) { - ret = _ctf_visitor_semantic_check(fd, depth + 1, iter); - if (ret) - return ret; - nr_declarators++; - } - if (nr_declarators > 1) { - fprintf(fd, "[error] %s: Too many declarators in typealias alias (%d, max is 1)\n", __func__, 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(fd, depth + 1, node->u.typealias.target); - if (ret) - return ret; - ret = _ctf_visitor_semantic_check(fd, depth + 1, node->u.typealias.alias); - if (ret) - return ret; - break; - - case NODE_TYPE_SPECIFIER_LIST: - ret = ctf_visitor_type_specifier_list(fd, depth, node); - if (ret) - return ret; - break; - case NODE_TYPE_SPECIFIER: - ret = ctf_visitor_type_specifier(fd, depth, node); - 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_type_declarator(fd, depth, node); - 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(fd, depth + 1, iter); - 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(fd, depth + 1, iter); - 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(fd, depth + 1, iter); - 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) { - fprintf(fd, "[error]: semantic error (first unary expression of enumerator is unexpected)\n"); - 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) { - fprintf(fd, "[error]: semantic error (second unary expression of enumerator is unexpected)\n"); - goto errperm; - } - break; - default: - goto errperm; - } - } - } - - bt_list_for_each_entry(iter, &node->u.enumerator.values, siblings) { - ret = _ctf_visitor_semantic_check(fd, depth + 1, iter); - 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(fd, depth + 1, node->u._enum.container_type); - if (ret) - return ret; - - bt_list_for_each_entry(iter, &node->u._enum.enumerator_list, siblings) { - ret = _ctf_visitor_semantic_check(fd, depth + 1, iter); - 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(fd, depth + 1, - node->u.struct_or_variant_declaration.type_specifier_list); - if (ret) - return ret; - bt_list_for_each_entry(iter, &node->u.struct_or_variant_declaration.type_declarators, siblings) { - ret = _ctf_visitor_semantic_check(fd, depth + 1, iter); - 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(fd, depth + 1, iter); - 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(fd, depth + 1, iter); - if (ret) - return ret; - } - break; - - case NODE_UNKNOWN: - default: - fprintf(fd, "[error] %s: unknown node type %d\n", __func__, - (int) node->type); - return -EINVAL; - } - return ret; - -errinval: - fprintf(fd, "[error] %s: incoherent parent type %s for node type %s\n", __func__, - node_type(node->parent), node_type(node)); - return -EINVAL; /* Incoherent structure */ - -errperm: - fprintf(fd, "[error] %s: semantic error (parent type %s for node type %s)\n", __func__, - node_type(node->parent), node_type(node)); - return -EPERM; /* Structure not allowed */ -} - -int ctf_visitor_semantic_check(FILE *fd, int depth, struct ctf_node *node) -{ - 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. - */ - printf_verbose("CTF visitor: parent links creation... "); - ret = ctf_visitor_parent_links(fd, depth, node); - if (ret) - return ret; - printf_verbose("done.\n"); - printf_verbose("CTF visitor: semantic check... "); - ret = _ctf_visitor_semantic_check(fd, depth, node); - if (ret) - return ret; - printf_verbose("done.\n"); - return ret; -} diff --git a/ctf-reader-proto/metadata-parsing/objstack.c b/ctf-reader-proto/metadata-parsing/objstack.c deleted file mode 100644 index 80877220..00000000 --- a/ctf-reader-proto/metadata-parsing/objstack.c +++ /dev/null @@ -1,137 +0,0 @@ -/* - * objstack.c - * - * Common Trace Format Object Stack. - * - * Copyright 2013 - Mathieu Desnoyers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include - -#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) - return NULL; - node = calloc(sizeof(struct objstack_node) + OBJSTACK_INIT_LEN, - sizeof(char)); - if (!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) { - 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 = ALIGN(len, OBJSTACK_ALIGN); - - /* Get last node */ - last_node = bt_list_entry(objstack->head.prev, - struct objstack_node, node); - while (last_node->len - last_node->used_len < len) { - last_node = objstack_append_node(objstack); - if (!last_node) { - return NULL; - } - } - p = &last_node->data[last_node->used_len]; - last_node->used_len += len; - return p; -} diff --git a/ctf-reader-proto/metadata-parsing/objstack.h b/ctf-reader-proto/metadata-parsing/objstack.h deleted file mode 100644 index c026eb54..00000000 --- a/ctf-reader-proto/metadata-parsing/objstack.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef _OBJSTACK_H -#define _OBJSTACK_H - -/* - * objstack.h - * - * Common Trace Format Object Stack. - * - * Copyright 2013 - Mathieu Desnoyers - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -struct objstack; - -BT_HIDDEN -struct objstack *objstack_create(void); -BT_HIDDEN -void objstack_destroy(struct objstack *objstack); - -/* - * Allocate len bytes of zeroed memory. - * Return NULL on error. - */ -BT_HIDDEN -void *objstack_alloc(struct objstack *objstack, size_t len); - -#endif /* _OBJSTACK_H */ diff --git a/plugins/ctf/Makefile.am b/plugins/ctf/Makefile.am index f93e9de5..bd665a11 100644 --- a/plugins/ctf/Makefile.am +++ b/plugins/ctf/Makefile.am @@ -1,6 +1,6 @@ AM_CFLAGS = $(PACKAGE_CFLAGS) -I$(top_srcdir)/include -SUBDIRS = fs lttng-live +SUBDIRS = common fs lttng-live plugindir = "$(PLUGINSDIR)" plugin_LTLIBRARIES = libbabeltrace-plugin-ctf.la diff --git a/plugins/ctf/common/Makefile.am b/plugins/ctf/common/Makefile.am new file mode 100644 index 00000000..3c57f0a2 --- /dev/null +++ b/plugins/ctf/common/Makefile.am @@ -0,0 +1,11 @@ +SUBDIRS = btr notif-iter metadata + +AM_CFLAGS = $(PACKAGE_CFLAGS) -I$(top_srcdir)/include + +noinst_LTLIBRARIES = libbabeltrace-plugin-ctf-common.la +libbabeltrace_plugin_ctf_common_la_SOURCES = +libbabeltrace_plugin_ctf_common_la_LIBADD = \ + $(builddir)/btr/libctf-btr.la \ + $(builddir)/metadata/libctf-parser.la \ + $(builddir)/metadata/libctf-ast.la \ + $(builddir)/notif-iter/libctf-notif-iter.la diff --git a/plugins/ctf/common/btr/btr.c b/plugins/ctf/common/btr/btr.c new file mode 100644 index 00000000..a7486793 --- /dev/null +++ b/plugins/ctf/common/btr/btr.c @@ -0,0 +1,1388 @@ +/* + * Babeltrace - CTF binary type reader (BTR) + * + * Copyright (c) 2015-2016 EfficiOS Inc. and Linux Foundation + * Copyright (c) 2015-2016 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "btr.h" + +#define PRINT_ERR_STREAM btr->err_stream +#define PRINT_PREFIX "ctf-btr" +#include "print.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 type of base field, one of: + * + * * Structure + * * Array + * * Sequence + * * Variant + * + * Owned by this. + */ + struct bt_ctf_field_type *base_type; + + /* Length of base field (always 1 for variant types) */ + int64_t base_len; + + /* Lndex of next field to read */ + int64_t index; +}; + +/* Visit stack */ +struct stack { + /* Entries (struct stack_entry *) (top is last element) */ + GPtrArray *entries; +}; + +/* Reading states */ +enum btr_state { + BTR_STATE_NEXT_FIELD, + BTR_STATE_ALIGN_BASIC, + BTR_STATE_ALIGN_COMPOUND, + BTR_STATE_READ_BASIC_BEGIN, + BTR_STATE_READ_BASIC_CONTINUE, + BTR_STATE_DONE, +}; + +/* Binary type reader */ +struct bt_ctf_btr { + /* Bisit stack */ + struct stack *stack; + + /* Error stream */ + FILE *err_stream; + + /* Current basic field type */ + struct bt_ctf_field_type *cur_basic_field_type; + + /* Current state */ + enum btr_state state; + + /* + * Last basic field type's byte order. + * + * This is used to detect errors since two contiguous basic + * types for which the common boundary is not the boundary of + * a byte cannot have different byte orders. + * + * This is set to BT_CTF_BYTE_ORDER_UNKNOWN on reset and when + * the last basic field type was a string type. + */ + enum bt_ctf_byte_order last_bo; + + /* Current byte order (copied to last_bo after a successful read) */ + enum bt_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_ctf_btr_cbs cbs; + + /* Private data */ + void *data; + } user; +}; + +static +void stack_entry_free_func(gpointer data) +{ + struct stack_entry *entry = data; + + BT_PUT(entry->base_type); + g_free(entry); +} + +static +struct stack *stack_new(void) +{ + struct stack *stack = NULL; + + stack = g_new0(struct stack, 1); + if (!stack) { + goto error; + } + + stack->entries = g_ptr_array_new_with_free_func(stack_entry_free_func); + if (!stack->entries) { + goto error; + } + + return stack; + +error: + g_free(stack); + + return NULL; +} + +static +void stack_destroy(struct stack *stack) +{ + if (!stack) { + return; + } + + g_ptr_array_free(stack->entries, TRUE); + g_free(stack); +} + +static inline +int64_t get_compound_field_type_length(struct bt_ctf_btr *btr, + struct bt_ctf_field_type *field_type) +{ + int64_t length; + + switch (bt_ctf_field_type_get_type_id(field_type)) { + case BT_CTF_TYPE_ID_STRUCT: + length = (int64_t) bt_ctf_field_type_structure_get_field_count( + field_type); + break; + case BT_CTF_TYPE_ID_VARIANT: + /* Variant field types always "contain" a single type */ + length = 1; + break; + case BT_CTF_TYPE_ID_ARRAY: + length = bt_ctf_field_type_array_get_length(field_type); + break; + case BT_CTF_TYPE_ID_SEQUENCE: + length = btr->user.cbs.query.get_sequence_length(field_type, + btr->user.data); + break; + default: + PERR("Cannot get length of field type with type ID %d\n", + bt_ctf_field_type_get_type_id(field_type)); + length = BT_CTF_BTR_STATUS_ERROR; + } + + return length; +} + +static +int stack_push(struct stack *stack, struct bt_ctf_field_type *base_type, + size_t base_len) +{ + int ret = 0; + struct stack_entry *entry; + + assert(stack); + assert(base_type); + + entry = g_new0(struct stack_entry, 1); + if (!entry) { + ret = BT_CTF_BTR_STATUS_ERROR; + goto end; + } + + entry->base_type = base_type; + bt_get(entry->base_type); + entry->base_len = base_len; + g_ptr_array_add(stack->entries, entry); + +end: + return ret; +} + +static +int stack_push_with_len(struct bt_ctf_btr *btr, + struct bt_ctf_field_type *base_type) +{ + int ret = 0; + int64_t base_len = get_compound_field_type_length(btr, base_type); + + if (base_len < 0) { + PERR("Failed to get compound field type's length\n"); + ret = BT_CTF_BTR_STATUS_ERROR; + goto end; + } + + ret = stack_push(btr->stack, base_type, (size_t) base_len); + +end: + return ret; +} + +static inline +unsigned int stack_size(struct stack *stack) +{ + assert(stack); + + return stack->entries->len; +} + +static +void stack_pop(struct stack *stack) +{ + assert(stack); + assert(stack_size(stack)); + g_ptr_array_remove_index(stack->entries, stack->entries->len - 1); +} + +static inline +bool stack_empty(struct stack *stack) +{ + return stack_size(stack) == 0; +} + +static +void stack_clear(struct stack *stack) +{ + assert(stack); + + if (!stack_empty(stack)) { + g_ptr_array_remove_range(stack->entries, 0, stack_size(stack)); + } + + assert(stack_empty(stack)); +} + +static inline +struct stack_entry *stack_top(struct stack *stack) +{ + assert(stack); + assert(stack_size(stack)); + + return g_ptr_array_index(stack->entries, stack->entries->len - 1); +} + +static inline +size_t available_bits(struct bt_ctf_btr *btr) +{ + return btr->buf.sz - btr->buf.at; +} + +static inline +void consume_bits(struct bt_ctf_btr *btr, size_t incr) +{ + btr->buf.at += incr; +} + +static inline +bool has_enough_bits(struct bt_ctf_btr *btr, size_t sz) +{ + return available_bits(btr) >= sz; +} + +static inline +bool at_least_one_bit_left(struct bt_ctf_btr *btr) +{ + return has_enough_bits(btr, 1); +} + +static inline +size_t packet_at(struct bt_ctf_btr *btr) +{ + return btr->buf.packet_offset + btr->buf.at; +} + +static inline +size_t buf_at_from_addr(struct bt_ctf_btr *btr) +{ + /* + * Considering this: + * + * ====== offset ===== (17) + * + * xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx + * ^ + * addr (0) ==== at ==== (12) + * + * We want this: + * + * =============================== (29) + */ + return btr->buf.offset + btr->buf.at; +} + +static inline +int get_basic_field_type_size(struct bt_ctf_btr *btr, + struct bt_ctf_field_type *field_type) +{ + int size; + + switch (bt_ctf_field_type_get_type_id(field_type)) { + case BT_CTF_TYPE_ID_INTEGER: + size = bt_ctf_field_type_integer_get_size(field_type); + break; + case BT_CTF_TYPE_ID_FLOAT: + { + int exp_dig, mant_dig; + + exp_dig = + bt_ctf_field_type_floating_point_get_exponent_digits( + field_type); + mant_dig = + bt_ctf_field_type_floating_point_get_mantissa_digits( + field_type); + if (exp_dig < 0 || mant_dig < 0) { + PERR("Failed to get floating point number type's sizes\n"); + size = BT_CTF_BTR_STATUS_ERROR; + } + + size = exp_dig + mant_dig; + break; + } + case BT_CTF_TYPE_ID_ENUM: + { + struct bt_ctf_field_type *int_type; + + int_type = bt_ctf_field_type_enumeration_get_container_type( + field_type); + if (!int_type) { + PERR("Failed to get enumeration type's container type\n"); + size = BT_CTF_BTR_STATUS_ERROR; + goto end; + } + + size = get_basic_field_type_size(btr, int_type); + BT_PUT(int_type); + break; + } + default: + size = BT_CTF_BTR_STATUS_ERROR; + break; + } + +end: + return size; +} + +static +void stitch_reset(struct bt_ctf_btr *btr) +{ + btr->stitch.offset = 0; + btr->stitch.at = 0; +} + +static inline +size_t stitch_at_from_addr(struct bt_ctf_btr *btr) +{ + return btr->stitch.offset + btr->stitch.at; +} + +static +void stitch_append_from_buf(struct bt_ctf_btr *btr, 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(btr)); + buf_byte_at = BITS_TO_BYTES_FLOOR(buf_at_from_addr(btr)); + nb_bytes = BITS_TO_BYTES_CEIL(sz); + assert(nb_bytes > 0); + memcpy(&btr->stitch.buf[stitch_byte_at], &btr->buf.addr[buf_byte_at], + nb_bytes); + btr->stitch.at += sz; + consume_bits(btr, sz); +} + +static +void stitch_append_from_remaining_buf(struct bt_ctf_btr *btr) +{ + stitch_append_from_buf(btr, available_bits(btr)); +} + +static +void stitch_set_from_remaining_buf(struct bt_ctf_btr *btr) +{ + stitch_reset(btr); + btr->stitch.offset = IN_BYTE_OFFSET(buf_at_from_addr(btr)); + stitch_append_from_remaining_buf(btr); +} + +static inline +enum bt_ctf_btr_status read_unsigned_bitfield(const uint8_t *buf, size_t at, + int64_t field_size, enum bt_ctf_byte_order bo, uint64_t *v) +{ + enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; + + switch (bo) { + case BT_CTF_BYTE_ORDER_BIG_ENDIAN: + case BT_CTF_BYTE_ORDER_NETWORK: + bt_bitfield_read_be(buf, uint8_t, at, field_size, v); + break; + case BT_CTF_BYTE_ORDER_LITTLE_ENDIAN: + bt_bitfield_read_le(buf, uint8_t, at, field_size, v); + break; + default: + status = BT_CTF_BTR_STATUS_ERROR; + } + + return status; +} + +static inline +enum bt_ctf_btr_status read_signed_bitfield(const uint8_t *buf, size_t at, + int64_t field_size, enum bt_ctf_byte_order bo, int64_t *v) +{ + enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; + + switch (bo) { + case BT_CTF_BYTE_ORDER_BIG_ENDIAN: + case BT_CTF_BYTE_ORDER_NETWORK: + bt_bitfield_read_be(buf, uint8_t, at, field_size, v); + break; + case BT_CTF_BYTE_ORDER_LITTLE_ENDIAN: + bt_bitfield_read_le(buf, uint8_t, at, field_size, v); + break; + default: + status = BT_CTF_BTR_STATUS_ERROR; + } + + return status; +} + +typedef enum bt_ctf_btr_status (* read_basic_and_call_cb_t)(struct bt_ctf_btr *, + const uint8_t *, size_t); + +static inline +enum bt_ctf_btr_status validate_contiguous_bo(struct bt_ctf_btr *btr, + enum bt_ctf_byte_order next_bo) +{ + enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; + + /* Always valid when at a byte boundary */ + if (packet_at(btr) % 8 == 0) { + goto end; + } + + /* Always valid if last byte order is unknown */ + if (btr->last_bo == BT_CTF_BYTE_ORDER_UNKNOWN) { + goto end; + } + + /* Always valid if next byte order is unknown */ + if (next_bo == BT_CTF_BYTE_ORDER_UNKNOWN) { + goto end; + } + + /* Make sure last byte order is compatible with the next byte order */ + switch (btr->last_bo) { + case BT_CTF_BYTE_ORDER_BIG_ENDIAN: + case BT_CTF_BYTE_ORDER_NETWORK: + if (next_bo != BT_CTF_BYTE_ORDER_BIG_ENDIAN && + next_bo != BT_CTF_BYTE_ORDER_NETWORK) { + status = BT_CTF_BTR_STATUS_ERROR; + } + break; + case BT_CTF_BYTE_ORDER_LITTLE_ENDIAN: + if (next_bo != BT_CTF_BYTE_ORDER_LITTLE_ENDIAN) { + status = BT_CTF_BTR_STATUS_ERROR; + } + break; + default: + status = BT_CTF_BTR_STATUS_ERROR; + } + +end: + return status; +} + +static +enum bt_ctf_btr_status read_basic_float_and_call_cb(struct bt_ctf_btr *btr, + const uint8_t *buf, size_t at) +{ + int ret; + double dblval; + int64_t field_size; + enum bt_ctf_byte_order bo; + enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; + + field_size = get_basic_field_type_size(btr, btr->cur_basic_field_type); + bo = bt_ctf_field_type_get_byte_order(btr->cur_basic_field_type); + btr->cur_bo = bo; + + switch (field_size) { + case 32: + { + uint64_t v; + union { + uint32_t u; + float f; + } f32; + + ret = bt_ctf_field_type_floating_point_get_mantissa_digits( + btr->cur_basic_field_type); + assert(ret == 24); + ret = bt_ctf_field_type_floating_point_get_exponent_digits( + btr->cur_basic_field_type); + assert(ret == 8); + status = read_unsigned_bitfield(buf, at, field_size, bo, &v); + if (status != BT_CTF_BTR_STATUS_OK) { + PERR("Failed to reader unsigned bitfield\n"); + goto end; + } + + f32.u = (uint32_t) v; + dblval = (double) f32.f; + break; + } + case 64: + { + union { + uint64_t u; + double d; + } f64; + + ret = bt_ctf_field_type_floating_point_get_mantissa_digits( + btr->cur_basic_field_type); + assert(ret == 53); + ret = bt_ctf_field_type_floating_point_get_exponent_digits( + btr->cur_basic_field_type); + assert(ret == 11); + status = read_unsigned_bitfield(buf, at, field_size, bo, + &f64.u); + if (status != BT_CTF_BTR_STATUS_OK) { + PERR("Failed to reader unsigned bitfield\n"); + goto end; + } + + dblval = f64.d; + break; + } + default: + /* Only 32-bit and 64-bit fields are supported currently */ + status = BT_CTF_BTR_STATUS_ERROR; + goto end; + } + + if (btr->user.cbs.types.floating_point) { + status = btr->user.cbs.types.floating_point(dblval, + btr->cur_basic_field_type, btr->user.data); + } + +end: + return status; +} + +static inline +enum bt_ctf_btr_status read_basic_int_and_call(struct bt_ctf_btr *btr, + const uint8_t *buf, size_t at, struct bt_ctf_field_type *int_type, + struct bt_ctf_field_type *orig_type) +{ + int signd; + int64_t field_size; + enum bt_ctf_byte_order bo; + enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; + + signd = bt_ctf_field_type_integer_get_signed(int_type); + field_size = get_basic_field_type_size(btr, int_type); + if (field_size < 1) { + PERR("Failed to get basic field type's size\n"); + status = BT_CTF_BTR_STATUS_ERROR; + goto end; + } + + bo = bt_ctf_field_type_get_byte_order(int_type); + + /* + * Update current byte order now because we could be reading + * the integer value of an enumeration type, and thus we know + * here the actual supporting integer type's byte order. + */ + btr->cur_bo = bo; + + if (signd) { + int64_t v; + + status = read_signed_bitfield(buf, at, field_size, bo, &v); + if (status != BT_CTF_BTR_STATUS_OK) { + PERR("Failed to reader signed bitfield\n"); + goto end; + } + + if (btr->user.cbs.types.signed_int) { + status = btr->user.cbs.types.signed_int(v, + btr->cur_basic_field_type, btr->user.data); + } + } else { + uint64_t v; + + status = read_unsigned_bitfield(buf, at, field_size, bo, &v); + if (status != BT_CTF_BTR_STATUS_OK) { + PERR("Failed to reader unsigned bitfield\n"); + goto end; + } + + if (btr->user.cbs.types.unsigned_int) { + status = btr->user.cbs.types.unsigned_int(v, + btr->cur_basic_field_type, btr->user.data); + } + } + +end: + return status; +} + +static +enum bt_ctf_btr_status read_basic_int_and_call_cb(struct bt_ctf_btr *btr, + const uint8_t *buf, size_t at) +{ + return read_basic_int_and_call(btr, buf, at, btr->cur_basic_field_type, + btr->cur_basic_field_type); +} + +static +enum bt_ctf_btr_status read_basic_enum_and_call_cb(struct bt_ctf_btr *btr, + const uint8_t *buf, size_t at) +{ + struct bt_ctf_field_type *int_field_type; + enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; + + int_field_type = bt_ctf_field_type_enumeration_get_container_type( + btr->cur_basic_field_type); + if (!int_field_type) { + PERR("Failed to get enumeration type's container type\n"); + status = BT_CTF_BTR_STATUS_ERROR; + goto end; + } + + status = read_basic_int_and_call(btr, buf, at, + int_field_type, btr->cur_basic_field_type); + +end: + BT_PUT(int_field_type); + + return status; +} + +static inline +enum bt_ctf_btr_status read_basic_type_and_call_continue(struct bt_ctf_btr *btr, + read_basic_and_call_cb_t read_basic_and_call_cb) +{ + size_t available; + int64_t field_size; + int64_t needed_bits; + enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; + + if (!at_least_one_bit_left(btr)) { + status = BT_CTF_BTR_STATUS_EOF; + goto end; + } + + field_size = get_basic_field_type_size(btr, btr->cur_basic_field_type); + if (field_size < 1) { + PERR("Failed to get basic field type's size\n"); + status = BT_CTF_BTR_STATUS_ERROR; + goto end; + } + + available = available_bits(btr); + needed_bits = field_size - btr->stitch.at; + if (needed_bits <= available) { + /* We have all the bits; append to stitch, then decode */ + stitch_append_from_buf(btr, needed_bits); + status = read_basic_and_call_cb(btr, btr->stitch.buf, + btr->stitch.offset); + if (status != BT_CTF_BTR_STATUS_OK) { + PERR("Failed to read basic field\n"); + goto end; + } + + if (stack_empty(btr->stack)) { + /* Root is a basic type */ + btr->state = BTR_STATE_DONE; + } else { + /* Go to next field */ + stack_top(btr->stack)->index++; + btr->state = BTR_STATE_NEXT_FIELD; + btr->last_bo = btr->cur_bo; + } + goto end; + } + + /* We are here; it means we don't have enough data to decode this */ + stitch_append_from_remaining_buf(btr); + status = BT_CTF_BTR_STATUS_EOF; + +end: + return status; +} + +static inline +enum bt_ctf_btr_status read_basic_type_and_call_begin(struct bt_ctf_btr *btr, + read_basic_and_call_cb_t read_basic_and_call_cb) +{ + size_t available; + int64_t field_size; + enum bt_ctf_byte_order bo; + enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; + + if (!at_least_one_bit_left(btr)) { + status = BT_CTF_BTR_STATUS_EOF; + goto end; + } + + field_size = get_basic_field_type_size(btr, btr->cur_basic_field_type); + + if (field_size < 1) { + PERR("Failed to get basic field type's size\n"); + status = BT_CTF_BTR_STATUS_ERROR; + goto end; + } + + bo = bt_ctf_field_type_get_byte_order(btr->cur_basic_field_type); + status = validate_contiguous_bo(btr, bo); + + if (status != BT_CTF_BTR_STATUS_OK) { + PERR("Invalid contiguous byte orders\n"); + goto end; + } + + available = available_bits(btr); + + if (field_size <= available) { + /* We have all the bits; decode and set now */ + status = read_basic_and_call_cb(btr, btr->buf.addr, + buf_at_from_addr(btr)); + + if (status != BT_CTF_BTR_STATUS_OK) { + PERR("Failed to read basic type\n"); + goto end; + } + + consume_bits(btr, field_size); + + if (stack_empty(btr->stack)) { + /* Root is a basic type */ + btr->state = BTR_STATE_DONE; + } else { + /* Go to next field */ + stack_top(btr->stack)->index++; + btr->state = BTR_STATE_NEXT_FIELD; + btr->last_bo = btr->cur_bo; + } + + goto end; + } + + /* We are here; it means we don't have enough data to decode this */ + stitch_set_from_remaining_buf(btr); + btr->state = BTR_STATE_READ_BASIC_CONTINUE; + status = BT_CTF_BTR_STATUS_EOF; + +end: + return status; +} + +static inline +enum bt_ctf_btr_status read_basic_int_type_and_call_begin( + struct bt_ctf_btr *btr) +{ + return read_basic_type_and_call_begin(btr, read_basic_int_and_call_cb); +} + +static inline +enum bt_ctf_btr_status read_basic_int_type_and_call_continue( + struct bt_ctf_btr *btr) +{ + return read_basic_type_and_call_continue(btr, + read_basic_int_and_call_cb); +} + +static inline +enum bt_ctf_btr_status read_basic_float_type_and_call_begin( + struct bt_ctf_btr *btr) +{ + return read_basic_type_and_call_begin(btr, + read_basic_float_and_call_cb); +} + +static inline +enum bt_ctf_btr_status read_basic_float_type_and_call_continue( + struct bt_ctf_btr *btr) +{ + return read_basic_type_and_call_continue(btr, + read_basic_float_and_call_cb); +} + +static inline +enum bt_ctf_btr_status read_basic_enum_type_and_call_begin( + struct bt_ctf_btr *btr) +{ + return read_basic_type_and_call_begin(btr, + read_basic_enum_and_call_cb); +} + +static inline +enum bt_ctf_btr_status read_basic_enum_type_and_call_continue( + struct bt_ctf_btr *btr) +{ + return read_basic_type_and_call_continue(btr, + read_basic_enum_and_call_cb); +} + +static inline +enum bt_ctf_btr_status read_basic_string_type_and_call( + struct bt_ctf_btr *btr, bool begin) +{ + size_t buf_at_bytes; + const uint8_t *result; + size_t available_bytes; + const uint8_t *first_chr; + enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; + + if (!at_least_one_bit_left(btr)) { + status = BT_CTF_BTR_STATUS_EOF; + goto end; + } + + assert(buf_at_from_addr(btr) % 8 == 0); + available_bytes = BITS_TO_BYTES_FLOOR(available_bits(btr)); + buf_at_bytes = BITS_TO_BYTES_FLOOR(buf_at_from_addr(btr)); + first_chr = &btr->buf.addr[buf_at_bytes]; + result = memchr(first_chr, '\0', available_bytes); + + if (begin && btr->user.cbs.types.string_begin) { + status = btr->user.cbs.types.string_begin( + btr->cur_basic_field_type, btr->user.data); + + if (status != BT_CTF_BTR_STATUS_OK) { + PERR("string_begin() user callback function failed\n"); + goto end; + } + } + + if (!result) { + /* No null character yet */ + if (btr->user.cbs.types.string) { + status = btr->user.cbs.types.string( + (const char *) first_chr, + available_bytes, btr->cur_basic_field_type, + btr->user.data); + if (status != BT_CTF_BTR_STATUS_OK) { + PERR("string() user callback function failed\n"); + goto end; + } + } + + consume_bits(btr, BYTES_TO_BITS(available_bytes)); + btr->state = BTR_STATE_READ_BASIC_CONTINUE; + status = BT_CTF_BTR_STATUS_EOF; + } else { + /* Found the null character */ + size_t result_len = (size_t) (result - first_chr); + + if (btr->user.cbs.types.string && result_len) { + status = btr->user.cbs.types.string( + (const char *) first_chr, + result_len, btr->cur_basic_field_type, + btr->user.data); + if (status != BT_CTF_BTR_STATUS_OK) { + PERR("string() user callback function failed\n"); + goto end; + } + } + + if (btr->user.cbs.types.string_end) { + status = btr->user.cbs.types.string_end( + btr->cur_basic_field_type, btr->user.data); + if (status != BT_CTF_BTR_STATUS_OK) { + PERR("string_end() user callback function failed\n"); + goto end; + } + } + + consume_bits(btr, BYTES_TO_BITS(result_len + 1)); + + if (stack_empty(btr->stack)) { + /* Root is a basic type */ + btr->state = BTR_STATE_DONE; + } else { + /* Go to next field */ + stack_top(btr->stack)->index++; + btr->state = BTR_STATE_NEXT_FIELD; + btr->last_bo = btr->cur_bo; + } + } + +end: + return status; +} + +static inline +enum bt_ctf_btr_status read_basic_begin_state(struct bt_ctf_btr *btr) +{ + enum bt_ctf_btr_status status; + + assert(btr->cur_basic_field_type); + + switch (bt_ctf_field_type_get_type_id(btr->cur_basic_field_type)) { + case BT_CTF_TYPE_ID_INTEGER: + status = read_basic_int_type_and_call_begin(btr); + break; + case BT_CTF_TYPE_ID_FLOAT: + status = read_basic_float_type_and_call_begin(btr); + break; + case BT_CTF_TYPE_ID_ENUM: + status = read_basic_enum_type_and_call_begin(btr); + break; + case BT_CTF_TYPE_ID_STRING: + status = read_basic_string_type_and_call(btr, true); + break; + default: + assert(false); + } + + return status; +} + +static inline +enum bt_ctf_btr_status read_basic_continue_state(struct bt_ctf_btr *btr) +{ + enum bt_ctf_btr_status status; + + assert(btr->cur_basic_field_type); + + switch (bt_ctf_field_type_get_type_id(btr->cur_basic_field_type)) { + case BT_CTF_TYPE_ID_INTEGER: + status = read_basic_int_type_and_call_continue(btr); + break; + case BT_CTF_TYPE_ID_FLOAT: + status = read_basic_float_type_and_call_continue(btr); + break; + case BT_CTF_TYPE_ID_ENUM: + status = read_basic_enum_type_and_call_continue(btr); + break; + case BT_CTF_TYPE_ID_STRING: + status = read_basic_string_type_and_call(btr, false); + break; + default: + assert(false); + } + + return status; +} + +static inline +size_t bits_to_skip_to_align_to(struct bt_ctf_btr *btr, size_t align) +{ + size_t aligned_packet_at; + + aligned_packet_at = ALIGN(packet_at(btr), align); + + return aligned_packet_at - packet_at(btr); +} + +static inline +enum bt_ctf_btr_status align_type_state(struct bt_ctf_btr *btr, + struct bt_ctf_field_type *field_type, enum btr_state next_state) +{ + int field_alignment; + size_t skip_bits; + enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; + + /* Get field's alignment */ + field_alignment = bt_ctf_field_type_get_alignment(field_type); + if (field_alignment < 0) { + PERR("Failed to get type alignment\n"); + status = BT_CTF_BTR_STATUS_ERROR; + goto end; + } + + /* + * 0 means "undefined" for variants; what we really want is 1 + * (always aligned) + */ + if (field_alignment == 0) { + field_alignment = 1; + } + + /* Compute how many bits we need to skip */ + skip_bits = bits_to_skip_to_align_to(btr, field_alignment); + + /* Nothing to skip? aligned */ + if (skip_bits == 0) { + btr->state = next_state; + goto end; + } + + /* Make sure there's at least one bit left */ + if (!at_least_one_bit_left(btr)) { + status = BT_CTF_BTR_STATUS_EOF; + goto end; + } + + /* Consume as many bits as possible in what's left */ + consume_bits(btr, MIN(available_bits(btr), skip_bits)); + + /* Are we done now? */ + skip_bits = bits_to_skip_to_align_to(btr, field_alignment); + + if (skip_bits == 0) { + /* Yes: go to next state */ + btr->state = next_state; + goto end; + } else { + /* No: need more data */ + status = BT_CTF_BTR_STATUS_EOF; + } + +end: + return status; +} + +static inline +bool is_compound_type(struct bt_ctf_field_type *field_type) +{ + enum bt_ctf_type_id id = bt_ctf_field_type_get_type_id(field_type); + + return id == BT_CTF_TYPE_ID_STRUCT || id == BT_CTF_TYPE_ID_ARRAY || + id == BT_CTF_TYPE_ID_SEQUENCE || id == BT_CTF_TYPE_ID_VARIANT; +} + +static inline +enum bt_ctf_btr_status next_field_state(struct bt_ctf_btr *btr) +{ + int ret; + struct stack_entry *top; + struct bt_ctf_field_type *next_field_type = NULL; + enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; + + if (stack_empty(btr->stack)) { + goto end; + } + + top = stack_top(btr->stack); + + /* Are we done with this base type? */ + while (top->index == top->base_len) { + if (btr->user.cbs.types.compound_end) { + status = btr->user.cbs.types.compound_end( + top->base_type, btr->user.data); + if (status != BT_CTF_BTR_STATUS_OK) { + PERR("compound_end() user callback function failed\n"); + goto end; + } + } + + stack_pop(btr->stack); + + /* Are we done with the root type? */ + if (stack_empty(btr->stack)) { + btr->state = BTR_STATE_DONE; + goto end; + } + + top = stack_top(btr->stack); + top->index++; + } + + /* Get next field's type */ + switch (bt_ctf_field_type_get_type_id(top->base_type)) { + case BT_CTF_TYPE_ID_STRUCT: + ret = bt_ctf_field_type_structure_get_field( + top->base_type, NULL, &next_field_type, + top->index); + if (ret) { + next_field_type = NULL; + } + break; + case BT_CTF_TYPE_ID_ARRAY: + next_field_type = + bt_ctf_field_type_array_get_element_type( + top->base_type); + break; + case BT_CTF_TYPE_ID_SEQUENCE: + next_field_type = + bt_ctf_field_type_sequence_get_element_type( + top->base_type); + break; + case BT_CTF_TYPE_ID_VARIANT: + /* Variant types are dynamic: query the user, he should know! */ + next_field_type = + btr->user.cbs.query.get_variant_type( + top->base_type, btr->user.data); + break; + default: + break; + } + + if (!next_field_type) { + PERR("Failed to get next field's type\n"); + status = BT_CTF_BTR_STATUS_ERROR; + goto end; + } + + if (is_compound_type(next_field_type)) { + if (btr->user.cbs.types.compound_begin) { + status = btr->user.cbs.types.compound_begin( + next_field_type, btr->user.data); + if (status != BT_CTF_BTR_STATUS_OK) { + PERR("compound_begin() user callback function failed\n"); + goto end; + } + } + + ret = stack_push_with_len(btr, next_field_type); + if (ret) { + PERR("Failed to push compound type onto the stack\n"); + status = BT_CTF_BTR_STATUS_ERROR; + goto end; + } + + /* Next state: align a compound type */ + btr->state = BTR_STATE_ALIGN_COMPOUND; + } else { + /* Replace current basic field type */ + BT_MOVE(btr->cur_basic_field_type, next_field_type); + + /* Next state: align a basic type */ + btr->state = BTR_STATE_ALIGN_BASIC; + } + +end: + BT_PUT(next_field_type); + + return status; +} + +static inline +enum bt_ctf_btr_status handle_state(struct bt_ctf_btr *btr) +{ + enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; + + switch (btr->state) { + case BTR_STATE_NEXT_FIELD: + status = next_field_state(btr); + break; + case BTR_STATE_ALIGN_BASIC: + status = align_type_state(btr, btr->cur_basic_field_type, + BTR_STATE_READ_BASIC_BEGIN); + break; + case BTR_STATE_ALIGN_COMPOUND: + status = align_type_state(btr, stack_top(btr->stack)->base_type, + BTR_STATE_NEXT_FIELD); + break; + case BTR_STATE_READ_BASIC_BEGIN: + status = read_basic_begin_state(btr); + break; + case BTR_STATE_READ_BASIC_CONTINUE: + status = read_basic_continue_state(btr); + break; + case BTR_STATE_DONE: + break; + } + + return status; +} + +struct bt_ctf_btr *bt_ctf_btr_create(struct bt_ctf_btr_cbs cbs, void *data, + FILE *err_stream) +{ + struct bt_ctf_btr *btr; + + btr = g_new0(struct bt_ctf_btr, 1); + if (!btr) { + PERR("Failed to allocate memory for binary type reader\n"); + goto end; + } + + btr->stack = stack_new(); + if (!btr->stack) { + PERR("Failed to create stack\n"); + bt_ctf_btr_destroy(btr); + btr = NULL; + goto end; + } + + btr->state = BTR_STATE_NEXT_FIELD; + btr->user.cbs = cbs; + btr->user.data = data; + btr->err_stream = err_stream; + +end: + return btr; +} + +void bt_ctf_btr_destroy(struct bt_ctf_btr *btr) +{ + if (btr->stack) { + stack_destroy(btr->stack); + } + + BT_PUT(btr->cur_basic_field_type); + g_free(btr); +} + +static +void reset(struct bt_ctf_btr *btr) +{ + stack_clear(btr->stack); + BT_PUT(btr->cur_basic_field_type); + stitch_reset(btr); + btr->buf.addr = NULL; + btr->last_bo = BT_CTF_BYTE_ORDER_UNKNOWN; +} + +size_t bt_ctf_btr_start(struct bt_ctf_btr *btr, + struct bt_ctf_field_type *type, const uint8_t *buf, + size_t offset, size_t packet_offset, size_t sz, + enum bt_ctf_btr_status *status) +{ + assert(btr); + assert(buf); + assert(sz > 0); + assert(BYTES_TO_BITS(sz) > offset); + reset(btr); + btr->buf.addr = buf; + btr->buf.offset = offset; + btr->buf.at = 0; + btr->buf.packet_offset = packet_offset; + btr->buf.buf_sz = sz; + btr->buf.sz = BYTES_TO_BITS(sz) - offset; + *status = BT_CTF_BTR_STATUS_OK; + + /* Set root type */ + if (is_compound_type(type)) { + /* Compound type: push on visit stack */ + int stack_ret; + + if (btr->user.cbs.types.compound_begin) { + *status = btr->user.cbs.types.compound_begin( + type, btr->user.data); + if (*status != BT_CTF_BTR_STATUS_OK) { + PERR("compound_begin() user callback function failed\n"); + goto end; + } + } + + stack_ret = stack_push_with_len(btr, type); + if (stack_ret) { + PERR("Failed to push initial compound type onto the stack\n"); + *status = BT_CTF_BTR_STATUS_ERROR; + goto end; + } + + btr->state = BTR_STATE_ALIGN_COMPOUND; + } else { + /* Basic type: set as current basic type */ + btr->cur_basic_field_type = type; + bt_get(btr->cur_basic_field_type); + btr->state = BTR_STATE_ALIGN_BASIC; + } + + /* Run the machine! */ + while (true) { + *status = handle_state(btr); + if (*status != BT_CTF_BTR_STATUS_OK) { + break; + } else if (btr->state == BTR_STATE_DONE) { + break; + } + } + + /* Update packet offset for next time */ + btr->buf.packet_offset += btr->buf.at; + +end: + return btr->buf.at; +} + +size_t bt_ctf_btr_continue(struct bt_ctf_btr *btr, + const uint8_t *buf, size_t sz, + enum bt_ctf_btr_status *status) +{ + assert(btr); + assert(buf); + assert(sz > 0); + btr->buf.addr = buf; + btr->buf.offset = 0; + btr->buf.at = 0; + btr->buf.buf_sz = sz; + btr->buf.sz = BYTES_TO_BITS(sz); + *status = BT_CTF_BTR_STATUS_OK; + + /* Continue running the machine */ + while (true) { + *status = handle_state(btr); + if (*status != BT_CTF_BTR_STATUS_OK) { + break; + } else if (btr->state == BTR_STATE_DONE) { + break; + } + } + + /* Update packet offset for next time */ + btr->buf.packet_offset += btr->buf.at; + + return btr->buf.at; +} diff --git a/plugins/ctf/common/btr/btr.gdb b/plugins/ctf/common/btr/btr.gdb new file mode 100644 index 00000000..9b42b1cd --- /dev/null +++ b/plugins/ctf/common/btr/btr.gdb @@ -0,0 +1,24 @@ +define ctf-btr-show-stack + if (stack_empty($arg0)) + printf "stack is empty!\n" + else + set $stack_size = stack_size($arg0) + set $stack_at = (int) ($stack_size - 1) + printf "%3s %10s %4s %3s\n", "pos", "base addr", "blen", "idx" + + while ($stack_at >= 0) + set $stack_entry = (struct stack_entry *) g_ptr_array_index($arg0->entries, $stack_at) + + if ($stack_at == $stack_size - 1) + printf "%3d %10p %3d %3d <-- top\n", $stack_at, \ + $stack_entry->base_type, $stack_entry->base_len, \ + $stack_entry->index + else + printf "%3d %10p %3d %3d\n", $stack_at, \ + $stack_entry->base_type, $stack_entry->base_len, \ + $stack_entry->index + end + set $stack_at = $stack_at - 1 + end + end +end diff --git a/plugins/ctf/common/btr/btr.h b/plugins/ctf/common/btr/btr.h new file mode 100644 index 00000000..75484155 --- /dev/null +++ b/plugins/ctf/common/btr/btr.h @@ -0,0 +1,352 @@ +#ifndef CTF_BTR_H +#define CTF_BTR_H + +/* + * Babeltrace - CTF binary type reader (BTR) + * + * Copyright (c) 2015-2016 EfficiOS Inc. and Linux Foundation + * Copyright (c) 2015-2016 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include + +/** + * @file ctf-btr.h + * + * Event-driven CTF binary type reader (BTR). + * + * This is a common, internal API used by CTF source plugins. It allows + * a binary CTF IR field type to be decoded from user-provided buffers. + * As the type is decoded (and, possibly, its nested types), registered + * user callback functions are called. + * + * This API is only concerned with reading one CTF type 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 types are + * requested to the user when needed. + */ + +/** + * Binary type reader API status codes. + */ +enum bt_ctf_btr_status { + /** + * The binary stream reader reached the end of the user-provided + * buffer, but data is still needed to finish decoding the + * requested type. + * + * The user needs to call bt_ctf_btr_continue() as long as + * #BT_CTF_BTR_STATUS_EOF is returned to complete the decoding + * process of a given type. + */ + BT_CTF_BTR_STATUS_EOF = -4, + + /** Invalid argument. */ + BT_CTF_BTR_STATUS_INVAL = -3, + + /** General error. */ + BT_CTF_BTR_STATUS_ERROR = -1, + + /** Everything okay. */ + BT_CTF_BTR_STATUS_OK = 0, +}; + +/** Type reader. */ +struct bt_ctf_btr; + +/* + * Type reader user callback functions. + */ +struct bt_ctf_btr_cbs { + /** + * Type callback functions. + * + * This CTF binary type reader is event-driven. The following + * functions are called during the decoding process, either when + * a compound type begins/ends, or when a basic type is + * completely decoded (along with its value). + * + * Each function also receives the CTF IR field type associated + * with the call, and user data (registered to the type reader + * calling them). This field type is a weak reference; the + * callback function must use bt_ctf_field_type_get() to keep + * its own reference of it. + * + * Actual CTF IR fields are \em not created here; this would be + * the responsibility of a type reader's user (the provider of + * those callback functions). + * + * All the type callback functions return one of the following + * values: + * + * - #BT_CTF_BTR_STATUS_OK: Everything is okay; + * continue the decoding process. + * - #BT_CTF_BTR_STATUS_ERROR: General error (reported + * to type reader's user). + * + * Any member of this structure may be set to \c NULL, should + * a specific notification be not needed. + */ + struct { + /** + * Called when a signed integer type is completely + * decoded. This could also be the supporting signed + * integer type of an enumeration type (\p type will + * indicate this). + * + * @param value Signed integer value + * @param type Integer or enumeration type (weak + * reference) + * @param data User data + * @returns #BT_CTF_BTR_STATUS_OK or + * #BT_CTF_BTR_STATUS_ERROR + */ + enum bt_ctf_btr_status (* signed_int)(int64_t value, + struct bt_ctf_field_type *type, void *data); + + /** + * Called when an unsigned integer type is completely + * decoded. This could also be the supporting signed + * integer type of an enumeration type (\p type will + * indicate this). + * + * @param value Unsigned integer value + * @param type Integer or enumeration type (weak + * reference) + * @param data User data + * @returns #BT_CTF_BTR_STATUS_OK or + * #BT_CTF_BTR_STATUS_ERROR + */ + enum bt_ctf_btr_status (* unsigned_int)(uint64_t value, + struct bt_ctf_field_type *type, void *data); + + /** + * Called when a floating point number type is + * completely decoded. + * + * @param value Floating point number value + * @param type Floating point number type (weak + * reference) + * @param data User data + * @returns #BT_CTF_BTR_STATUS_OK or + * #BT_CTF_BTR_STATUS_ERROR + */ + enum bt_ctf_btr_status (* floating_point)(double value, + struct bt_ctf_field_type *type, void *data); + + /** + * Called when a string type begins. + * + * All the following user callback function calls will + * be made to bt_ctf_btr_cbs::types::string(), each of + * them providing one substring of the complete string + * type's value. + * + * @param type Beginning string type (weak reference) + * @param data User data + * @returns #BT_CTF_BTR_STATUS_OK or + * #BT_CTF_BTR_STATUS_ERROR + */ + enum bt_ctf_btr_status (* string_begin)( + struct bt_ctf_field_type *type, void *data); + + /** + * Called when a string type's substring is decoded + * (between a call to bt_ctf_btr_cbs::types::string_begin() + * and a call to bt_ctf_btr_cbs::types::string_end()). + * + * @param value String value (\em not null-terminated) + * @param len String value length + * @param type String type (weak reference) + * @param data User data + * @returns #BT_CTF_BTR_STATUS_OK or + * #BT_CTF_BTR_STATUS_ERROR + */ + enum bt_ctf_btr_status (* string)(const char *value, + size_t len, struct bt_ctf_field_type *type, + void *data); + + /** + * Called when a string type ends. + * + * @param type Ending string type (weak reference) + * @param data User data + * @returns #BT_CTF_BTR_STATUS_OK or + * #BT_CTF_BTR_STATUS_ERROR + */ + enum bt_ctf_btr_status (* string_end)( + struct bt_ctf_field_type *type, void *data); + + /** + * Called when a compound type begins. + * + * All the following type callback function calls will + * signal sequential elements of this compound type, + * until the next corresponding + * bt_ctf_btr_cbs::types::compound_end() is called. + * + * If \p type is a variant type, then only one type + * callback function call will follow before the call to + * bt_ctf_btr_cbs::types::compound_end(). This single + * call indicates the selected type of this variant + * type. + * + * @param type Beginning compound type (weak reference) + * @param data User data + * @returns #BT_CTF_BTR_STATUS_OK or + * #BT_CTF_BTR_STATUS_ERROR + */ + enum bt_ctf_btr_status (* compound_begin)( + struct bt_ctf_field_type *type, void *data); + + /** + * Called when a compound type ends. + * + * @param type Ending compound type (weak reference) + * @param data User data + * @returns #BT_CTF_BTR_STATUS_OK or + * #BT_CTF_BTR_STATUS_ERROR + */ + enum bt_ctf_btr_status (* compound_end)( + struct bt_ctf_field_type *type, void *data); + } types; + + /** + * Query callback functions are used when the type reader needs + * dynamic information, i.e. a sequence type's current length + * or a variant type's current selected type. + * + * 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 + * type. + * + * @param type Sequence type (weak reference) + * @param data User data + * @returns Sequence length or + * #BT_CTF_BTR_STATUS_ERROR on error + */ + int64_t (* get_sequence_length)(struct bt_ctf_field_type *type, + void *data); + + /** + * Called to query the current selected type of a given + * variant type. + * + * @param type Variant type (weak reference) + * @param data User data + * @returns Current selected type (owned by + * this) or \c NULL on error + */ + struct bt_ctf_field_type * (* get_variant_type)( + struct bt_ctf_field_type *type, void *data); + } query; +}; + +/** + * Creates a CTF binary type reader. + * + * @param cbs User callback functions + * @param data User data (passed to user callback functions) + * @param err_stream Error stream (can be \c NULL to disable) + * @returns New binary type reader on success, or \c NULL on error + */ +struct bt_ctf_btr *bt_ctf_btr_create(struct bt_ctf_btr_cbs cbs, void *data, + FILE *err_stream); + +/** + * Destroys a CTF binary type reader, freeing all internal resources. + * + * @param btr Binary type reader + */ +void bt_ctf_btr_destroy(struct bt_ctf_btr *btr); + +/** + * Decodes a given CTF type 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: + * + * - #BT_CTF_BTR_STATUS_OK: Decoding is done. + * - #BT_CTF_BTR_STATUS_EOF: The end of the buffer was reached, + * but more data is needed to finish the decoding process of the + * requested type. The user needs to call bt_ctf_btr_continue() + * as long as #BT_CTF_BTR_STATUS_EOF is returned to complete the + * decoding process of the original type. + * - #BT_CTF_BTR_STATUS_INVAL: Invalid argument. + * - #BT_CTF_BTR_STATUS_ERROR: General error. + * + * Calling this function resets the type reader's internal state. If + * #BT_CTF_BTR_STATUS_EOF is returned, bt_ctf_btr_continue() needs to + * be called next, \em not bt_ctf_btr_decode(). + * + * @param btr Binary type reader + * @param type Type to decode (weak reference) + * @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 type (bits) + * @param sz Size of buffer in bytes (from \p buf) + * @param status Returned status (see description above) + * @returns Number of consumed bits + */ +size_t bt_ctf_btr_start(struct bt_ctf_btr *btr, + struct bt_ctf_field_type *type, const uint8_t *buf, + size_t offset, size_t packet_offset, size_t sz, + enum bt_ctf_btr_status *status); + +/** + * Continues the decoding process a given CTF type. + * + * The number of bits consumed by this function is returned. + * + * The \p status output parameter is where a status is placed, amongst + * the following: + * + * - #BT_CTF_BTR_STATUS_OK: decoding is done. + * - #BT_CTF_BTR_STATUS_EOF: the end of the buffer was reached, + * but more data is needed to finish the decoding process of the + * requested type. The user needs to call bt_ctf_btr_continue() + * as long as #BT_CTF_BTR_STATUS_EOF is returned to complete the + * decoding process of the original type. + * - #BT_CTF_BTR_STATUS_INVAL: invalid argument. + * - #BT_CTF_BTR_STATUS_ERROR: general error. + * + * @param btr Binary type 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 + */ +size_t bt_ctf_btr_continue(struct bt_ctf_btr *btr, + const uint8_t *buf, size_t sz, + enum bt_ctf_btr_status *status); + +#endif /* CTF_BTR_H */ diff --git a/plugins/ctf/common/btr/print.h b/plugins/ctf/common/btr/print.h new file mode 100644 index 00000000..22381dc1 --- /dev/null +++ b/plugins/ctf/common/btr/print.h @@ -0,0 +1,58 @@ +#ifndef CTF_BTR_PRINT_H +#define CTF_BTR_PRINT_H + +/* + * Define PRINT_PREFIX and PRINT_ERR_STREAM, then include this file. + * + * Copyright (c) 2016 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +#define PERR(fmt, ...) \ + do { \ + if (PRINT_ERR_STREAM) { \ + fprintf(PRINT_ERR_STREAM, \ + "Error: " PRINT_PREFIX ": " fmt, \ + ##__VA_ARGS__); \ + } \ + } while (0) + +#define PWARN(fmt, ...) \ + do { \ + if (PRINT_ERR_STREAM) { \ + fprintf(PRINT_ERR_STREAM, \ + "Warning: " PRINT_PREFIX ": " fmt, \ + ##__VA_ARGS__); \ + } \ + } while (0) + +#define PDBG(fmt, ...) \ + do { \ + if (babeltrace_debug) { \ + fprintf(stderr, \ + "Debug: " PRINT_PREFIX ": " fmt, \ + ##__VA_ARGS__); \ + } \ + } while (0) + +#endif /* CTF_BTR_PRINT_H */ diff --git a/plugins/ctf/common/metadata/Makefile.am b/plugins/ctf/common/metadata/Makefile.am new file mode 100644 index 00000000..e29800b2 --- /dev/null +++ b/plugins/ctf/common/metadata/Makefile.am @@ -0,0 +1,35 @@ +AM_CPPFLAGS = $(CPPFLAGS) -I$(top_srcdir)/include -I$(srcdir) +AM_CFLAGS = $(PACKAGE_CFLAGS) +BUILT_SOURCES = parser.h parser.c lexer.c +AM_YFLAGS = -t -d -v + +noinst_LTLIBRARIES = libctf-parser.la libctf-ast.la + +noinst_HEADERS = scanner.h ast.h scanner-symbols.h + +libctf_parser_la_SOURCES = lexer.l parser.y objstack.c +# ctf-scanner-symbols.h is included to prefix generated yy_* symbols +# with bt_. +libctf_parser_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir) \ + -include $(srcdir)/scanner-symbols.h + +libctf_ast_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(builddir) +libctf_ast_la_SOURCES = \ + visitor-generate-ir.c \ + visitor-semantic-validator.c \ + visitor-parent-links.c +libctf_ast_la_LIBADD = $(top_builddir)/lib/libbabeltrace.la + +if BABELTRACE_BUILD_WITH_LIBUUID +libctf_ast_la_LIBADD += -luuid +endif + +if BABELTRACE_BUILD_WITH_LIBC_UUID +libctf_ast_la_LIBADD += -lc +endif + +if BABELTRACE_BUILD_WITH_MINGW +libctf_ast_la_LIBADD += -lrpcrt4 -lintl -liconv -lole32 -lpopt +endif + +CLEANFILES = $(BUILT_SOURCES) parser.output diff --git a/plugins/ctf/common/metadata/ast.h b/plugins/ctf/common/metadata/ast.h new file mode 100644 index 00000000..c8843270 --- /dev/null +++ b/plugins/ctf/common/metadata/ast.h @@ -0,0 +1,321 @@ +#ifndef _CTF_AST_H +#define _CTF_AST_H + +/* + * ctf-ast.h + * + * Copyright 2011-2012 - Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + */ + +#include +#include +#include +#include +#include + +// 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; + +#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, typedef, + * typealias and type_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, typedef, + * typealias and type_specifier_list. + */ + struct bt_list_head declaration_list; + } event; + struct { + /* + * Children nodes are ctf_expression, typedef, + * typealias and type_specifier_list. + */ + struct bt_list_head declaration_list; + } stream; + struct { + /* + * Children nodes are ctf_expression, typedef, + * typealias and type_specifier_list. + */ + struct bt_list_head declaration_list; + } env; + struct { + /* + * Children nodes are ctf_expression, typedef, + * typealias and type_specifier_list. + */ + struct bt_list_head declaration_list; + } trace; + struct { + /* + * Children nodes are ctf_expression, typedef, + * typealias and type_specifier_list. + */ + struct bt_list_head declaration_list; + } clock; + struct { + /* + * Children nodes are ctf_expression, typedef, + * typealias and type_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 *type_specifier_list; + struct bt_list_head type_declarators; + } _typedef; + /* new type is "alias", existing type "target" */ + struct { + struct ctf_node *type_specifier_list; + struct bt_list_head type_declarators; + } typealias_target; + struct { + struct ctf_node *type_specifier_list; + struct bt_list_head type_declarators; + } typealias_alias; + struct { + struct ctf_node *target; + struct ctf_node *alias; + } typealias; + 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; + } type_specifier; + struct { + /* list of type_specifier */ + struct bt_list_head head; + } type_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 *type_declarator; + /* + * unary expression (value) or + * type_specifier_list. + */ + struct bt_list_head length; + /* for abstract type declarator */ + unsigned int abstract_array; + } nested; + } u; + struct ctf_node *bitfield_len; + } type_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 + * type_specifier_list. + */ + struct ctf_node *container_type; + struct bt_list_head enumerator_list; + int has_body; + } _enum; + struct { + struct ctf_node *type_specifier_list; + struct bt_list_head type_declarators; + } struct_or_variant_declaration; + struct { + char *name; + char *choice; + /* list of typedef, typealias and declarations */ + struct bt_list_head declaration_list; + int has_body; + } variant; + struct { + char *name; + /* list of typedef, typealias 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 ctf_trace; + +BT_HIDDEN +int ctf_visitor_generate_ir(FILE *efd, struct ctf_node *node, + struct bt_ctf_trace **trace); + +BT_HIDDEN +int ctf_visitor_semantic_check(FILE *fd, int depth, struct ctf_node *node); + +BT_HIDDEN +int ctf_visitor_parent_links(FILE *fd, int depth, struct ctf_node *node); + +BT_HIDDEN +int ctf_destroy_metadata(struct ctf_trace *trace); + +#endif /* _CTF_AST_H */ diff --git a/plugins/ctf/common/metadata/lexer.l b/plugins/ctf/common/metadata/lexer.l new file mode 100644 index 00000000..b71308a1 --- /dev/null +++ b/plugins/ctf/common/metadata/lexer.l @@ -0,0 +1,142 @@ +%{ +/* + * lexer.l + * + * Common Trace Formal Lexer + * + * Copyright 2010 - Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include "scanner.h" +#include "parser.h" +#include "ast.h" + +#define PARSE_INTEGER_LITERAL(base) \ + do { \ + errno = 0; \ + yylval->ull = strtoull(yytext, NULL, base); \ + if (errno) { \ + printfl_perror(yylineno, "Integer literal"); \ + return ERROR; \ + } \ + } while (0) + +BT_HIDDEN +void setstring(struct ctf_scanner *scanner, YYSTYPE *lvalp, const char *src); + +static void yyunput (int c, register char * yy_bp , yyscan_t yyscanner) + __attribute__((unused)); +static int input (yyscan_t yyscanner) __attribute__((unused)); + +BT_HIDDEN +int import_string(struct ctf_scanner *scanner, YYSTYPE *lvalp, const char *src, char delim); + +%} + +%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); +[^*\n]* /* eat anything that's not a '*' */ +"*"+[^*/\n]* /* eat up '*'s not followed by '/'s */ +\n +"*"+"/" BEGIN(INITIAL); + +"//"[^\n]*\n /* skip comment */ + +L?\"(\\.|[^\\"])*\" { if (import_string(yyextra, yylval, yytext, '\"') < 0) return ERROR; else return STRING_LITERAL; } +L?\'(\\.|[^\\'])*\' { if (import_string(yyextra, yylval, yytext, '\'') < 0) return ERROR; else return CHARACTER_LITERAL; } + +"[" return LSBRAC; +"]" return RSBRAC; +"(" return LPAREN; +")" return RPAREN; +"{" return LBRAC; +"}" return RBRAC; +"->" return RARROW; +"*" return STAR; +"+" return PLUS; +"-" return MINUS; +"<" return LT; +">" return GT; +:= return TYPEASSIGN; +: return COLON; +; return SEMICOLON; +"..." return DOTDOTDOT; +"." return DOT; += return EQUAL; +"," return COMMA; +align setstring(yyextra, yylval, yytext); return TOK_ALIGN; +const setstring(yyextra, yylval, yytext); return CONST; +char setstring(yyextra, yylval, yytext); return CHAR; +clock setstring(yyextra, yylval, yytext); return CLOCK; +double setstring(yyextra, yylval, yytext); return DOUBLE; +enum setstring(yyextra, yylval, yytext); return ENUM; +env setstring(yyextra, yylval, yytext); return ENV; +event setstring(yyextra, yylval, yytext); return EVENT; +floating_point setstring(yyextra, yylval, yytext); return FLOATING_POINT; +float setstring(yyextra, yylval, yytext); return FLOAT; +integer setstring(yyextra, yylval, yytext); return INTEGER; +int setstring(yyextra, yylval, yytext); return INT; +long setstring(yyextra, yylval, yytext); return LONG; +short setstring(yyextra, yylval, yytext); return SHORT; +signed setstring(yyextra, yylval, yytext); return SIGNED; +stream setstring(yyextra, yylval, yytext); return STREAM; +string setstring(yyextra, yylval, yytext); return STRING; +struct setstring(yyextra, yylval, yytext); return STRUCT; +trace setstring(yyextra, yylval, yytext); return TRACE; +callsite setstring(yyextra, yylval, yytext); return CALLSITE; +typealias setstring(yyextra, yylval, yytext); return TYPEALIAS; +typedef setstring(yyextra, yylval, yytext); return TYPEDEF; +unsigned setstring(yyextra, yylval, yytext); return UNSIGNED; +variant setstring(yyextra, yylval, yytext); return VARIANT; +void setstring(yyextra, yylval, yytext); return VOID; +_Bool setstring(yyextra, yylval, yytext); return _BOOL; +_Complex setstring(yyextra, yylval, yytext); return _COMPLEX; +_Imaginary setstring(yyextra, yylval, yytext); return _IMAGINARY; +[1-9]{DIGIT}*{INTEGER_SUFFIX}? PARSE_INTEGER_LITERAL(10); return INTEGER_LITERAL; +0{OCTALDIGIT}*{INTEGER_SUFFIX}? PARSE_INTEGER_LITERAL(8); return INTEGER_LITERAL; +0[xX]{HEXDIGIT}+{INTEGER_SUFFIX}? PARSE_INTEGER_LITERAL(16); return INTEGER_LITERAL; + +{IDENTIFIER} printf_debug("\n", yytext); setstring(yyextra, yylval, yytext); if (is_type(yyextra, yytext)) return ID_TYPE; else return IDENTIFIER; +[ \t\r\n] ; /* ignore */ +. printfl_error(yylineno, "invalid character '0x%02X'", yytext[0]); return ERROR; +%% diff --git a/plugins/ctf/common/metadata/objstack.c b/plugins/ctf/common/metadata/objstack.c new file mode 100644 index 00000000..80877220 --- /dev/null +++ b/plugins/ctf/common/metadata/objstack.c @@ -0,0 +1,137 @@ +/* + * objstack.c + * + * Common Trace Format Object Stack. + * + * Copyright 2013 - Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include + +#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) + return NULL; + node = calloc(sizeof(struct objstack_node) + OBJSTACK_INIT_LEN, + sizeof(char)); + if (!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) { + 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 = ALIGN(len, OBJSTACK_ALIGN); + + /* Get last node */ + last_node = bt_list_entry(objstack->head.prev, + struct objstack_node, node); + while (last_node->len - last_node->used_len < len) { + last_node = objstack_append_node(objstack); + if (!last_node) { + return NULL; + } + } + p = &last_node->data[last_node->used_len]; + last_node->used_len += len; + return p; +} diff --git a/plugins/ctf/common/metadata/objstack.h b/plugins/ctf/common/metadata/objstack.h new file mode 100644 index 00000000..c026eb54 --- /dev/null +++ b/plugins/ctf/common/metadata/objstack.h @@ -0,0 +1,44 @@ +#ifndef _OBJSTACK_H +#define _OBJSTACK_H + +/* + * objstack.h + * + * Common Trace Format Object Stack. + * + * Copyright 2013 - Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +struct objstack; + +BT_HIDDEN +struct objstack *objstack_create(void); +BT_HIDDEN +void objstack_destroy(struct objstack *objstack); + +/* + * Allocate len bytes of zeroed memory. + * Return NULL on error. + */ +BT_HIDDEN +void *objstack_alloc(struct objstack *objstack, size_t len); + +#endif /* _OBJSTACK_H */ diff --git a/plugins/ctf/common/metadata/parser.y b/plugins/ctf/common/metadata/parser.y new file mode 100644 index 00000000..1934b24d --- /dev/null +++ b/plugins/ctf/common/metadata/parser.y @@ -0,0 +1,2586 @@ +%{ +/* + * ctf-parser.y + * + * Common Trace Format Metadata Grammar. + * + * Copyright 2010 - Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "scanner.h" +#include "parser.h" +#include "ast.h" +#include "objstack.h" + +BT_HIDDEN +int yydebug; + +/* 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 yyparse(struct ctf_scanner *scanner, yyscan_t yyscanner); +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; + } + } + assert(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 + printfl_error(yyget_lineno(scanner), + "Wide string not supported yet."); + 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->types = 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->types); +} + +static void push_scope(struct ctf_scanner *scanner) +{ + struct ctf_scanner_scope *ns; + + printf_debug("push scope\n"); + 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; + + printf_debug("pop scope\n"); + 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 = (int) (long) g_hash_table_lookup(s->types, id); + printf_debug("lookup %p %s %d\n", 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 != NULL; it = it->parent) { + if (lookup_type(it, id)) { + ret = 1; + break; + } + } + printf_debug("is type %s %d\n", id, ret); + return ret; +} + +static void add_type(struct ctf_scanner *scanner, char *id) +{ + printf_debug("add type %s\n", id); + if (lookup_type(scanner->cs, id)) + return; + g_hash_table_insert(scanner->cs->types, 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) { + printfl_fatal(yyget_lineno(scanner->scanner), "out of memory"); + 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; + printfn_fatal(node, "trying to create root node"); + 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._typedef.type_declarators); + break; + case NODE_TYPEALIAS_TARGET: + BT_INIT_LIST_HEAD(&node->u.typealias_target.type_declarators); + break; + case NODE_TYPEALIAS_ALIAS: + BT_INIT_LIST_HEAD(&node->u.typealias_alias.type_declarators); + break; + case NODE_TYPEALIAS: + break; + + case NODE_TYPE_SPECIFIER: + break; + case NODE_TYPE_SPECIFIER_LIST: + BT_INIT_LIST_HEAD(&node->u.type_specifier_list.head); + break; + case NODE_POINTER: + break; + case NODE_TYPE_DECLARATOR: + BT_INIT_LIST_HEAD(&node->u.type_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.type_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; + printfn_fatal(node, "unknown node type '%d'", (int) 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: + printfn_fatal(node, "unknown node type '%d'", (int) 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: + printfn_fatal(node, "unknown node type %d", parent->type); + return -EINVAL; + } + return 0; +} + +static int reparent_typealias(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: + printfn_fatal(node, "unknown node type '%d'", (int) parent->type); + return -EINVAL; + } + return 0; +} + +static int reparent_type_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.type_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: + printfn_fatal(node, "unknown node type '%d'", (int) parent->type); + return -EINVAL; + } + return 0; +} + +static int reparent_type_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._typedef.type_specifier_list = node; + break; + case NODE_TYPEALIAS_TARGET: + parent->u.typealias_target.type_specifier_list = node; + break; + case NODE_TYPEALIAS_ALIAS: + parent->u.typealias_alias.type_specifier_list = node; + break; + case NODE_ENUM: + parent->u._enum.container_type = node; + break; + case NODE_STRUCT_OR_VARIANT_DECLARATION: + parent->u.struct_or_variant_declaration.type_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: + printfn_fatal(node, "unknown node type '%d'", (int) parent->type); + return -EINVAL; + } + return 0; +} + +static int reparent_type_declarator(struct ctf_node *node, + struct ctf_node *parent) +{ + switch (parent->type) { + case NODE_TYPE_DECLARATOR: + parent->u.type_declarator.type = TYPEDEC_NESTED; + parent->u.type_declarator.u.nested.type_declarator = node; + break; + case NODE_STRUCT_OR_VARIANT_DECLARATION: + _bt_list_splice_tail(&node->tmp_head, &parent->u.struct_or_variant_declaration.type_declarators); + break; + case NODE_TYPEDEF: + _bt_list_splice_tail(&node->tmp_head, &parent->u._typedef.type_declarators); + break; + case NODE_TYPEALIAS_TARGET: + _bt_list_splice_tail(&node->tmp_head, &parent->u.typealias_target.type_declarators); + break; + case NODE_TYPEALIAS_ALIAS: + _bt_list_splice_tail(&node->tmp_head, &parent->u.typealias_alias.type_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: + printfn_fatal(node, "unknown node type '%d'", (int) 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: + printfn_fatal(node, "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.type_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.typealias.target = node; + else + return -EINVAL; + case NODE_TYPEALIAS_ALIAS: + if (parent->type == NODE_TYPEALIAS) + parent->u.typealias.alias = node; + else + return -EINVAL; + case NODE_TYPEALIAS: + return reparent_typealias(node, parent); + + case NODE_POINTER: + if (parent->type == NODE_TYPE_DECLARATOR) { + _bt_list_splice_tail(&node->tmp_head, &parent->u.type_declarator.pointers); + } else + return -EPERM; + break; + case NODE_TYPE_DECLARATOR: + return reparent_type_declarator(node, parent); + + case NODE_TYPE_SPECIFIER_LIST: + return reparent_type_specifier_list(node, parent); + + case NODE_TYPE_SPECIFIER: + return reparent_type_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: + printfn_fatal(node, "unknown node type '%d'", (int) parent->type); + return -EINVAL; + } + return 0; +} + +BT_HIDDEN +void yyerror(struct ctf_scanner *scanner, yyscan_t yyscanner, const char *str) +{ + printfl_error(yyget_lineno(scanner->scanner), + "token \"%s\": %s\n", + yyget_text(scanner->scanner), str); +} + +BT_HIDDEN +int yywrap(void) +{ + return 1; +} + +#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); + if (yydebug) + fprintf(stdout, "Scanner input is a%s.\n", + isatty(fileno(input)) ? "n interactive tty" : + " noninteractive file"); + return yyparse(scanner, scanner->scanner); +} + +struct ctf_scanner *ctf_scanner_alloc(void) +{ + struct ctf_scanner *scanner; + int ret; + + yydebug = babeltrace_debug; + + scanner = malloc(sizeof(*scanner)); + if (!scanner) + return NULL; + memset(scanner, 0, sizeof(*scanner)); + ret = yylex_init_extra(scanner, &scanner->scanner); + if (ret) { + printf_fatal("yylex_init error"); + 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) + printf_fatal("yylex_destroy error"); +cleanup_scanner: + free(scanner); + return NULL; +} + +void ctf_scanner_free(struct ctf_scanner *scanner) +{ + int ret; + + if (!scanner) + return; + finalize_scope(&scanner->root_scope); + objstack_destroy(scanner->objstack); + ret = yylex_destroy(scanner->scanner); + if (ret) + printf_error("yylex_destroy error"); + free(scanner); +} + +%} + +%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 INTEGER_LITERAL STRING_LITERAL CHARACTER_LITERAL LSBRAC RSBRAC LPAREN RPAREN LBRAC RBRAC RARROW STAR PLUS MINUS LT GT TYPEASSIGN COLON SEMICOLON DOTDOTDOT DOT EQUAL COMMA CONST CHAR DOUBLE ENUM ENV EVENT FLOATING_POINT FLOAT INTEGER INT LONG SHORT SIGNED STREAM STRING STRUCT TRACE CALLSITE CLOCK TYPEALIAS TYPEDEF UNSIGNED VARIANT VOID _BOOL _COMPLEX _IMAGINARY TOK_ALIGN +%token IDENTIFIER ID_TYPE +%token ERROR +%union +{ + long long ll; + unsigned long long ull; + char c; + char *s; + struct ctf_node *n; +} + +%type STRING_LITERAL CHARACTER_LITERAL + +%type keywords + +%type INTEGER_LITERAL +%type postfix_expression unary_expression unary_expression_or_range + +%type declaration +%type event_declaration +%type stream_declaration +%type env_declaration +%type trace_declaration +%type clock_declaration +%type callsite_declaration +%type integer_declaration_specifiers +%type declaration_specifiers +%type alias_declaration_specifiers + +%type type_declarator_list +%type integer_type_specifier +%type type_specifier +%type struct_type_specifier +%type variant_type_specifier +%type enum_type_specifier +%type struct_or_variant_declaration_list +%type struct_or_variant_declaration +%type struct_or_variant_declarator_list +%type struct_or_variant_declarator +%type enumerator_list +%type enumerator +%type abstract_declarator_list +%type abstract_declarator +%type direct_abstract_declarator +%type alias_abstract_declarator_list +%type alias_abstract_declarator +%type direct_alias_abstract_declarator +%type declarator +%type direct_declarator +%type type_declarator +%type direct_type_declarator +%type pointer +%type ctf_assignment_expression_list +%type 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: + VOID + { $$ = yylval.s; } + | CHAR + { $$ = yylval.s; } + | SHORT + { $$ = yylval.s; } + | INT + { $$ = yylval.s; } + | LONG + { $$ = yylval.s; } + | FLOAT + { $$ = yylval.s; } + | DOUBLE + { $$ = yylval.s; } + | SIGNED + { $$ = yylval.s; } + | UNSIGNED + { $$ = yylval.s; } + | _BOOL + { $$ = yylval.s; } + | _COMPLEX + { $$ = yylval.s; } + | _IMAGINARY + { $$ = yylval.s; } + | FLOATING_POINT + { $$ = yylval.s; } + | INTEGER + { $$ = yylval.s; } + | STRING + { $$ = yylval.s; } + | ENUM + { $$ = yylval.s; } + | VARIANT + { $$ = yylval.s; } + | STRUCT + { $$ = yylval.s; } + | CONST + { $$ = yylval.s; } + | TYPEDEF + { $$ = yylval.s; } + | EVENT + { $$ = yylval.s; } + | STREAM + { $$ = yylval.s; } + | ENV + { $$ = yylval.s; } + | TRACE + { $$ = yylval.s; } + | CLOCK + { $$ = yylval.s; } + | CALLSITE + { $$ = yylval.s; } + | 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; + } + | INTEGER_LITERAL + { + $$ = make_node(scanner, NODE_UNARY_EXPRESSION); + $$->u.unary_expression.type = UNARY_UNSIGNED_CONSTANT; + $$->u.unary_expression.u.unsigned_constant = $1; + } + | STRING_LITERAL + { + $$ = make_node(scanner, NODE_UNARY_EXPRESSION); + $$->u.unary_expression.type = UNARY_STRING; + $$->u.unary_expression.u.string = $1; + } + | CHARACTER_LITERAL + { + $$ = make_node(scanner, NODE_UNARY_EXPRESSION); + $$->u.unary_expression.type = UNARY_STRING; + $$->u.unary_expression.u.string = $1; + } + | LPAREN unary_expression RPAREN + { + $$ = $2; + } + | postfix_expression LSBRAC unary_expression 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 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 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 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 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 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; } + | 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"); + } + } + | 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_UNSIGNED_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 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 SEMICOLON + { $$ = $1; } + | event_declaration + { $$ = $1; } + | stream_declaration + { $$ = $1; } + | env_declaration + { $$ = $1; } + | trace_declaration + { $$ = $1; } + | clock_declaration + { $$ = $1; } + | callsite_declaration + { $$ = $1; } + | declaration_specifiers TYPEDEF declaration_specifiers type_declarator_list SEMICOLON + { + struct ctf_node *list; + + $$ = make_node(scanner, NODE_TYPEDEF); + list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + $$->u._typedef.type_specifier_list = list; + _bt_list_splice_tail(&($1)->u.type_specifier_list.head, &list->u.type_specifier_list.head); + _bt_list_splice_tail(&($3)->u.type_specifier_list.head, &list->u.type_specifier_list.head); + _bt_list_splice_tail(&($4)->tmp_head, &($$)->u._typedef.type_declarators); + } + | TYPEDEF declaration_specifiers type_declarator_list SEMICOLON + { + struct ctf_node *list; + + $$ = make_node(scanner, NODE_TYPEDEF); + list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + $$->u._typedef.type_specifier_list = list; + _bt_list_splice_tail(&($2)->u.type_specifier_list.head, &list->u.type_specifier_list.head); + _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._typedef.type_declarators); + } + | declaration_specifiers TYPEDEF type_declarator_list SEMICOLON + { + struct ctf_node *list; + + $$ = make_node(scanner, NODE_TYPEDEF); + list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + $$->u._typedef.type_specifier_list = list; + _bt_list_splice_tail(&($1)->u.type_specifier_list.head, &list->u.type_specifier_list.head); + _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._typedef.type_declarators); + } + | TYPEALIAS declaration_specifiers abstract_declarator_list TYPEASSIGN alias_declaration_specifiers alias_abstract_declarator_list SEMICOLON + { + struct ctf_node *list; + + $$ = make_node(scanner, NODE_TYPEALIAS); + $$->u.typealias.target = make_node(scanner, NODE_TYPEALIAS_TARGET); + $$->u.typealias.alias = make_node(scanner, NODE_TYPEALIAS_ALIAS); + + list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + $$->u.typealias.target->u.typealias_target.type_specifier_list = list; + _bt_list_splice_tail(&($2)->u.type_specifier_list.head, &list->u.type_specifier_list.head); + _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.typealias.target->u.typealias_target.type_declarators); + + list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + $$->u.typealias.alias->u.typealias_alias.type_specifier_list = list; + _bt_list_splice_tail(&($5)->u.type_specifier_list.head, &list->u.type_specifier_list.head); + _bt_list_splice_tail(&($6)->tmp_head, &($$)->u.typealias.alias->u.typealias_alias.type_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: + EVENT LBRAC + { push_scope(scanner); } + ; + +event_declaration_end: + RBRAC 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: + STREAM LBRAC + { push_scope(scanner); } + ; + +stream_declaration_end: + RBRAC 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: + ENV LBRAC + { push_scope(scanner); } + ; + +env_declaration_end: + RBRAC 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: + TRACE LBRAC + { push_scope(scanner); } + ; + +trace_declaration_end: + RBRAC SEMICOLON + { pop_scope(scanner); } + ; + +clock_declaration: + CLOCK clock_declaration_begin clock_declaration_end + { + $$ = make_node(scanner, NODE_CLOCK); + } + | 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: + LBRAC + { push_scope(scanner); } + ; + +clock_declaration_end: + RBRAC SEMICOLON + { pop_scope(scanner); } + ; + +callsite_declaration: + CALLSITE callsite_declaration_begin callsite_declaration_end + { + $$ = make_node(scanner, NODE_CALLSITE); + } + | 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: + LBRAC + { push_scope(scanner); } + ; + +callsite_declaration_end: + RBRAC SEMICOLON + { pop_scope(scanner); } + ; + +integer_declaration_specifiers: + CONST + { + struct ctf_node *node; + + $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + node = make_node(scanner, NODE_TYPE_SPECIFIER); + node->u.type_specifier.type = TYPESPEC_CONST; + bt_list_add_tail(&node->siblings, &($$)->u.type_specifier_list.head); + } + | integer_type_specifier + { + struct ctf_node *node; + + $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + node = $1; + bt_list_add_tail(&node->siblings, &($$)->u.type_specifier_list.head); + } + | integer_declaration_specifiers CONST + { + struct ctf_node *node; + + $$ = $1; + node = make_node(scanner, NODE_TYPE_SPECIFIER); + node->u.type_specifier.type = TYPESPEC_CONST; + bt_list_add_tail(&node->siblings, &($$)->u.type_specifier_list.head); + } + | integer_declaration_specifiers integer_type_specifier + { + $$ = $1; + bt_list_add_tail(&($2)->siblings, &($$)->u.type_specifier_list.head); + } + ; + +declaration_specifiers: + CONST + { + struct ctf_node *node; + + $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + node = make_node(scanner, NODE_TYPE_SPECIFIER); + node->u.type_specifier.type = TYPESPEC_CONST; + bt_list_add_tail(&node->siblings, &($$)->u.type_specifier_list.head); + } + | type_specifier + { + struct ctf_node *node; + + $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + node = $1; + bt_list_add_tail(&node->siblings, &($$)->u.type_specifier_list.head); + } + | declaration_specifiers CONST + { + struct ctf_node *node; + + $$ = $1; + node = make_node(scanner, NODE_TYPE_SPECIFIER); + node->u.type_specifier.type = TYPESPEC_CONST; + bt_list_add_tail(&node->siblings, &($$)->u.type_specifier_list.head); + } + | declaration_specifiers type_specifier + { + $$ = $1; + bt_list_add_tail(&($2)->siblings, &($$)->u.type_specifier_list.head); + } + ; + +type_declarator_list: + type_declarator + { $$ = $1; } + | type_declarator_list COMMA type_declarator + { + $$ = $1; + bt_list_add_tail(&($3)->siblings, &($$)->tmp_head); + } + ; + +integer_type_specifier: + CHAR + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.type_specifier.type = TYPESPEC_CHAR; + } + | SHORT + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.type_specifier.type = TYPESPEC_SHORT; + } + | INT + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.type_specifier.type = TYPESPEC_INT; + } + | LONG + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.type_specifier.type = TYPESPEC_LONG; + } + | SIGNED + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.type_specifier.type = TYPESPEC_SIGNED; + } + | UNSIGNED + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.type_specifier.type = TYPESPEC_UNSIGNED; + } + | _BOOL + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.type_specifier.type = TYPESPEC_BOOL; + } + | ID_TYPE + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.type_specifier.type = TYPESPEC_ID_TYPE; + $$->u.type_specifier.id_type = yylval.s; + } + | INTEGER LBRAC RBRAC + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.type_specifier.type = TYPESPEC_INTEGER; + $$->u.type_specifier.node = make_node(scanner, NODE_INTEGER); + } + | INTEGER LBRAC ctf_assignment_expression_list RBRAC + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.type_specifier.type = TYPESPEC_INTEGER; + $$->u.type_specifier.node = make_node(scanner, NODE_INTEGER); + if (set_parent_node($3, $$->u.type_specifier.node)) + reparent_error(scanner, "integer reparent error"); + } + ; + +type_specifier: + VOID + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.type_specifier.type = TYPESPEC_VOID; + } + | CHAR + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.type_specifier.type = TYPESPEC_CHAR; + } + | SHORT + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.type_specifier.type = TYPESPEC_SHORT; + } + | INT + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.type_specifier.type = TYPESPEC_INT; + } + | LONG + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.type_specifier.type = TYPESPEC_LONG; + } + | FLOAT + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.type_specifier.type = TYPESPEC_FLOAT; + } + | DOUBLE + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.type_specifier.type = TYPESPEC_DOUBLE; + } + | SIGNED + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.type_specifier.type = TYPESPEC_SIGNED; + } + | UNSIGNED + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.type_specifier.type = TYPESPEC_UNSIGNED; + } + | _BOOL + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.type_specifier.type = TYPESPEC_BOOL; + } + | _COMPLEX + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.type_specifier.type = TYPESPEC_COMPLEX; + } + | _IMAGINARY + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.type_specifier.type = TYPESPEC_IMAGINARY; + } + | ID_TYPE + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.type_specifier.type = TYPESPEC_ID_TYPE; + $$->u.type_specifier.id_type = yylval.s; + } + | FLOATING_POINT LBRAC RBRAC + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.type_specifier.type = TYPESPEC_FLOATING_POINT; + $$->u.type_specifier.node = make_node(scanner, NODE_FLOATING_POINT); + } + | FLOATING_POINT LBRAC ctf_assignment_expression_list RBRAC + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.type_specifier.type = TYPESPEC_FLOATING_POINT; + $$->u.type_specifier.node = make_node(scanner, NODE_FLOATING_POINT); + if (set_parent_node($3, $$->u.type_specifier.node)) + reparent_error(scanner, "floating point reparent error"); + } + | INTEGER LBRAC RBRAC + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.type_specifier.type = TYPESPEC_INTEGER; + $$->u.type_specifier.node = make_node(scanner, NODE_INTEGER); + } + | INTEGER LBRAC ctf_assignment_expression_list RBRAC + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.type_specifier.type = TYPESPEC_INTEGER; + $$->u.type_specifier.node = make_node(scanner, NODE_INTEGER); + if (set_parent_node($3, $$->u.type_specifier.node)) + reparent_error(scanner, "integer reparent error"); + } + | STRING + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.type_specifier.type = TYPESPEC_STRING; + $$->u.type_specifier.node = make_node(scanner, NODE_STRING); + } + | STRING LBRAC RBRAC + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.type_specifier.type = TYPESPEC_STRING; + $$->u.type_specifier.node = make_node(scanner, NODE_STRING); + } + | STRING LBRAC ctf_assignment_expression_list RBRAC + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.type_specifier.type = TYPESPEC_STRING; + $$->u.type_specifier.node = make_node(scanner, NODE_STRING); + if (set_parent_node($3, $$->u.type_specifier.node)) + reparent_error(scanner, "string reparent error"); + } + | ENUM enum_type_specifier + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.type_specifier.type = TYPESPEC_ENUM; + $$->u.type_specifier.node = $2; + } + | VARIANT variant_type_specifier + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.type_specifier.type = TYPESPEC_VARIANT; + $$->u.type_specifier.node = $2; + } + | STRUCT struct_type_specifier + { + $$ = make_node(scanner, NODE_TYPE_SPECIFIER); + $$->u.type_specifier.type = TYPESPEC_STRUCT; + $$->u.type_specifier.node = $2; + } + ; + +struct_type_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 TOK_ALIGN LPAREN unary_expression 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 TOK_ALIGN LPAREN unary_expression 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 TOK_ALIGN LPAREN unary_expression 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: + LBRAC + { push_scope(scanner); } + ; + +struct_declaration_end: + RBRAC + { pop_scope(scanner); } + ; + +variant_type_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"); + } + | LT IDENTIFIER 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"); + } + | LT ID_TYPE 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 LT IDENTIFIER 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 LT IDENTIFIER GT + { + $$ = make_node(scanner, NODE_VARIANT); + $$->u.variant.has_body = 0; + $$->u.variant.name = $1; + $$->u.variant.choice = $3; + } + | IDENTIFIER LT ID_TYPE 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 LT ID_TYPE 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 LT IDENTIFIER 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 LT IDENTIFIER GT + { + $$ = make_node(scanner, NODE_VARIANT); + $$->u.variant.has_body = 0; + $$->u.variant.name = $1; + $$->u.variant.choice = $3; + } + | ID_TYPE LT ID_TYPE 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 LT ID_TYPE GT + { + $$ = make_node(scanner, NODE_VARIANT); + $$->u.variant.has_body = 0; + $$->u.variant.name = $1; + $$->u.variant.choice = $3; + } + ; + +variant_declaration_begin: + LBRAC + { push_scope(scanner); } + ; + +variant_declaration_end: + RBRAC + { pop_scope(scanner); } + ; + +enum_type_specifier: + LBRAC enumerator_list RBRAC + { + $$ = make_node(scanner, NODE_ENUM); + $$->u._enum.has_body = 1; + _bt_list_splice_tail(&($2)->tmp_head, &($$)->u._enum.enumerator_list); + } + | COLON integer_declaration_specifiers LBRAC enumerator_list RBRAC + { + $$ = make_node(scanner, NODE_ENUM); + $$->u._enum.has_body = 1; + ($$)->u._enum.container_type = $2; + _bt_list_splice_tail(&($4)->tmp_head, &($$)->u._enum.enumerator_list); + } + | IDENTIFIER LBRAC enumerator_list 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 COLON integer_declaration_specifiers LBRAC enumerator_list RBRAC + { + $$ = make_node(scanner, NODE_ENUM); + $$->u._enum.has_body = 1; + $$->u._enum.enum_id = $1; + ($$)->u._enum.container_type = $3; + _bt_list_splice_tail(&($5)->tmp_head, &($$)->u._enum.enumerator_list); + } + | ID_TYPE LBRAC enumerator_list 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 COLON integer_declaration_specifiers LBRAC enumerator_list RBRAC + { + $$ = make_node(scanner, NODE_ENUM); + $$->u._enum.has_body = 1; + $$->u._enum.enum_id = $1; + ($$)->u._enum.container_type = $3; + _bt_list_splice_tail(&($5)->tmp_head, &($$)->u._enum.enumerator_list); + } + | LBRAC enumerator_list COMMA RBRAC + { + $$ = make_node(scanner, NODE_ENUM); + $$->u._enum.has_body = 1; + _bt_list_splice_tail(&($2)->tmp_head, &($$)->u._enum.enumerator_list); + } + | COLON integer_declaration_specifiers LBRAC enumerator_list COMMA RBRAC + { + $$ = make_node(scanner, NODE_ENUM); + $$->u._enum.has_body = 1; + ($$)->u._enum.container_type = $2; + _bt_list_splice_tail(&($4)->tmp_head, &($$)->u._enum.enumerator_list); + } + | IDENTIFIER LBRAC enumerator_list COMMA 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 COLON integer_declaration_specifiers LBRAC enumerator_list COMMA RBRAC + { + $$ = make_node(scanner, NODE_ENUM); + $$->u._enum.has_body = 1; + $$->u._enum.enum_id = $1; + ($$)->u._enum.container_type = $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 LBRAC enumerator_list COMMA 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 COLON integer_declaration_specifiers LBRAC enumerator_list COMMA RBRAC + { + $$ = make_node(scanner, NODE_ENUM); + $$->u._enum.has_body = 1; + $$->u._enum.enum_id = $1; + ($$)->u._enum.container_type = $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 SEMICOLON + { + struct ctf_node *list; + + list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + _bt_list_splice_tail(&($1)->u.type_specifier_list.head, &list->u.type_specifier_list.head); + $$ = make_node(scanner, NODE_STRUCT_OR_VARIANT_DECLARATION); + ($$)->u.struct_or_variant_declaration.type_specifier_list = list; + _bt_list_splice_tail(&($2)->tmp_head, &($$)->u.struct_or_variant_declaration.type_declarators); + } + | declaration_specifiers TYPEDEF declaration_specifiers type_declarator_list SEMICOLON + { + struct ctf_node *list; + + $$ = make_node(scanner, NODE_TYPEDEF); + list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + $$->u._typedef.type_specifier_list = list; + _bt_list_splice_tail(&($1)->u.type_specifier_list.head, &list->u.type_specifier_list.head); + _bt_list_splice_tail(&($3)->u.type_specifier_list.head, &list->u.type_specifier_list.head); + _bt_list_splice_tail(&($4)->tmp_head, &($$)->u._typedef.type_declarators); + } + | TYPEDEF declaration_specifiers type_declarator_list SEMICOLON + { + struct ctf_node *list; + + $$ = make_node(scanner, NODE_TYPEDEF); + list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + $$->u._typedef.type_specifier_list = list; + _bt_list_splice_tail(&($2)->u.type_specifier_list.head, &list->u.type_specifier_list.head); + _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._typedef.type_declarators); + } + | declaration_specifiers TYPEDEF type_declarator_list SEMICOLON + { + struct ctf_node *list; + + list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + _bt_list_splice_tail(&($1)->u.type_specifier_list.head, &list->u.type_specifier_list.head); + $$ = make_node(scanner, NODE_TYPEDEF); + ($$)->u.struct_or_variant_declaration.type_specifier_list = list; + _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._typedef.type_declarators); + } + | TYPEALIAS declaration_specifiers abstract_declarator_list TYPEASSIGN alias_declaration_specifiers alias_abstract_declarator_list SEMICOLON + { + struct ctf_node *list; + + $$ = make_node(scanner, NODE_TYPEALIAS); + $$->u.typealias.target = make_node(scanner, NODE_TYPEALIAS_TARGET); + $$->u.typealias.alias = make_node(scanner, NODE_TYPEALIAS_ALIAS); + + list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + $$->u.typealias.target->u.typealias_target.type_specifier_list = list; + _bt_list_splice_tail(&($2)->u.type_specifier_list.head, &list->u.type_specifier_list.head); + _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.typealias.target->u.typealias_target.type_declarators); + + list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + $$->u.typealias.alias->u.typealias_alias.type_specifier_list = list; + _bt_list_splice_tail(&($5)->u.type_specifier_list.head, &list->u.type_specifier_list.head); + _bt_list_splice_tail(&($6)->tmp_head, &($$)->u.typealias.alias->u.typealias_alias.type_declarators); + } + ; + +alias_declaration_specifiers: + CONST + { + struct ctf_node *node; + + $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + node = make_node(scanner, NODE_TYPE_SPECIFIER); + node->u.type_specifier.type = TYPESPEC_CONST; + bt_list_add_tail(&node->siblings, &($$)->u.type_specifier_list.head); + } + | type_specifier + { + struct ctf_node *node; + + $$ = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + node = $1; + bt_list_add_tail(&node->siblings, &($$)->u.type_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.type_specifier.type = TYPESPEC_ID_TYPE; + node->u.type_specifier.id_type = yylval.s; + bt_list_add_tail(&node->siblings, &($$)->u.type_specifier_list.head); + } + | alias_declaration_specifiers CONST + { + struct ctf_node *node; + + $$ = $1; + node = make_node(scanner, NODE_TYPE_SPECIFIER); + node->u.type_specifier.type = TYPESPEC_CONST; + bt_list_add_tail(&node->siblings, &($$)->u.type_specifier_list.head); + } + | alias_declaration_specifiers type_specifier + { + $$ = $1; + bt_list_add_tail(&($2)->siblings, &($$)->u.type_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.type_specifier.type = TYPESPEC_ID_TYPE; + node->u.type_specifier.id_type = yylval.s; + bt_list_add_tail(&node->siblings, &($$)->u.type_specifier_list.head); + } + ; + +struct_or_variant_declarator_list: + struct_or_variant_declarator + { $$ = $1; } + | struct_or_variant_declarator_list COMMA struct_or_variant_declarator + { + $$ = $1; + bt_list_add_tail(&($3)->siblings, &($$)->tmp_head); + } + ; + +struct_or_variant_declarator: + declarator + { $$ = $1; } + | COLON unary_expression + { $$ = $2; } + | declarator COLON unary_expression + { + $$ = $1; + if (set_parent_node($3, $1)) + reparent_error(scanner, "struct_or_variant_declarator"); + } + ; + +enumerator_list: + enumerator + { $$ = $1; } + | enumerator_list 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; + } + | STRING_LITERAL + { + $$ = make_node(scanner, NODE_ENUMERATOR); + $$->u.enumerator.id = $1; + } + | IDENTIFIER 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 EQUAL unary_expression_or_range + { + $$ = make_node(scanner, NODE_ENUMERATOR); + $$->u.enumerator.id = $1; + bt_list_splice(&($3)->tmp_head, &($$)->u.enumerator.values); + } + | keywords EQUAL unary_expression_or_range + { + $$ = make_node(scanner, NODE_ENUMERATOR); + $$->u.enumerator.id = $1; + bt_list_splice(&($3)->tmp_head, &($$)->u.enumerator.values); + } + | STRING_LITERAL 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 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.type_declarator.pointers); + } + ; + +direct_abstract_declarator: + /* empty */ + { + $$ = make_node(scanner, NODE_TYPE_DECLARATOR); + $$->u.type_declarator.type = TYPEDEC_ID; + /* id is NULL */ + } + | IDENTIFIER + { + $$ = make_node(scanner, NODE_TYPE_DECLARATOR); + $$->u.type_declarator.type = TYPEDEC_ID; + $$->u.type_declarator.u.id = $1; + } + | LPAREN abstract_declarator RPAREN + { + $$ = make_node(scanner, NODE_TYPE_DECLARATOR); + $$->u.type_declarator.type = TYPEDEC_NESTED; + $$->u.type_declarator.u.nested.type_declarator = $2; + } + | direct_abstract_declarator LSBRAC unary_expression RSBRAC + { + $$ = make_node(scanner, NODE_TYPE_DECLARATOR); + $$->u.type_declarator.type = TYPEDEC_NESTED; + $$->u.type_declarator.u.nested.type_declarator = $1; + BT_INIT_LIST_HEAD(&($$)->u.type_declarator.u.nested.length); + _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.type_declarator.u.nested.length); + } + | direct_abstract_declarator LSBRAC RSBRAC + { + $$ = make_node(scanner, NODE_TYPE_DECLARATOR); + $$->u.type_declarator.type = TYPEDEC_NESTED; + $$->u.type_declarator.u.nested.type_declarator = $1; + $$->u.type_declarator.u.nested.abstract_array = 1; + } + ; + +alias_abstract_declarator_list: + alias_abstract_declarator + { $$ = $1; } + | alias_abstract_declarator_list 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.type_declarator.pointers); + } + ; + +direct_alias_abstract_declarator: + /* empty */ + { + $$ = make_node(scanner, NODE_TYPE_DECLARATOR); + $$->u.type_declarator.type = TYPEDEC_ID; + /* id is NULL */ + } + | LPAREN alias_abstract_declarator RPAREN + { + $$ = make_node(scanner, NODE_TYPE_DECLARATOR); + $$->u.type_declarator.type = TYPEDEC_NESTED; + $$->u.type_declarator.u.nested.type_declarator = $2; + } + | direct_alias_abstract_declarator LSBRAC unary_expression RSBRAC + { + $$ = make_node(scanner, NODE_TYPE_DECLARATOR); + $$->u.type_declarator.type = TYPEDEC_NESTED; + $$->u.type_declarator.u.nested.type_declarator = $1; + BT_INIT_LIST_HEAD(&($$)->u.type_declarator.u.nested.length); + _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.type_declarator.u.nested.length); + } + | direct_alias_abstract_declarator LSBRAC RSBRAC + { + $$ = make_node(scanner, NODE_TYPE_DECLARATOR); + $$->u.type_declarator.type = TYPEDEC_NESTED; + $$->u.type_declarator.u.nested.type_declarator = $1; + $$->u.type_declarator.u.nested.abstract_array = 1; + } + ; + +declarator: + direct_declarator + { $$ = $1; } + | pointer direct_declarator + { + $$ = $2; + bt_list_splice(&($1)->tmp_head, &($$)->u.type_declarator.pointers); + } + ; + +direct_declarator: + IDENTIFIER + { + $$ = make_node(scanner, NODE_TYPE_DECLARATOR); + $$->u.type_declarator.type = TYPEDEC_ID; + $$->u.type_declarator.u.id = $1; + } + | LPAREN declarator RPAREN + { + $$ = make_node(scanner, NODE_TYPE_DECLARATOR); + $$->u.type_declarator.type = TYPEDEC_NESTED; + $$->u.type_declarator.u.nested.type_declarator = $2; + } + | direct_declarator LSBRAC unary_expression RSBRAC + { + $$ = make_node(scanner, NODE_TYPE_DECLARATOR); + $$->u.type_declarator.type = TYPEDEC_NESTED; + $$->u.type_declarator.u.nested.type_declarator = $1; + BT_INIT_LIST_HEAD(&($$)->u.type_declarator.u.nested.length); + _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.type_declarator.u.nested.length); + } + ; + +type_declarator: + direct_type_declarator + { $$ = $1; } + | pointer direct_type_declarator + { + $$ = $2; + bt_list_splice(&($1)->tmp_head, &($$)->u.type_declarator.pointers); + } + ; + +direct_type_declarator: + IDENTIFIER + { + add_type(scanner, $1); + $$ = make_node(scanner, NODE_TYPE_DECLARATOR); + $$->u.type_declarator.type = TYPEDEC_ID; + $$->u.type_declarator.u.id = $1; + } + | LPAREN type_declarator RPAREN + { + $$ = make_node(scanner, NODE_TYPE_DECLARATOR); + $$->u.type_declarator.type = TYPEDEC_NESTED; + $$->u.type_declarator.u.nested.type_declarator = $2; + } + | direct_type_declarator LSBRAC unary_expression RSBRAC + { + $$ = make_node(scanner, NODE_TYPE_DECLARATOR); + $$->u.type_declarator.type = TYPEDEC_NESTED; + $$->u.type_declarator.u.nested.type_declarator = $1; + BT_INIT_LIST_HEAD(&($$)->u.type_declarator.u.nested.length); + _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.type_declarator.u.nested.length); + } + ; + +pointer: + STAR + { + $$ = make_node(scanner, NODE_POINTER); + } + | STAR pointer + { + $$ = make_node(scanner, NODE_POINTER); + bt_list_splice(&($2)->tmp_head, &($$)->tmp_head); + } + | 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 */ + CONST + | type_qualifier_list CONST + ; + +/* 2.3: CTF-specific declarations */ + +ctf_assignment_expression_list: + ctf_assignment_expression SEMICOLON + { $$ = $1; } + | ctf_assignment_expression_list ctf_assignment_expression SEMICOLON + { + $$ = $1; + bt_list_add_tail(&($2)->siblings, &($$)->tmp_head); + } + ; + +ctf_assignment_expression: + unary_expression 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 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 TYPEDEF declaration_specifiers type_declarator_list + { + struct ctf_node *list; + + list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + _bt_list_splice_tail(&($1)->u.type_specifier_list.head, &list->u.type_specifier_list.head); + _bt_list_splice_tail(&($3)->u.type_specifier_list.head, &list->u.type_specifier_list.head); + $$ = make_node(scanner, NODE_TYPEDEF); + ($$)->u.struct_or_variant_declaration.type_specifier_list = list; + _bt_list_splice_tail(&($4)->tmp_head, &($$)->u._typedef.type_declarators); + } + | TYPEDEF declaration_specifiers type_declarator_list + { + struct ctf_node *list; + + $$ = make_node(scanner, NODE_TYPEDEF); + list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + $$->u._typedef.type_specifier_list = list; + _bt_list_splice_tail(&($2)->u.type_specifier_list.head, &list->u.type_specifier_list.head); + _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._typedef.type_declarators); + } + | declaration_specifiers TYPEDEF type_declarator_list + { + struct ctf_node *list; + + list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + _bt_list_splice_tail(&($1)->u.type_specifier_list.head, &list->u.type_specifier_list.head); + $$ = make_node(scanner, NODE_TYPEDEF); + ($$)->u.struct_or_variant_declaration.type_specifier_list = list; + _bt_list_splice_tail(&($3)->tmp_head, &($$)->u._typedef.type_declarators); + } + | TYPEALIAS declaration_specifiers abstract_declarator_list TYPEASSIGN alias_declaration_specifiers alias_abstract_declarator_list + { + struct ctf_node *list; + + $$ = make_node(scanner, NODE_TYPEALIAS); + $$->u.typealias.target = make_node(scanner, NODE_TYPEALIAS_TARGET); + $$->u.typealias.alias = make_node(scanner, NODE_TYPEALIAS_ALIAS); + + list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + $$->u.typealias.target->u.typealias_target.type_specifier_list = list; + _bt_list_splice_tail(&($2)->u.type_specifier_list.head, &list->u.type_specifier_list.head); + _bt_list_splice_tail(&($3)->tmp_head, &($$)->u.typealias.target->u.typealias_target.type_declarators); + + list = make_node(scanner, NODE_TYPE_SPECIFIER_LIST); + $$->u.typealias.alias->u.typealias_alias.type_specifier_list = list; + _bt_list_splice_tail(&($5)->u.type_specifier_list.head, &list->u.type_specifier_list.head); + _bt_list_splice_tail(&($6)->tmp_head, &($$)->u.typealias.alias->u.typealias_alias.type_declarators); + } + ; diff --git a/plugins/ctf/common/metadata/scanner-symbols.h b/plugins/ctf/common/metadata/scanner-symbols.h new file mode 100644 index 00000000..9b9e3631 --- /dev/null +++ b/plugins/ctf/common/metadata/scanner-symbols.h @@ -0,0 +1,50 @@ +#ifndef _CTF_SCANNER_SYMBOLS +#define _CTF_SCANNER_SYMBOLS + +/* + * ctf-scanner-symbols.h + * + * Copyright 2011-2012 - Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + */ + +#define yy_create_buffer bt_yy_create_buffer +#define yy_delete_buffer bt_yy_delete_buffer +#define yy_flush_buffer bt_yy_flush_buffer +#define yy_scan_buffer bt_yy_scan_buffer +#define yy_scan_bytes bt_yy_scan_bytes +#define yy_scan_string bt_yy_scan_string +#define yy_switch_to_buffer bt_yy_switch_to_buffer +#define yyalloc bt_yyalloc +#define yyfree bt_yyfree +#define yyget_column bt_yyget_column +#define yyget_debug bt_yyget_debug +#define yyget_extra bt_yyget_extra +#define yyget_in bt_yyget_in +#define yyget_leng bt_yyget_leng +#define yyget_lineno bt_yyget_lineno +#define yyget_lval bt_yyget_lval +#define yyget_out bt_yyget_out +#define yyget_text bt_yyget_text +#define yylex_init bt_yylex_init +#define yypop_buffer_state bt_yypop_buffer_state +#define yypush_buffer_state bt_yypush_buffer_state +#define yyrealloc bt_yyrealloc +#define yyset_column bt_yyset_column +#define yyset_debug bt_yyset_debug +#define yyset_extra bt_yyset_extra +#define yyset_in bt_yyset_in +#define yyset_lineno bt_yyset_lineno +#define yyset_lval bt_yyset_lval +#define yyset_out bt_yyset_out + +#endif /* _CTF_SCANNER_SYMBOLS */ diff --git a/plugins/ctf/common/metadata/scanner.h b/plugins/ctf/common/metadata/scanner.h new file mode 100644 index 00000000..a0b87158 --- /dev/null +++ b/plugins/ctf/common/metadata/scanner.h @@ -0,0 +1,55 @@ +#ifndef _CTF_SCANNER_H +#define _CTF_SCANNER_H + +/* + * ctf-scanner.h + * + * Copyright 2011-2012 - Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + */ + +#include +#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 *types; +}; + +struct ctf_scanner { + yyscan_t scanner; + struct ctf_ast *ast; + struct ctf_scanner_scope root_scope; + struct ctf_scanner_scope *cs; + struct objstack *objstack; +}; + +struct ctf_scanner *ctf_scanner_alloc(void); +void ctf_scanner_free(struct ctf_scanner *scanner); +int ctf_scanner_append_ast(struct ctf_scanner *scanner, FILE *input); + +static inline +struct ctf_ast *ctf_scanner_get_ast(struct ctf_scanner *scanner) +{ + return scanner->ast; +} + +BT_HIDDEN +int is_type(struct ctf_scanner *scanner, const char *id); + +#endif /* _CTF_SCANNER_H */ diff --git a/plugins/ctf/common/metadata/visitor-generate-ir.c b/plugins/ctf/common/metadata/visitor-generate-ir.c new file mode 100644 index 00000000..ec406bab --- /dev/null +++ b/plugins/ctf/common/metadata/visitor-generate-ir.c @@ -0,0 +1,4662 @@ +/* + * ctf-visitor-generate-ir.c + * + * Common Trace Format metadata visitor (generates CTF IR objects). + * + * Based on older ctf-visitor-generate-io-struct.c. + * + * Copyright 2010 - Mathieu Desnoyers + * Copyright 2015-2016 - Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "scanner.h" +#include "parser.h" +#include "ast.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)) + +/* 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_LOGLEVEL_SET = _BV(4), + _EVENT_CONTEXT_SET = _BV(5), + _EVENT_FIELDS_SET = _BV(6), +}; + +/* Prefixes of type 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, _type, _member) \ + bt_list_entry((_ptr)->next, _type, _member) + +#define _BT_CTF_FIELD_TYPE_INIT(_name) struct bt_ctf_field_type *_name = NULL; + +/* Error printing wrappers */ +#define _PERROR(_fmt, ...) \ + do { \ + fprintf(ctx->efd, "[error] %s: " _fmt "\n", \ + __func__, __VA_ARGS__); \ + } while (0) + +#define _PWARNING(_fmt, ...) \ + do { \ + fprintf(ctx->efd, "[warning] %s: " _fmt "\n", \ + __func__, __VA_ARGS__); \ + } while (0) + +#define _FPERROR(_stream, _fmt, ...) \ + do { \ + fprintf(_stream, "[error] %s: " _fmt "\n", \ + __func__, __VA_ARGS__); \ + } while (0) + +#define _FPWARNING(_stream, _fmt, ...) \ + do { \ + fprintf(_stream, "[warning] %s: " _fmt "\n", \ + __func__, __VA_ARGS__); \ + } while (0) + +#define _PERROR_DUP_ATTR(_attr, _entity) \ + do { \ + fprintf(ctx->efd, \ + "[error] %s: duplicate attribute \"" \ + _attr "\" in " _entity "\n", __func__); \ + } while (0) + +/* + * 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 type. + * + * GQuark -> struct bt_ctf_field_type * + */ + GHashTable *decl_map; + + /* Parent scope; NULL if this is the root declaration scope */ + struct ctx_decl_scope *parent_scope; +}; + +/* + * Visitor context. + */ +struct ctx { + /* Trace being filled (weak ref.) */ + struct bt_ctf_trace *trace; + + /* Error stream to use during visit */ + FILE *efd; + + /* Current declaration scope (top of the stack) */ + struct ctx_decl_scope *current_scope; + + /* 1 if trace declaration is visited */ + int is_trace_visited; + + /* Trace attributes */ + uint64_t trace_major; + uint64_t trace_minor; + unsigned char trace_uuid[BABELTRACE_UUID_LEN]; + + /* + * Stream IDs to stream classes. + * + * int64_t -> struct bt_ctf_stream_class * + */ + GHashTable *stream_classes; +}; + +/** + * 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 ctx_decl_scope *par_scope) +{ + struct ctx_decl_scope *scope; + + scope = g_new(struct ctx_decl_scope, 1); + if (!scope) { + goto end; + } + + scope->decl_map = g_hash_table_new_full(g_direct_hash, g_direct_equal, + NULL, (GDestroyNotify) bt_ctf_field_type_put); + 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(char prefix, const char *name) +{ + GQuark qname = 0; + + assert(name); + + /* Prefix character + original string + '\0' */ + char *prname = g_new(char, strlen(name) + 2); + if (!prname) { + goto end; + } + + sprintf(prname, "%c%s", prefix, name); + qname = g_quark_from_string(prname); + g_free(prname); + +end: + return qname; +} + +/** + * Looks up a prefixed type alias within a declaration scope. + * + * @param scope Declaration scope + * @param prefix Prefix character + * @param name Alias name + * @param level Number of levels to dig (-1 means infinite) + * @returns Declaration, or NULL if not found + */ +static +struct bt_ctf_field_type *ctx_decl_scope_lookup_prefix_alias( + struct ctx_decl_scope *scope, char prefix, + const char *name, int levels) +{ + GQuark qname = 0; + int cur_levels = 0; + _BT_CTF_FIELD_TYPE_INIT(decl); + struct ctx_decl_scope *cur_scope = scope; + + assert(scope); + assert(name); + qname = get_prefixed_named_quark(prefix, name); + if (!qname) { + goto error; + } + + if (levels < 0) { + levels = INT_MAX; + } + + while (cur_scope && cur_levels < levels) { + decl = g_hash_table_lookup(cur_scope->decl_map, + (gconstpointer) (unsigned long) qname); + if (decl) { + /* Caller's reference */ + bt_get(decl); + break; + } + + cur_scope = cur_scope->parent_scope; + cur_levels++; + } + + return decl; + +error: + return NULL; +} + +/** + * Looks up a type alias within a declaration scope. + * + * @param scope Declaration scope + * @param name Alias name + * @param level Number of levels to dig (-1 means infinite) + * @returns Declaration, or NULL if not found + */ +static +struct bt_ctf_field_type *ctx_decl_scope_lookup_alias( + struct ctx_decl_scope *scope, const char *name, int levels) +{ + return ctx_decl_scope_lookup_prefix_alias(scope, _PREFIX_ALIAS, + name, levels); +} + +/** + * Looks up an enumeration within a declaration scope. + * + * @param scope Declaration scope + * @param name Enumeration name + * @param level Number of levels to dig (-1 means infinite) + * @returns Declaration, or NULL if not found + */ +static +struct bt_ctf_field_type *ctx_decl_scope_lookup_enum( + struct ctx_decl_scope *scope, const char *name, int levels) +{ + return ctx_decl_scope_lookup_prefix_alias(scope, _PREFIX_ENUM, + name, levels); +} + +/** + * Looks up a structure within a declaration scope. + * + * @param scope Declaration scope + * @param name Structure name + * @param level Number of levels to dig (-1 means infinite) + * @returns Declaration, or NULL if not found + */ +static +struct bt_ctf_field_type *ctx_decl_scope_lookup_struct( + struct ctx_decl_scope *scope, const char *name, int levels) +{ + return ctx_decl_scope_lookup_prefix_alias(scope, _PREFIX_STRUCT, + name, levels); +} + +/** + * Looks up a variant within a declaration scope. + * + * @param scope Declaration scope + * @param name Variant name + * @param level Number of levels to dig (-1 means infinite) + * @returns Declaration, or NULL if not found + */ +static +struct bt_ctf_field_type *ctx_decl_scope_lookup_variant( + struct ctx_decl_scope *scope, const char *name, int levels) +{ + return ctx_decl_scope_lookup_prefix_alias(scope, _PREFIX_VARIANT, + name, levels); +} + +/** + * Registers a prefixed type alias within a declaration scope. + * + * @param scope Declaration scope + * @param prefix Prefix character + * @param name Alias name (non-NULL) + * @param decl Declaration to register + * @returns 0 if registration went okay, negative value otherwise + */ +static +int ctx_decl_scope_register_prefix_alias(struct ctx_decl_scope *scope, + char prefix, const char *name, struct bt_ctf_field_type *decl) +{ + int ret = 0; + GQuark qname = 0; + _BT_CTF_FIELD_TYPE_INIT(edecl); + + assert(scope); + assert(name); + assert(decl); + qname = get_prefixed_named_quark(prefix, name); + if (!qname) { + ret = -ENOMEM; + goto error; + } + + /* Make sure alias does not exist in local scope */ + edecl = ctx_decl_scope_lookup_prefix_alias(scope, prefix, name, 1); + if (edecl) { + BT_PUT(edecl); + ret = -EEXIST; + goto error; + } + + g_hash_table_insert(scope->decl_map, + (gpointer) (unsigned long) qname, decl); + + /* Hash table's reference */ + bt_get(decl); + + return 0; + +error: + return ret; +} + +/** + * Registers a type alias within a declaration scope. + * + * @param scope Declaration scope + * @param name Alias name (non-NULL) + * @param decl Declaration to register + * @returns 0 if registration went okay, negative value otherwise + */ +static +int ctx_decl_scope_register_alias(struct ctx_decl_scope *scope, + const char *name, struct bt_ctf_field_type *decl) +{ + return ctx_decl_scope_register_prefix_alias(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 declaration to register + * @returns 0 if registration went okay, negative value otherwise + */ +static +int ctx_decl_scope_register_enum(struct ctx_decl_scope *scope, + const char *name, struct bt_ctf_field_type *decl) +{ + return ctx_decl_scope_register_prefix_alias(scope, _PREFIX_ENUM, + name, decl); +} + +/** + * Registers a structure declaration within a declaration scope. + * + * @param scope Declaration scope + * @param name Structure name (non-NULL) + * @param decl Structure declaration to register + * @returns 0 if registration went okay, negative value otherwise + */ +static +int ctx_decl_scope_register_struct(struct ctx_decl_scope *scope, + const char *name, struct bt_ctf_field_type *decl) +{ + return ctx_decl_scope_register_prefix_alias(scope, _PREFIX_STRUCT, + name, decl); +} + +/** + * Registers a variant declaration within a declaration scope. + * + * @param scope Declaration scope + * @param name Variant name (non-NULL) + * @param decl Variant declaration to register + * @returns 0 if registration went okay, negative value otherwise + */ +static +int ctx_decl_scope_register_variant(struct ctx_decl_scope *scope, + const char *name, struct bt_ctf_field_type *decl) +{ + return ctx_decl_scope_register_prefix_alias(scope, _PREFIX_VARIANT, + name, decl); +} + +/** + * Creates a new visitor context. + * + * @param trace Associated trace + * @param efd Error stream + * @returns New visitor context, or NULL on error + */ +static +struct ctx *ctx_create(struct bt_ctf_trace *trace, FILE *efd) +{ + struct ctx *ctx = NULL; + struct ctx_decl_scope *scope = NULL; + + ctx = g_new(struct ctx, 1); + if (!ctx) { + goto error; + } + + /* Root declaration scope */ + scope = ctx_decl_scope_create(NULL); + if (!scope) { + goto error; + } + + ctx->stream_classes = g_hash_table_new_full(g_direct_hash, + g_direct_equal, NULL, (GDestroyNotify) bt_put); + if (!ctx->stream_classes) { + goto error; + } + + ctx->trace = trace; + ctx->efd = efd; + ctx->current_scope = scope; + ctx->is_trace_visited = FALSE; + + return ctx; + +error: + g_free(ctx); + ctx_decl_scope_destroy(scope); + + return NULL; +} + +/** + * Destroys a visitor context. + * + * @param ctx Visitor context to destroy + */ +static +void ctx_destroy(struct ctx *ctx) +{ + struct ctx_decl_scope *scope; + /* + * Destroy all scopes, from current one to the root scope. + */ + + if (!ctx) { + goto end; + } + + scope = ctx->current_scope; + + while (scope) { + struct ctx_decl_scope *parent_scope = scope->parent_scope; + + ctx_decl_scope_destroy(scope); + scope = parent_scope; + } + + g_hash_table_destroy(ctx->stream_classes); + g_free(ctx); + +end: + return; +} + +/** + * 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 ctx *ctx) +{ + int ret = 0; + struct ctx_decl_scope *new_scope; + + assert(ctx); + new_scope = ctx_decl_scope_create(ctx->current_scope); + if (!new_scope) { + ret = -ENOMEM; + goto end; + } + + ctx->current_scope = new_scope; + +end: + return ret; +} + +static +void ctx_pop_scope(struct ctx *ctx) +{ + struct ctx_decl_scope *parent_scope = NULL; + + 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_type_specifier_list(struct ctx *ctx, struct ctf_node *ts_list, + struct bt_ctf_field_type **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 +char *concatenate_unary_strings(struct bt_list_head *head) +{ + int i = 0; + GString *str; + struct ctf_node *node; + + str = g_string_new(NULL); + + 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 +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 bt_list_head *head, uint64_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_link != UNARY_LINK_UNKNOWN || i != 0; + if (cond) { + 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_UNSIGNED_CONSTANT && + uexpr_type != UNARY_SIGNED_CONSTANT) || + uexpr_link != UNARY_LINK_UNKNOWN || i != 0; + if (cond) { + ret = -EINVAL; + goto end; + } + + switch (node->u.unary_expression.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 bt_list_head *head, unsigned char *uuid) +{ + 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_parse(src_string, uuid); + if (ret) { + goto end; + } + } + +end: + return ret; +} + +static +int get_boolean(FILE *efd, struct ctf_node *unary_expr) +{ + int ret = 0; + + if (unary_expr->type != NODE_UNARY_EXPRESSION) { + _FPERROR(efd, "%s", "expecting unary expression"); + 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") || !strcmp(str, "TRUE")) { + ret = TRUE; + } else if (!strcmp(str, "false") || !strcmp(str, "FALSE")) { + ret = FALSE; + } else { + _FPERROR(efd, "unexpected string \"%s\"", str); + ret = -EINVAL; + goto end; + } + break; + } + default: + _FPERROR(efd, "%s", "unexpected unary expression type"); + ret = -EINVAL; + goto end; + } + +end: + return ret; +} + +static +enum bt_ctf_byte_order byte_order_from_unary_expr(FILE *efd, + struct ctf_node *unary_expr) +{ + const char *str; + enum bt_ctf_byte_order bo = BT_CTF_BYTE_ORDER_UNKNOWN; + + if (unary_expr->u.unary_expression.type != UNARY_STRING) { + _FPERROR(efd, "%s", + "\"byte_order\" attribute: expecting string"); + goto end; + } + + str = unary_expr->u.unary_expression.u.string; + + if (!strcmp(str, "be") || !strcmp(str, "network")) { + bo = BT_CTF_BYTE_ORDER_BIG_ENDIAN; + } else if (!strcmp(str, "le")) { + bo = BT_CTF_BYTE_ORDER_LITTLE_ENDIAN; + } else if (!strcmp(str, "native")) { + bo = BT_CTF_BYTE_ORDER_NATIVE; + } else { + _FPERROR(efd, "unexpected \"byte_order\" attribute value \"%s\"; should be \"be\", \"le\", \"network\", or \"native\"", + str); + goto end; + } + +end: + return bo; +} + +static +enum bt_ctf_byte_order get_real_byte_order(struct ctx *ctx, + struct ctf_node *uexpr) +{ + enum bt_ctf_byte_order bo = byte_order_from_unary_expr(ctx->efd, uexpr); + + if (bo == BT_CTF_BYTE_ORDER_NATIVE) { + bo = bt_ctf_trace_get_byte_order(ctx->trace); + } + + return bo; +} + +static +int is_align_valid(uint64_t align) +{ + return (align != 0) && !(align & (align - 1)); +} + +static +int get_type_specifier_name(struct ctx *ctx, struct ctf_node *type_specifier, + GString *str) +{ + int ret = 0; + + if (type_specifier->type != NODE_TYPE_SPECIFIER) { + ret = -EINVAL; + goto end; + } + + switch (type_specifier->u.type_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 (type_specifier->u.type_specifier.id_type) { + g_string_append(str, + type_specifier->u.type_specifier.id_type); + } + break; + case TYPESPEC_STRUCT: + { + struct ctf_node *node = type_specifier->u.type_specifier.node; + + if (!node->u._struct.name) { + _PERROR("%s", "unexpected empty structure 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 = type_specifier->u.type_specifier.node; + + if (!node->u.variant.name) { + _PERROR("%s", "unexpected empty variant 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 = type_specifier->u.type_specifier.node; + + if (!node->u._enum.enum_id) { + _PERROR("%s", "unexpected empty 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: + _PERROR("%s", "unknown specifier"); + ret = -EINVAL; + goto end; + } + +end: + return ret; +} + +static +int get_type_specifier_list_name(struct ctx *ctx, + struct ctf_node *type_specifier_list, GString *str) +{ + int ret; + struct ctf_node *iter; + int alias_item_nr = 0; + struct bt_list_head *head = + &type_specifier_list->u.type_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_type_specifier_name(ctx, iter, str); + if (ret) { + goto end; + } + } + +end: + return ret; +} + +static +GQuark create_typealias_identifier(struct ctx *ctx, + struct ctf_node *type_specifier_list, + struct ctf_node *node_type_declarator) +{ + int ret; + char *str_c; + GString *str; + GQuark qalias = 0; + struct ctf_node *iter; + struct bt_list_head *pointers = + &node_type_declarator->u.type_declarator.pointers; + + str = g_string_new(""); + ret = get_type_specifier_list_name(ctx, type_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_type_declarator(struct ctx *ctx, struct ctf_node *type_specifier_list, + GQuark *field_name, struct ctf_node *node_type_declarator, + struct bt_ctf_field_type **field_decl, + struct bt_ctf_field_type *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 type declarator node */ + if (node_type_declarator) { + if (node_type_declarator->u.type_declarator.type == + TYPEDEC_UNKNOWN) { + ret = -EINVAL; + goto error; + } + + /* TODO: GCC bitfields not supported yet */ + if (node_type_declarator->u.type_declarator.bitfield_len != + NULL) { + _PERROR("%s", "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_type_declarator->u.type_declarator.pointers; + + if (node_type_declarator && !bt_list_empty(pointers)) { + GQuark qalias; + _BT_CTF_FIELD_TYPE_INIT(nested_decl_copy); + + /* + * If we have a pointer declarator, it HAS to + * be present in the typealiases (else fail). + */ + qalias = create_typealias_identifier(ctx, + type_specifier_list, node_type_declarator); + nested_decl = + ctx_decl_scope_lookup_alias(ctx->current_scope, + g_quark_to_string(qalias), -1); + if (!nested_decl) { + _PERROR("cannot find typealias \"%s\"", + g_quark_to_string(qalias)); + ret = -EINVAL; + goto error; + } + + /* Make a copy of it */ + nested_decl_copy = bt_ctf_field_type_copy(nested_decl); + BT_PUT(nested_decl); + if (!nested_decl_copy) { + _PERROR("%s", "cannot copy nested declaration"); + ret = -EINVAL; + goto error; + } + + BT_MOVE(nested_decl, nested_decl_copy); + + /* Force integer's base to 16 since it's a pointer */ + if (bt_ctf_field_type_is_integer(nested_decl)) { + bt_ctf_field_type_integer_set_base(nested_decl, + BT_CTF_INTEGER_BASE_HEXADECIMAL); + } + } else { + ret = visit_type_specifier_list(ctx, + type_specifier_list, &nested_decl); + if (ret) { + assert(!nested_decl); + goto error; + } + } + } + + assert(nested_decl); + + if (!node_type_declarator) { + BT_MOVE(*field_decl, nested_decl); + goto end; + } + + if (node_type_declarator->u.type_declarator.type == TYPEDEC_ID) { + if (node_type_declarator->u.type_declarator.u.id) { + const char *id = + node_type_declarator->u.type_declarator.u.id; + + *field_name = g_quark_from_string(id); + } else { + *field_name = 0; + } + + BT_MOVE(*field_decl, nested_decl); + goto end; + } else { + struct ctf_node *first; + _BT_CTF_FIELD_TYPE_INIT(decl); + _BT_CTF_FIELD_TYPE_INIT(outer_field_decl); + struct bt_list_head *length = + &node_type_declarator-> + u.type_declarator.u.nested.length; + + /* Create array/sequence, pass nested_decl as child */ + if (bt_list_empty(length)) { + _PERROR("%s", + "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) { + ret = -EINVAL; + goto error; + } + + switch (first->u.unary_expression.type) { + case UNARY_UNSIGNED_CONSTANT: + { + size_t len; + _BT_CTF_FIELD_TYPE_INIT(array_decl); + + len = first->u.unary_expression.u.unsigned_constant; + array_decl = bt_ctf_field_type_array_create(nested_decl, + len); + BT_PUT(nested_decl); + if (!array_decl) { + _PERROR("%s", + "cannot create array declaration"); + ret = -ENOMEM; + goto error; + } + + BT_MOVE(decl, array_decl); + break; + } + case UNARY_STRING: + { + /* Lookup unsigned integer definition, create seq. */ + _BT_CTF_FIELD_TYPE_INIT(seq_decl); + char *length_name = concatenate_unary_strings(length); + + if (!length_name) { + ret = -EINVAL; + goto error; + } + + seq_decl = bt_ctf_field_type_sequence_create( + nested_decl, length_name); + g_free(length_name); + BT_PUT(nested_decl); + if (!seq_decl) { + _PERROR("%s", + "cannot create sequence declaration"); + ret = -ENOMEM; + goto error; + } + + BT_MOVE(decl, seq_decl); + break; + } + default: + ret = -EINVAL; + goto error; + } + + assert(!nested_decl); + assert(decl); + 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_type_declarator(ctx, type_specifier_list, + field_name, + node_type_declarator-> + u.type_declarator.u.nested.type_declarator, + &outer_field_decl, decl); + decl = NULL; + if (ret) { + assert(!outer_field_decl); + ret = -EINVAL; + goto error; + } + + assert(outer_field_decl); + BT_MOVE(*field_decl, outer_field_decl); + } + +end: + BT_PUT(nested_decl); + assert(*field_decl); + + return 0; + +error: + BT_PUT(nested_decl); + BT_PUT(*field_decl); + + return ret; +} + +static +int visit_struct_decl_field(struct ctx *ctx, + struct bt_ctf_field_type *struct_decl, + struct ctf_node *type_specifier_list, + struct bt_list_head *type_declarators) +{ + int ret = 0; + struct ctf_node *iter; + _BT_CTF_FIELD_TYPE_INIT(field_decl); + + bt_list_for_each_entry(iter, type_declarators, siblings) { + field_decl = NULL; + GQuark qfield_name; + const char *field_name; + _BT_CTF_FIELD_TYPE_INIT(efield_decl); + + ret = visit_type_declarator(ctx, type_specifier_list, + &qfield_name, iter, &field_decl, NULL); + if (ret) { + assert(!field_decl); + _PERROR("%s", "unable to find structure field declaration type"); + goto error; + } + + assert(field_decl); + field_name = g_quark_to_string(qfield_name); + + /* Check if field with same name already exists */ + efield_decl = + bt_ctf_field_type_structure_get_field_type_by_name( + struct_decl, field_name); + if (efield_decl) { + BT_PUT(efield_decl); + _PERROR("duplicate field \"%s\" in structure", + field_name); + ret = -EINVAL; + goto error; + } + + /* Add field to structure */ + ret = bt_ctf_field_type_structure_add_field(struct_decl, + field_decl, field_name); + BT_PUT(field_decl); + if (ret) { + _PERROR("cannot add field \"%s\" to structure", + g_quark_to_string(qfield_name)); + goto error; + } + } + + return 0; + +error: + BT_PUT(field_decl); + + return ret; +} + +static +int visit_variant_decl_field(struct ctx *ctx, + struct bt_ctf_field_type *variant_decl, + struct ctf_node *type_specifier_list, + struct bt_list_head *type_declarators) +{ + int ret = 0; + struct ctf_node *iter; + _BT_CTF_FIELD_TYPE_INIT(field_decl); + + bt_list_for_each_entry(iter, type_declarators, siblings) { + field_decl = NULL; + GQuark qfield_name; + const char *field_name; + _BT_CTF_FIELD_TYPE_INIT(efield_decl); + + ret = visit_type_declarator(ctx, type_specifier_list, + &qfield_name, iter, &field_decl, NULL); + if (ret) { + assert(!field_decl); + _PERROR("%s", + "unable to find variant field declaration type"); + goto error; + } + + assert(field_decl); + field_name = g_quark_to_string(qfield_name); + + /* Check if field with same name already exists */ + efield_decl = + bt_ctf_field_type_variant_get_field_type_by_name( + variant_decl, field_name); + if (efield_decl) { + BT_PUT(efield_decl); + _PERROR("duplicate field \"%s\" in variant", + field_name); + ret = -EINVAL; + goto error; + } + + /* Add field to structure */ + ret = bt_ctf_field_type_variant_add_field(variant_decl, + field_decl, field_name); + BT_PUT(field_decl); + if (ret) { + _PERROR("cannot add field \"%s\" to variant", + g_quark_to_string(qfield_name)); + goto error; + } + } + + return 0; + +error: + BT_PUT(field_decl); + + return ret; +} + +static +int visit_typedef(struct ctx *ctx, struct ctf_node *type_specifier_list, + struct bt_list_head *type_declarators) +{ + int ret = 0; + GQuark qidentifier; + struct ctf_node *iter; + _BT_CTF_FIELD_TYPE_INIT(type_decl); + + bt_list_for_each_entry(iter, type_declarators, siblings) { + ret = visit_type_declarator(ctx, type_specifier_list, + &qidentifier, iter, &type_decl, NULL); + if (ret) { + _PERROR("%s", "problem creating type declaration"); + ret = -EINVAL; + goto end; + } + + /* Do not allow typedef and typealias of untagged variants */ + if (bt_ctf_field_type_is_variant(type_decl)) { + if (bt_ctf_field_type_variant_get_tag_name(type_decl)) { + _PERROR("%s", "typedef of untagged variant is not allowed"); + ret = -EPERM; + goto end; + } + } + + ret = ctx_decl_scope_register_alias(ctx->current_scope, + g_quark_to_string(qidentifier), type_decl); + if (ret) { + _PERROR("cannot register typedef \"%s\"", + g_quark_to_string(qidentifier)); + goto end; + } + } + +end: + BT_PUT(type_decl); + + return ret; +} + +static +int visit_typealias(struct ctx *ctx, struct ctf_node *target, + struct ctf_node *alias) +{ + int ret = 0; + GQuark qalias; + struct ctf_node *node; + GQuark qdummy_field_name; + _BT_CTF_FIELD_TYPE_INIT(type_decl); + + /* Create target type declaration */ + if (bt_list_empty(&target->u.typealias_target.type_declarators)) { + node = NULL; + } else { + node = _BT_LIST_FIRST_ENTRY( + &target->u.typealias_target.type_declarators, + struct ctf_node, siblings); + } + + ret = visit_type_declarator(ctx, + target->u.typealias_target.type_specifier_list, + &qdummy_field_name, node, &type_decl, NULL); + if (ret) { + assert(!type_decl); + _PERROR("%s", "problem creating type declaration"); + goto end; + } + + /* Do not allow typedef and typealias of untagged variants */ + if (bt_ctf_field_type_is_variant(type_decl)) { + if (bt_ctf_field_type_variant_get_tag_name(type_decl)) { + _PERROR("%s", + "typealias of untagged variant 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) { + _PERROR("%s", "expecting empty identifier"); + ret = -EINVAL; + goto end; + } + + /* Create alias identifier */ + node = _BT_LIST_FIRST_ENTRY(&alias->u.typealias_alias.type_declarators, + struct ctf_node, siblings); + qalias = create_typealias_identifier(ctx, + alias->u.typealias_alias.type_specifier_list, node); + ret = ctx_decl_scope_register_alias(ctx->current_scope, + g_quark_to_string(qalias), type_decl); + if (ret) { + _PERROR("cannot register typealias \"%s\"", + g_quark_to_string(qalias)); + goto end; + } + +end: + BT_PUT(type_decl); + + return ret; +} + +static +int visit_struct_decl_entry(struct ctx *ctx, struct ctf_node *entry_node, + struct bt_ctf_field_type *struct_decl) +{ + int ret = 0; + + switch (entry_node->type) { + case NODE_TYPEDEF: + ret = visit_typedef(ctx, + entry_node->u._typedef.type_specifier_list, + &entry_node->u._typedef.type_declarators); + if (ret) { + _PERROR("%s", + "cannot add typedef in \"struct\" declaration"); + goto end; + } + break; + case NODE_TYPEALIAS: + ret = visit_typealias(ctx, entry_node->u.typealias.target, + entry_node->u.typealias.alias); + if (ret) { + _PERROR("%s", + "cannot add typealias in \"struct\" declaration"); + 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. + type_specifier_list, + &entry_node->u.struct_or_variant_declaration. + type_declarators); + if (ret) { + goto end; + } + break; + default: + _PERROR("unexpected node type: %d", (int) entry_node->type); + ret = -EINVAL; + goto end; + } + +end: + return ret; +} + +static +int visit_variant_decl_entry(struct ctx *ctx, struct ctf_node *entry_node, + struct bt_ctf_field_type *variant_decl) +{ + int ret = 0; + + switch (entry_node->type) { + case NODE_TYPEDEF: + ret = visit_typedef(ctx, + entry_node->u._typedef.type_specifier_list, + &entry_node->u._typedef.type_declarators); + if (ret) { + _PERROR("%s", + "cannot add typedef in \"variant\" declaration"); + goto end; + } + break; + case NODE_TYPEALIAS: + ret = visit_typealias(ctx, entry_node->u.typealias.target, + entry_node->u.typealias.alias); + if (ret) { + _PERROR("%s", + "cannot add typealias in \"variant\" declaration"); + 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. + type_specifier_list, + &entry_node->u.struct_or_variant_declaration. + type_declarators); + if (ret) { + goto end; + } + break; + default: + _PERROR("unexpected node type: %d", (int) entry_node->type); + ret = -EINVAL; + goto end; + } + +end: + return ret; +} + +static +int visit_struct_decl(struct ctx *ctx, const char *name, + struct bt_list_head *decl_list, int has_body, + struct bt_list_head *min_align, + struct bt_ctf_field_type **struct_decl) +{ + int ret = 0; + + *struct_decl = NULL; + + /* For named struct (without body), lookup in declaration scope */ + if (!has_body) { + _BT_CTF_FIELD_TYPE_INIT(struct_decl_copy); + + if (!name) { + ret = -EPERM; + goto error; + } + + *struct_decl = ctx_decl_scope_lookup_struct(ctx->current_scope, + name, -1); + if (!*struct_decl) { + _PERROR("cannot find \"struct %s\"", name); + ret = -EINVAL; + goto error; + } + + /* Make a copy of it */ + struct_decl_copy = bt_ctf_field_type_copy(*struct_decl); + if (!struct_decl_copy) { + _PERROR("%s", + "cannot create copy of structure declaration"); + ret = -EINVAL; + goto error; + } + + BT_MOVE(*struct_decl, struct_decl_copy); + } else { + struct ctf_node *entry_node; + uint64_t min_align_value = 0; + + if (name) { + _BT_CTF_FIELD_TYPE_INIT(estruct_decl); + + estruct_decl = ctx_decl_scope_lookup_struct( + ctx->current_scope, name, 1); + if (estruct_decl) { + BT_PUT(estruct_decl); + _PERROR("\"struct %s\" already declared in local scope", + name); + ret = -EINVAL; + goto error; + } + } + + if (!bt_list_empty(min_align)) { + ret = get_unary_unsigned(min_align, &min_align_value); + if (ret) { + _PERROR("%s", "unexpected unary expression for structure declaration's \"align\" attribute"); + goto error; + } + } + + *struct_decl = bt_ctf_field_type_structure_create(); + if (!*struct_decl) { + _PERROR("%s", "cannot create structure declaration"); + ret = -ENOMEM; + goto error; + } + + ret = ctx_push_scope(ctx); + if (ret) { + _PERROR("%s", "cannot push scope"); + goto error; + } + + bt_list_for_each_entry(entry_node, decl_list, siblings) { + ret = visit_struct_decl_entry(ctx, entry_node, + *struct_decl); + if (ret) { + ctx_pop_scope(ctx); + goto error; + } + } + + ctx_pop_scope(ctx); + + if (name) { + ret = ctx_decl_scope_register_struct(ctx->current_scope, + name, *struct_decl); + if (ret) { + _PERROR("cannot register \"struct %s\" in declaration scope", + name); + goto error; + } + } + } + + return 0; + +error: + BT_PUT(*struct_decl); + + return ret; +} + +static +int visit_variant_decl(struct ctx *ctx, const char *name, + const char *tag, struct bt_list_head *decl_list, + int has_body, struct bt_ctf_field_type **variant_decl) +{ + int ret = 0; + _BT_CTF_FIELD_TYPE_INIT(untagged_variant_decl); + + *variant_decl = NULL; + + /* For named variant (without body), lookup in declaration scope */ + if (!has_body) { + _BT_CTF_FIELD_TYPE_INIT(variant_decl_copy); + + if (!name) { + ret = -EPERM; + goto error; + } + + untagged_variant_decl = + ctx_decl_scope_lookup_variant(ctx->current_scope, + name, -1); + if (!untagged_variant_decl) { + _PERROR("cannot find \"variant %s\"", name); + ret = -EINVAL; + goto error; + } + + /* Make a copy of it */ + variant_decl_copy = bt_ctf_field_type_copy( + untagged_variant_decl); + if (!variant_decl_copy) { + _PERROR("%s", + "cannot create copy of structure declaration"); + ret = -EINVAL; + goto error; + } + + BT_MOVE(untagged_variant_decl, variant_decl_copy); + } else { + struct ctf_node *entry_node; + + if (name) { + struct bt_ctf_field_type *evariant_decl = + ctx_decl_scope_lookup_struct(ctx->current_scope, + name, 1); + + if (evariant_decl) { + BT_PUT(evariant_decl); + _PERROR("\"variant %s\" already declared in local scope", + name); + ret = -EINVAL; + goto error; + } + } + + untagged_variant_decl = bt_ctf_field_type_variant_create(NULL, + NULL); + if (!untagged_variant_decl) { + _PERROR("%s", "cannot create variant declaration"); + ret = -ENOMEM; + goto error; + } + + ret = ctx_push_scope(ctx); + if (ret) { + _PERROR("%s", "cannot push scope"); + 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) { + ctx_pop_scope(ctx); + goto error; + } + } + + ctx_pop_scope(ctx); + + if (name) { + ret = ctx_decl_scope_register_variant( + ctx->current_scope, name, + untagged_variant_decl); + if (ret) { + _PERROR("cannot register \"variant %s\" in declaration scope", + name); + goto error; + } + } + } + + /* + * If tagged, create tagged variant and return; otherwise + * return untagged variant. + */ + if (!tag) { + BT_MOVE(*variant_decl, untagged_variant_decl); + } else { + /* + * At this point, we have a fresh untagged variant; nobody + * else owns it. Set its tag now. + */ + ret = bt_ctf_field_type_variant_set_tag_name( + untagged_variant_decl, tag); + if (ret) { + goto error; + } + + BT_MOVE(*variant_decl, untagged_variant_decl); + } + + assert(!untagged_variant_decl); + assert(*variant_decl); + + return 0; + +error: + BT_PUT(untagged_variant_decl); + BT_PUT(*variant_decl); + + return ret; +} + +static +int visit_enum_decl_entry(struct ctx *ctx, struct ctf_node *enumerator, + struct bt_ctf_field_type *enum_decl, int64_t *last, int is_signed) +{ + int ret = 0; + int nr_vals = 0; + struct ctf_node *iter; + int64_t start = 0, end = 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) { + int64_t *target; + + if (iter->type != NODE_UNARY_EXPRESSION) { + _PERROR("wrong unary expression for enumeration label \"%s\"", + 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 = iter->u.unary_expression.u.signed_constant; + break; + case UNARY_UNSIGNED_CONSTANT: + *target = (int64_t) + iter->u.unary_expression.u.unsigned_constant; + break; + default: + _PERROR("invalid enumeration entry: \"%s\"", + label); + ret = -EINVAL; + goto error; + } + + if (nr_vals > 1) { + _PERROR("invalid enumeration entry: \"%s\"", + label); + ret = -EINVAL; + goto error; + } + + nr_vals++; + } + + if (nr_vals == 0) { + start = *last; + } + + if (nr_vals <= 1) { + end = start; + } + + *last = end + 1; + + if (is_signed) { + ret = bt_ctf_field_type_enumeration_add_mapping(enum_decl, label, + start, end); + } else { + ret = bt_ctf_field_type_enumeration_add_mapping_unsigned(enum_decl, + label, (uint64_t) start, (uint64_t) end); + } + if (ret) { + _PERROR("cannot add mapping to enumeration for label \"%s\"", + label); + goto error; + } + + return 0; + +error: + return ret; +} + +static +int visit_enum_decl(struct ctx *ctx, const char *name, + struct ctf_node *container_type, + struct bt_list_head *enumerator_list, + int has_body, + struct bt_ctf_field_type **enum_decl) +{ + int ret = 0; + GQuark qdummy_id; + _BT_CTF_FIELD_TYPE_INIT(integer_decl); + + *enum_decl = NULL; + + /* For named enum (without body), lookup in declaration scope */ + if (!has_body) { + _BT_CTF_FIELD_TYPE_INIT(enum_decl_copy); + + if (!name) { + ret = -EPERM; + goto error; + } + + *enum_decl = ctx_decl_scope_lookup_enum(ctx->current_scope, + name, -1); + if (!*enum_decl) { + _PERROR("cannot find \"enum %s\"", name); + ret = -EINVAL; + goto error; + } + + /* Make a copy of it */ + enum_decl_copy = bt_ctf_field_type_copy(*enum_decl); + if (!enum_decl_copy) { + _PERROR("%s", + "cannot create copy of enumeration declaration"); + ret = -EINVAL; + goto error; + } + + BT_PUT(*enum_decl); + BT_MOVE(*enum_decl, enum_decl_copy); + } else { + struct ctf_node *iter; + int64_t last_value = 0; + + if (name) { + _BT_CTF_FIELD_TYPE_INIT(eenum_decl); + + eenum_decl = ctx_decl_scope_lookup_enum( + ctx->current_scope, name, 1); + if (eenum_decl) { + BT_PUT(eenum_decl); + _PERROR("\"enum %s\" already declared in local scope", + name); + ret = -EINVAL; + goto error; + } + } + + if (!container_type) { + integer_decl = ctx_decl_scope_lookup_alias( + ctx->current_scope, "int", -1); + if (!integer_decl) { + _PERROR("%s", "cannot find \"int\" type for enumeration"); + ret = -EINVAL; + goto error; + } + } else { + ret = visit_type_declarator(ctx, container_type, + &qdummy_id, NULL, &integer_decl, NULL); + if (ret) { + assert(!integer_decl); + ret = -EINVAL; + goto error; + } + } + + assert(integer_decl); + + if (!bt_ctf_field_type_is_integer(integer_decl)) { + _PERROR("%s", "container type for enumeration is not an integer"); + ret = -EINVAL; + goto error; + } + + *enum_decl = bt_ctf_field_type_enumeration_create(integer_decl); + if (!*enum_decl) { + _PERROR("%s", "cannot create enumeration declaration"); + ret = -ENOMEM; + goto error; + } + + bt_list_for_each_entry(iter, enumerator_list, siblings) { + ret = visit_enum_decl_entry(ctx, iter, *enum_decl, + &last_value, + bt_ctf_field_type_integer_get_signed(integer_decl)); + if (ret) { + goto error; + } + } + + if (name) { + ret = ctx_decl_scope_register_enum(ctx->current_scope, + name, *enum_decl); + if (ret) { + goto error; + } + } + } + + BT_PUT(integer_decl); + + return 0; + +error: + BT_PUT(integer_decl); + BT_PUT(*enum_decl); + + return ret; +} + +static +int visit_type_specifier(struct ctx *ctx, + struct ctf_node *type_specifier_list, + struct bt_ctf_field_type **decl) +{ + int ret = 0; + GString *str = NULL; + _BT_CTF_FIELD_TYPE_INIT(decl_copy); + + *decl = NULL; + str = g_string_new(""); + ret = get_type_specifier_list_name(ctx, type_specifier_list, str); + if (ret) { + goto error; + } + + *decl = ctx_decl_scope_lookup_alias(ctx->current_scope, str->str, -1); + if (!*decl) { + _PERROR("cannot find type alias \"%s\"", str->str); + ret = -EINVAL; + goto error; + } + + /* Make a copy of the type declaration */ + decl_copy = bt_ctf_field_type_copy(*decl); + if (!decl_copy) { + _PERROR("%s", "cannot create copy of type declaration"); + ret = -EINVAL; + goto error; + } + + BT_MOVE(*decl, decl_copy); + (void) g_string_free(str, TRUE); + str = NULL; + + return 0; + +error: + if (str) { + (void) g_string_free(str, TRUE); + } + + BT_PUT(*decl); + + return ret; +} + +static +int visit_integer_decl(struct ctx *ctx, + struct bt_list_head *expressions, + struct bt_ctf_field_type **integer_decl) +{ + int set = 0; + int ret = 0; + int signedness = 0; + struct ctf_node *expression; + uint64_t alignment = 0, size = 0; + struct bt_ctf_clock *mapped_clock = NULL; + enum bt_ctf_string_encoding encoding = BT_CTF_STRING_ENCODING_NONE; + enum bt_ctf_integer_base base = BT_CTF_INTEGER_BASE_DECIMAL; + enum bt_ctf_byte_order byte_order = + bt_ctf_trace_get_byte_order(ctx->trace); + + *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) { + ret = -EINVAL; + goto error; + } + + if (!strcmp(left->u.unary_expression.u.string, "signed")) { + if (_IS_SET(&set, _INTEGER_SIGNED_SET)) { + _PERROR_DUP_ATTR("signed", + "integer declaration"); + ret = -EPERM; + goto error; + } + + signedness = get_boolean(ctx->efd, right); + if (signedness < 0) { + ret = -EINVAL; + goto error; + } + + _SET(&set, _INTEGER_SIGNED_SET); + } else if (!strcmp(left->u.unary_expression.u.string, + "byte_order")) { + if (_IS_SET(&set, _INTEGER_BYTE_ORDER_SET)) { + _PERROR_DUP_ATTR("byte_order", + "integer declaration"); + ret = -EPERM; + goto error; + } + + byte_order = get_real_byte_order(ctx, right); + if (byte_order == BT_CTF_BYTE_ORDER_UNKNOWN) { + _PERROR("%s", "invalid \"byte_order\" attribute in integer declaration"); + ret = -EINVAL; + goto error; + } + + _SET(&set, _INTEGER_BYTE_ORDER_SET); + } else if (!strcmp(left->u.unary_expression.u.string, "size")) { + if (_IS_SET(&set, _INTEGER_SIZE_SET)) { + _PERROR_DUP_ATTR("size", + "integer declaration"); + ret = -EPERM; + goto error; + } + + if (right->u.unary_expression.type != + UNARY_UNSIGNED_CONSTANT) { + _PERROR("%s", "invalid \"size\" attribute in integer declaration: expecting unsigned constant"); + ret = -EINVAL; + goto error; + } + + size = right->u.unary_expression.u.unsigned_constant; + if (size == 0) { + _PERROR("%s", "invalid \"size\" attribute in integer declaration: expecting positive constant"); + ret = -EINVAL; + goto error; + } else if (size > 64) { + _PERROR("%s", "invalid \"size\" attribute in integer declaration: integers over 64-bit are not supported as of this version"); + ret = -EINVAL; + goto error; + } + + _SET(&set, _INTEGER_SIZE_SET); + } else if (!strcmp(left->u.unary_expression.u.string, + "align")) { + if (_IS_SET(&set, _INTEGER_ALIGN_SET)) { + _PERROR_DUP_ATTR("align", + "integer declaration"); + ret = -EPERM; + goto error; + } + + if (right->u.unary_expression.type != + UNARY_UNSIGNED_CONSTANT) { + _PERROR("%s", "invalid \"align\" attribute in integer declaration: expecting unsigned constant"); + ret = -EINVAL; + goto error; + } + + alignment = + right->u.unary_expression.u.unsigned_constant; + if (!is_align_valid(alignment)) { + _PERROR("%s", "invalid \"align\" attribute in integer declaration: expecting power of two"); + ret = -EINVAL; + goto error; + } + + _SET(&set, _INTEGER_ALIGN_SET); + } else if (!strcmp(left->u.unary_expression.u.string, "base")) { + if (_IS_SET(&set, _INTEGER_BASE_SET)) { + _PERROR_DUP_ATTR("base", "integer declaration"); + 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_CTF_INTEGER_BASE_BINARY; + break; + case 8: + base = BT_CTF_INTEGER_BASE_OCTAL; + break; + case 10: + base = BT_CTF_INTEGER_BASE_DECIMAL; + break; + case 16: + base = BT_CTF_INTEGER_BASE_HEXADECIMAL; + break; + default: + _PERROR("invalid \"base\" attribute in integer declaration: %" PRIu64, + right->u.unary_expression.u.unsigned_constant); + ret = -EINVAL; + goto error; + } + break; + } + case UNARY_STRING: + { + char *s_right = concatenate_unary_strings( + &expression->u.ctf_expression.right); + if (!s_right) { + _PERROR("%s", "unexpected unary expression for integer declaration's \"base\" attribute"); + ret = -EINVAL; + goto error; + } + + if (!strcmp(s_right, "decimal") || + !strcmp(s_right, "dec") || + !strcmp(s_right, "d") || + !strcmp(s_right, "i") || + !strcmp(s_right, "u")) { + base = BT_CTF_INTEGER_BASE_DECIMAL; + } else if (!strcmp(s_right, "hexadecimal") || + !strcmp(s_right, "hex") || + !strcmp(s_right, "x") || + !strcmp(s_right, "X") || + !strcmp(s_right, "p")) { + base = BT_CTF_INTEGER_BASE_HEXADECIMAL; + } else if (!strcmp(s_right, "octal") || + !strcmp(s_right, "oct") || + !strcmp(s_right, "o")) { + base = BT_CTF_INTEGER_BASE_OCTAL; + } else if (!strcmp(s_right, "binary") || + !strcmp(s_right, "b")) { + base = BT_CTF_INTEGER_BASE_BINARY; + } else { + _PERROR("unexpected unary expression for integer declaration's \"base\" attribute: \"%s\"", + s_right); + g_free(s_right); + ret = -EINVAL; + goto error; + } + + g_free(s_right); + break; + } + default: + _PERROR("%s", "invalid \"base\" attribute in integer declaration: expecting unsigned constant or unary string"); + ret = -EINVAL; + goto error; + } + + _SET(&set, _INTEGER_BASE_SET); + } else if (!strcmp(left->u.unary_expression.u.string, + "encoding")) { + char *s_right; + + if (_IS_SET(&set, _INTEGER_ENCODING_SET)) { + _PERROR_DUP_ATTR("encoding", + "integer declaration"); + ret = -EPERM; + goto error; + } + + if (right->u.unary_expression.type != UNARY_STRING) { + _PERROR("%s", "invalid \"encoding\" attribute in integer declaration: expecting unary string"); + ret = -EINVAL; + goto error; + } + + s_right = concatenate_unary_strings( + &expression->u.ctf_expression.right); + if (!s_right) { + _PERROR("%s", "unexpected unary expression for integer declaration's \"encoding\" attribute"); + ret = -EINVAL; + goto error; + } + + if (!strcmp(s_right, "UTF8") || + !strcmp(s_right, "utf8") || + !strcmp(s_right, "utf-8") || + !strcmp(s_right, "UTF-8")) { + encoding = BT_CTF_STRING_ENCODING_UTF8; + } else if (!strcmp(s_right, "ASCII") || + !strcmp(s_right, "ascii")) { + encoding = BT_CTF_STRING_ENCODING_ASCII; + } else if (!strcmp(s_right, "none")) { + encoding = BT_CTF_STRING_ENCODING_NONE; + } else { + _PERROR("invalid \"encoding\" attribute in integer declaration: unknown 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")) { + const char *clock_name; + + if (_IS_SET(&set, _INTEGER_MAP_SET)) { + _PERROR_DUP_ATTR("map", "integer declaration"); + ret = -EPERM; + goto error; + } + + if (right->u.unary_expression.type != UNARY_STRING) { + _PERROR("%s", "invalid \"map\" attribute in integer declaration: 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 = concatenate_unary_strings( + &expression->u.ctf_expression.right); + + if (!s_right) { + _PERROR("%s", "unexpected unary expression for integer declaration's \"map\" attribute"); + ret = -EINVAL; + goto error; + } + + _PWARNING("invalid \"map\" attribute in integer declaration: unknown clock: \"%s\"", + s_right); + _SET(&set, _INTEGER_MAP_SET); + g_free(s_right); + continue; + } + + mapped_clock = bt_ctf_trace_get_clock_by_name( + ctx->trace, clock_name); + if (!mapped_clock) { + _PERROR("invalid \"map\" attribute in integer declaration: cannot find clock \"%s\"", + clock_name); + ret = -EINVAL; + goto error; + } + + _SET(&set, _INTEGER_MAP_SET); + } else { + _PWARNING("unknown attribute \"%s\" in integer declaration", + left->u.unary_expression.u.string); + } + } + + if (!_IS_SET(&set, _INTEGER_SIZE_SET)) { + _PERROR("%s", + "missing \"size\" attribute in integer declaration"); + 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 = bt_ctf_field_type_integer_create((unsigned int) size); + if (!*integer_decl) { + _PERROR("%s", "cannot create integer declaration"); + ret = -ENOMEM; + goto error; + } + + ret = bt_ctf_field_type_integer_set_signed(*integer_decl, signedness); + ret |= bt_ctf_field_type_integer_set_base(*integer_decl, base); + ret |= bt_ctf_field_type_integer_set_encoding(*integer_decl, encoding); + ret |= bt_ctf_field_type_set_alignment(*integer_decl, + (unsigned int) alignment); + ret |= bt_ctf_field_type_set_byte_order(*integer_decl, byte_order); + + if (mapped_clock) { + /* Move clock */ + ret |= bt_ctf_field_type_integer_set_mapped_clock( + *integer_decl, mapped_clock); + bt_put(mapped_clock); + mapped_clock = NULL; + } + + if (ret) { + _PERROR("%s", "cannot configure integer declaration"); + ret = -EINVAL; + goto error; + } + + return 0; + +error: + if (mapped_clock) { + bt_put(mapped_clock); + } + + BT_PUT(*integer_decl); + + return ret; +} + +static +int visit_floating_point_number_decl(struct ctx *ctx, + struct bt_list_head *expressions, + struct bt_ctf_field_type **float_decl) +{ + int set = 0; + int ret = 0; + struct ctf_node *expression; + uint64_t alignment = 1, exp_dig = 0, mant_dig = 0; + enum bt_ctf_byte_order byte_order = + bt_ctf_trace_get_byte_order(ctx->trace); + + *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) { + ret = -EINVAL; + goto error; + } + + if (!strcmp(left->u.unary_expression.u.string, "byte_order")) { + if (_IS_SET(&set, _FLOAT_BYTE_ORDER_SET)) { + _PERROR_DUP_ATTR("byte_order", + "floating point number declaration"); + ret = -EPERM; + goto error; + } + + byte_order = get_real_byte_order(ctx, right); + if (byte_order == BT_CTF_BYTE_ORDER_UNKNOWN) { + _PERROR("%s", "invalid \"byte_order\" attribute in floating point number declaration"); + ret = -EINVAL; + goto error; + } + + _SET(&set, _FLOAT_BYTE_ORDER_SET); + } else if (!strcmp(left->u.unary_expression.u.string, + "exp_dig")) { + if (_IS_SET(&set, _FLOAT_EXP_DIG_SET)) { + _PERROR_DUP_ATTR("exp_dig", + "floating point number declaration"); + ret = -EPERM; + goto error; + } + + if (right->u.unary_expression.type != + UNARY_UNSIGNED_CONSTANT) { + _PERROR("%s", "invalid \"exp_dig\" attribute in floating point number declaration: expecting unsigned constant"); + 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")) { + if (_IS_SET(&set, _FLOAT_MANT_DIG_SET)) { + _PERROR_DUP_ATTR("mant_dig", + "floating point number declaration"); + ret = -EPERM; + goto error; + } + + if (right->u.unary_expression.type != + UNARY_UNSIGNED_CONSTANT) { + _PERROR("%s", "invalid \"mant_dig\" attribute in floating point number declaration: expecting unsigned constant"); + 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")) { + if (_IS_SET(&set, _FLOAT_ALIGN_SET)) { + _PERROR_DUP_ATTR("align", + "floating point number declaration"); + ret = -EPERM; + goto error; + } + + if (right->u.unary_expression.type != + UNARY_UNSIGNED_CONSTANT) { + _PERROR("%s", "invalid \"align\" attribute in floating point number declaration: expecting unsigned constant"); + ret = -EINVAL; + goto error; + } + + alignment = right->u.unary_expression.u. + unsigned_constant; + + if (!is_align_valid(alignment)) { + _PERROR("%s", "invalid \"align\" attribute in floating point number declaration: expecting power of two"); + ret = -EINVAL; + goto error; + } + + _SET(&set, _FLOAT_ALIGN_SET); + } else { + _PWARNING("unknown attribute \"%s\" in floating point number declaration", + left->u.unary_expression.u.string); + } + } + + if (!_IS_SET(&set, _FLOAT_MANT_DIG_SET)) { + _PERROR("%s", "missing \"mant_dig\" attribute in floating point number declaration"); + ret = -EPERM; + goto error; + } + + if (!_IS_SET(&set, _FLOAT_EXP_DIG_SET)) { + _PERROR("%s", "missing \"exp_dig\" attribute in floating point number declaration"); + 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 = bt_ctf_field_type_floating_point_create(); + if (!*float_decl) { + _PERROR("%s", + "cannot create floating point number declaration"); + ret = -ENOMEM; + goto error; + } + + ret = bt_ctf_field_type_floating_point_set_exponent_digits( + *float_decl, exp_dig); + ret |= bt_ctf_field_type_floating_point_set_mantissa_digits( + *float_decl, mant_dig); + ret |= bt_ctf_field_type_set_byte_order(*float_decl, byte_order); + ret |= bt_ctf_field_type_set_alignment(*float_decl, alignment); + if (ret) { + _PERROR("%s", + "cannot configure floating point number declaration"); + ret = -EINVAL; + goto error; + } + + return 0; + +error: + BT_PUT(*float_decl); + + return ret; +} + +static +int visit_string_decl(struct ctx *ctx, + struct bt_list_head *expressions, + struct bt_ctf_field_type **string_decl) +{ + int set = 0; + int ret = 0; + struct ctf_node *expression; + enum bt_ctf_string_encoding encoding = BT_CTF_STRING_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) { + ret = -EINVAL; + goto error; + } + + if (!strcmp(left->u.unary_expression.u.string, "encoding")) { + char *s_right; + + if (_IS_SET(&set, _STRING_ENCODING_SET)) { + _PERROR_DUP_ATTR("encoding", + "string declaration"); + ret = -EPERM; + goto error; + } + + if (right->u.unary_expression.type != UNARY_STRING) { + _PERROR("%s", "invalid \"encoding\" attribute in string declaration: expecting unary string"); + ret = -EINVAL; + goto error; + } + + s_right = concatenate_unary_strings( + &expression->u.ctf_expression.right); + if (!s_right) { + _PERROR("%s", "unexpected unary expression for string declaration's \"encoding\" attribute"); + ret = -EINVAL; + goto error; + } + + if (!strcmp(s_right, "UTF8") || + !strcmp(s_right, "utf8") || + !strcmp(s_right, "utf-8") || + !strcmp(s_right, "UTF-8")) { + encoding = BT_CTF_STRING_ENCODING_UTF8; + } else if (!strcmp(s_right, "ASCII") || + !strcmp(s_right, "ascii")) { + encoding = BT_CTF_STRING_ENCODING_ASCII; + } else if (!strcmp(s_right, "none")) { + encoding = BT_CTF_STRING_ENCODING_NONE; + } else { + _PERROR("invalid \"encoding\" attribute in string declaration: unknown encoding \"%s\"", + s_right); + g_free(s_right); + ret = -EINVAL; + goto error; + } + + g_free(s_right); + _SET(&set, _STRING_ENCODING_SET); + } else { + _PWARNING("unknown attribute \"%s\" in string declaration", + left->u.unary_expression.u.string); + } + } + + *string_decl = bt_ctf_field_type_string_create(); + if (!*string_decl) { + _PERROR("%s", "cannot create string declaration"); + ret = -ENOMEM; + goto error; + } + + ret = bt_ctf_field_type_string_set_encoding(*string_decl, encoding); + if (ret) { + _PERROR("%s", "cannot configure string declaration"); + ret = -EINVAL; + goto error; + } + + return 0; + +error: + BT_PUT(*string_decl); + + return ret; +} + +static +int visit_type_specifier_list(struct ctx *ctx, + struct ctf_node *ts_list, + struct bt_ctf_field_type **decl) +{ + int ret = 0; + struct ctf_node *first, *node; + + *decl = NULL; + + if (ts_list->type != NODE_TYPE_SPECIFIER_LIST) { + ret = -EINVAL; + goto error; + } + + first = _BT_LIST_FIRST_ENTRY(&ts_list->u.type_specifier_list.head, + struct ctf_node, siblings); + if (first->type != NODE_TYPE_SPECIFIER) { + ret = -EINVAL; + goto error; + } + + node = first->u.type_specifier.node; + + switch (first->u.type_specifier.type) { + case TYPESPEC_INTEGER: + ret = visit_integer_decl(ctx, &node->u.integer.expressions, + decl); + if (ret) { + assert(!*decl); + goto error; + } + break; + case TYPESPEC_FLOATING_POINT: + ret = visit_floating_point_number_decl(ctx, + &node->u.floating_point.expressions, decl); + if (ret) { + assert(!*decl); + goto error; + } + break; + case TYPESPEC_STRING: + ret = visit_string_decl(ctx, + &node->u.string.expressions, decl); + if (ret) { + 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, decl); + if (ret) { + 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, decl); + if (ret) { + assert(!*decl); + goto error; + } + break; + case TYPESPEC_ENUM: + ret = visit_enum_decl(ctx, node->u._enum.enum_id, + node->u._enum.container_type, + &node->u._enum.enumerator_list, + node->u._enum.has_body, decl); + if (ret) { + 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_type_specifier(ctx, ts_list, decl); + if (ret) { + assert(!*decl); + goto error; + } + break; + default: + _PERROR("unexpected node type: %d", + (int) first->u.type_specifier.type); + ret = -EINVAL; + goto error; + } + + assert(*decl); + + return 0; + +error: + BT_PUT(*decl); + + return ret; +} + +static +int visit_event_decl_entry(struct ctx *ctx, struct ctf_node *node, + struct bt_ctf_event_class *event_class, int64_t *stream_id, + int *set) +{ + int ret = 0; + char *left = NULL; + _BT_CTF_FIELD_TYPE_INIT(decl); + + switch (node->type) { + case NODE_TYPEDEF: + ret = visit_typedef(ctx, node->u._typedef.type_specifier_list, + &node->u._typedef.type_declarators); + if (ret) { + _PERROR("%s", + "cannot add typedef in \"event\" declaration"); + goto error; + } + break; + case NODE_TYPEALIAS: + ret = visit_typealias(ctx, node->u.typealias.target, + node->u.typealias.alias); + if (ret) { + _PERROR("%s", "cannot add typealias in \"event\" declaration"); + goto error; + } + break; + case NODE_CTF_EXPRESSION: + { + left = concatenate_unary_strings(&node->u.ctf_expression.left); + if (!left) { + ret = -EINVAL; + goto error; + } + + if (!strcmp(left, "name")) { + /* This is already known at this stage */ + if (_IS_SET(set, _EVENT_NAME_SET)) { + _PERROR_DUP_ATTR("name", "event declaration"); + ret = -EPERM; + goto error; + } + + _SET(set, _EVENT_NAME_SET); + } else if (!strcmp(left, "id")) { + int64_t id; + + if (_IS_SET(set, _EVENT_ID_SET)) { + _PERROR_DUP_ATTR("id", "event declaration"); + ret = -EPERM; + goto error; + } + + ret = get_unary_unsigned(&node->u.ctf_expression.right, + (uint64_t *) &id); + if (ret || id < 0) { + _PERROR("%s", "unexpected unary expression for event declaration's \"id\" attribute"); + ret = -EINVAL; + goto error; + } + + ret = bt_ctf_event_class_set_id(event_class, id); + if (ret) { + _PERROR("%s", + "cannot set event declaration's ID"); + goto error; + } + + _SET(set, _EVENT_ID_SET); + } else if (!strcmp(left, "stream_id")) { + if (_IS_SET(set, _EVENT_STREAM_ID_SET)) { + _PERROR_DUP_ATTR("stream_id", + "event declaration"); + ret = -EPERM; + goto error; + } + + ret = get_unary_unsigned(&node->u.ctf_expression.right, + (uint64_t *) stream_id); + if (ret || *stream_id < 0) { + _PERROR("%s", "unexpected unary expression for event declaration's \"stream_id\" attribute"); + ret = -EINVAL; + goto error; + } + + _SET(set, _EVENT_STREAM_ID_SET); + } else if (!strcmp(left, "context")) { + if (_IS_SET(set, _EVENT_CONTEXT_SET)) { + _PERROR("%s", "duplicate \"context\" entry in event declaration"); + ret = -EPERM; + goto error; + } + + ret = visit_type_specifier_list(ctx, + _BT_LIST_FIRST_ENTRY( + &node->u.ctf_expression.right, + struct ctf_node, siblings), + &decl); + if (ret) { + _PERROR("%s", "cannot create event context declaration"); + goto error; + } + + assert(decl); + ret = bt_ctf_event_class_set_context_type( + event_class, decl); + BT_PUT(decl); + if (ret) { + _PERROR("%s", "cannot set event's context declaration"); + goto error; + } + + _SET(set, _EVENT_CONTEXT_SET); + } else if (!strcmp(left, "fields")) { + if (_IS_SET(set, _EVENT_FIELDS_SET)) { + _PERROR("%s", "duplicate \"fields\" entry in event declaration"); + ret = -EPERM; + goto error; + } + + ret = visit_type_specifier_list(ctx, + _BT_LIST_FIRST_ENTRY( + &node->u.ctf_expression.right, + struct ctf_node, siblings), + &decl); + if (ret) { + _PERROR("%s", "cannot create event payload declaration"); + goto error; + } + + assert(decl); + ret = bt_ctf_event_class_set_payload_type( + event_class, decl); + BT_PUT(decl); + if (ret) { + _PERROR("%s", "cannot set event's payload declaration"); + goto error; + } + + _SET(set, _EVENT_FIELDS_SET); + } else if (!strcmp(left, "loglevel")) { + uint64_t loglevel; + + if (_IS_SET(set, _EVENT_LOGLEVEL_SET)) { + _PERROR_DUP_ATTR("loglevel", + "event declaration"); + ret = -EPERM; + goto error; + } + + ret = get_unary_unsigned(&node->u.ctf_expression.right, + &loglevel); + if (ret) { + _PERROR("%s", "unexpected unary expression for event declaration's \"loglevel\" attribute"); + ret = -EINVAL; + goto error; + } + + // TODO: FIXME: set log level here + + _SET(set, _EVENT_LOGLEVEL_SET); + } else if (!strcmp(left, "model.emf.uri")) { + char *right; + + if (_IS_SET(set, _EVENT_MODEL_EMF_URI_SET)) { + _PERROR_DUP_ATTR("model.emf.uri", + "event declaration"); + ret = -EPERM; + goto error; + } + + right = concatenate_unary_strings( + &node->u.ctf_expression.right); + if (!right) { + _PERROR("%s", "unexpected unary expression for event declaration's \"model.emf.uri\" attribute"); + ret = -EINVAL; + goto error; + } + + // TODO: FIXME: set model EMF URI here + + g_free(right); + _SET(set, _EVENT_MODEL_EMF_URI_SET); + } else { + _PWARNING("unknown attribute \"%s\" in event declaration", + left); + } + + g_free(left); + left = NULL; + break; + } + default: + ret = -EPERM; + goto error; + } + + return 0; + +error: + if (left) { + g_free(left); + } + + BT_PUT(decl); + + return ret; +} + +static +char *get_event_decl_name(struct ctx *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 = concatenate_unary_strings(&iter->u.ctf_expression.left); + if (!left) { + goto error; + } + + if (!strcmp(left, "name")) { + name = concatenate_unary_strings( + &iter->u.ctf_expression.right); + if (!name) { + _PERROR("%s", "unexpected unary expression for event declaration's \"name\" attribute"); + goto error; + } + } + + g_free(left); + left = NULL; + + if (name) { + break; + } + } + + return name; + +error: + g_free(left); + + return NULL; +} + +static +int reset_event_decl_types(struct ctx *ctx, + struct bt_ctf_event_class *event_class) +{ + int ret = 0; + _BT_CTF_FIELD_TYPE_INIT(decl); + + /* Event context */ + decl = bt_ctf_field_type_structure_create(); + if (!decl) { + _PERROR("%s", "cannot create initial, empty event context structure"); + ret = -ENOMEM; + goto error; + } + + ret = bt_ctf_event_class_set_context_type(event_class, decl); + BT_PUT(decl); + if (ret) { + _PERROR("%s", "cannot set initial, empty event context structure"); + goto error; + } + + /* Event payload */ + decl = bt_ctf_field_type_structure_create(); + if (!decl) { + _PERROR("%s", "cannot create initial, empty event payload structure"); + ret = -ENOMEM; + goto error; + } + + ret = bt_ctf_event_class_set_payload_type(event_class, decl); + BT_PUT(decl); + if (ret) { + _PERROR("%s", "cannot set initial, empty event payload structure"); + goto error; + } + + return 0; + +error: + BT_PUT(decl); + + return ret; +} + +static +int reset_stream_decl_types(struct ctx *ctx, + struct bt_ctf_stream_class *stream_class) +{ + int ret = 0; + _BT_CTF_FIELD_TYPE_INIT(decl); + + /* Packet context */ + decl = bt_ctf_field_type_structure_create(); + if (!decl) { + _PERROR("%s", "cannot create initial, empty packet context structure"); + ret = -ENOMEM; + goto error; + } + + ret = bt_ctf_stream_class_set_packet_context_type(stream_class, decl); + BT_PUT(decl); + if (ret) { + _PERROR("%s", "cannot set initial, empty packet context structure"); + goto error; + } + + /* Event header */ + decl = bt_ctf_field_type_structure_create(); + if (!decl) { + _PERROR("%s", "cannot create initial, empty event header structure"); + ret = -ENOMEM; + goto error; + } + + ret = bt_ctf_stream_class_set_event_header_type(stream_class, decl); + BT_PUT(decl); + if (ret) { + _PERROR("%s", "cannot set initial, empty event header structure"); + goto error; + } + + /* Event context */ + decl = bt_ctf_field_type_structure_create(); + if (!decl) { + _PERROR("%s", "cannot create initial, empty stream event context structure"); + ret = -ENOMEM; + goto error; + } + + ret = bt_ctf_stream_class_set_event_context_type(stream_class, decl); + BT_PUT(decl); + if (ret) { + _PERROR("%s", "cannot set initial, empty stream event context structure"); + goto error; + } + + return 0; + +error: + BT_PUT(decl); + + return ret; +} + +static +struct bt_ctf_stream_class *create_reset_stream_class(struct ctx *ctx) +{ + int ret; + struct bt_ctf_stream_class *stream_class; + + stream_class = bt_ctf_stream_class_create(NULL); + if (!stream_class) { + _PERROR("%s", "cannot create stream class"); + goto error; + } + + /* + * Set packet context, event header, and event context to empty + * structures to override the default ones. + */ + ret = reset_stream_decl_types(ctx, stream_class); + if (ret) { + goto error; + } + + return stream_class; + +error: + BT_PUT(stream_class); + + return NULL; +} + +static +int visit_event_decl(struct ctx *ctx, struct ctf_node *node) +{ + int ret = 0; + int set = 0; + int64_t event_id; + struct ctf_node *iter; + int64_t stream_id = -1; + char *event_name = NULL; + struct bt_ctf_event_class *event_class = NULL; + struct bt_ctf_event_class *eevent_class; + struct bt_ctf_stream_class *stream_class; + struct bt_list_head *decl_list = &node->u.event.declaration_list; + + if (node->visited) { + goto end; + } + + node->visited = TRUE; + event_name = get_event_decl_name(ctx, node); + if (!event_name) { + _PERROR("%s", + "missing \"name\" attribute in event declaration"); + ret = -EPERM; + goto error; + } + + event_class = bt_ctf_event_class_create(event_name); + + /* + * Set context and fields to empty structures to override the + * default ones. + */ + ret = reset_event_decl_types(ctx, event_class); + if (ret) { + goto error; + } + + + ret = ctx_push_scope(ctx); + if (ret) { + _PERROR("%s", "cannot push scope"); + goto error; + } + + bt_list_for_each_entry(iter, decl_list, siblings) { + ret = visit_event_decl_entry(ctx, iter, event_class, + &stream_id, &set); + if (ret) { + goto error; + } + } + + if (!_IS_SET(&set, _EVENT_STREAM_ID_SET)) { + GList *keys = NULL; + struct bt_ctf_stream_class *new_stream_class; + + /* Allow missing stream_id if there is only a single stream */ + switch (g_hash_table_size(ctx->stream_classes)) { + case 0: + /* Create stream if there's none */ + new_stream_class = create_reset_stream_class(ctx); + if (!new_stream_class) { + ret = -EINVAL; + goto error; + } + + ret = bt_ctf_stream_class_set_id(new_stream_class, 0); + if (ret) { + _PERROR("%s", "cannot set stream's ID"); + BT_PUT(new_stream_class); + goto error; + } + + stream_id = 0; + + /* Move reference to visitor's context */ + g_hash_table_insert(ctx->stream_classes, + (gpointer) stream_id, new_stream_class); + new_stream_class = NULL; + + break; + case 1: + /* Single stream: get its ID */ + keys = g_hash_table_get_keys(ctx->stream_classes); + stream_id = (int64_t) keys->data; + g_list_free(keys); + keys = NULL; + break; + default: + _PERROR("%s", "missing \"stream_id\" attribute in event declaration"); + ret = -EPERM; + goto error; + } + } + + + + assert(stream_id >= 0); + + /* We have the stream ID now; borrow the stream class if found */ + stream_class = g_hash_table_lookup(ctx->stream_classes, + (gpointer) stream_id); + if (!stream_class) { + _PERROR("cannot find stream class with ID %" PRId64, + stream_id); + ret = -EINVAL; + goto error; + } + + if (!_IS_SET(&set, _EVENT_ID_SET)) { + /* Allow only one event without ID per stream */ + if (bt_ctf_stream_class_get_event_class_count(stream_class) != + 0) { + _PERROR("%s", + "missing \"id\" field in event declaration"); + ret = -EPERM; + goto error; + } + + /* Automatic ID */ + ret = bt_ctf_event_class_set_id(event_class, 0); + if (ret) { + _PERROR("%s", "cannot set event's ID"); + goto error; + } + } + + event_id = bt_ctf_event_class_get_id(event_class); + if (event_id < 0) { + _PERROR("%s", "cannot get event's ID"); + ret = -EINVAL; + goto error; + } + + eevent_class = bt_ctf_stream_class_get_event_class_by_id(stream_class, + event_id); + if (eevent_class) { + BT_PUT(eevent_class); + _PERROR("%s", "duplicate event with ID %" PRId64 " in same stream"); + ret = -EEXIST; + goto error; + } + + eevent_class = bt_ctf_stream_class_get_event_class_by_name(stream_class, + event_name); + if (eevent_class) { + BT_PUT(eevent_class); + eevent_class = NULL; + _PERROR("%s", + "duplicate event with name \"%s\" in same stream"); + ret = -EEXIST; + goto error; + } + + g_free(event_name); + ret = bt_ctf_stream_class_add_event_class(stream_class, event_class); + BT_PUT(event_class); + event_class = NULL; + + if (ret) { + _PERROR("%s", "cannot add event class to stream class"); + goto error; + } + +end: + return 0; + +error: + g_free(event_name); + BT_PUT(event_class); + + /* stream_class is borrowed; it still belongs to the hash table */ + + return ret; +} + +static +int visit_stream_decl_entry(struct ctx *ctx, struct ctf_node *node, + struct bt_ctf_stream_class *stream_class, int *set) +{ + int ret = 0; + char *left = NULL; + _BT_CTF_FIELD_TYPE_INIT(decl); + + switch (node->type) { + case NODE_TYPEDEF: + ret = visit_typedef(ctx, node->u._typedef.type_specifier_list, + &node->u._typedef.type_declarators); + if (ret) { + _PERROR("%s", + "cannot add typedef in \"stream\" declaration"); + goto error; + } + break; + case NODE_TYPEALIAS: + ret = visit_typealias(ctx, node->u.typealias.target, + node->u.typealias.alias); + if (ret) { + _PERROR("%s", "cannot add typealias in \"stream\" declaration"); + goto error; + } + break; + case NODE_CTF_EXPRESSION: + { + left = concatenate_unary_strings(&node->u.ctf_expression.left); + if (!left) { + ret = -EINVAL; + goto error; + } + + if (!strcmp(left, "id")) { + int64_t id; + gpointer ptr; + + if (_IS_SET(set, _STREAM_ID_SET)) { + _PERROR_DUP_ATTR("id", "stream declaration"); + ret = -EPERM; + goto error; + } + + ret = get_unary_unsigned(&node->u.ctf_expression.right, + (uint64_t *) &id); + if (ret || id < 0) { + _PERROR("%s", "unexpected unary expression for stream declaration's \"id\" attribute"); + ret = -EINVAL; + goto error; + } + + ptr = g_hash_table_lookup(ctx->stream_classes, + (gpointer) id); + if (ptr) { + _PERROR("duplicate stream with ID %" PRId64, + id); + ret = -EEXIST; + goto error; + } + + ret = bt_ctf_stream_class_set_id(stream_class, id); + if (ret) { + _PERROR("%s", + "cannot set stream declaration's ID"); + goto error; + } + + _SET(set, _STREAM_ID_SET); + } else if (!strcmp(left, "event.header")) { + if (_IS_SET(set, _STREAM_EVENT_HEADER_SET)) { + _PERROR("%s", "duplicate \"event.header\" entry in stream declaration"); + ret = -EPERM; + goto error; + } + + ret = visit_type_specifier_list(ctx, + _BT_LIST_FIRST_ENTRY( + &node->u.ctf_expression.right, + struct ctf_node, siblings), + &decl); + if (ret) { + _PERROR("%s", "cannot create event header declaration"); + goto error; + } + + assert(decl); + + ret = bt_ctf_stream_class_set_event_header_type( + stream_class, decl); + BT_PUT(decl); + if (ret) { + _PERROR("%s", "cannot set stream's event header declaration"); + goto error; + } + + _SET(set, _STREAM_EVENT_HEADER_SET); + } else if (!strcmp(left, "event.context")) { + if (_IS_SET(set, _STREAM_EVENT_CONTEXT_SET)) { + _PERROR("%s", "duplicate \"event.context\" entry in stream declaration"); + ret = -EPERM; + goto error; + } + + ret = visit_type_specifier_list(ctx, + _BT_LIST_FIRST_ENTRY( + &node->u.ctf_expression.right, + struct ctf_node, siblings), + &decl); + if (ret) { + _PERROR("%s", "cannot create stream event context declaration"); + goto error; + } + + assert(decl); + + ret = bt_ctf_stream_class_set_event_context_type( + stream_class, decl); + BT_PUT(decl); + if (ret) { + _PERROR("%s", "cannot set stream's event context declaration"); + goto error; + } + + _SET(set, _STREAM_EVENT_CONTEXT_SET); + } else if (!strcmp(left, "packet.context")) { + if (_IS_SET(set, _STREAM_PACKET_CONTEXT_SET)) { + _PERROR("%s", "duplicate \"packet.context\" entry in stream declaration"); + ret = -EPERM; + goto error; + } + + ret = visit_type_specifier_list(ctx, + _BT_LIST_FIRST_ENTRY( + &node->u.ctf_expression.right, + struct ctf_node, siblings), + &decl); + if (ret) { + _PERROR("%s", "cannot create packet context declaration"); + goto error; + } + + assert(decl); + + ret = bt_ctf_stream_class_set_packet_context_type( + stream_class, decl); + BT_PUT(decl); + if (ret) { + _PERROR("%s", "cannot set stream's packet context declaration"); + goto error; + } + + _SET(set, _STREAM_PACKET_CONTEXT_SET); + } else { + _PWARNING("unknown attribute \"%s\" in stream declaration", + left); + } + + g_free(left); + left = NULL; + break; + } + + default: + ret = -EPERM; + goto error; + } + + return 0; + +error: + g_free(left); + BT_PUT(decl); + + return ret; +} + +static +int visit_stream_decl(struct ctx *ctx, struct ctf_node *node) +{ + int64_t id; + int set = 0; + int ret = 0; + struct ctf_node *iter; + struct bt_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 = create_reset_stream_class(ctx); + if (!stream_class) { + ret = -EINVAL; + goto error; + } + + ret = ctx_push_scope(ctx); + if (ret) { + _PERROR("%s", "cannot push scope"); + goto error; + } + + bt_list_for_each_entry(iter, decl_list, siblings) { + ret = visit_stream_decl_entry(ctx, iter, stream_class, &set); + if (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 */ + _BT_CTF_FIELD_TYPE_INIT(stream_id_decl); + _BT_CTF_FIELD_TYPE_INIT(packet_header_decl); + + packet_header_decl = + bt_ctf_trace_get_packet_header_type(ctx->trace); + if (!packet_header_decl) { + _PERROR("%s", + "cannot get trace packet header declaration"); + goto error; + } + + stream_id_decl = + bt_ctf_field_type_structure_get_field_type_by_name( + packet_header_decl, "stream_id"); + BT_PUT(packet_header_decl); + if (!stream_id_decl) { + _PERROR("%s", "missing \"stream_id\" field in packet header declaration, but \"id\" attribute is declared for stream"); + goto error; + } + + if (!bt_ctf_field_type_is_integer(stream_id_decl)) { + BT_PUT(stream_id_decl); + _PERROR("%s", "\"stream_id\" field in packet header declaration is not an integer"); + goto error; + } + + BT_PUT(stream_id_decl); + } else { + /* Allow only _one_ ID-less stream */ + if (g_hash_table_size(ctx->stream_classes) != 0) { + _PERROR("%s", + "missing \"id\" field in stream declaration"); + ret = -EPERM; + goto error; + } + + /* Automatic ID: 0 */ + ret = bt_ctf_stream_class_set_id(stream_class, 0); + } + + id = bt_ctf_stream_class_get_id(stream_class); + if (id < 0) { + _PERROR("wrong stream ID: %" PRId64, id); + ret = -EINVAL; + goto error; + } + + /* Move reference to visitor's context */ + g_hash_table_insert(ctx->stream_classes, (gpointer) (int64_t) id, + stream_class); + stream_class = NULL; + +end: + return 0; + +error: + BT_PUT(stream_class); + + return ret; +} + +static +int visit_trace_decl_entry(struct ctx *ctx, struct ctf_node *node, int *set) +{ + int ret = 0; + char *left = NULL; + _BT_CTF_FIELD_TYPE_INIT(packet_header_decl); + + switch (node->type) { + case NODE_TYPEDEF: + ret = visit_typedef(ctx, node->u._typedef.type_specifier_list, + &node->u._typedef.type_declarators); + if (ret) { + _PERROR("%s", + "cannot add typedef in \"trace\" declaration"); + goto error; + } + break; + case NODE_TYPEALIAS: + ret = visit_typealias(ctx, node->u.typealias.target, + node->u.typealias.alias); + if (ret) { + _PERROR("%s", + "cannot add typealias in \"trace\" declaration"); + goto error; + } + break; + case NODE_CTF_EXPRESSION: + { + left = concatenate_unary_strings(&node->u.ctf_expression.left); + if (!left) { + ret = -EINVAL; + goto error; + } + + if (!strcmp(left, "major")) { + if (_IS_SET(set, _TRACE_MAJOR_SET)) { + _PERROR_DUP_ATTR("major", "trace declaration"); + ret = -EPERM; + goto error; + } + + ret = get_unary_unsigned(&node->u.ctf_expression.right, + &ctx->trace_major); + if (ret) { + _PERROR("%s", "unexpected unary expression for trace's \"major\" attribute"); + ret = -EINVAL; + goto error; + } + + _SET(set, _TRACE_MAJOR_SET); + } else if (!strcmp(left, "minor")) { + if (_IS_SET(set, _TRACE_MINOR_SET)) { + _PERROR_DUP_ATTR("minor", "trace declaration"); + ret = -EPERM; + goto error; + } + + ret = get_unary_unsigned(&node->u.ctf_expression.right, + &ctx->trace_minor); + if (ret) { + _PERROR("%s", "unexpected unary expression for trace's \"minor\" attribute"); + ret = -EINVAL; + goto error; + } + + _SET(set, _TRACE_MINOR_SET); + } else if (!strcmp(left, "uuid")) { + if (_IS_SET(set, _TRACE_UUID_SET)) { + _PERROR_DUP_ATTR("uuid", "trace declaration"); + ret = -EPERM; + goto error; + } + + ret = get_unary_uuid(&node->u.ctf_expression.right, + ctx->trace_uuid); + if (ret) { + _PERROR("%s", + "invalid trace declaration's UUID"); + goto error; + } + + _SET(set, _TRACE_UUID_SET); + } else if (!strcmp(left, "byte_order")) { + /* Native byte order is already known at this stage */ + if (_IS_SET(set, _TRACE_BYTE_ORDER_SET)) { + _PERROR_DUP_ATTR("byte_order", + "trace declaration"); + ret = -EPERM; + goto error; + } + + _SET(set, _TRACE_BYTE_ORDER_SET); + } else if (!strcmp(left, "packet.header")) { + if (_IS_SET(set, _TRACE_PACKET_HEADER_SET)) { + _PERROR("%s", "duplicate \"packet.header\" entry in trace declaration"); + ret = -EPERM; + goto error; + } + + ret = visit_type_specifier_list(ctx, + _BT_LIST_FIRST_ENTRY( + &node->u.ctf_expression.right, + struct ctf_node, siblings), + &packet_header_decl); + if (ret) { + _PERROR("%s", "cannot create packet header declaration"); + goto error; + } + + assert(packet_header_decl); + ret = bt_ctf_trace_set_packet_header_type(ctx->trace, + packet_header_decl); + BT_PUT(packet_header_decl); + if (ret) { + _PERROR("%s", "cannot set trace declaration's packet header declaration"); + goto error; + } + + _SET(set, _TRACE_PACKET_HEADER_SET); + } else { + _PWARNING("%s", "unknown attribute \"%s\" in trace declaration"); + } + + g_free(left); + left = NULL; + break; + } + default: + _PERROR("%s", "unknown expression in trace declaration"); + ret = -EINVAL; + goto error; + } + + return 0; + +error: + g_free(left); + BT_PUT(packet_header_decl); + + return ret; +} + +static +int visit_trace_decl(struct ctx *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) { + _PERROR("%s", "duplicate \"trace\" block"); + ret = -EEXIST; + goto error; + } + + ret = ctx_push_scope(ctx); + if (ret) { + _PERROR("%s", "cannot push scope"); + goto error; + } + + bt_list_for_each_entry(iter, decl_list, siblings) { + ret = visit_trace_decl_entry(ctx, iter, &set); + if (ret) { + ctx_pop_scope(ctx); + goto error; + } + } + + ctx_pop_scope(ctx); + + if (!_IS_SET(&set, _TRACE_MAJOR_SET)) { + _PERROR("%s", + "missing \"major\" attribute in trace declaration"); + ret = -EPERM; + goto error; + } + + if (!_IS_SET(&set, _TRACE_MINOR_SET)) { + _PERROR("%s", + "missing \"minor\" attribute in trace declaration"); + ret = -EPERM; + goto error; + } + + if (!_IS_SET(&set, _TRACE_BYTE_ORDER_SET)) { + _PERROR("%s", "missing \"byte_order\" attribute in trace declaration"); + ret = -EPERM; + goto error; + } + + ctx->is_trace_visited = TRUE; + +end: + return 0; + +error: + return ret; +} + +static +int visit_env(struct ctx *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) { + _PERROR("%s", "wrong expression in environment entry"); + ret = -EPERM; + goto error; + } + + left = concatenate_unary_strings( + &entry_node->u.ctf_expression.left); + if (!left) { + _PERROR("%s", "cannot get environment entry name"); + ret = -EINVAL; + goto error; + } + + if (is_unary_string(right_head)) { + char *right = concatenate_unary_strings(right_head); + + if (!right) { + _PERROR("unexpected unary expression for environment entry's value (\"%s\")", + left); + ret = -EINVAL; + goto error; + } + + printf_verbose("env.%s = \"%s\"\n", left, right); + ret = bt_ctf_trace_set_environment_field_string( + ctx->trace, left, right); + g_free(right); + + if (ret) { + _PERROR("environment: cannot add entry \"%s\" to trace", + left); + goto error; + } + } 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(right_head, + (uint64_t *) &v); + } else { + ret = get_unary_signed(right_head, &v); + } + if (ret) { + _PERROR("unexpected unary expression for environment entry's value (\"%s\")", + left); + ret = -EINVAL; + goto error; + } + + printf_verbose("env.%s = %" PRId64 "\n", left, v); + ret = bt_ctf_trace_set_environment_field_integer( + ctx->trace, left, v); + if (ret) { + _PERROR("environment: cannot add entry \"%s\" to trace", + left); + goto error; + } + } else { + printf_verbose("%s: environment entry \"%s\" has unknown type\n", + __func__, left); + } + + g_free(left); + left = NULL; + } + +end: + return 0; + +error: + g_free(left); + + return ret; +} + +static +int set_trace_byte_order(struct ctx *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 = concatenate_unary_strings( + &node->u.ctf_expression.left); + if (!left) { + ret = -EINVAL; + goto error; + } + + if (!strcmp(left, "byte_order")) { + enum bt_ctf_byte_order bo; + + if (_IS_SET(&set, _TRACE_BYTE_ORDER_SET)) { + _PERROR_DUP_ATTR("byte_order", + "trace declaration"); + 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->efd, + right_node); + if (bo == BT_CTF_BYTE_ORDER_UNKNOWN) { + _PERROR("%s", "unknown \"byte_order\" attribute in trace declaration"); + ret = -EINVAL; + goto error; + } else if (bo == BT_CTF_BYTE_ORDER_NATIVE) { + _PERROR("%s", "\"byte_order\" attribute cannot be set to \"native\" in trace declaration"); + ret = -EPERM; + goto error; + } + + ret = bt_ctf_trace_set_byte_order( + ctx->trace, bo); + if (ret) { + _PERROR("cannot set trace's byte order (%d)", + ret); + goto error; + } + } + + g_free(left); + left = NULL; + } + } + + if (!_IS_SET(&set, _TRACE_BYTE_ORDER_SET)) { + _PERROR("%s", "missing \"byte_order\" attribute in trace declaration"); + ret = -EINVAL; + goto error; + } + + return 0; + +error: + g_free(left); + + return ret; +} + +static +int visit_clock_decl_entry(struct ctx *ctx, struct ctf_node *entry_node, + struct bt_ctf_clock *clock, int *set) +{ + int ret = 0; + char *left = NULL; + + if (entry_node->type != NODE_CTF_EXPRESSION) { + ret = -EPERM; + goto error; + } + + left = concatenate_unary_strings(&entry_node->u.ctf_expression.left); + if (!left) { + ret = -EINVAL; + goto error; + } + + if (!strcmp(left, "name")) { + char *right; + + if (_IS_SET(set, _CLOCK_NAME_SET)) { + _PERROR_DUP_ATTR("name", "clock declaration"); + ret = -EPERM; + goto error; + } + + right = concatenate_unary_strings( + &entry_node->u.ctf_expression.right); + if (!right) { + _PERROR("%s", "unexpected unary expression for clock declaration's \"name\" attribute"); + ret = -EINVAL; + goto error; + } + + ret = bt_ctf_clock_set_name(clock, right); + if (ret) { + _PERROR("%s", "cannot set clock's name"); + g_free(right); + goto error; + } + + g_free(right); + _SET(set, _CLOCK_NAME_SET); + } else if (!strcmp(left, "uuid")) { + unsigned char uuid[BABELTRACE_UUID_LEN]; + + if (_IS_SET(set, _CLOCK_UUID_SET)) { + _PERROR_DUP_ATTR("uuid", "clock declaration"); + ret = -EPERM; + goto error; + } + + ret = get_unary_uuid(&entry_node->u.ctf_expression.right, uuid); + if (ret) { + _PERROR("%s", "invalid clock UUID"); + goto error; + } + + ret = bt_ctf_clock_set_uuid(clock, uuid); + if (ret) { + _PERROR("%s", "cannot set clock's UUID"); + goto error; + } + + _SET(set, _CLOCK_UUID_SET); + } else if (!strcmp(left, "description")) { + char *right; + + if (_IS_SET(set, _CLOCK_DESCRIPTION_SET)) { + _PERROR_DUP_ATTR("description", "clock declaration"); + ret = -EPERM; + goto error; + } + + right = concatenate_unary_strings( + &entry_node->u.ctf_expression.right); + if (!right) { + _PERROR("%s", "unexpected unary expression for clock's \"description\" attribute"); + ret = -EINVAL; + goto error; + } + + ret = bt_ctf_clock_set_description(clock, right); + if (ret) { + _PERROR("%s", "cannot set clock's description"); + g_free(right); + goto error; + } + + g_free(right); + _SET(set, _CLOCK_DESCRIPTION_SET); + } else if (!strcmp(left, "freq")) { + uint64_t freq; + + if (_IS_SET(set, _CLOCK_FREQ_SET)) { + _PERROR_DUP_ATTR("freq", "clock declaration"); + ret = -EPERM; + goto error; + } + + ret = get_unary_unsigned( + &entry_node->u.ctf_expression.right, &freq); + if (ret) { + _PERROR("%s", "unexpected unary expression for clock declaration's \"freq\" attribute"); + ret = -EINVAL; + goto error; + } + + ret = bt_ctf_clock_set_frequency(clock, freq); + if (ret) { + _PERROR("%s", "cannot set clock's frequency"); + goto error; + } + + _SET(set, _CLOCK_FREQ_SET); + } else if (!strcmp(left, "precision")) { + uint64_t precision; + + if (_IS_SET(set, _CLOCK_PRECISION_SET)) { + _PERROR_DUP_ATTR("precision", "clock declaration"); + ret = -EPERM; + goto error; + } + + ret = get_unary_unsigned( + &entry_node->u.ctf_expression.right, &precision); + if (ret) { + _PERROR("%s", "unexpected unary expression for clock declaration's \"precision\" attribute"); + ret = -EINVAL; + goto error; + } + + ret = bt_ctf_clock_set_precision(clock, precision); + if (ret) { + _PERROR("%s", "cannot set clock's precision"); + goto error; + } + + _SET(set, _CLOCK_PRECISION_SET); + } else if (!strcmp(left, "offset_s")) { + uint64_t offset_s; + + if (_IS_SET(set, _CLOCK_OFFSET_S_SET)) { + _PERROR_DUP_ATTR("offset_s", "clock declaration"); + ret = -EPERM; + goto error; + } + + ret = get_unary_unsigned( + &entry_node->u.ctf_expression.right, &offset_s); + if (ret) { + _PERROR("%s", "unexpected unary expression for clock declaration's \"offset_s\" attribute"); + ret = -EINVAL; + goto error; + } + + ret = bt_ctf_clock_set_offset_s(clock, offset_s); + if (ret) { + _PERROR("%s", "cannot set clock's offset in seconds"); + goto error; + } + + _SET(set, _CLOCK_OFFSET_S_SET); + } else if (!strcmp(left, "offset")) { + uint64_t offset; + + if (_IS_SET(set, _CLOCK_OFFSET_SET)) { + _PERROR_DUP_ATTR("offset", "clock declaration"); + ret = -EPERM; + goto error; + } + + ret = get_unary_unsigned( + &entry_node->u.ctf_expression.right, &offset); + if (ret) { + _PERROR("%s", "unexpected unary expression for clock declaration's \"offset\" attribute"); + ret = -EINVAL; + goto error; + } + + ret = bt_ctf_clock_set_offset(clock, offset); + if (ret) { + _PERROR("%s", "cannot set clock's offset in cycles"); + goto error; + } + + _SET(set, _CLOCK_OFFSET_SET); + } else if (!strcmp(left, "absolute")) { + struct ctf_node *right; + + if (_IS_SET(set, _CLOCK_ABSOLUTE_SET)) { + _PERROR_DUP_ATTR("absolute", "clock declaration"); + ret = -EPERM; + goto error; + } + + right = _BT_LIST_FIRST_ENTRY( + &entry_node->u.ctf_expression.right, + struct ctf_node, siblings); + ret = get_boolean(ctx->efd, right); + if (ret < 0) { + _PERROR("%s", "unexpected unary expression for clock declaration's \"absolute\" attribute"); + ret = -EINVAL; + goto error; + } + + ret = bt_ctf_clock_set_is_absolute(clock, ret); + if (ret) { + _PERROR("%s", "cannot set clock's absolute option"); + goto error; + } + + _SET(set, _CLOCK_ABSOLUTE_SET); + } else { + _PWARNING("unknown attribute \"%s\" in clock declaration", + left); + } + + g_free(left); + left = NULL; + + return 0; + +error: + g_free(left); + + return ret; +} + +static +int visit_clock_decl(struct ctx *ctx, struct ctf_node *clock_node) +{ + int ret = 0; + int set = 0; + struct bt_ctf_clock *clock; + struct ctf_node *entry_node; + struct bt_list_head *decl_list = &clock_node->u.clock.declaration_list; + + if (clock_node->visited) { + return 0; + } + + clock_node->visited = TRUE; + clock = bt_ctf_clock_create(NULL); + if (!clock) { + _PERROR("%s", "cannot create clock"); + ret = -ENOMEM; + goto error; + } + + bt_list_for_each_entry(entry_node, decl_list, siblings) { + ret = visit_clock_decl_entry(ctx, entry_node, clock, &set); + if (ret) { + goto error; + } + } + + if (!_IS_SET(&set, _CLOCK_NAME_SET)) { + _PERROR("%s", + "missing \"name\" attribute in clock declaration"); + ret = -EPERM; + goto error; + } + + if (bt_ctf_trace_get_clock_count(ctx->trace) != 0) { + _PERROR("%s", "only CTF traces with a single clock declaration are supported as of this version"); + ret = -EINVAL; + goto error; + } + + ret = bt_ctf_trace_add_clock(ctx->trace, clock); + if (ret) { + _PERROR("%s", "cannot add clock to trace"); + goto error; + } + +error: + BT_PUT(clock); + + return ret; +} + +static +int visit_root_decl(struct ctx *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_typedef(ctx, + root_decl_node->u._typedef.type_specifier_list, + &root_decl_node->u._typedef.type_declarators); + if (ret) { + _PERROR("%s", "cannot add typedef in root scope"); + goto end; + } + break; + case NODE_TYPEALIAS: + ret = visit_typealias(ctx, root_decl_node->u.typealias.target, + root_decl_node->u.typealias.alias); + if (ret) { + _PERROR("%s", "cannot add typealias in root scope"); + goto end; + } + break; + case NODE_TYPE_SPECIFIER_LIST: + { + _BT_CTF_FIELD_TYPE_INIT(decl); + + /* + * Just add the type specifier to the root + * declaration scope. Put local reference. + */ + ret = visit_type_specifier_list(ctx, root_decl_node, &decl); + if (ret) { + assert(!decl); + goto end; + } + + BT_PUT(decl); + break; + } + default: + ret = -EPERM; + goto end; + } + +end: + return ret; +} + +static +int add_stream_classes_to_trace(struct ctx *ctx) +{ + int ret; + GHashTableIter iter; + gpointer key, stream_class; + + g_hash_table_iter_init(&iter, ctx->stream_classes); + + while (g_hash_table_iter_next(&iter, &key, &stream_class)) { + ret = bt_ctf_trace_add_stream_class(ctx->trace, + stream_class); + if (ret) { + int64_t id = bt_ctf_stream_class_get_id(stream_class); + _PERROR("cannot add stream class %" PRId64 " to trace", + id); + goto end; + } + } + +end: + return ret; +} + +int ctf_visitor_generate_ir(FILE *efd, struct ctf_node *node, + struct bt_ctf_trace **trace) +{ + int ret = 0; + struct ctx *ctx = NULL; + _BT_CTF_FIELD_TYPE_INIT(packet_header_decl); + + printf_verbose("CTF visitor: AST -> CTF IR...\n"); + + *trace = bt_ctf_trace_create(); + if (!*trace) { + _FPERROR(efd, "%s", "cannot create trace"); + ret = -ENOMEM; + goto error; + } + + /* Set packet header to an empty struct tu override the default one */ + packet_header_decl = bt_ctf_field_type_structure_create(); + + if (!packet_header_decl) { + _FPERROR(efd, + "%s", + "cannot create initial, empty packet header structure"); + ret = -ENOMEM; + goto error; + } + + ret = bt_ctf_trace_set_packet_header_type(*trace, packet_header_decl); + BT_PUT(packet_header_decl); + if (ret) { + _FPERROR(efd, + "%s", + "cannot set initial, empty packet header structure"); + goto error; + } + + ctx = ctx_create(*trace, efd); + if (!ctx) { + _FPERROR(efd, "%s", "cannot create visitor context"); + ret = -ENOMEM; + goto error; + } + + switch (node->type) { + case NODE_ROOT: + { + struct ctf_node *iter; + int got_trace_decl = FALSE; + int found_callsite = FALSE; + + /* + * Find trace declaration's byte order first (for early + * type aliases). + */ + bt_list_for_each_entry(iter, &node->u.root.trace, siblings) { + if (got_trace_decl) { + _PERROR("%s", "duplicate trace declaration"); + goto error; + } + + ret = set_trace_byte_order(ctx, iter); + if (ret) { + _PERROR("cannot set trace's byte order (%d)", + ret); + goto error; + } + + got_trace_decl = TRUE; + } + + if (!got_trace_decl) { + _PERROR("no trace declaration found (%d)", ret); + ret = -EPERM; + goto error; + } + + /* + * Visit clocks first since any early integer can be mapped + * to one. + */ + bt_list_for_each_entry(iter, &node->u.root.clock, siblings) { + ret = visit_clock_decl(ctx, iter); + if (ret) { + _PERROR("error while visiting clock declaration (%d)", + ret); + goto error; + } + } + + /* + * 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) { + _PERROR("error while visiting root declaration (%d)", + ret); + goto error; + } + } + + /* Callsite are not supported */ + bt_list_for_each_entry(iter, &node->u.root.callsite, siblings) { + found_callsite = TRUE; + break; + } + + if (found_callsite) { + _PWARNING("%s", "\"callsite\" blocks are not supported as of this version"); + } + + /* Environment */ + bt_list_for_each_entry(iter, &node->u.root.env, siblings) { + ret = visit_env(ctx, iter); + if (ret) { + _PERROR("error while visiting environment block (%d)", + ret); + goto error; + } + } + + /* Trace */ + bt_list_for_each_entry(iter, &node->u.root.trace, siblings) { + ret = visit_trace_decl(ctx, iter); + if (ret) { + _PERROR("%s", "error while visiting trace declaration"); + goto error; + } + } + + /* Streams */ + bt_list_for_each_entry(iter, &node->u.root.stream, siblings) { + ret = visit_stream_decl(ctx, iter); + if (ret) { + _PERROR("%s", "error while visiting stream declaration"); + goto error; + } + } + + /* Events */ + bt_list_for_each_entry(iter, &node->u.root.event, siblings) { + ret = visit_event_decl(ctx, iter); + if (ret) { + _PERROR("%s", "error while visiting event declaration"); + goto error; + } + } + break; + } + case NODE_UNKNOWN: + default: + _PERROR("unknown node type: %d", (int) node->type); + ret = -EINVAL; + goto error; + } + + /* Add stream classes to trace now */ + ret = add_stream_classes_to_trace(ctx); + if (ret) { + _PERROR("%s", "cannot add stream classes to trace"); + } + + ctx_destroy(ctx); + printf_verbose("done!\n"); + + return ret; + +error: + BT_PUT(packet_header_decl); + ctx_destroy(ctx); + BT_PUT(*trace); + + return ret; +} diff --git a/plugins/ctf/common/metadata/visitor-parent-links.c b/plugins/ctf/common/metadata/visitor-parent-links.c new file mode 100644 index 00000000..4a017fc8 --- /dev/null +++ b/plugins/ctf/common/metadata/visitor-parent-links.c @@ -0,0 +1,461 @@ +/* + * ctf-visitor-parent-links.c + * + * Common Trace Format Metadata Parent Link Creator. + * + * Copyright 2010 - Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "scanner.h" +#include "parser.h" +#include "ast.h" + +#define fprintf_dbg(fd, fmt, args...) fprintf(fd, "%s: " fmt, __func__, ## args) + +static +int ctf_visitor_unary_expression(FILE *fd, int depth, struct ctf_node *node) +{ + int ret = 0; + + switch (node->u.unary_expression.link) { + case UNARY_LINK_UNKNOWN: + case UNARY_DOTLINK: + case UNARY_ARROWLINK: + case UNARY_DOTDOTDOT: + break; + default: + fprintf(fd, "[error] %s: unknown expression link type %d\n", __func__, + (int) 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(fd, depth + 1, + node->u.unary_expression.u.sbrac_exp); + if (ret) + return ret; + break; + + case UNARY_UNKNOWN: + default: + fprintf(fd, "[error] %s: unknown expression type %d\n", __func__, + (int) node->u.unary_expression.type); + return -EINVAL; + } + return 0; +} + +static +int ctf_visitor_type_specifier(FILE *fd, int depth, struct ctf_node *node) +{ + int ret; + + switch (node->u.type_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.type_specifier.node->parent = node; + ret = ctf_visitor_parent_links(fd, depth + 1, node->u.type_specifier.node); + if (ret) + return ret; + break; + + case TYPESPEC_UNKNOWN: + default: + fprintf(fd, "[error] %s: unknown type specifier %d\n", __func__, + (int) node->u.type_specifier.type); + return -EINVAL; + } + return 0; +} + +static +int ctf_visitor_type_declarator(FILE *fd, int depth, struct ctf_node *node) +{ + int ret = 0; + struct ctf_node *iter; + + depth++; + + bt_list_for_each_entry(iter, &node->u.type_declarator.pointers, + siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(fd, depth + 1, iter); + if (ret) + return ret; + } + + switch (node->u.type_declarator.type) { + case TYPEDEC_ID: + break; + case TYPEDEC_NESTED: + if (node->u.type_declarator.u.nested.type_declarator) { + node->u.type_declarator.u.nested.type_declarator->parent = node; + ret = ctf_visitor_parent_links(fd, depth + 1, + node->u.type_declarator.u.nested.type_declarator); + if (ret) + return ret; + } + if (!node->u.type_declarator.u.nested.abstract_array) { + bt_list_for_each_entry(iter, &node->u.type_declarator.u.nested.length, + siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(fd, depth + 1, iter); + if (ret) + return ret; + } + } + if (node->u.type_declarator.bitfield_len) { + node->u.type_declarator.bitfield_len = node; + ret = ctf_visitor_parent_links(fd, depth + 1, + node->u.type_declarator.bitfield_len); + if (ret) + return ret; + } + break; + case TYPEDEC_UNKNOWN: + default: + fprintf(fd, "[error] %s: unknown type declarator %d\n", __func__, + (int) node->u.type_declarator.type); + return -EINVAL; + } + depth--; + return 0; +} + +int ctf_visitor_parent_links(FILE *fd, int depth, struct ctf_node *node) +{ + 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(fd, depth + 1, iter); + if (ret) + return ret; + } + bt_list_for_each_entry(iter, &node->u.root.trace, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(fd, depth + 1, iter); + if (ret) + return ret; + } + bt_list_for_each_entry(iter, &node->u.root.stream, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(fd, depth + 1, iter); + if (ret) + return ret; + } + bt_list_for_each_entry(iter, &node->u.root.event, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(fd, depth + 1, iter); + if (ret) + return ret; + } + bt_list_for_each_entry(iter, &node->u.root.clock, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(fd, depth + 1, iter); + if (ret) + return ret; + } + bt_list_for_each_entry(iter, &node->u.root.callsite, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(fd, depth + 1, iter); + 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(fd, depth + 1, iter); + 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(fd, depth + 1, iter); + 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(fd, depth + 1, iter); + 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(fd, depth + 1, iter); + 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(fd, depth + 1, iter); + 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(fd, depth + 1, iter); + 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(fd, depth + 1, iter); + if (ret) + return ret; + } + bt_list_for_each_entry(iter, &node->u.ctf_expression.right, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(fd, depth + 1, iter); + if (ret) + return ret; + } + depth--; + break; + case NODE_UNARY_EXPRESSION: + return ctf_visitor_unary_expression(fd, depth, node); + + case NODE_TYPEDEF: + depth++; + node->u._typedef.type_specifier_list->parent = node; + ret = ctf_visitor_parent_links(fd, depth + 1, node->u._typedef.type_specifier_list); + if (ret) + return ret; + bt_list_for_each_entry(iter, &node->u._typedef.type_declarators, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(fd, depth + 1, iter); + if (ret) + return ret; + } + depth--; + break; + case NODE_TYPEALIAS_TARGET: + depth++; + node->u.typealias_target.type_specifier_list->parent = node; + ret = ctf_visitor_parent_links(fd, depth + 1, node->u.typealias_target.type_specifier_list); + if (ret) + return ret; + bt_list_for_each_entry(iter, &node->u.typealias_target.type_declarators, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(fd, depth + 1, iter); + if (ret) + return ret; + } + depth--; + break; + case NODE_TYPEALIAS_ALIAS: + depth++; + node->u.typealias_alias.type_specifier_list->parent = node; + ret = ctf_visitor_parent_links(fd, depth + 1, node->u.typealias_alias.type_specifier_list); + if (ret) + return ret; + bt_list_for_each_entry(iter, &node->u.typealias_alias.type_declarators, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(fd, depth + 1, iter); + if (ret) + return ret; + } + depth--; + break; + case NODE_TYPEALIAS: + node->u.typealias.target->parent = node; + ret = ctf_visitor_parent_links(fd, depth + 1, node->u.typealias.target); + if (ret) + return ret; + node->u.typealias.alias->parent = node; + ret = ctf_visitor_parent_links(fd, depth + 1, node->u.typealias.alias); + if (ret) + return ret; + break; + + case NODE_TYPE_SPECIFIER_LIST: + bt_list_for_each_entry(iter, &node->u.type_specifier_list.head, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(fd, depth + 1, iter); + if (ret) + return ret; + } + break; + + case NODE_TYPE_SPECIFIER: + ret = ctf_visitor_type_specifier(fd, depth, node); + if (ret) + return ret; + break; + case NODE_POINTER: + break; + case NODE_TYPE_DECLARATOR: + ret = ctf_visitor_type_declarator(fd, depth, node); + 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(fd, depth + 1, iter); + 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(fd, depth + 1, iter); + 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(fd, depth + 1, iter); + 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(fd, depth + 1, iter); + if (ret) + return ret; + } + break; + case NODE_ENUM: + depth++; + if (node->u._enum.container_type) { + ret = ctf_visitor_parent_links(fd, depth + 1, node->u._enum.container_type); + if (ret) + return ret; + } + + bt_list_for_each_entry(iter, &node->u._enum.enumerator_list, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(fd, depth + 1, iter); + if (ret) + return ret; + } + depth--; + break; + case NODE_STRUCT_OR_VARIANT_DECLARATION: + node->u.struct_or_variant_declaration.type_specifier_list->parent = node; + ret = ctf_visitor_parent_links(fd, depth + 1, + node->u.struct_or_variant_declaration.type_specifier_list); + if (ret) + return ret; + bt_list_for_each_entry(iter, &node->u.struct_or_variant_declaration.type_declarators, siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(fd, depth + 1, iter); + 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(fd, depth + 1, iter); + 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(fd, depth + 1, iter); + if (ret) + return ret; + } + bt_list_for_each_entry(iter, &node->u._struct.min_align, + siblings) { + iter->parent = node; + ret = ctf_visitor_parent_links(fd, depth + 1, iter); + if (ret) + return ret; + } + break; + + case NODE_UNKNOWN: + default: + fprintf(fd, "[error] %s: unknown node type %d\n", __func__, + (int) node->type); + return -EINVAL; + } + return ret; +} diff --git a/plugins/ctf/common/metadata/visitor-semantic-validator.c b/plugins/ctf/common/metadata/visitor-semantic-validator.c new file mode 100644 index 00000000..bf00008d --- /dev/null +++ b/plugins/ctf/common/metadata/visitor-semantic-validator.c @@ -0,0 +1,978 @@ +/* + * ctf-visitor-semantic-validator.c + * + * Common Trace Format Metadata Semantic Validator. + * + * Copyright 2010 - Mathieu Desnoyers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "scanner.h" +#include "parser.h" +#include "ast.h" + +#define _bt_list_first_entry(ptr, type, member) \ + bt_list_entry((ptr)->next, type, member) + +#define fprintf_dbg(fd, fmt, args...) fprintf(fd, "%s: " fmt, __func__, ## args) + +static +int _ctf_visitor_semantic_check(FILE *fd, int depth, struct ctf_node *node); + +static +int ctf_visitor_unary_expression(FILE *fd, int depth, struct ctf_node *node) +{ + 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) { + fprintf(fd, "[error]: semantic error (left child of a ctf expression is only allowed to be a string)\n"); + + 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: + fprintf(fd, "[error]: semantic error (children of type declarator and enum can only be unsigned numeric constants or references to fields (a.b.c))\n"); + 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: + fprintf(fd, "[error]: semantic error (structure alignment attribute can only be unsigned numeric constants)\n"); + 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. + */ + fprintf(fd, "[error]: semantic error (nested unary expressions not allowed ( () and [] ))\n"); + 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) { + fprintf(fd, "[error]: semantic error (empty link not allowed except on first node of unary expression (need to separate nodes with \".\" or \"->\")\n"); + 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) { + fprintf(fd, "[error]: semantic error (links \".\" and \"->\" are only allowed as children of ctf expression)\n"); + goto errperm; + } + /* + * Only strings can be separated linked by . or ->. + * This includes "", '' and non-quoted identifiers. + */ + if (node->u.unary_expression.type != UNARY_STRING) { + fprintf(fd, "[error]: semantic error (links \".\" and \"->\" are only allowed to separate strings and identifiers)\n"); + 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) { + fprintf(fd, "[error]: semantic error (links \".\" and \"->\" are not allowed before first node of the unary expression list)\n"); + goto errperm; + } + break; + case UNARY_DOTDOTDOT: + /* We only allow ... link between children of enumerator. */ + if (node->parent->type != NODE_ENUMERATOR) { + fprintf(fd, "[error]: semantic error (link \"...\" is only allowed within enumerator)\n"); + 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) { + fprintf(fd, "[error]: semantic error (link \"...\" is not allowed on the first node of the unary expression list)\n"); + goto errperm; + } + break; + default: + fprintf(fd, "[error] %s: unknown expression link type %d\n", __func__, + (int) node->u.unary_expression.link); + return -EINVAL; + } + return 0; + +errinval: + fprintf(fd, "[error] %s: incoherent parent type %s for node type %s\n", __func__, + node_type(node->parent), node_type(node)); + return -EINVAL; /* Incoherent structure */ + +errperm: + fprintf(fd, "[error] %s: semantic error (parent type %s for node type %s)\n", __func__, + node_type(node->parent), node_type(node)); + return -EPERM; /* Structure not allowed */ +} + +static +int ctf_visitor_type_specifier_list(FILE *fd, int depth, struct ctf_node *node) +{ + 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: + fprintf(fd, "[error] %s: incoherent parent type %s for node type %s\n", __func__, + node_type(node->parent), node_type(node)); + return -EINVAL; /* Incoherent structure */ +} + +static +int ctf_visitor_type_specifier(FILE *fd, int depth, struct ctf_node *node) +{ + 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: + fprintf(fd, "[error] %s: incoherent parent type %s for node type %s\n", __func__, + node_type(node->parent), node_type(node)); + return -EINVAL; /* Incoherent structure */ +} + +static +int ctf_visitor_type_declarator(FILE *fd, int depth, struct ctf_node *node) +{ + int ret = 0; + struct ctf_node *iter; + + depth++; + + switch (node->parent->type) { + case NODE_TYPE_DECLARATOR: + /* + * A nested type declarator is not allowed to contain pointers. + */ + if (!bt_list_empty(&node->u.type_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 typealias). + * NOT accepting alias with identifier. The declarator should + * be either empty or contain pointer(s). + */ + if (node->u.type_declarator.type == TYPEDEC_NESTED) + goto errperm; + bt_list_for_each_entry(iter, &node->parent->u.typealias_alias.type_specifier_list->u.type_specifier_list.head, + siblings) { + switch (iter->u.type_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.type_declarator.pointers)) + goto errperm; + break; + default: + break; + } + } + if (node->u.type_declarator.type == TYPEDEC_ID && + node->u.type_declarator.u.id != NULL) + 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.type_declarator.pointers, + siblings) { + ret = _ctf_visitor_semantic_check(fd, depth + 1, iter); + if (ret) + return ret; + } + + switch (node->u.type_declarator.type) { + case TYPEDEC_ID: + break; + case TYPEDEC_NESTED: + { + if (node->u.type_declarator.u.nested.type_declarator) { + ret = _ctf_visitor_semantic_check(fd, depth + 1, + node->u.type_declarator.u.nested.type_declarator); + if (ret) + return ret; + } + if (!node->u.type_declarator.u.nested.abstract_array) { + bt_list_for_each_entry(iter, &node->u.type_declarator.u.nested.length, + siblings) { + if (iter->type != NODE_UNARY_EXPRESSION) { + fprintf(fd, "[error] %s: expecting unary expression as length\n", __func__); + return -EINVAL; + } + ret = _ctf_visitor_semantic_check(fd, depth + 1, iter); + if (ret) + return ret; + } + } else { + if (node->parent->type == NODE_TYPEALIAS_TARGET) { + fprintf(fd, "[error] %s: abstract array declarator not permitted as target of typealias\n", __func__); + return -EINVAL; + } + } + if (node->u.type_declarator.bitfield_len) { + ret = _ctf_visitor_semantic_check(fd, depth + 1, + node->u.type_declarator.bitfield_len); + if (ret) + return ret; + } + break; + } + case TYPEDEC_UNKNOWN: + default: + fprintf(fd, "[error] %s: unknown type declarator %d\n", __func__, + (int) node->u.type_declarator.type); + return -EINVAL; + } + depth--; + return 0; + +errinval: + fprintf(fd, "[error] %s: incoherent parent type %s for node type %s\n", __func__, + node_type(node->parent), node_type(node)); + return -EINVAL; /* Incoherent structure */ + +errperm: + fprintf(fd, "[error] %s: semantic error (parent type %s for node type %s)\n", __func__, + node_type(node->parent), node_type(node)); + return -EPERM; /* Structure not allowed */ +} + +static +int _ctf_visitor_semantic_check(FILE *fd, int depth, struct ctf_node *node) +{ + 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(fd, depth + 1, iter); + if (ret) + return ret; + } + bt_list_for_each_entry(iter, &node->u.root.trace, siblings) { + ret = _ctf_visitor_semantic_check(fd, depth + 1, iter); + if (ret) + return ret; + } + bt_list_for_each_entry(iter, &node->u.root.stream, siblings) { + ret = _ctf_visitor_semantic_check(fd, depth + 1, iter); + if (ret) + return ret; + } + bt_list_for_each_entry(iter, &node->u.root.event, siblings) { + ret = _ctf_visitor_semantic_check(fd, depth + 1, iter); + 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(fd, depth + 1, iter); + 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(fd, depth + 1, iter); + 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(fd, depth + 1, iter); + 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(fd, depth + 1, iter); + 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(fd, depth + 1, iter); + 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(fd, depth + 1, iter); + 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(fd, depth + 1, iter); + if (ret) + return ret; + } + bt_list_for_each_entry(iter, &node->u.ctf_expression.right, siblings) { + ret = _ctf_visitor_semantic_check(fd, depth + 1, iter); + if (ret) + return ret; + } + depth--; + break; + case NODE_UNARY_EXPRESSION: + return ctf_visitor_unary_expression(fd, depth, node); + + 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(fd, depth + 1, + node->u._typedef.type_specifier_list); + if (ret) + return ret; + bt_list_for_each_entry(iter, &node->u._typedef.type_declarators, siblings) { + ret = _ctf_visitor_semantic_check(fd, depth + 1, iter); + 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(fd, depth + 1, + node->u.typealias_target.type_specifier_list); + if (ret) + return ret; + nr_declarators = 0; + bt_list_for_each_entry(iter, &node->u.typealias_target.type_declarators, siblings) { + ret = _ctf_visitor_semantic_check(fd, depth + 1, iter); + if (ret) + return ret; + nr_declarators++; + } + if (nr_declarators > 1) { + fprintf(fd, "[error] %s: Too many declarators in typealias alias (%d, max is 1)\n", __func__, 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(fd, depth + 1, + node->u.typealias_alias.type_specifier_list); + if (ret) + return ret; + nr_declarators = 0; + bt_list_for_each_entry(iter, &node->u.typealias_alias.type_declarators, siblings) { + ret = _ctf_visitor_semantic_check(fd, depth + 1, iter); + if (ret) + return ret; + nr_declarators++; + } + if (nr_declarators > 1) { + fprintf(fd, "[error] %s: Too many declarators in typealias alias (%d, max is 1)\n", __func__, 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(fd, depth + 1, node->u.typealias.target); + if (ret) + return ret; + ret = _ctf_visitor_semantic_check(fd, depth + 1, node->u.typealias.alias); + if (ret) + return ret; + break; + + case NODE_TYPE_SPECIFIER_LIST: + ret = ctf_visitor_type_specifier_list(fd, depth, node); + if (ret) + return ret; + break; + case NODE_TYPE_SPECIFIER: + ret = ctf_visitor_type_specifier(fd, depth, node); + 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_type_declarator(fd, depth, node); + 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(fd, depth + 1, iter); + 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(fd, depth + 1, iter); + 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(fd, depth + 1, iter); + 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) { + fprintf(fd, "[error]: semantic error (first unary expression of enumerator is unexpected)\n"); + 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) { + fprintf(fd, "[error]: semantic error (second unary expression of enumerator is unexpected)\n"); + goto errperm; + } + break; + default: + goto errperm; + } + } + } + + bt_list_for_each_entry(iter, &node->u.enumerator.values, siblings) { + ret = _ctf_visitor_semantic_check(fd, depth + 1, iter); + 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(fd, depth + 1, node->u._enum.container_type); + if (ret) + return ret; + + bt_list_for_each_entry(iter, &node->u._enum.enumerator_list, siblings) { + ret = _ctf_visitor_semantic_check(fd, depth + 1, iter); + 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(fd, depth + 1, + node->u.struct_or_variant_declaration.type_specifier_list); + if (ret) + return ret; + bt_list_for_each_entry(iter, &node->u.struct_or_variant_declaration.type_declarators, siblings) { + ret = _ctf_visitor_semantic_check(fd, depth + 1, iter); + 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(fd, depth + 1, iter); + 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(fd, depth + 1, iter); + if (ret) + return ret; + } + break; + + case NODE_UNKNOWN: + default: + fprintf(fd, "[error] %s: unknown node type %d\n", __func__, + (int) node->type); + return -EINVAL; + } + return ret; + +errinval: + fprintf(fd, "[error] %s: incoherent parent type %s for node type %s\n", __func__, + node_type(node->parent), node_type(node)); + return -EINVAL; /* Incoherent structure */ + +errperm: + fprintf(fd, "[error] %s: semantic error (parent type %s for node type %s)\n", __func__, + node_type(node->parent), node_type(node)); + return -EPERM; /* Structure not allowed */ +} + +int ctf_visitor_semantic_check(FILE *fd, int depth, struct ctf_node *node) +{ + 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. + */ + printf_verbose("CTF visitor: parent links creation... "); + ret = ctf_visitor_parent_links(fd, depth, node); + if (ret) + return ret; + printf_verbose("done.\n"); + printf_verbose("CTF visitor: semantic check... "); + ret = _ctf_visitor_semantic_check(fd, depth, node); + if (ret) + return ret; + printf_verbose("done.\n"); + return ret; +} diff --git a/plugins/ctf/common/notif-iter/Makefile.am b/plugins/ctf/common/notif-iter/Makefile.am new file mode 100644 index 00000000..e9c69311 --- /dev/null +++ b/plugins/ctf/common/notif-iter/Makefile.am @@ -0,0 +1,6 @@ +AM_CFLAGS = $(PACKAGE_CFLAGS) +AM_CPPFLAGS = -I$(top_srcdir)/include + +noinst_LTLIBRARIES = libctf-notif-iter.la + +libctf_notif_iter_la_SOURCES = notif-iter.c diff --git a/plugins/ctf/common/notif-iter/notif-iter.c b/plugins/ctf/common/notif-iter/notif-iter.c new file mode 100644 index 00000000..37b5da02 --- /dev/null +++ b/plugins/ctf/common/notif-iter/notif-iter.c @@ -0,0 +1,1938 @@ +/* + * Babeltrace - CTF notification iterator + * + * Copyright (c) 2015-2016 EfficiOS Inc. and Linux Foundation + * Copyright (c) 2015-2016 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PRINT_ERR_STREAM notit->err_stream +#define PRINT_PREFIX "ctf-notif-iter" +#include "print.h" + +#include "notif-iter.h" +#include "../btr/btr.h" + +#define BYTES_TO_BITS(x) ((x) * 8) + +struct bt_ctf_notif_iter; + +/* A visit stack entry */ +struct stack_entry { + /* + * Current base field, one of: + * + * * string + * * structure + * * array + * * sequence + * * variant + * + * Field is owned by this. + */ + struct bt_ctf_field *base; + + /* index of next field to set */ + size_t index; +}; + +/* Visit stack */ +struct stack { + /* Entries (struct stack_entry *) (top is last element) */ + GPtrArray *entries; + + /* Link to owner */ + struct bt_ctf_notif_iter *notit; +}; + +/* State */ +enum state { + STATE_INIT, + 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_NOTIF_NEW_PACKET, + STATE_DSCOPE_STREAM_EVENT_HEADER_BEGIN, + STATE_DSCOPE_STREAM_EVENT_HEADER_CONTINUE, + STATE_AFTER_STREAM_EVENT_HEADER, + STATE_DSCOPE_STREAM_EVENT_CONTEXT_BEGIN, + STATE_DSCOPE_STREAM_EVENT_CONTEXT_CONTINUE, + STATE_DSCOPE_EVENT_CONTEXT_BEGIN, + STATE_DSCOPE_EVENT_CONTEXT_CONTINUE, + STATE_DSCOPE_EVENT_PAYLOAD_BEGIN, + STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE, + STATE_EMIT_NOTIF_EVENT, + STATE_EMIT_NOTIF_END_OF_PACKET, + STATE_SKIP_PACKET_PADDING, +}; + +/* CTF notification iterator */ +struct bt_ctf_notif_iter { + /* Visit stack */ + struct stack *stack; + + /* Error stream (may be NULL) */ + FILE *err_stream; + + /* + * Current dynamic scope field pointer. + * + * This is set when a dynamic scope field is first created by + * btr_compound_begin_cb(). It points to one of the fields in + * dscopes below. + */ + struct bt_ctf_field **cur_dscope_field; + + /* Trace and classes (owned by this) */ + struct { + struct bt_ctf_trace *trace; + struct bt_ctf_stream_class *stream_class; + struct bt_ctf_event_class *event_class; + } meta; + + /* Current packet (NULL if not created yet) */ + struct bt_ctf_packet *packet; + + /* Database of current dynamic scopes (owned by this) */ + struct { + struct bt_ctf_field *trace_packet_header; + struct bt_ctf_field *stream_packet_context; + struct bt_ctf_field *stream_event_header; + struct bt_ctf_field *stream_event_context; + struct bt_ctf_field *event_context; + struct bt_ctf_field *event_payload; + } dscopes; + + /* Current state */ + enum state state; + + /* User buffer stuff */ + 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; + } buf; + + /* Binary type reader */ + struct bt_ctf_btr *btr; + + /* Medium stuff */ + struct { + struct bt_ctf_notif_iter_medium_ops medops; + size_t max_request_sz; + void *data; + } medium; + + /* Current packet size (bits) (-1 if unknown) */ + size_t cur_packet_size; + + /* Current content size (bits) (-1 if unknown) */ + size_t cur_content_size; +}; + +static +void stack_entry_free_func(gpointer data) +{ + struct stack_entry *entry = data; + + bt_put(entry->base); + g_free(entry); +} + +static +struct stack *stack_new(struct bt_ctf_notif_iter *notit) +{ + struct stack *stack = NULL; + + stack = g_new0(struct stack, 1); + if (!stack) { + goto error; + } + + stack->entries = g_ptr_array_new_with_free_func(stack_entry_free_func); + if (!stack->entries) { + goto error; + } + + stack->notit = notit; + + return stack; + +error: + g_free(stack); + + return NULL; +} + +static +void stack_destroy(struct stack *stack) +{ + assert(stack); + g_ptr_array_free(stack->entries, TRUE); + g_free(stack); +} + +static +int stack_push(struct stack *stack, struct bt_ctf_field *base) +{ + int ret = 0; + struct stack_entry *entry; + struct bt_ctf_notif_iter *notit; + + assert(stack); + assert(base); + notit = stack->notit; + entry = g_new0(struct stack_entry, 1); + if (!entry) { + PERR("Cannot create new stack entry\n"); + ret = -1; + goto end; + } + + entry->base = bt_get(base); + g_ptr_array_add(stack->entries, entry); + +end: + return ret; +} + +static inline +unsigned int stack_size(struct stack *stack) +{ + assert(stack); + + return stack->entries->len; +} + +static +void stack_pop(struct stack *stack) +{ + assert(stack); + assert(stack_size(stack)); + g_ptr_array_remove_index(stack->entries, stack->entries->len - 1); +} + +static inline +struct stack_entry *stack_top(struct stack *stack) +{ + assert(stack); + assert(stack_size(stack)); + + return g_ptr_array_index(stack->entries, stack->entries->len - 1); +} + +static inline +bool stack_empty(struct stack *stack) +{ + return stack_size(stack) == 0; +} + +static +void stack_clear(struct stack *stack) +{ + assert(stack); + + if (!stack_empty(stack)) { + g_ptr_array_remove_range(stack->entries, 0, stack_size(stack)); + } + + assert(stack_empty(stack)); +} + +static inline +enum bt_ctf_notif_iter_status notif_iter_status_from_m_status( + enum bt_ctf_notif_iter_medium_status m_status) +{ + return m_status; +} + +static inline +size_t buf_size_bits(struct bt_ctf_notif_iter *notit) +{ + return BYTES_TO_BITS(notit->buf.sz); +} + +static inline +size_t buf_available_bits(struct bt_ctf_notif_iter *notit) +{ + return buf_size_bits(notit) - notit->buf.at; +} + +static inline +size_t packet_at(struct bt_ctf_notif_iter *notit) +{ + return notit->buf.packet_offset + notit->buf.at; +} + +static inline +size_t remaining_content_bits(struct bt_ctf_notif_iter *notit) +{ + if (notit->cur_content_size == -1) { + return -1; + } + + return notit->cur_content_size - packet_at(notit); +} + +static inline +size_t remaining_packet_bits(struct bt_ctf_notif_iter *notit) +{ + if (notit->cur_packet_size == -1) { + return -1; + } + + return notit->cur_packet_size - packet_at(notit); +} + +static inline +void buf_consume_bits(struct bt_ctf_notif_iter *notit, size_t incr) +{ + notit->buf.at += incr; +} + +static inline +bool buf_has_enough_bits(struct bt_ctf_notif_iter *notit, size_t sz) +{ + return buf_available_bits(notit) >= sz; +} + +static +enum bt_ctf_notif_iter_status request_medium_bytes(struct bt_ctf_notif_iter *notit) +{ + uint8_t *buffer_addr; + size_t buffer_sz; + enum bt_ctf_notif_iter_medium_status m_status; + + m_status = notit->medium.medops.request_bytes( + notit->medium.max_request_sz, &buffer_addr, + &buffer_sz, notit->medium.data); + if (m_status == BT_CTF_NOTIF_ITER_MEDIUM_STATUS_OK) { + assert(buffer_sz != 0); + + /* New packet offset is old one + old size (in bits) */ + notit->buf.packet_offset += buf_size_bits(notit); + + /* Restart at the beginning of the new medium buffer */ + notit->buf.at = 0; + + /* New medium buffer size */ + notit->buf.sz = buffer_sz; + + /* New medium buffer address */ + notit->buf.addr = buffer_addr; + } + + return notif_iter_status_from_m_status(m_status); +} + +static inline +enum bt_ctf_notif_iter_status buf_ensure_available_bits( + struct bt_ctf_notif_iter *notit) +{ + enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK; + + if (buf_available_bits(notit) == 0) { + /* + * This _cannot_ return BT_CTF_NOTIF_ITER_STATUS_OK + * _and_ no bits. + */ + status = request_medium_bytes(notit); + } + + return status; +} + +static +enum bt_ctf_notif_iter_status read_dscope_begin_state( + struct bt_ctf_notif_iter *notit, + struct bt_ctf_field_type *dscope_field_type, + enum state done_state, enum state continue_state, + struct bt_ctf_field **dscope_field) +{ + enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK; + enum bt_ctf_btr_status btr_status; + size_t consumed_bits; + + status = buf_ensure_available_bits(notit); + if (status != BT_CTF_NOTIF_ITER_STATUS_OK) { + goto end; + } + + bt_put(*dscope_field); + notit->cur_dscope_field = dscope_field; + consumed_bits = bt_ctf_btr_start(notit->btr, dscope_field_type, + notit->buf.addr, notit->buf.at, packet_at(notit), + notit->buf.sz, &btr_status); + + switch (btr_status) { + case BT_CTF_BTR_STATUS_OK: + /* type was read completely */ + notit->state = done_state; + break; + case BT_CTF_BTR_STATUS_EOF: + notit->state = continue_state; + break; + default: + PERR("Binary type reader failed to start\n"); + status = BT_CTF_NOTIF_ITER_STATUS_ERROR; + goto end; + } + + /* Consume bits now since we know we're not in an error state */ + buf_consume_bits(notit, consumed_bits); + +end: + return status; +} + +static +enum bt_ctf_notif_iter_status read_dscope_continue_state( + struct bt_ctf_notif_iter *notit, enum state done_state) +{ + enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK; + enum bt_ctf_btr_status btr_status; + size_t consumed_bits; + + status = buf_ensure_available_bits(notit); + if (status != BT_CTF_NOTIF_ITER_STATUS_OK) { + goto end; + } + + consumed_bits = bt_ctf_btr_continue(notit->btr, notit->buf.addr, + notit->buf.sz, &btr_status); + + switch (btr_status) { + case BT_CTF_BTR_STATUS_OK: + /* Type was read completely */ + notit->state = done_state; + break; + case BT_CTF_BTR_STATUS_EOF: + /* Stay in this continue state */ + break; + default: + PERR("Binary type reader failed to continue\n"); + status = BT_CTF_NOTIF_ITER_STATUS_ERROR; + goto end; + } + + /* Consume bits now since we know we're not in an error state */ + buf_consume_bits(notit, consumed_bits); + +end: + return status; +} + +static +void put_event_dscopes(struct bt_ctf_notif_iter *notit) +{ + BT_PUT(notit->dscopes.stream_event_header); + BT_PUT(notit->dscopes.stream_event_context); + BT_PUT(notit->dscopes.event_context); + BT_PUT(notit->dscopes.event_payload); +} + +static +void put_all_dscopes(struct bt_ctf_notif_iter *notit) +{ + BT_PUT(notit->dscopes.trace_packet_header); + BT_PUT(notit->dscopes.stream_packet_context); + put_event_dscopes(notit); +} + +static +enum bt_ctf_notif_iter_status read_packet_header_begin_state( + struct bt_ctf_notif_iter *notit) +{ + enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK; + struct bt_ctf_field_type *packet_header_type; + + /* Reset all dynamic scopes since we're reading a new packet */ + put_all_dscopes(notit); + BT_PUT(notit->packet); + BT_PUT(notit->meta.stream_class); + BT_PUT(notit->meta.event_class); + + /* Packet header type is common to the whole trace */ + packet_header_type = bt_ctf_trace_get_packet_header_type( + notit->meta.trace); + if (!packet_header_type) { + PERR("Failed to retrieve trace's packet header type\n"); + status = BT_CTF_NOTIF_ITER_STATUS_ERROR; + goto end; + } + + status = read_dscope_begin_state(notit, packet_header_type, + STATE_AFTER_TRACE_PACKET_HEADER, + STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE, + ¬it->dscopes.trace_packet_header); + +end: + BT_PUT(packet_header_type); + + return status; +} + +static +enum bt_ctf_notif_iter_status read_packet_header_continue_state( + struct bt_ctf_notif_iter *notit) +{ + return read_dscope_continue_state(notit, + STATE_AFTER_TRACE_PACKET_HEADER); +} + +static inline +bool is_struct_type(struct bt_ctf_field_type *field_type) +{ + return bt_ctf_field_type_get_type_id(field_type) == BT_CTF_TYPE_ID_STRUCT; +} + +static inline +bool is_variant_type(struct bt_ctf_field_type *field_type) +{ + return bt_ctf_field_type_get_type_id(field_type) == BT_CTF_TYPE_ID_VARIANT; +} + +static inline +enum bt_ctf_notif_iter_status set_current_stream_class(struct bt_ctf_notif_iter *notit) +{ + enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK; + struct bt_ctf_field_type *packet_header_type; + struct bt_ctf_field_type *stream_id_field_type = NULL; + uint64_t stream_id; + + /* Is there any "stream_id" field in the packet header? */ + packet_header_type = bt_ctf_trace_get_packet_header_type( + notit->meta.trace); + if (!packet_header_type) { + PERR("Failed to retrieve trace's packet header type\n"); + status = BT_CTF_NOTIF_ITER_STATUS_ERROR; + goto end; + } + + assert(is_struct_type(packet_header_type)); + + // TODO: optimalize! + stream_id_field_type = + bt_ctf_field_type_structure_get_field_type_by_name( + packet_header_type, "stream_id"); + if (stream_id_field_type) { + /* Find appropriate stream class using current stream ID */ + struct bt_ctf_field *stream_id_field = NULL; + int ret; + + assert(notit->dscopes.trace_packet_header); + + // TODO: optimalize! + stream_id_field = bt_ctf_field_structure_get_field( + notit->dscopes.trace_packet_header, "stream_id"); + assert(stream_id_field); + ret = bt_ctf_field_unsigned_integer_get_value( + stream_id_field, &stream_id); + assert(!ret); + BT_PUT(stream_id_field); + } else { + /* Only one stream: pick the first stream class */ + assert(bt_ctf_trace_get_stream_class_count( + notit->meta.trace) == 1); + stream_id = 0; + } + + BT_PUT(notit->meta.stream_class); + + // TODO: get by ID + notit->meta.stream_class = bt_ctf_trace_get_stream_class( + notit->meta.trace, stream_id); + if (!notit->meta.stream_class) { + PERR("Cannot find stream class with ID %" PRIu64 "\n", + stream_id); + status = BT_CTF_NOTIF_ITER_STATUS_ERROR; + goto end; + } + +end: + BT_PUT(packet_header_type); + BT_PUT(stream_id_field_type); + + return status; +} + +static +enum bt_ctf_notif_iter_status after_packet_header_state( + struct bt_ctf_notif_iter *notit) +{ + enum bt_ctf_notif_iter_status status; + + status = set_current_stream_class(notit); + if (status == BT_CTF_NOTIF_ITER_STATUS_OK) { + notit->state = STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN; + } + + return status; +} + +static +enum bt_ctf_notif_iter_status read_packet_context_begin_state( + struct bt_ctf_notif_iter *notit) +{ + enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK; + struct bt_ctf_field_type *packet_context_type; + + assert(notit->meta.stream_class); + packet_context_type = bt_ctf_stream_class_get_packet_context_type( + notit->meta.stream_class); + if (!packet_context_type) { + PERR("Failed to retrieve stream class's packet context\n"); + status = BT_CTF_NOTIF_ITER_STATUS_ERROR; + goto end; + } + + status = read_dscope_begin_state(notit, packet_context_type, + STATE_AFTER_STREAM_PACKET_CONTEXT, + STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE, + ¬it->dscopes.stream_packet_context); + +end: + BT_PUT(packet_context_type); + + return status; +} + +static +enum bt_ctf_notif_iter_status read_packet_context_continue_state( + struct bt_ctf_notif_iter *notit) +{ + return read_dscope_continue_state(notit, + STATE_AFTER_STREAM_PACKET_CONTEXT); +} + +static inline +enum bt_ctf_notif_iter_status set_current_packet_content_sizes( + struct bt_ctf_notif_iter *notit) +{ + enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK; + struct bt_ctf_field *packet_size_field = NULL; + struct bt_ctf_field *content_size_field = NULL; + uint64_t content_size = -1, packet_size = -1; + + assert(notit->dscopes.stream_packet_context); + + // TODO: optimalize! + packet_size_field = bt_ctf_field_structure_get_field( + notit->dscopes.stream_packet_context, "packet_size"); + content_size_field = bt_ctf_field_structure_get_field( + notit->dscopes.stream_packet_context, "content_size"); + if (packet_size_field) { + int ret = bt_ctf_field_unsigned_integer_get_value( + packet_size_field, &packet_size); + assert(!ret); + + if (packet_size == 0) { + PERR("Decoded packet size is 0\n"); + status = BT_CTF_NOTIF_ITER_STATUS_ERROR; + goto end; + } else if ((packet_size % 8) != 0) { + PERR("Decoded packet size is not a multiple of 8\n"); + status = BT_CTF_NOTIF_ITER_STATUS_ERROR; + goto end; + } + } + if (content_size_field) { + int ret = bt_ctf_field_unsigned_integer_get_value( + content_size_field, &content_size); + assert(!ret); + } else { + content_size = packet_size; + } + + notit->cur_packet_size = packet_size; + notit->cur_content_size = content_size; + +end: + BT_PUT(packet_size_field); + BT_PUT(content_size_field); + + return status; +} + +static +enum bt_ctf_notif_iter_status after_packet_context_state( + struct bt_ctf_notif_iter *notit) +{ + enum bt_ctf_notif_iter_status status; + + status = set_current_packet_content_sizes(notit); + if (status == BT_CTF_NOTIF_ITER_STATUS_OK) { + notit->state = STATE_EMIT_NOTIF_NEW_PACKET; + } + + return status; +} + +static +enum bt_ctf_notif_iter_status read_event_header_begin_state( + struct bt_ctf_notif_iter *notit) +{ + enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK; + struct bt_ctf_field_type *event_header_type = NULL; + + /* Check if we have some content left */ + if (notit->cur_content_size >= 0) { + if (packet_at(notit) == notit->cur_content_size) { + /* No more events! */ + notit->state = STATE_EMIT_NOTIF_END_OF_PACKET; + goto end; + } else if (packet_at(notit) > notit->cur_content_size) { + /* That's not supposed to happen */ + PERR("Cursor passed packet's content size:\n"); + PERR(" Decoded content size: %zu\n", + notit->cur_content_size); + PERR(" Cursor position: %zu\n", packet_at(notit)); + status = BT_CTF_NOTIF_ITER_STATUS_ERROR; + goto end; + } + } + + event_header_type = bt_ctf_stream_class_get_event_header_type( + notit->meta.stream_class); + if (!event_header_type) { + PERR("Failed to retrieve stream class's event header type\n"); + status = BT_CTF_NOTIF_ITER_STATUS_ERROR; + goto end; + } + + put_event_dscopes(notit); + status = read_dscope_begin_state(notit, event_header_type, + STATE_AFTER_STREAM_EVENT_HEADER, + STATE_DSCOPE_STREAM_EVENT_HEADER_CONTINUE, + ¬it->dscopes.stream_event_header); + +end: + BT_PUT(event_header_type); + + return status; +} + +static +enum bt_ctf_notif_iter_status read_event_header_continue_state( + struct bt_ctf_notif_iter *notit) +{ + return read_dscope_continue_state(notit, + STATE_AFTER_STREAM_EVENT_HEADER); +} + +static inline +enum bt_ctf_notif_iter_status set_current_event_class(struct bt_ctf_notif_iter *notit) +{ + /* + * The assert() calls in this function are okay because it is + * assumed here that all the metadata objects have been + * validated for CTF correctness before decoding actual streams. + */ + + enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK; + struct bt_ctf_field_type *event_header_type; + struct bt_ctf_field_type *id_field_type = NULL; + struct bt_ctf_field_type *v_field_type = NULL; + uint64_t event_id = -1ULL; + int ret; + + event_header_type = bt_ctf_stream_class_get_event_header_type( + notit->meta.stream_class); + if (!event_header_type) { + PERR("Failed to retrieve stream class's event header type\n"); + status = BT_CTF_NOTIF_ITER_STATUS_ERROR; + goto end; + } + + /* Is there any "id"/"v" field in the event header? */ + assert(is_struct_type(event_header_type)); + id_field_type = bt_ctf_field_type_structure_get_field_type_by_name( + event_header_type, "id"); + v_field_type = bt_ctf_field_type_structure_get_field_type_by_name( + event_header_type, "v"); + assert(notit->dscopes.stream_event_header); + if (v_field_type) { + /* + * _ _____ _____ + * | | |_ _|_ _| __ __ _ + * | | | | | || '_ \ / _` | + * | |___| | | || | | | (_| | S P E C I A L + * |_____|_| |_||_| |_|\__, | C A S E ™ + * |___/ + */ + struct bt_ctf_field *v_field = NULL; + struct bt_ctf_field *v_struct_field = NULL; + struct bt_ctf_field *v_struct_id_field = NULL; + + // TODO: optimalize! + v_field = bt_ctf_field_structure_get_field( + notit->dscopes.stream_event_header, "v"); + assert(v_field); + + v_struct_field = + bt_ctf_field_variant_get_current_field(v_field); + if (!v_struct_field) { + goto end_v_field_type; + } + + // TODO: optimalize! + v_struct_id_field = + bt_ctf_field_structure_get_field(v_struct_field, "id"); + if (!v_struct_id_field) { + goto end_v_field_type; + } + + ret = bt_ctf_field_unsigned_integer_get_value( + v_struct_id_field, &event_id); + if (ret) { + event_id = -1ULL; + } + +end_v_field_type: + BT_PUT(v_field); + BT_PUT(v_struct_field); + BT_PUT(v_struct_id_field); + } + + if (id_field_type && event_id == -1ULL) { + /* Check "id" field */ + struct bt_ctf_field *id_field = NULL; + int ret; + + // TODO: optimalize! + id_field = bt_ctf_field_structure_get_field( + notit->dscopes.stream_event_header, "id"); + assert(id_field); + assert(bt_ctf_field_is_integer(id_field) || + bt_ctf_field_is_enumeration(id_field)); + + if (bt_ctf_field_is_integer(id_field)) { + ret = bt_ctf_field_unsigned_integer_get_value( + id_field, &event_id); + } else { + struct bt_ctf_field *container; + + container = bt_ctf_field_enumeration_get_container( + id_field); + assert(container); + ret = bt_ctf_field_unsigned_integer_get_value( + container, &event_id); + BT_PUT(container); + } + assert(!ret); + BT_PUT(id_field); + } + + if (event_id == -1ULL) { + /* Event ID not found: single event? */ + assert(bt_ctf_stream_class_get_event_class_count( + notit->meta.stream_class) == 1); + event_id = 0; + } + + BT_PUT(notit->meta.event_class); + notit->meta.event_class = bt_ctf_stream_class_get_event_class_by_id( + notit->meta.stream_class, event_id); + if (!notit->meta.event_class) { + PERR("Cannot find event class with ID %" PRIu64 "\n", event_id); + status = BT_CTF_NOTIF_ITER_STATUS_ERROR; + goto end; + } + +end: + BT_PUT(event_header_type); + BT_PUT(id_field_type); + BT_PUT(v_field_type); + + return status; +} + +static +enum bt_ctf_notif_iter_status after_event_header_state( + struct bt_ctf_notif_iter *notit) +{ + enum bt_ctf_notif_iter_status status; + + status = set_current_packet_content_sizes(notit); + if (status != BT_CTF_NOTIF_ITER_STATUS_OK) { + PERR("Failed to set current packet and content sizes\n"); + goto end; + } + + status = set_current_event_class(notit); + if (status != BT_CTF_NOTIF_ITER_STATUS_OK) { + PERR("Failed to set current event class\n"); + goto end; + } + + notit->state = STATE_DSCOPE_STREAM_EVENT_CONTEXT_BEGIN; + +end: + return status; +} + +static +enum bt_ctf_notif_iter_status read_stream_event_context_begin_state( + struct bt_ctf_notif_iter *notit) +{ + enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK; + struct bt_ctf_field_type *stream_event_context_type; + + stream_event_context_type = bt_ctf_stream_class_get_event_context_type( + notit->meta.stream_class); + if (!stream_event_context_type) { + PERR("Failed to retrieve stream class's event context type\n"); + status = BT_CTF_NOTIF_ITER_STATUS_ERROR; + goto end; + } + + status = read_dscope_begin_state(notit, stream_event_context_type, + STATE_DSCOPE_EVENT_CONTEXT_BEGIN, + STATE_DSCOPE_STREAM_EVENT_CONTEXT_CONTINUE, + ¬it->dscopes.stream_event_context); + +end: + BT_PUT(stream_event_context_type); + + return status; +} + +static +enum bt_ctf_notif_iter_status read_stream_event_context_continue_state( + struct bt_ctf_notif_iter *notit) +{ + return read_dscope_continue_state(notit, + STATE_DSCOPE_EVENT_CONTEXT_BEGIN); +} + +static +enum bt_ctf_notif_iter_status read_event_context_begin_state( + struct bt_ctf_notif_iter *notit) +{ + enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK; + struct bt_ctf_field_type *event_context_type; + + event_context_type = bt_ctf_event_class_get_context_type( + notit->meta.event_class); + if (!event_context_type) { + PERR("Failed to retrieve event class's context type\n"); + status = BT_CTF_NOTIF_ITER_STATUS_ERROR; + goto end; + } + + status = read_dscope_begin_state(notit, event_context_type, + STATE_DSCOPE_EVENT_PAYLOAD_BEGIN, + STATE_DSCOPE_EVENT_CONTEXT_CONTINUE, + ¬it->dscopes.event_context); + +end: + BT_PUT(event_context_type); + + return status; +} + +static +enum bt_ctf_notif_iter_status read_event_context_continue_state( + struct bt_ctf_notif_iter *notit) +{ + return read_dscope_continue_state(notit, + STATE_DSCOPE_EVENT_PAYLOAD_BEGIN); +} + +static +enum bt_ctf_notif_iter_status read_event_payload_begin_state( + struct bt_ctf_notif_iter *notit) +{ + enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK; + struct bt_ctf_field_type *event_payload_type; + + event_payload_type = bt_ctf_event_class_get_payload_type( + notit->meta.event_class); + if (!event_payload_type) { + PERR("Failed to retrieve event class's payload type\n"); + status = BT_CTF_NOTIF_ITER_STATUS_ERROR; + goto end; + } + + status = read_dscope_begin_state(notit, event_payload_type, + STATE_EMIT_NOTIF_EVENT, + STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE, + ¬it->dscopes.event_payload); + +end: + BT_PUT(event_payload_type); + + return status; +} + +static +enum bt_ctf_notif_iter_status read_event_payload_continue_state( + struct bt_ctf_notif_iter *notit) +{ + return read_dscope_continue_state(notit, STATE_EMIT_NOTIF_EVENT); +} + +static +enum bt_ctf_notif_iter_status skip_packet_padding_state( + struct bt_ctf_notif_iter *notit) +{ + enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK; + size_t bits_to_skip; + + assert(notit->cur_packet_size > 0); + bits_to_skip = notit->cur_packet_size - packet_at(notit); + if (bits_to_skip == 0) { + notit->state = STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN; + goto end; + } else { + size_t bits_to_consume; + status = buf_ensure_available_bits(notit); + if (status != BT_CTF_NOTIF_ITER_STATUS_OK) { + goto end; + } + + bits_to_consume = MIN(buf_available_bits(notit), bits_to_skip); + buf_consume_bits(notit, bits_to_consume); + bits_to_skip = notit->cur_packet_size - packet_at(notit); + if (bits_to_skip == 0) { + notit->state = STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN; + goto end; + } + } + +end: + return status; +} + +static inline +enum bt_ctf_notif_iter_status handle_state(struct bt_ctf_notif_iter *notit) +{ + enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK; + + PDBG("Handling state %d\n", notit->state); + + // TODO: optimalize! + switch (notit->state) { + case STATE_INIT: + notit->state = STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN; + break; + case STATE_DSCOPE_TRACE_PACKET_HEADER_BEGIN: + status = read_packet_header_begin_state(notit); + break; + case STATE_DSCOPE_TRACE_PACKET_HEADER_CONTINUE: + status = read_packet_header_continue_state(notit); + break; + case STATE_AFTER_TRACE_PACKET_HEADER: + status = after_packet_header_state(notit); + break; + case STATE_DSCOPE_STREAM_PACKET_CONTEXT_BEGIN: + status = read_packet_context_begin_state(notit); + break; + case STATE_DSCOPE_STREAM_PACKET_CONTEXT_CONTINUE: + status = read_packet_context_continue_state(notit); + break; + case STATE_AFTER_STREAM_PACKET_CONTEXT: + status = after_packet_context_state(notit); + break; + case STATE_EMIT_NOTIF_NEW_PACKET: + notit->state = STATE_DSCOPE_STREAM_EVENT_HEADER_BEGIN; + break; + case STATE_DSCOPE_STREAM_EVENT_HEADER_BEGIN: + status = read_event_header_begin_state(notit); + break; + case STATE_DSCOPE_STREAM_EVENT_HEADER_CONTINUE: + status = read_event_header_continue_state(notit); + break; + case STATE_AFTER_STREAM_EVENT_HEADER: + status = after_event_header_state(notit); + break; + case STATE_DSCOPE_STREAM_EVENT_CONTEXT_BEGIN: + status = read_stream_event_context_begin_state(notit); + break; + case STATE_DSCOPE_STREAM_EVENT_CONTEXT_CONTINUE: + status = read_stream_event_context_continue_state(notit); + break; + case STATE_DSCOPE_EVENT_CONTEXT_BEGIN: + status = read_event_context_begin_state(notit); + break; + case STATE_DSCOPE_EVENT_CONTEXT_CONTINUE: + status = read_event_context_continue_state(notit); + break; + case STATE_DSCOPE_EVENT_PAYLOAD_BEGIN: + status = read_event_payload_begin_state(notit); + break; + case STATE_DSCOPE_EVENT_PAYLOAD_CONTINUE: + status = read_event_payload_continue_state(notit); + break; + case STATE_EMIT_NOTIF_EVENT: + notit->state = STATE_DSCOPE_STREAM_EVENT_HEADER_BEGIN; + break; + case STATE_SKIP_PACKET_PADDING: + status = skip_packet_padding_state(notit); + break; + case STATE_EMIT_NOTIF_END_OF_PACKET: + notit->state = STATE_SKIP_PACKET_PADDING; + break; + } + + return status; +} + +void bt_ctf_notif_iter_reset(struct bt_ctf_notif_iter *notit) +{ + assert(notit); + stack_clear(notit->stack); + BT_PUT(notit->meta.stream_class); + BT_PUT(notit->meta.event_class); + BT_PUT(notit->packet); + put_all_dscopes(notit); + notit->buf.addr = NULL; + notit->buf.sz = 0; + notit->buf.at = 0; + notit->buf.packet_offset = 0; + notit->state = STATE_INIT; + notit->cur_content_size = -1; + notit->cur_packet_size = -1; +} + +static +struct bt_ctf_field *get_next_field(struct bt_ctf_notif_iter *notit) +{ + struct bt_ctf_field *next_field = NULL; + struct bt_ctf_field *base_field; + struct bt_ctf_field_type *base_type; + size_t index; + + assert(!stack_empty(notit->stack)); + index = stack_top(notit->stack)->index; + base_field = stack_top(notit->stack)->base; + base_type = bt_ctf_field_get_type(base_field); + if (!base_type) { + PERR("Failed to get base field's type\n"); + goto end; + } + + switch (bt_ctf_field_type_get_type_id(base_type)) { + case BT_CTF_TYPE_ID_STRUCT: + next_field = bt_ctf_field_structure_get_field_by_index( + base_field, index); + break; + case BT_CTF_TYPE_ID_ARRAY: + next_field = bt_ctf_field_array_get_field(base_field, index); + break; + case BT_CTF_TYPE_ID_SEQUENCE: + next_field = bt_ctf_field_sequence_get_field(base_field, index); + break; + case BT_CTF_TYPE_ID_VARIANT: + next_field = bt_ctf_field_variant_get_current_field(base_field); + break; + default: + assert(false); + break; + } + +end: + BT_PUT(base_type); + + return next_field; +} + +static +enum bt_ctf_btr_status btr_signed_int_cb(int64_t value, + struct bt_ctf_field_type *type, void *data) +{ + enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; + struct bt_ctf_field *field = NULL; + struct bt_ctf_field *int_field = NULL; + struct bt_ctf_notif_iter *notit = data; + int ret; + + /* create next field */ + field = get_next_field(notit); + if (!field) { + PERR("Failed to get next field (signed int)\n"); + status = BT_CTF_BTR_STATUS_ERROR; + goto end; + } + + switch(bt_ctf_field_type_get_type_id(type)) { + case BT_CTF_TYPE_ID_INTEGER: + /* Integer field is created field */ + BT_MOVE(int_field, field); + break; + case BT_CTF_TYPE_ID_ENUM: + int_field = bt_ctf_field_enumeration_get_container(field); + break; + default: + break; + } + + if (!int_field) { + PERR("Failed to get integer field\n"); + status = BT_CTF_BTR_STATUS_ERROR; + goto end; + } + + ret = bt_ctf_field_signed_integer_set_value(int_field, value); + assert(!ret); + stack_top(notit->stack)->index++; + +end: + BT_PUT(field); + BT_PUT(int_field); + + return status; +} + +static +enum bt_ctf_btr_status btr_unsigned_int_cb(uint64_t value, + struct bt_ctf_field_type *type, void *data) +{ + enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; + struct bt_ctf_field *field = NULL; + struct bt_ctf_field *int_field = NULL; + struct bt_ctf_notif_iter *notit = data; + int ret; + + /* Create next field */ + field = get_next_field(notit); + if (!field) { + PERR("Failed to get next field (unsigned int)\n"); + status = BT_CTF_BTR_STATUS_ERROR; + goto end; + } + + switch(bt_ctf_field_type_get_type_id(type)) { + case BT_CTF_TYPE_ID_INTEGER: + /* Integer field is created field */ + BT_MOVE(int_field, field); + break; + case BT_CTF_TYPE_ID_ENUM: + int_field = bt_ctf_field_enumeration_get_container(field); + break; + default: + break; + } + + if (!int_field) { + PERR("Failed to get integer field\n"); + status = BT_CTF_BTR_STATUS_ERROR; + goto end; + } + + ret = bt_ctf_field_unsigned_integer_set_value(int_field, value); + assert(!ret); + stack_top(notit->stack)->index++; + +end: + BT_PUT(field); + BT_PUT(int_field); + + return status; +} + +static +enum bt_ctf_btr_status btr_floating_point_cb(double value, + struct bt_ctf_field_type *type, void *data) +{ + enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; + struct bt_ctf_field *field = NULL; + struct bt_ctf_notif_iter *notit = data; + int ret; + + /* Create next field */ + field = get_next_field(notit); + if (!field) { + PERR("Failed to get next field (floating point number)\n"); + status = BT_CTF_BTR_STATUS_ERROR; + goto end; + } + + ret = bt_ctf_field_floating_point_set_value(field, value); + assert(!ret); + stack_top(notit->stack)->index++; + +end: + BT_PUT(field); + + return status; +} + +static +enum bt_ctf_btr_status btr_string_begin_cb( + struct bt_ctf_field_type *type, void *data) +{ + enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; + struct bt_ctf_field *field = NULL; + struct bt_ctf_notif_iter *notit = data; + int ret; + + /* Create next field */ + field = get_next_field(notit); + if (!field) { + PERR("Failed to get next field (string)\n"); + status = BT_CTF_BTR_STATUS_ERROR; + goto end; + } + + /* + * Push on stack. Not a compound type per se, but we know that only + * btr_string_cb() may be called between this call and a subsequent + * call to btr_string_end_cb(). + */ + ret = stack_push(notit->stack, field); + if (ret) { + PERR("Failed to push string field onto the stack\n"); + status = BT_CTF_BTR_STATUS_ERROR; + goto end; + } + +end: + BT_PUT(field); + + return status; +} + +static +enum bt_ctf_btr_status btr_string_cb(const char *value, + size_t len, struct bt_ctf_field_type *type, void *data) +{ + enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; + struct bt_ctf_field *field = NULL; + struct bt_ctf_notif_iter *notit = data; + int ret; + + /* Get string field */ + field = stack_top(notit->stack)->base; + assert(field); + + /* Append current string */ + ret = bt_ctf_field_string_append_len(field, value, len); + if (ret) { + PERR("Failed to append a string to a string field\n"); + status = BT_CTF_BTR_STATUS_ERROR; + goto end; + } + +end: + return status; +} + +static +enum bt_ctf_btr_status btr_string_end_cb( + struct bt_ctf_field_type *type, void *data) +{ + struct bt_ctf_notif_iter *notit = data; + + /* Pop string field */ + stack_pop(notit->stack); + + /* Go to next field */ + stack_top(notit->stack)->index++; + + return BT_CTF_BTR_STATUS_OK; +} + +enum bt_ctf_btr_status btr_compound_begin_cb( + struct bt_ctf_field_type *type, void *data) +{ + enum bt_ctf_btr_status status = BT_CTF_BTR_STATUS_OK; + struct bt_ctf_notif_iter *notit = data; + struct bt_ctf_field *field; + int ret; + + /* Create field */ + if (stack_empty(notit->stack)) { + /* Root: create dynamic scope field */ + *notit->cur_dscope_field = bt_ctf_field_create(type); + field = *notit->cur_dscope_field; + + /* + * Field will be put at the end of this function + * (stack_push() will take one reference, but this + * reference is lost upon the equivalent stack_pop() + * later), so also get it for our context to own it. + */ + bt_get(*notit->cur_dscope_field); + } else { + field = get_next_field(notit); + } + + if (!field) { + PERR("Failed to get next field or create dynamic scope field\n"); + status = BT_CTF_BTR_STATUS_ERROR; + goto end; + } + + /* Push field */ + ret = stack_push(notit->stack, field); + if (ret) { + PERR("Failed to push compound field onto the stack\n"); + status = BT_CTF_BTR_STATUS_ERROR; + goto end; + } + +end: + BT_PUT(field); + + return status; +} + +enum bt_ctf_btr_status btr_compound_end_cb( + struct bt_ctf_field_type *type, void *data) +{ + struct bt_ctf_notif_iter *notit = data; + + assert(!stack_empty(notit->stack)); + + /* Pop stack */ + stack_pop(notit->stack); + + /* If the stack is not empty, increment the base's index */ + if (!stack_empty(notit->stack)) { + stack_top(notit->stack)->index++; + } + + return BT_CTF_BTR_STATUS_OK; +} + +static +struct bt_ctf_field *resolve_field(struct bt_ctf_notif_iter *notit, + struct bt_ctf_field_path *path) +{ + struct bt_ctf_field *field = NULL; + unsigned int i; + + switch (bt_ctf_field_path_get_root_scope(path)) { + case BT_CTF_SCOPE_TRACE_PACKET_HEADER: + field = notit->dscopes.trace_packet_header; + break; + case BT_CTF_SCOPE_STREAM_PACKET_CONTEXT: + field = notit->dscopes.stream_packet_context; + break; + case BT_CTF_SCOPE_STREAM_EVENT_HEADER: + field = notit->dscopes.stream_event_header; + break; + case BT_CTF_SCOPE_STREAM_EVENT_CONTEXT: + field = notit->dscopes.stream_event_context; + break; + case BT_CTF_SCOPE_EVENT_CONTEXT: + field = notit->dscopes.event_context; + break; + case BT_CTF_SCOPE_EVENT_FIELDS: + field = notit->dscopes.event_payload; + break; + default: + break; + } + + if (!field) { + goto end; + } + + bt_get(field); + + for (i = 0; i < bt_ctf_field_path_get_index_count(path); ++i) { + struct bt_ctf_field *next_field = NULL; + struct bt_ctf_field_type *field_type; + int index = bt_ctf_field_path_get_index(path, i); + + field_type = bt_ctf_field_get_type(field); + if (!field_type) { + BT_PUT(field); + goto end; + } + + if (is_struct_type(field_type)) { + next_field = bt_ctf_field_structure_get_field_by_index( + field, index); + } else if (is_variant_type(field_type)) { + next_field = + bt_ctf_field_variant_get_current_field(field); + } + + BT_PUT(field); + BT_PUT(field_type); + + if (!next_field) { + goto end; + } + + /* Move next field -> field */ + BT_MOVE(field, next_field); + } + +end: + return field; +} + +static +int64_t btr_get_sequence_length_cb(struct bt_ctf_field_type *type, void *data) +{ + int64_t ret = -1; + int iret; + struct bt_ctf_field_path *field_path; + struct bt_ctf_notif_iter *notit = data; + struct bt_ctf_field *field = NULL; + uint64_t length; + + field_path = bt_ctf_field_type_sequence_get_length_field_path(type); + if (!field_path) { + goto end; + } + + field = resolve_field(notit, field_path); + if (!field) { + goto end; + } + + iret = bt_ctf_field_unsigned_integer_get_value(field, &length); + if (iret) { + goto end; + } + + ret = (int64_t) length; + +end: + BT_PUT(field); + BT_PUT(field_path); + + return ret; +} + +static +struct bt_ctf_field_type *btr_get_variant_type_cb( + struct bt_ctf_field_type *type, void *data) +{ + struct bt_ctf_field_path *path; + struct bt_ctf_notif_iter *notit = data; + struct bt_ctf_field *tag_field = NULL; + struct bt_ctf_field *selected_field = NULL; + struct bt_ctf_field_type *selected_field_type = NULL; + + path = bt_ctf_field_type_variant_get_tag_field_path(type); + if (!path) { + goto end; + } + + tag_field = resolve_field(notit, path); + if (!tag_field) { + goto end; + } + + /* + * We found the enumeration tag field instance which should be + * able to select a current field for this variant. This + * callback function we're in is called _after_ + * compound_begin(), so the current stack top's base field is + * the variant field in question. We get the selected field here + * thanks to this tag field (thus creating the selected field), + * which will also provide us with its type. Then, this field + * will remain the current selected one until the next callback + * function call which is used to fill the current selected + * field. + */ + selected_field = bt_ctf_field_variant_get_field( + stack_top(notit->stack)->base, tag_field); + if (!selected_field) { + goto end; + } + + selected_field_type = bt_ctf_field_get_type(selected_field); + +end: + BT_PUT(tag_field); + BT_PUT(selected_field); + + return selected_field_type; +} + +static struct bt_ctf_event *create_event(struct bt_ctf_notif_iter *notit) +{ + struct bt_ctf_event *event; + int ret; + + /* Create event object */ + event = bt_ctf_event_create(notit->meta.event_class); + if (!event) { + goto error; + } + + /* Set header, stream event context, context, and payload fields */ + ret = bt_ctf_event_set_header(event, + notit->dscopes.stream_event_header); + if (ret) { + goto error; + } + + ret = bt_ctf_event_set_stream_event_context(event, + notit->dscopes.stream_event_context); + if (ret) { + goto error; + } + + ret = bt_ctf_event_set_event_context(event, + notit->dscopes.event_context); + if (ret) { + goto error; + } + + ret = bt_ctf_event_set_payload_field(event, + notit->dscopes.event_payload); + if (ret) { + goto error; + } + + /* Associate with current packet */ + assert(notit->packet); + ret = bt_ctf_event_set_packet(event, notit->packet); + if (ret) { + goto error; + } + + goto end; + +error: + BT_PUT(event); + +end: + return event; +} + +static void create_packet(struct bt_ctf_notif_iter *notit) +{ + int ret; + struct bt_ctf_stream *stream = NULL; + struct bt_ctf_packet *packet = NULL; + + /* Ask the user for the stream */ + stream = notit->medium.medops.get_stream(notit->meta.stream_class, + notit->medium.data); + if (!stream) { + goto error; + } + + /* Create packet */ + packet = bt_ctf_packet_create(stream); + if (!packet) { + goto error; + } + + /* Set packet's context and header fields */ + if (notit->dscopes.trace_packet_header) { + ret = bt_ctf_packet_set_header(packet, + notit->dscopes.trace_packet_header); + if (ret) { + goto error; + } + } + + if (notit->dscopes.stream_packet_context) { + ret = bt_ctf_packet_set_context(packet, + notit->dscopes.stream_packet_context); + if (ret) { + goto error; + } + } + + goto end; + +error: + BT_PUT(packet); + +end: + BT_MOVE(notit->packet, packet); +} + +static void notify_new_packet(struct bt_ctf_notif_iter *notit, + struct bt_ctf_notif_iter_notif **notification) +{ + struct bt_ctf_notif_iter_notif_new_packet *rnotif; + + rnotif = g_new0(struct bt_ctf_notif_iter_notif_new_packet, 1); + if (!rnotif) { + goto error; + } + + rnotif->base.type = BT_CTF_NOTIF_ITER_NOTIF_NEW_PACKET; + + /* Create packet */ + create_packet(notit); + if (!notit->packet) { + goto error; + } + + rnotif->packet = bt_get(notit->packet); + *notification = (struct bt_ctf_notif_iter_notif *) rnotif; + return; + +error: + bt_ctf_notif_iter_notif_destroy(rnotif); +} + +static void notify_end_of_packet(struct bt_ctf_notif_iter *notit, + struct bt_ctf_notif_iter_notif **notification) +{ + struct bt_ctf_notif_iter_notif_end_of_packet *rnotif; + + rnotif = g_new0(struct bt_ctf_notif_iter_notif_end_of_packet, 1); + if (!rnotif) { + goto error; + } + + rnotif->base.type = BT_CTF_NOTIF_ITER_NOTIF_END_OF_PACKET; + + /* Create packet */ + create_packet(notit); + if (!notit->packet) { + goto error; + } + + rnotif->packet = bt_get(notit->packet); + *notification = (struct bt_ctf_notif_iter_notif *) rnotif; + return; + +error: + bt_ctf_notif_iter_notif_destroy(rnotif); +} + +static void notify_event(struct bt_ctf_notif_iter *notit, + struct bt_ctf_notif_iter_notif **notification) +{ + struct bt_ctf_notif_iter_notif_event *rnotif; + struct bt_ctf_event *event = NULL; + + rnotif = g_new0(struct bt_ctf_notif_iter_notif_event, 1); + if (!rnotif) { + goto error; + } + + rnotif->base.type = BT_CTF_NOTIF_ITER_NOTIF_EVENT; + + /* Create event */ + event = create_event(notit); + if (!event) { + goto error; + } + + BT_MOVE(rnotif->event, event); + *notification = (struct bt_ctf_notif_iter_notif *) rnotif; + return; + +error: + BT_PUT(event); + bt_ctf_notif_iter_notif_destroy(rnotif); +} + +void bt_ctf_notif_iter_notif_destroy(void *vnotif) +{ + struct bt_ctf_notif_iter_notif *notif = vnotif; + + switch (notif->type) { + case BT_CTF_NOTIF_ITER_NOTIF_NEW_PACKET: + { + struct bt_ctf_notif_iter_notif_new_packet *rnotif = + (struct bt_ctf_notif_iter_notif_new_packet *) notif; + + BT_PUT(rnotif->packet); + break; + } + case BT_CTF_NOTIF_ITER_NOTIF_END_OF_PACKET: + { + struct bt_ctf_notif_iter_notif_end_of_packet *rnotif = + (struct bt_ctf_notif_iter_notif_end_of_packet *) notif; + + BT_PUT(rnotif->packet); + break; + } + case BT_CTF_NOTIF_ITER_NOTIF_EVENT: + { + struct bt_ctf_notif_iter_notif_event *rnotif = + (struct bt_ctf_notif_iter_notif_event *) notif; + + BT_PUT(rnotif->event); + break; + } + default: + assert(false); + } + + g_free(notif); +} + +struct bt_ctf_notif_iter *bt_ctf_notif_iter_create(struct bt_ctf_trace *trace, + size_t max_request_sz, + struct bt_ctf_notif_iter_medium_ops medops, + void *data, FILE *err_stream) +{ + struct bt_ctf_notif_iter *notit = NULL; + struct bt_ctf_btr_cbs cbs = { + .types = { + .signed_int = btr_signed_int_cb, + .unsigned_int = btr_unsigned_int_cb, + .floating_point = btr_floating_point_cb, + .string_begin = btr_string_begin_cb, + .string = btr_string_cb, + .string_end = btr_string_end_cb, + .compound_begin = btr_compound_begin_cb, + .compound_end = btr_compound_end_cb, + }, + .query = { + .get_sequence_length = btr_get_sequence_length_cb, + .get_variant_type = btr_get_variant_type_cb, + }, + }; + + assert(trace); + assert(medops.request_bytes); + notit = g_new0(struct bt_ctf_notif_iter, 1); + if (!notit) { + PERR("Failed to allocate memory for CTF notification iterator\n"); + goto end; + } + + notit->meta.trace = trace; + bt_get(notit->meta.trace); + notit->medium.medops = medops; + notit->medium.max_request_sz = max_request_sz; + notit->medium.data = data; + notit->err_stream = err_stream; + notit->stack = stack_new(notit); + if (!notit->stack) { + PERR("Failed to create stack\n"); + bt_ctf_notif_iter_destroy(notit); + notit = NULL; + goto end; + } + + notit->btr = bt_ctf_btr_create(cbs, notit, err_stream); + if (!notit->btr) { + PERR("Failed to create binary type reader\n"); + bt_ctf_notif_iter_destroy(notit); + notit = NULL; + goto end; + } + + bt_ctf_notif_iter_reset(notit); + +end: + return notit; +} + +void bt_ctf_notif_iter_destroy(struct bt_ctf_notif_iter *notit) +{ + BT_PUT(notit->meta.trace); + BT_PUT(notit->meta.stream_class); + BT_PUT(notit->meta.event_class); + BT_PUT(notit->packet); + put_all_dscopes(notit); + + if (notit->stack) { + stack_destroy(notit->stack); + } + + if (notit->btr) { + bt_ctf_btr_destroy(notit->btr); + } + + g_free(notit); +} + +enum bt_ctf_notif_iter_status bt_ctf_notif_iter_get_next_notification( + struct bt_ctf_notif_iter *notit, + struct bt_ctf_notif_iter_notif **notification) +{ + enum bt_ctf_notif_iter_status status = BT_CTF_NOTIF_ITER_STATUS_OK; + + assert(notit); + assert(notification); + + while (true) { + status = handle_state(notit); + if (status != BT_CTF_NOTIF_ITER_STATUS_OK) { + if (status == BT_CTF_NOTIF_ITER_STATUS_EOF) { + PDBG("Medium operation reported end of file\n"); + } else { + PERR("Failed to handle state:\n"); + PERR(" State: %d\n", notit->state); + } + goto end; + } + + switch (notit->state) { + case STATE_EMIT_NOTIF_NEW_PACKET: + PDBG("Emitting new packet notification\n"); + notify_new_packet(notit, notification); + if (!*notification) { + status = BT_CTF_NOTIF_ITER_STATUS_ERROR; + } + goto end; + case STATE_EMIT_NOTIF_EVENT: + PDBG("Emitting event notification\n"); + notify_event(notit, notification); + if (!*notification) { + status = BT_CTF_NOTIF_ITER_STATUS_ERROR; + } + goto end; + case STATE_EMIT_NOTIF_END_OF_PACKET: + PDBG("Emitting end of packet notification\n"); + notify_end_of_packet(notit, notification); + if (!*notification) { + status = BT_CTF_NOTIF_ITER_STATUS_ERROR; + } + goto end; + default: + /* Non-emitting state: continue */ + break; + } + } + +end: + return status; +} diff --git a/plugins/ctf/common/notif-iter/notif-iter.h b/plugins/ctf/common/notif-iter/notif-iter.h new file mode 100644 index 00000000..ed1dbbcd --- /dev/null +++ b/plugins/ctf/common/notif-iter/notif-iter.h @@ -0,0 +1,295 @@ +#ifndef CTF_NOTIF_ITER_H +#define CTF_NOTIF_ITER_H + +/* + * Babeltrace - CTF notification iterator + * ¯¯¯¯¯ ¯¯¯¯ + * Copyright (c) 2015-2016 EfficiOS Inc. and Linux Foundation + * Copyright (c) 2015-2016 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +/** + * @file ctf-notif-iter.h + * + * CTF notification iterator + * ¯¯¯¯¯ ¯¯¯¯ + * This is a common internal API used by CTF source plugins. It allows + * one to get notifications from a user-provided medium. + */ + +/** + * Medium operations status codes. + */ +enum bt_ctf_notif_iter_medium_status { + /** + * End of file. + * + * The medium function called by the notification iterator + * function reached the end of the file. + */ + BT_CTF_NOTIF_ITER_MEDIUM_STATUS_EOF = -4, + + /** + * There is no data available right now, try again later. + */ + BT_CTF_NOTIF_ITER_MEDIUM_STATUS_AGAIN = -3, + + /** Invalid argument. */ + BT_CTF_NOTIF_ITER_MEDIUM_STATUS_INVAL = -2, + + /** General error. */ + BT_CTF_NOTIF_ITER_MEDIUM_STATUS_ERROR = -1, + + /** Everything okay. */ + BT_CTF_NOTIF_ITER_MEDIUM_STATUS_OK = 0, +}; + +/** + * CTF notification iterator API status code. + */ +enum bt_ctf_notif_iter_status { + /** + * End of file. + * + * The medium function called by the notification iterator + * function reached the end of the file. + */ + BT_CTF_NOTIF_ITER_STATUS_EOF = -4, + + /** + * There is no data available right now, try again later. + * + * Some condition resulted in the + * bt_ctf_notif_iter_medium_ops::request_bytes() user function not + * having access to any data now. You should retry calling the + * last called notification iterator function once the situation + * is resolved. + */ + BT_CTF_NOTIF_ITER_STATUS_AGAIN = -3, + + /** Invalid argument. */ + BT_CTF_NOTIF_ITER_STATUS_INVAL = -2, + + /** General error. */ + BT_CTF_NOTIF_ITER_STATUS_ERROR = -1, + + /** Everything okay. */ + BT_CTF_NOTIF_ITER_STATUS_OK = 0, +}; + +/** + * Medium operations. + * + * Those user functions are called by the notification iterator + * functions to request medium actions. + */ +struct bt_ctf_notif_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 notification 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 notification iterator functions. The + * returned buffer won't be modified by the notification + * 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: + * + * - #BT_CTF_NOTIF_ITER_MEDIUM_STATUS_OK: 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. + * - #BT_CTF_NOTIF_ITER_MEDIUM_STATUS_AGAIN: No data is + * available right now. In this case, the notification + * iterator function called by the user returns + * #BT_CTF_NOTIF_ITER_STATUS_AGAIN, and it is the user's + * responsibility to make sure enough data becomes available + * before calling the \em same notification iterator + * function again to continue the decoding process. + * - #BT_CTF_NOTIF_ITER_MEDIUM_STATUS_EOF: The end of + * the file was reached, and no more data will ever be + * available for this file. In this case, the notification + * iterator function called by the user returns + * #BT_CTF_NOTIF_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 + * #BT_CTF_NOTIF_ITER_MEDIUM_STATUS_EOF on the \em following + * call. + * - #BT_CTF_NOTIF_ITER_MEDIUM_STATUS_ERROR: A fatal + * error occured during this operation. In this case, the + * notification iterator function called by the user returns + * #BT_CTF_NOTIF_ITER_STATUS_ERROR. + * + * If #BT_CTF_NOTIF_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 bt_ctf_notif_iter_medium_status (* request_bytes)( + size_t request_sz, uint8_t **buffer_addr, + size_t *buffer_sz, 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 notification + * iterator. + * + * @param stream_class Stream class associated to the stream + * @param data User data + * @returns Stream instance (weak reference) or + * \c NULL on error + */ + struct bt_ctf_stream * (* get_stream)( + struct bt_ctf_stream_class *stream_class, void *data); +}; + +/** CTF notification iterator. */ +struct bt_ctf_notif_iter; + +// TODO: Replace by the real thing +enum bt_ctf_notif_iter_notif_type { + BT_CTF_NOTIF_ITER_NOTIF_NEW_PACKET, + BT_CTF_NOTIF_ITER_NOTIF_END_OF_PACKET, + BT_CTF_NOTIF_ITER_NOTIF_EVENT, +}; + +struct bt_ctf_notif_iter_notif { + enum bt_ctf_notif_iter_notif_type type; +}; + +struct bt_ctf_notif_iter_notif_new_packet { + struct bt_ctf_notif_iter_notif base; + struct bt_ctf_packet *packet; +}; + +struct bt_ctf_notif_iter_notif_end_of_packet { + struct bt_ctf_notif_iter_notif base; + struct bt_ctf_packet *packet; +}; + +struct bt_ctf_notif_iter_notif_event { + struct bt_ctf_notif_iter_notif base; + struct bt_ctf_event *event; +}; + +void bt_ctf_notif_iter_notif_destroy(void *notif); + +/** + * Creates a CTF notification 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 + * bt_ctf_notif_iter_medium_ops::request_bytes() + * at a time + * @param medops Medium operations + * @param medops_data User data (passed to medium operations) + * @param err_stream Error stream (can be \c NULL to disable) + * @returns New CTF notification iterator on + * success, or \c NULL on error + */ +struct bt_ctf_notif_iter *bt_ctf_notif_iter_create(struct bt_ctf_trace *trace, + size_t max_request_sz, struct bt_ctf_notif_iter_medium_ops medops, + void *medops_data, FILE *err_stream); + +/** + * Destroys a CTF notification iterator, freeing all internal resources. + * + * The registered trace's reference count is decremented. + * + * @param notif_iter CTF notification iterator + */ +void bt_ctf_notif_iter_destroy(struct bt_ctf_notif_iter *notif_iter); + +/** + * Resets the internal state of a CTF notification iterator. + * + * This function can be used when it is desired to seek to the beginning + * of another packet. It is expected that the next call to + * bt_ctf_notif_iter_medium_ops::request_bytes() made by this + * notification iterator will return the \em first bytes of a \em + * packet. + * + * @param notif_iter CTF notification iterator + */ +void bt_ctf_notif_iter_reset(struct bt_ctf_notif_iter *notif_iter); + +/** + * Returns the next notification from a CTF notification iterator. + * + * Upon successful completion, #BT_CTF_NOTIF_ITER_STATUS_OK is + * returned, and the next notification is written to \p notif. + * In this case, the caller is responsible for calling + * bt_notification_put() on the returned notification. + * + * If this function returns #BT_CTF_NOTIF_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 notif_iter CTF notification iterator + * @param notification Returned notification if the function's + * return value is #BT_CTF_NOTIF_ITER_STATUS_OK + * @returns One of #bt_ctf_notif_iter_status values + */ +enum bt_ctf_notif_iter_status bt_ctf_notif_iter_get_next_notification( + struct bt_ctf_notif_iter *notif_iter, + struct bt_ctf_notif_iter_notif **notification); + +#endif /* CTF_NOTIF_ITER_H */ diff --git a/plugins/ctf/common/notif-iter/print.h b/plugins/ctf/common/notif-iter/print.h new file mode 100644 index 00000000..bb5c62a3 --- /dev/null +++ b/plugins/ctf/common/notif-iter/print.h @@ -0,0 +1,58 @@ +#ifndef CTF_NOTIF_ITER_PRINT_H +#define CTF_NOTIF_ITER_PRINT_H + +/* + * Define PRINT_PREFIX and PRINT_ERR_STREAM, then include this file. + * + * Copyright (c) 2016 Philippe Proulx + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +#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_NOTIF_ITER_PRINT_H */