From e98a2d6e0c0c7a9f28e620ce975b8c242ee20c9f Mon Sep 17 00:00:00 2001 From: Philippe Proulx Date: Sun, 14 Aug 2016 21:09:18 -0400 Subject: [PATCH] Add ctf-reader prototype MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Proulx Signed-off-by: Jérémie Galarneau --- Makefile.am | 2 +- configure.ac | 4 + ctf-reader-proto/Makefile.am | 20 + ctf-reader-proto/ctf-btr.gdb | 24 + ctf-reader-proto/ctf-btr/Makefile.am | 6 + ctf-reader-proto/ctf-btr/ctf-btr.c | 1388 +++++ ctf-reader-proto/ctf-btr/ctf-btr.h | 352 ++ ctf-reader-proto/ctf-btr/print.h | 58 + ctf-reader-proto/ctf-fs-data-stream.c | 362 ++ ctf-reader-proto/ctf-fs-data-stream.h | 62 + ctf-reader-proto/ctf-fs-file.c | 120 + ctf-reader-proto/ctf-fs-file.h | 49 + ctf-reader-proto/ctf-fs-metadata.c | 404 ++ ctf-reader-proto/ctf-fs-metadata.h | 52 + ctf-reader-proto/ctf-fs.c | 150 + ctf-reader-proto/ctf-fs.h | 48 + ctf-reader-proto/ctf-notif-iter.gdb | 22 + ctf-reader-proto/ctf-notif-iter/Makefile.am | 6 + .../ctf-notif-iter/ctf-notif-iter.c | 1938 +++++++ .../ctf-notif-iter/ctf-notif-iter.h | 295 ++ ctf-reader-proto/ctf-notif-iter/print.h | 58 + ctf-reader-proto/metadata-parsing/Makefile.am | 38 + ctf-reader-proto/metadata-parsing/ctf-ast.h | 321 ++ ctf-reader-proto/metadata-parsing/ctf-lexer.l | 142 + .../metadata-parsing/ctf-parser.y | 2586 +++++++++ .../metadata-parsing/ctf-scanner-symbols.h | 50 + .../metadata-parsing/ctf-scanner.h | 55 + .../ctf-visitor-generate-ir.c | 4662 +++++++++++++++++ .../ctf-visitor-parent-links.c | 461 ++ .../ctf-visitor-semantic-validator.c | 978 ++++ ctf-reader-proto/metadata-parsing/objstack.c | 137 + ctf-reader-proto/metadata-parsing/objstack.h | 44 + ctf-reader-proto/print.h | 59 + ctf-reader-proto/protorectoral.c | 33 + 34 files changed, 14985 insertions(+), 1 deletion(-) create mode 100644 ctf-reader-proto/Makefile.am create mode 100644 ctf-reader-proto/ctf-btr.gdb create mode 100644 ctf-reader-proto/ctf-btr/Makefile.am create mode 100644 ctf-reader-proto/ctf-btr/ctf-btr.c create mode 100644 ctf-reader-proto/ctf-btr/ctf-btr.h create mode 100644 ctf-reader-proto/ctf-btr/print.h create mode 100644 ctf-reader-proto/ctf-fs-data-stream.c create mode 100644 ctf-reader-proto/ctf-fs-data-stream.h create mode 100644 ctf-reader-proto/ctf-fs-file.c create mode 100644 ctf-reader-proto/ctf-fs-file.h create mode 100644 ctf-reader-proto/ctf-fs-metadata.c create mode 100644 ctf-reader-proto/ctf-fs-metadata.h create mode 100644 ctf-reader-proto/ctf-fs.c create mode 100644 ctf-reader-proto/ctf-fs.h create mode 100644 ctf-reader-proto/ctf-notif-iter.gdb create mode 100644 ctf-reader-proto/ctf-notif-iter/Makefile.am create mode 100644 ctf-reader-proto/ctf-notif-iter/ctf-notif-iter.c create mode 100644 ctf-reader-proto/ctf-notif-iter/ctf-notif-iter.h create mode 100644 ctf-reader-proto/ctf-notif-iter/print.h create mode 100644 ctf-reader-proto/metadata-parsing/Makefile.am create mode 100644 ctf-reader-proto/metadata-parsing/ctf-ast.h create mode 100644 ctf-reader-proto/metadata-parsing/ctf-lexer.l create mode 100644 ctf-reader-proto/metadata-parsing/ctf-parser.y create mode 100644 ctf-reader-proto/metadata-parsing/ctf-scanner-symbols.h create mode 100644 ctf-reader-proto/metadata-parsing/ctf-scanner.h create mode 100644 ctf-reader-proto/metadata-parsing/ctf-visitor-generate-ir.c create mode 100644 ctf-reader-proto/metadata-parsing/ctf-visitor-parent-links.c create mode 100644 ctf-reader-proto/metadata-parsing/ctf-visitor-semantic-validator.c create mode 100644 ctf-reader-proto/metadata-parsing/objstack.c create mode 100644 ctf-reader-proto/metadata-parsing/objstack.h create mode 100644 ctf-reader-proto/print.h create mode 100644 ctf-reader-proto/protorectoral.c diff --git a/Makefile.am b/Makefile.am index 0544bd16..fab48f19 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 +SUBDIRS = include types compat lib formats plugins converter bindings tests doc extras ctf-reader-proto 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 be939a2d..c3809bee 100644 --- a/configure.ac +++ b/configure.ac @@ -343,6 +343,10 @@ 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 diff --git a/ctf-reader-proto/Makefile.am b/ctf-reader-proto/Makefile.am new file mode 100644 index 00000000..567114ed --- /dev/null +++ b/ctf-reader-proto/Makefile.am @@ -0,0 +1,20 @@ +SUBDIRS = metadata-parsing ctf-notif-iter ctf-btr + +AM_CFLAGS = $(PACKAGE_CFLAGS) +AM_CPPFLAGS = -I$(top_srcdir)/include + +noinst_PROGRAMS = protorectoral + +protorectoral_SOURCES = \ + protorectoral.c \ + ctf-fs.c \ + ctf-fs-file.c \ + ctf-fs-metadata.c \ + ctf-fs-data-stream.c +protorectoral_LDADD = \ + metadata-parsing/libctf-parser.la \ + metadata-parsing/libctf-ast.la \ + ctf-notif-iter/libctf-notif-iter.la \ + ctf-btr/libctf-btr.la \ + $(top_builddir)/lib/libbabeltrace.la \ + $(top_builddir)/formats/ctf/libbabeltrace-ctf.la diff --git a/ctf-reader-proto/ctf-btr.gdb b/ctf-reader-proto/ctf-btr.gdb new file mode 100644 index 00000000..9b42b1cd --- /dev/null +++ b/ctf-reader-proto/ctf-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/ctf-reader-proto/ctf-btr/Makefile.am b/ctf-reader-proto/ctf-btr/Makefile.am new file mode 100644 index 00000000..93dd221e --- /dev/null +++ b/ctf-reader-proto/ctf-btr/Makefile.am @@ -0,0 +1,6 @@ +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 new file mode 100644 index 00000000..cb250f9f --- /dev/null +++ b/ctf-reader-proto/ctf-btr/ctf-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 "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 new file mode 100644 index 00000000..18244b08 --- /dev/null +++ b/ctf-reader-proto/ctf-btr/ctf-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/ctf-reader-proto/ctf-btr/print.h b/ctf-reader-proto/ctf-btr/print.h new file mode 100644 index 00000000..22381dc1 --- /dev/null +++ b/ctf-reader-proto/ctf-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/ctf-reader-proto/ctf-fs-data-stream.c b/ctf-reader-proto/ctf-fs-data-stream.c new file mode 100644 index 00000000..c696927a --- /dev/null +++ b/ctf-reader-proto/ctf-fs-data-stream.c @@ -0,0 +1,362 @@ +/* + * Copyright 2016 - Philippe Proulx + * Copyright 2010-2011 - EfficiOS Inc. and Linux Foundation + * + * Some functions are based on older functions written by 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 + +#define PRINT_ERR_STREAM ctf_fs->error_fp +#define PRINT_PREFIX "ctf-fs-data-stream" +#include "print.h" + +#include "ctf-fs.h" +#include "ctf-fs-file.h" +#include "ctf-fs-metadata.h" +#include "ctf-notif-iter/ctf-notif-iter.h" + +static void ctf_fs_stream_destroy(struct ctf_fs_stream *stream) +{ + if (stream->file) { + ctf_fs_file_destroy(stream->file); + } + + if (stream->stream) { + BT_PUT(stream->stream); + } + + if (stream->notif_iter) { + bt_ctf_notif_iter_destroy(stream->notif_iter); + } + + g_free(stream); +} + +static size_t remaining_mmap_bytes(struct ctf_fs_stream *stream) +{ + return stream->mmap_offset + stream->mmap_len - + stream->request_offset; +} + +static int stream_munmap(struct ctf_fs_stream *stream) +{ + struct ctf_fs *ctf_fs = stream->file->ctf_fs; + + if (munmap(stream->mmap_addr, stream->mmap_len)) { + PERR("Cannot memory-unmap address %p (size %zu) of file \"%s\" (%p): %s\n", + stream->mmap_addr, stream->mmap_len, + stream->file->path->str, stream->file->fp, + strerror(errno)); + return -1; + } + + return 0; +} + +static int mmap_next(struct ctf_fs_stream *stream) +{ + struct ctf_fs *ctf_fs = stream->file->ctf_fs; + int ret = 0; + + /* Unmap old region */ + if (stream->mmap_addr) { + if (stream_munmap(stream)) { + goto error; + } + + stream->mmap_offset += stream->mmap_len; + stream->request_offset = stream->mmap_offset; + } + + /* Map new region */ + stream->mmap_addr = mmap((void *) 0, stream->mmap_len, + PROT_READ, MAP_PRIVATE, fileno(stream->file->fp), + stream->mmap_offset); + if (stream->mmap_addr == MAP_FAILED) { + PERR("Cannot memory-map address (size %zu) of file \"%s\" (%p) at offset %zu: %s\n", + stream->mmap_len, stream->file->path->str, + stream->file->fp, stream->mmap_offset, + strerror(errno)); + goto error; + } + + goto end; + +error: + stream_munmap(stream); + ret = -1; + +end: + return ret; +} + +static enum bt_ctf_notif_iter_medium_status medop_request_bytes( + size_t request_sz, uint8_t **buffer_addr, + size_t *buffer_sz, void *data) +{ + enum bt_ctf_notif_iter_medium_status status = + BT_CTF_NOTIF_ITER_MEDIUM_STATUS_OK; + struct ctf_fs_stream *stream = data; + struct ctf_fs *ctf_fs = stream->file->ctf_fs; + + if (request_sz == 0) { + goto end; + } + + /* Check if we need an initial memory map */ + if (!stream->mmap_addr) { + if (mmap_next(stream)) { + PERR("Cannot memory-map initial region of file \"%s\" (%p)\n", + stream->file->path->str, stream->file->fp); + goto error; + } + } + + /* Check if we have at least one memory-mapped byte left */ + if (remaining_mmap_bytes(stream) == 0) { + /* Are we at the end of the file? */ + if (stream->request_offset == stream->file->size) { + PDBG("Reached end of file \"%s\" (%p)\n", + stream->file->path->str, stream->file->fp); + status = BT_CTF_NOTIF_ITER_MEDIUM_STATUS_EOF; + goto end; + } + + if (mmap_next(stream)) { + PERR("Cannot memory-map next region of file \"%s\" (%p)\n", + stream->file->path->str, stream->file->fp); + goto error; + } + } + + *buffer_sz = MIN(remaining_mmap_bytes(stream), request_sz); + *buffer_addr = ((uint8_t *) stream->mmap_addr) + + stream->request_offset - stream->mmap_offset; + stream->request_offset += *buffer_sz; + goto end; + +error: + status = BT_CTF_NOTIF_ITER_MEDIUM_STATUS_ERROR; + +end: + return status; +} + +static struct bt_ctf_stream *medop_get_stream( + struct bt_ctf_stream_class *stream_class, void *data) +{ + struct ctf_fs_stream *fs_stream = data; + struct ctf_fs *ctf_fs = fs_stream->file->ctf_fs; + + if (!fs_stream->stream) { + int64_t id = bt_ctf_stream_class_get_id(stream_class); + + PDBG("Creating stream out of stream class %" PRId64 "\n", id); + fs_stream->stream = bt_ctf_stream_create(stream_class, + fs_stream->file->path->str); + if (!fs_stream->stream) { + PERR("Cannot create stream (stream class %" PRId64 ")\n", + id); + } + } + + return fs_stream->stream; +} + +static struct bt_ctf_notif_iter_medium_ops medops = { + .request_bytes = medop_request_bytes, + .get_stream = medop_get_stream, +}; + +static struct ctf_fs_stream *ctf_fs_stream_create(struct ctf_fs *ctf_fs, + struct ctf_fs_file *file) +{ + struct ctf_fs_stream *stream = g_new0(struct ctf_fs_stream, 1); + + if (!stream) { + goto error; + } + + stream->file = file; + stream->notif_iter = bt_ctf_notif_iter_create(ctf_fs->metadata.trace, + 12, medops, stream, ctf_fs->error_fp); + if (!stream->notif_iter) { + goto error; + } + stream->mmap_len = ctf_fs->page_size; + + goto end; + +error: + /* Do not touch borrowed file */ + stream->file = NULL; + ctf_fs_stream_destroy(stream); + stream = NULL; + +end: + return stream; +} + +int ctf_fs_data_stream_open_streams(struct ctf_fs *ctf_fs) +{ + int ret = 0; + GError *error = NULL; + GDir *dir = g_dir_open(ctf_fs->trace_path->str, 0, &error); + const char *name; + + if (!dir) { + PERR("Cannot open directory \"%s\": %s (code %d)\n", + ctf_fs->trace_path->str, error->message, + error->code); + goto error; + } + + while ((name = g_dir_read_name(dir))) { + struct ctf_fs_file *file = NULL; + struct ctf_fs_stream *stream = NULL; + + if (strcmp(name, CTF_FS_METADATA_FILENAME) == 0) { + /* Ignore the metadata stream */ + PDBG("Ignoring metadata file \"%s\"\n", + name); + continue; + } + + if (name[0] == '.') { + PDBG("Ignoring hidden file \"%s\"\n", + name); + continue; + } + + /* Create the file */ + file = ctf_fs_file_create(ctf_fs); + if (!file) { + PERR("Cannot create stream file object\n"); + goto error; + } + + /* Create full path string */ + g_string_append(file->path, ctf_fs->trace_path->str); + g_string_append(file->path, "/"); + g_string_append(file->path, name); + + if (!g_file_test(file->path->str, G_FILE_TEST_IS_REGULAR)) { + PDBG("Ignoring non-regular file \"%s\"\n", name); + ctf_fs_file_destroy(file); + continue; + } + + /* Open the file */ + if (ctf_fs_file_open(ctf_fs, file, "rb")) { + ctf_fs_file_destroy(file); + goto error; + } + + /* Create a private stream */ + stream = ctf_fs_stream_create(ctf_fs, file); + if (!stream) { + ctf_fs_file_destroy(file); + goto error; + } + + /* Append file to the array of files */ + g_ptr_array_add(ctf_fs->data_stream.streams, stream); + } + + goto end; + +error: + ret = -1; + +end: + if (dir) { + g_dir_close(dir); + dir = NULL; + } + + if (error) { + g_error_free(error); + } + + return ret; +} + +int ctf_fs_data_stream_init(struct ctf_fs *ctf_fs, + struct ctf_fs_data_stream *data_stream) +{ + int ret = 0; + + data_stream->streams = g_ptr_array_new_with_free_func( + (GDestroyNotify) ctf_fs_stream_destroy); + if (!data_stream->streams) { + PERR("Cannot allocate array of streams\n"); + goto error; + } + + goto end; + +error: + ret = -1; + +end: + return ret; +} + +void ctf_fs_data_stream_deinit(struct ctf_fs_data_stream *data_stream) +{ + g_ptr_array_free(data_stream->streams, TRUE); +} + +int ctf_fs_data_stream_get_next_notification( + struct ctf_fs *ctf_fs, + struct bt_ctf_notif_iter_notif **notification) +{ + int ret = 0; + struct ctf_fs_stream *stream = g_ptr_array_index( + ctf_fs->data_stream.streams, 0); + enum bt_ctf_notif_iter_status status; + + status = bt_ctf_notif_iter_get_next_notification( + stream->notif_iter, notification); + if (status != BT_CTF_NOTIF_ITER_STATUS_OK && + status != BT_CTF_NOTIF_ITER_STATUS_EOF) { + goto error; + } + if (status == BT_CTF_NOTIF_ITER_STATUS_EOF) { + *notification = NULL; + } + + goto end; + +error: + ret = -1; + +end: + return ret; +} diff --git a/ctf-reader-proto/ctf-fs-data-stream.h b/ctf-reader-proto/ctf-fs-data-stream.h new file mode 100644 index 00000000..d8f67466 --- /dev/null +++ b/ctf-reader-proto/ctf-fs-data-stream.h @@ -0,0 +1,62 @@ +#ifndef CTF_FS_DATA_STREAM_H +#define CTF_FS_DATA_STREAM_H + +/* + * Copyright 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 "ctf-notif-iter/ctf-notif-iter.h" + +struct ctf_fs_stream { + struct ctf_fs_file *file; + struct bt_ctf_stream *stream; + struct bt_ctf_notif_iter *notif_iter; + void *mmap_addr; + size_t mmap_len; + off_t mmap_offset; + off_t request_offset; +}; + +struct ctf_fs_data_stream { + GPtrArray *streams; +}; + +BT_HIDDEN +int ctf_fs_data_stream_init(struct ctf_fs *ctf_fs, + struct ctf_fs_data_stream *data_stream); + +BT_HIDDEN +void ctf_fs_data_stream_deinit(struct ctf_fs_data_stream *data_stream); + +BT_HIDDEN +int ctf_fs_data_stream_open_streams(struct ctf_fs *ctf_fs); + +BT_HIDDEN +int ctf_fs_data_stream_get_next_notification( + struct ctf_fs *ctf_fs, + struct bt_ctf_notif_iter_notif **notification); + +#endif /* CTF_FS_DATA_STREAM_H */ diff --git a/ctf-reader-proto/ctf-fs-file.c b/ctf-reader-proto/ctf-fs-file.c new file mode 100644 index 00000000..45819ef7 --- /dev/null +++ b/ctf-reader-proto/ctf-fs-file.c @@ -0,0 +1,120 @@ +/* + * Copyright 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 + +#define PRINT_ERR_STREAM ctf_fs->error_fp +#define PRINT_PREFIX "ctf-fs-file" +#include "print.h" + +#include "ctf-fs-file.h" + +void ctf_fs_file_destroy(struct ctf_fs_file *file) +{ + struct ctf_fs *ctf_fs = file->ctf_fs; + + if (!file) { + return; + } + + if (file->fp) { + PDBG("Closing file \"%s\" (%p)\n", file->path->str, file->fp); + + if (fclose(file->fp)) { + PERR("Cannot close file \"%s\": %s\n", file->path->str, + strerror(errno)); + } + } + + if (file->path) { + g_string_free(file->path, TRUE); + } + + g_free(file); +} + +struct ctf_fs_file *ctf_fs_file_create(struct ctf_fs *ctf_fs) +{ + struct ctf_fs_file *file = g_new0(struct ctf_fs_file, 1); + + if (!file) { + goto error; + } + + file->ctf_fs = ctf_fs; + file->path = g_string_new(NULL); + if (!file->path) { + goto error; + } + + goto end; + +error: + ctf_fs_file_destroy(file); + file = NULL; + +end: + return file; +} + +int ctf_fs_file_open(struct ctf_fs *ctf_fs, struct ctf_fs_file *file, + const char *mode) +{ + int ret = 0; + struct stat stat; + + PDBG("Opening file \"%s\" with mode \"%s\"\n", file->path->str, mode); + file->fp = fopen(file->path->str, mode); + if (!file->fp) { + PERR("Cannot open file \"%s\" with mode \"%s\": %s", + file->path->str, mode, strerror(errno)); + goto error; + } + + PDBG("Opened file: %p\n", file->fp); + + if (fstat(fileno(file->fp), &stat)) { + PERR("Cannot get file informations: %s\n", strerror(errno)); + goto error; + } + + file->size = stat.st_size; + PDBG(" File is %zu bytes\n", file->size); + goto end; + +error: + ret = -1; + + if (file->fp) { + if (fclose(file->fp)) { + PERR("Cannot close file \"%s\": %s\n", file->path->str, + strerror(errno)); + } + } + +end: + return ret; +} diff --git a/ctf-reader-proto/ctf-fs-file.h b/ctf-reader-proto/ctf-fs-file.h new file mode 100644 index 00000000..5da082eb --- /dev/null +++ b/ctf-reader-proto/ctf-fs-file.h @@ -0,0 +1,49 @@ +#ifndef CTF_FS_FILE_H +#define CTF_FS_FILE_H + +/* + * Copyright 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 "ctf-fs.h" + +struct ctf_fs_file { + struct ctf_fs *ctf_fs; + GString *path; + FILE *fp; + off_t size; +}; + +BT_HIDDEN +void ctf_fs_file_destroy(struct ctf_fs_file *file); + +BT_HIDDEN +struct ctf_fs_file *ctf_fs_file_create(struct ctf_fs *ctf_fs); + +BT_HIDDEN +int ctf_fs_file_open(struct ctf_fs *ctf_fs, struct ctf_fs_file *file, + const char *mode); + +#endif /* CTF_FS_FILE_H */ diff --git a/ctf-reader-proto/ctf-fs-metadata.c b/ctf-reader-proto/ctf-fs-metadata.c new file mode 100644 index 00000000..bfe24728 --- /dev/null +++ b/ctf-reader-proto/ctf-fs-metadata.c @@ -0,0 +1,404 @@ +/* + * Copyright 2016 - Philippe Proulx + * Copyright 2010-2011 - EfficiOS Inc. and Linux Foundation + * + * Some functions are based on older functions written by 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 + +#define PRINT_ERR_STREAM ctf_fs->error_fp +#define PRINT_PREFIX "ctf-fs-metadata" +#include "print.h" + +#include "ctf-fs.h" +#include "ctf-fs-file.h" +#include "metadata-parsing/ctf-ast.h" +#include "metadata-parsing/ctf-scanner.h" + +#define TSDL_MAGIC 0x75d11d57 + +struct packet_header { + uint32_t magic; + uint8_t uuid[16]; + uint32_t checksum; + uint32_t content_size; + uint32_t packet_size; + uint8_t compression_scheme; + uint8_t encryption_scheme; + uint8_t checksum_scheme; + uint8_t major; + uint8_t minor; +} __attribute__((__packed__)); + +static struct ctf_fs_file *get_file(struct ctf_fs *ctf_fs, + const char *trace_path) +{ + struct ctf_fs_file *file = ctf_fs_file_create(ctf_fs); + + if (!file) { + goto error; + } + + g_string_append(file->path, trace_path); + g_string_append(file->path, "/" CTF_FS_METADATA_FILENAME); + + if (ctf_fs_file_open(ctf_fs, file, "rb")) { + goto error; + } + + goto end; + +error: + if (file) { + ctf_fs_file_destroy(file); + file = NULL; + } + +end: + return file; +} + +static +bool is_packetized(struct ctf_fs *ctf_fs, struct ctf_fs_file *file) +{ + uint32_t magic; + size_t len; + int ret = 0; + + len = fread(&magic, sizeof(magic), 1, file->fp); + if (len != 1) { + goto end; + } + + if (magic == TSDL_MAGIC) { + ret = 1; + ctf_fs->metadata.bo = BYTE_ORDER; + } else if (magic == GUINT32_SWAP_LE_BE(TSDL_MAGIC)) { + ret = 1; + ctf_fs->metadata.bo = BYTE_ORDER == BIG_ENDIAN ? + LITTLE_ENDIAN : BIG_ENDIAN; + } + +end: + rewind(file->fp); + + return ret; +} + +static +bool is_version_valid(unsigned int major, unsigned int minor) +{ + return major == 1 && minor == 8; +} + +static +int decode_packet(struct ctf_fs *ctf_fs, FILE *in_fp, FILE *out_fp) +{ + struct packet_header header; + size_t readlen, writelen, toread; + uint8_t buf[512 + 1]; /* + 1 for debug-mode \0 */ + int ret = 0; + + readlen = fread(&header, sizeof(header), 1, in_fp); + if (feof(in_fp) != 0) { + goto end; + } + if (readlen < 1) { + goto error; + } + + if (ctf_fs->metadata.bo != BYTE_ORDER) { + header.magic = GUINT32_SWAP_LE_BE(header.magic); + header.checksum = GUINT32_SWAP_LE_BE(header.checksum); + header.content_size = GUINT32_SWAP_LE_BE(header.content_size); + header.packet_size = GUINT32_SWAP_LE_BE(header.packet_size); + } + + if (header.compression_scheme) { + PERR("Metadata packet compression not supported yet\n"); + goto error; + } + + if (header.encryption_scheme) { + PERR("Metadata packet encryption not supported yet\n"); + goto error; + } + + if (header.checksum || header.checksum_scheme) { + PERR("Metadata packet checksum verification not supported yet\n"); + goto error; + } + + if (!is_version_valid(header.major, header.minor)) { + PERR("Invalid metadata version: %u.%u\n", header.major, + header.minor); + goto error; + } + + /* Set expected trace UUID if not set; otherwise validate it */ + if (!ctf_fs->metadata.is_uuid_set) { + memcpy(ctf_fs->metadata.uuid, header.uuid, sizeof(header.uuid)); + ctf_fs->metadata.is_uuid_set = true; + } else if (bt_uuid_compare(header.uuid, ctf_fs->metadata.uuid)) { + PERR("Metadata UUID mismatch between packets of the same file\n"); + goto error; + } + + if ((header.content_size / CHAR_BIT) < sizeof(header)) { + PERR("Bad metadata packet content size: %u\n", + header.content_size); + goto error; + } + + toread = header.content_size / CHAR_BIT - sizeof(header); + + for (;;) { + readlen = fread(buf, sizeof(uint8_t), + MIN(sizeof(buf) - 1, toread), in_fp); + if (ferror(in_fp)) { + PERR("Cannot read metadata packet buffer (at position %ld)\n", + ftell(in_fp)); + goto error; + } + + writelen = fwrite(buf, sizeof(uint8_t), readlen, out_fp); + if (writelen < readlen || ferror(out_fp)) { + PERR("Cannot write decoded metadata text to buffer\n"); + goto error; + } + + toread -= readlen; + if (toread == 0) { + int fseek_ret; + + /* Read leftover padding */ + toread = (header.packet_size - header.content_size) / + CHAR_BIT; + fseek_ret = fseek(in_fp, toread, SEEK_CUR); + if (fseek_ret < 0) { + PWARN("Missing padding at the end of the metadata file\n"); + } + + break; + } + } + + goto end; + +error: + ret = -1; + +end: + return ret; +} + +static +int packetized_file_to_buf(struct ctf_fs *ctf_fs, struct ctf_fs_file *file, + uint8_t **buf) +{ + FILE *out_fp; + size_t size; + int ret = 0; + int tret; + size_t packet_index = 0; + + out_fp = babeltrace_open_memstream((char **) buf, &size); + if (out_fp == NULL) { + PERR("Cannot open memory stream: %s\n", strerror(errno)); + goto error; + } + + for (;;) { + if (feof(file->fp) != 0) { + break; + } + + tret = decode_packet(ctf_fs, file->fp, out_fp); + if (tret) { + PERR("Cannot decode packet #%zu\n", packet_index); + goto error; + } + + packet_index++; + } + + /* Make sure the whole string ends with a null character */ + tret = fputc('\0', out_fp); + if (tret == EOF) { + PERR("Cannot append '\\0' to the decoded metadata buffer\n"); + goto error; + } + + /* Close stream, which also flushes the buffer */ + ret = babeltrace_close_memstream((char **) buf, &size, out_fp); + if (ret < 0) { + PERR("Cannot close memory stream: %s\n", strerror(errno)); + goto error; + } + + goto end; + +error: + ret = -1; + + if (out_fp) { + babeltrace_close_memstream((char **) buf, &size, out_fp); + } + + if (*buf) { + free(*buf); + } + +end: + return ret; +} + +void ctf_fs_metadata_set_trace(struct ctf_fs *ctf_fs) +{ + int ret = 0; + struct ctf_fs_file *file = get_file(ctf_fs, ctf_fs->trace_path->str); + struct ctf_scanner *scanner = NULL; + uint8_t *buf = NULL; + char *cbuf; + + if (!file) { + PERR("Cannot create metadata file object\n"); + goto error; + } + + if (babeltrace_debug) { + // yydebug = 1; + } + + if (is_packetized(ctf_fs, file)) { + PDBG("Metadata file \"%s\" is packetized\n", file->path->str); + ret = packetized_file_to_buf(ctf_fs, file, &buf); + if (ret) { + // log: details + goto error; + } + + cbuf = (char *) buf; + ctf_fs->metadata.text = (char *) cbuf; + + /* Convert the real file pointer to a memory file pointer */ + ret = fclose(file->fp); + file->fp = NULL; + if (ret) { + PERR("Cannot close file \"%s\": %s\n", file->path->str, + strerror(errno)); + goto error; + } + + file->fp = babeltrace_fmemopen(cbuf, strlen(cbuf), "rb"); + if (!file->fp) { + PERR("Cannot memory-open metadata buffer: %s\n", + strerror(errno)); + goto error; + } + } else { + unsigned int major, minor; + ssize_t nr_items; + + PDBG("Metadata file \"%s\" is plain text\n", file->path->str); + + /* Check text-only metadata header and version */ + nr_items = fscanf(file->fp, "/* CTF %10u.%10u", &major, &minor); + if (nr_items < 2) { + PWARN("Ill-shapen or missing \"/* CTF major.minor\" header in plain text metadata file\n"); + } + PDBG("Metadata version: %u.%u\n", major, minor); + + if (!is_version_valid(major, minor)) { + PERR("Invalid metadata version: %u.%u\n", major, minor); + goto error; + } + + rewind(file->fp); + } + + /* Allocate a scanner and append the metadata text content */ + scanner = ctf_scanner_alloc(); + if (!scanner) { + PERR("Cannot allocate a metadata lexical scanner\n"); + goto error; + } + + ret = ctf_scanner_append_ast(scanner, file->fp); + if (ret) { + PERR("Cannot create the metadata AST\n"); + goto error; + } + + ret = ctf_visitor_semantic_check(stderr, 0, &scanner->ast->root); + if (ret) { + PERR("Metadata semantic validation failed\n"); + goto error; + } + + ret = ctf_visitor_generate_ir(ctf_fs->error_fp, &scanner->ast->root, + &ctf_fs->metadata.trace); + if (ret) { + PERR("Cannot create trace object from metadata AST\n"); + goto error; + } + + goto end; + +error: + if (ctf_fs->metadata.text) { + free(ctf_fs->metadata.text); + ctf_fs->metadata.text = NULL; + } + +end: + if (file) { + ctf_fs_file_destroy(file); + } + + if (scanner) { + ctf_scanner_free(scanner); + } +} + +int ctf_fs_metadata_init(struct ctf_fs_metadata *metadata) +{ + /* Nothing to initialize for the moment */ + return 0; +} + +void ctf_fs_metadata_deinit(struct ctf_fs_metadata *metadata) +{ + if (metadata->text) { + free(metadata->text); + } + + if (metadata->trace) { + BT_PUT(metadata->trace); + } +} diff --git a/ctf-reader-proto/ctf-fs-metadata.h b/ctf-reader-proto/ctf-fs-metadata.h new file mode 100644 index 00000000..ed9b7853 --- /dev/null +++ b/ctf-reader-proto/ctf-fs-metadata.h @@ -0,0 +1,52 @@ +#ifndef CTF_FS_METADATA_H +#define CTF_FS_METADATA_H + +/* + * Copyright 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 + +#define CTF_FS_METADATA_FILENAME "metadata" + +struct ctf_fs; + +struct ctf_fs_metadata { + struct bt_ctf_trace *trace; + uint8_t uuid[16]; + bool is_uuid_set; + int bo; + char *text; +}; + +BT_HIDDEN +int ctf_fs_metadata_init(struct ctf_fs_metadata *metadata); + +BT_HIDDEN +void ctf_fs_metadata_deinit(struct ctf_fs_metadata *metadata); + +BT_HIDDEN +void ctf_fs_metadata_set_trace(struct ctf_fs *ctf_fs); + +#endif /* CTF_FS_METADATA_H */ diff --git a/ctf-reader-proto/ctf-fs.c b/ctf-reader-proto/ctf-fs.c new file mode 100644 index 00000000..5bb022d8 --- /dev/null +++ b/ctf-reader-proto/ctf-fs.c @@ -0,0 +1,150 @@ +/* + * Copyright 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 + +#define PRINT_ERR_STREAM ctf_fs->error_fp +#define PRINT_PREFIX "ctf-fs" +#include "print.h" + +#include "ctf-fs.h" +#include "ctf-fs-file.h" +#include "ctf-fs-metadata.h" +#include "ctf-fs-data-stream.h" + +bool ctf_fs_debug = false; + +static void ctf_fs_destroy(struct ctf_fs *ctf_fs) +{ + if (!ctf_fs) { + return; + } + + if (ctf_fs->trace_path) { + g_string_free(ctf_fs->trace_path, TRUE); + } + + ctf_fs_metadata_deinit(&ctf_fs->metadata); + ctf_fs_data_stream_deinit(&ctf_fs->data_stream); + g_free(ctf_fs); +} + +static struct ctf_fs *ctf_fs_create(const char *trace_path) +{ + struct ctf_fs *ctf_fs = g_new0(struct ctf_fs, 1); + + if (!ctf_fs) { + goto error; + } + + ctf_fs->trace_path = g_string_new(trace_path); + if (!ctf_fs->trace_path) { + goto error; + } + + ctf_fs->error_fp = stderr; + ctf_fs->page_size = getpagesize(); + + if (ctf_fs_metadata_init(&ctf_fs->metadata)) { + PERR("Cannot initialize metadata structure\n"); + goto error; + } + + if (ctf_fs_data_stream_init(ctf_fs, &ctf_fs->data_stream)) { + PERR("Cannot initialize data stream structure\n"); + goto error; + } + + goto end; + +error: + ctf_fs_destroy(ctf_fs); + ctf_fs = NULL; + +end: + return ctf_fs; +} + +void ctf_fs_init(void) +{ + if (g_strcmp0(getenv("CTF_FS_DEBUG"), "1") == 0) { + ctf_fs_debug = true; + } +} + +void ctf_fs_test(const char *trace_path) +{ + ctf_fs_init(); + + struct ctf_fs *ctf_fs = ctf_fs_create(trace_path); + struct bt_ctf_notif_iter_notif *notification; + + if (!ctf_fs) { + return; + } + + /* Set trace from metadata file */ + ctf_fs_metadata_set_trace(ctf_fs); + ctf_fs_data_stream_open_streams(ctf_fs); + + while (true) { + int ret = ctf_fs_data_stream_get_next_notification(ctf_fs, ¬ification); + assert(ret == 0); + + if (!notification) { + goto end; + } + + switch (notification->type) { + case BT_CTF_NOTIF_ITER_NOTIF_NEW_PACKET: + { + struct bt_ctf_notif_iter_notif_new_packet *notif = + (struct bt_ctf_notif_iter_notif_new_packet *) notification; + break; + } + case BT_CTF_NOTIF_ITER_NOTIF_EVENT: + { + struct bt_ctf_notif_iter_notif_event *notif = + (struct bt_ctf_notif_iter_notif_event *) notification; + break; + } + case BT_CTF_NOTIF_ITER_NOTIF_END_OF_PACKET: + { + struct bt_ctf_notif_iter_notif_end_of_packet *notif = + (struct bt_ctf_notif_iter_notif_end_of_packet *) notification; + break; + } + default: + break; + } + + bt_ctf_notif_iter_notif_destroy(notification); + } + +end: + ctf_fs_destroy(ctf_fs); +} diff --git a/ctf-reader-proto/ctf-fs.h b/ctf-reader-proto/ctf-fs.h new file mode 100644 index 00000000..3206e9e9 --- /dev/null +++ b/ctf-reader-proto/ctf-fs.h @@ -0,0 +1,48 @@ +#ifndef CTF_FS_H +#define CTF_FS_H + +/* + * Copyright 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 "ctf-fs-metadata.h" +#include "ctf-fs-data-stream.h" + +extern bool ctf_fs_debug; + +struct ctf_fs { + GString *trace_path; + FILE *error_fp; + int page_size; + struct ctf_fs_metadata metadata; + struct ctf_fs_data_stream data_stream; +}; + +void ctf_fs_test(const char *trace_path); + +#endif /* CTF_FS_H */ diff --git a/ctf-reader-proto/ctf-notif-iter.gdb b/ctf-reader-proto/ctf-notif-iter.gdb new file mode 100644 index 00000000..b5369436 --- /dev/null +++ b/ctf-reader-proto/ctf-notif-iter.gdb @@ -0,0 +1,22 @@ +define ctf-notif-iter-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 %3s\n", "pos", "base addr", "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 <-- top\n", $stack_at, \ + $stack_entry->base, $stack_entry->index + else + printf "%3d %10p %3d\n", $stack_at, \ + $stack_entry->base, $stack_entry->index + end + set $stack_at = $stack_at - 1 + end + end +end diff --git a/ctf-reader-proto/ctf-notif-iter/Makefile.am b/ctf-reader-proto/ctf-notif-iter/Makefile.am new file mode 100644 index 00000000..fe901365 --- /dev/null +++ b/ctf-reader-proto/ctf-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 = 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 new file mode 100644 index 00000000..9fefbd8f --- /dev/null +++ b/ctf-reader-proto/ctf-notif-iter/ctf-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 "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 new file mode 100644 index 00000000..ed1dbbcd --- /dev/null +++ b/ctf-reader-proto/ctf-notif-iter/ctf-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/ctf-reader-proto/ctf-notif-iter/print.h b/ctf-reader-proto/ctf-notif-iter/print.h new file mode 100644 index 00000000..bb5c62a3 --- /dev/null +++ b/ctf-reader-proto/ctf-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 */ diff --git a/ctf-reader-proto/metadata-parsing/Makefile.am b/ctf-reader-proto/metadata-parsing/Makefile.am new file mode 100644 index 00000000..2195aff3 --- /dev/null +++ b/ctf-reader-proto/metadata-parsing/Makefile.am @@ -0,0 +1,38 @@ +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 new file mode 100644 index 00000000..c8843270 --- /dev/null +++ b/ctf-reader-proto/metadata-parsing/ctf-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/ctf-reader-proto/metadata-parsing/ctf-lexer.l b/ctf-reader-proto/metadata-parsing/ctf-lexer.l new file mode 100644 index 00000000..6b605a0f --- /dev/null +++ b/ctf-reader-proto/metadata-parsing/ctf-lexer.l @@ -0,0 +1,142 @@ +%{ +/* + * 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 new file mode 100644 index 00000000..821f0f74 --- /dev/null +++ b/ctf-reader-proto/metadata-parsing/ctf-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 "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 new file mode 100644 index 00000000..9b9e3631 --- /dev/null +++ b/ctf-reader-proto/metadata-parsing/ctf-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/ctf-reader-proto/metadata-parsing/ctf-scanner.h b/ctf-reader-proto/metadata-parsing/ctf-scanner.h new file mode 100644 index 00000000..51484a1d --- /dev/null +++ b/ctf-reader-proto/metadata-parsing/ctf-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 "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 new file mode 100644 index 00000000..714a4528 --- /dev/null +++ b/ctf-reader-proto/metadata-parsing/ctf-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 "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_empty(); + 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 new file mode 100644 index 00000000..037496af --- /dev/null +++ b/ctf-reader-proto/metadata-parsing/ctf-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 "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 new file mode 100644 index 00000000..96776a95 --- /dev/null +++ b/ctf-reader-proto/metadata-parsing/ctf-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 "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 new file mode 100644 index 00000000..80877220 --- /dev/null +++ b/ctf-reader-proto/metadata-parsing/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/ctf-reader-proto/metadata-parsing/objstack.h b/ctf-reader-proto/metadata-parsing/objstack.h new file mode 100644 index 00000000..c026eb54 --- /dev/null +++ b/ctf-reader-proto/metadata-parsing/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/ctf-reader-proto/print.h b/ctf-reader-proto/print.h new file mode 100644 index 00000000..4c47f620 --- /dev/null +++ b/ctf-reader-proto/print.h @@ -0,0 +1,59 @@ +#ifndef CTF_FS_PRINT_H +#define CTF_FS_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 "ctf-fs.h" + +#define PERR(fmt, ...) \ + do { \ + if (PRINT_ERR_STREAM) { \ + fprintf(PRINT_ERR_STREAM, \ + "Error: " PRINT_PREFIX ": " fmt, \ + ##__VA_ARGS__); \ + } \ + } while (0) + +#define PWARN(fmt, ...) \ + do { \ + if (PRINT_ERR_STREAM) { \ + fprintf(PRINT_ERR_STREAM, \ + "Warning: " PRINT_PREFIX ": " fmt, \ + ##__VA_ARGS__); \ + } \ + } while (0) + +#define PDBG(fmt, ...) \ + do { \ + if (ctf_fs_debug) { \ + fprintf(stderr, \ + "Debug: " PRINT_PREFIX ": " fmt, \ + ##__VA_ARGS__); \ + } \ + } while (0) + +#endif /* CTF_FS_PRINT_H */ diff --git a/ctf-reader-proto/protorectoral.c b/ctf-reader-proto/protorectoral.c new file mode 100644 index 00000000..2844ee83 --- /dev/null +++ b/ctf-reader-proto/protorectoral.c @@ -0,0 +1,33 @@ +/* + * Copyright 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 "ctf-fs.h" + +int main(int main, char *argv[]) +{ + ctf_fs_test(argv[1]); + + return 0; +} -- 2.34.1