+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2010 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Copyright 2015-2018 Philippe Proulx <philippe.proulx@efficios.com>
+ *
+ * Common Trace Format metadata visitor (generates CTF IR objects).
+ */
+
+#define BT_COMP_LOG_SELF_COMP (ctx->log_cfg.self_comp)
+#define BT_COMP_LOG_SELF_COMP_CLASS (ctx->log_cfg.self_comp_class)
+#define BT_LOG_OUTPUT_LEVEL (ctx->log_cfg.log_level)
+#define BT_LOG_TAG "PLUGIN/CTF/META/IR-VISITOR"
+#include "logging/comp-logging.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include "common/assert.h"
+#include <glib.h>
+#include <inttypes.h>
+#include <errno.h>
+#include "common/common.h"
+#include "common/uuid.h"
+#include "compat/endian.h"
+#include <babeltrace2/babeltrace.h>
+
+#include "logging.hpp"
+#include "scanner.hpp"
+#include "ast.hpp"
+#include "decoder.hpp"
+#include "ctf-meta.hpp"
+#include "ctf-meta-visitors.hpp"
+
+/* Bit value (left shift) */
+#define _BV(_val) (1 << (_val))
+
+/* Bit is set in a set of bits */
+#define _IS_SET(_set, _mask) (*(_set) & (_mask))
+
+/* Set bit in a set of bits */
+#define _SET(_set, _mask) (*(_set) |= (_mask))
+
+/* Try to push scope, or go to the `error` label */
+#define _TRY_PUSH_SCOPE_OR_GOTO_ERROR() \
+ do { \
+ ret = ctx_push_scope(ctx); \
+ if (ret) { \
+ _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot push scope."); \
+ goto error; \
+ } \
+ } while (0)
+
+/* Bits for verifying existing attributes in various declarations */
+enum {
+ _CLOCK_NAME_SET = _BV(0),
+ _CLOCK_UUID_SET = _BV(1),
+ _CLOCK_FREQ_SET = _BV(2),
+ _CLOCK_PRECISION_SET = _BV(3),
+ _CLOCK_OFFSET_S_SET = _BV(4),
+ _CLOCK_OFFSET_SET = _BV(5),
+ _CLOCK_ABSOLUTE_SET = _BV(6),
+ _CLOCK_DESCRIPTION_SET = _BV(7),
+};
+
+enum {
+ _INTEGER_ALIGN_SET = _BV(0),
+ _INTEGER_SIZE_SET = _BV(1),
+ _INTEGER_BASE_SET = _BV(2),
+ _INTEGER_ENCODING_SET = _BV(3),
+ _INTEGER_BYTE_ORDER_SET = _BV(4),
+ _INTEGER_SIGNED_SET = _BV(5),
+ _INTEGER_MAP_SET = _BV(6),
+};
+
+enum {
+ _FLOAT_ALIGN_SET = _BV(0),
+ _FLOAT_MANT_DIG_SET = _BV(1),
+ _FLOAT_EXP_DIG_SET = _BV(2),
+ _FLOAT_BYTE_ORDER_SET = _BV(3),
+};
+
+enum {
+ _STRING_ENCODING_SET = _BV(0),
+};
+
+enum {
+ _TRACE_MINOR_SET = _BV(0),
+ _TRACE_MAJOR_SET = _BV(1),
+ _TRACE_BYTE_ORDER_SET = _BV(2),
+ _TRACE_UUID_SET = _BV(3),
+ _TRACE_PACKET_HEADER_SET = _BV(4),
+};
+
+enum {
+ _STREAM_ID_SET = _BV(0),
+ _STREAM_PACKET_CONTEXT_SET = _BV(1),
+ _STREAM_EVENT_HEADER_SET = _BV(2),
+ _STREAM_EVENT_CONTEXT_SET = _BV(3),
+};
+
+enum {
+ _EVENT_NAME_SET = _BV(0),
+ _EVENT_ID_SET = _BV(1),
+ _EVENT_MODEL_EMF_URI_SET = _BV(2),
+ _EVENT_STREAM_ID_SET = _BV(3),
+ _EVENT_LOG_LEVEL_SET = _BV(4),
+ _EVENT_CONTEXT_SET = _BV(5),
+ _EVENT_FIELDS_SET = _BV(6),
+};
+
+enum loglevel {
+ LOG_LEVEL_EMERG = 0,
+ LOG_LEVEL_ALERT = 1,
+ LOG_LEVEL_CRIT = 2,
+ LOG_LEVEL_ERR = 3,
+ LOG_LEVEL_WARNING = 4,
+ LOG_LEVEL_NOTICE = 5,
+ LOG_LEVEL_INFO = 6,
+ LOG_LEVEL_DEBUG_SYSTEM = 7,
+ LOG_LEVEL_DEBUG_PROGRAM = 8,
+ LOG_LEVEL_DEBUG_PROCESS = 9,
+ LOG_LEVEL_DEBUG_MODULE = 10,
+ LOG_LEVEL_DEBUG_UNIT = 11,
+ LOG_LEVEL_DEBUG_FUNCTION = 12,
+ LOG_LEVEL_DEBUG_LINE = 13,
+ LOG_LEVEL_DEBUG = 14,
+ _NR_LOGLEVELS = 15,
+};
+
+/* Prefixes of class aliases */
+#define _PREFIX_ALIAS 'a'
+#define _PREFIX_ENUM 'e'
+#define _PREFIX_STRUCT 's'
+#define _PREFIX_VARIANT 'v'
+
+/* First entry in a BT list */
+#define _BT_LIST_FIRST_ENTRY(_ptr, _class, _member) \
+ bt_list_entry((_ptr)->next, _class, _member)
+
+#define _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(_node, _attr, _entity) \
+ _BT_COMP_LOGE_APPEND_CAUSE_LINENO((_node)->lineno, \
+ "Duplicate attribute in %s: attr-name=\"%s\"", \
+ _entity, _attr)
+
+#define _BT_COMP_LOGE_NODE(_node, _msg, args...) \
+ _BT_COMP_LOGE_LINENO((_node)->lineno, _msg, ## args)
+
+#define _BT_COMP_LOGE_APPEND_CAUSE_NODE(_node, _msg, args...) \
+ _BT_COMP_LOGE_APPEND_CAUSE_LINENO((_node)->lineno, _msg, ## args)
+
+#define _BT_COMP_LOGW_NODE(_node, _msg, args...) \
+ _BT_COMP_LOGW_LINENO((_node)->lineno, _msg, ## args)
+
+#define _BT_COMP_LOGT_NODE(_node, _msg, args...) \
+ _BT_COMP_LOGT_LINENO((_node)->lineno, _msg, ## args)
+
+/*
+ * Declaration scope of a visitor context. This represents a TSDL
+ * lexical scope, so that aliases and named structures, variants,
+ * and enumerations may be registered and looked up hierarchically.
+ */
+struct ctx_decl_scope {
+ /*
+ * Alias name to field class.
+ *
+ * GQuark -> struct ctf_field_class * (owned by this)
+ */
+ GHashTable *decl_map;
+
+ /* Parent scope; NULL if this is the root declaration scope */
+ struct ctx_decl_scope *parent_scope;
+};
+
+/*
+ * Visitor context (private).
+ */
+struct ctf_visitor_generate_ir {
+ struct meta_log_config log_cfg;
+
+ /* Trace IR trace class being filled (owned by this) */
+ bt_trace_class *trace_class;
+
+ /* CTF meta trace being filled (owned by this) */
+ struct ctf_trace_class *ctf_tc;
+
+ /* Current declaration scope (top of the stack) (owned by this) */
+ struct ctx_decl_scope *current_scope;
+
+ /* True if trace declaration is visited */
+ bool is_trace_visited;
+
+ /* True if this is an LTTng trace */
+ bool is_lttng;
+
+ /* Config passed by the user */
+ struct ctf_metadata_decoder_config decoder_config;
+};
+
+/*
+ * Visitor (public).
+ */
+struct ctf_visitor_generate_ir;
+
+/**
+ * Creates a new declaration scope.
+ *
+ * @param par_scope Parent scope (NULL if creating a root scope)
+ * @returns New declaration scope, or NULL on error
+ */
+static
+struct ctx_decl_scope *ctx_decl_scope_create(struct ctf_visitor_generate_ir *ctx,
+ struct ctx_decl_scope *par_scope)
+{
+ struct ctx_decl_scope *scope;
+
+ scope = g_new(struct ctx_decl_scope, 1);
+ if (!scope) {
+ _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Failed to allocate one declaration scope.");
+ goto end;
+ }
+
+ scope->decl_map = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+ NULL, (GDestroyNotify) ctf_field_class_destroy);
+ scope->parent_scope = par_scope;
+
+end:
+ return scope;
+}
+
+/**
+ * Destroys a declaration scope.
+ *
+ * This function does not destroy the parent scope.
+ *
+ * @param scope Scope to destroy
+ */
+static
+void ctx_decl_scope_destroy(struct ctx_decl_scope *scope)
+{
+ if (!scope) {
+ goto end;
+ }
+
+ g_hash_table_destroy(scope->decl_map);
+ g_free(scope);
+
+end:
+ return;
+}
+
+/**
+ * Returns the GQuark of a prefixed alias.
+ *
+ * @param prefix Prefix character
+ * @param name Name
+ * @returns Associated GQuark, or 0 on error
+ */
+static
+GQuark get_prefixed_named_quark(struct ctf_visitor_generate_ir *ctx, char prefix,
+ const char *name)
+{
+ GQuark qname = 0;
+
+ BT_ASSERT(name);
+
+ /* Prefix character + original string + '\0' */
+ char *prname = g_new(char, strlen(name) + 2);
+ if (!prname) {
+ _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Failed to allocate a string.");
+ goto end;
+ }
+
+ sprintf(prname, "%c%s", prefix, name);
+ qname = g_quark_from_string(prname);
+ g_free(prname);
+
+end:
+ return qname;
+}
+
+/**
+ * Looks up a prefixed class alias within a declaration scope.
+ *
+ * @param scope Declaration scope
+ * @param prefix Prefix character
+ * @param name Alias name
+ * @param levels Number of levels to dig into (-1 means infinite)
+ * @param copy True to return a copy
+ * @returns Declaration (owned by caller if \p copy is true),
+ * or NULL if not found
+ */
+static
+struct ctf_field_class *ctx_decl_scope_lookup_prefix_alias(
+ struct ctf_visitor_generate_ir *ctx, struct ctx_decl_scope *scope, char prefix,
+ const char *name, int levels, bool copy)
+{
+ GQuark qname = 0;
+ int cur_levels = 0;
+ struct ctf_field_class *decl = NULL;
+ struct ctx_decl_scope *cur_scope = scope;
+
+ BT_ASSERT(scope);
+ BT_ASSERT(name);
+ qname = get_prefixed_named_quark(ctx, prefix, name);
+ if (!qname) {
+ goto end;
+ }
+
+ if (levels < 0) {
+ levels = INT_MAX;
+ }
+
+ while (cur_scope && cur_levels < levels) {
+ decl = (ctf_field_class *) g_hash_table_lookup(cur_scope->decl_map,
+ (gconstpointer) GUINT_TO_POINTER(qname));
+ if (decl) {
+ /* Caller's reference */
+ if (copy) {
+ decl = ctf_field_class_copy(decl);
+ BT_ASSERT(decl);
+ }
+
+ goto end;
+ }
+
+ cur_scope = cur_scope->parent_scope;
+ cur_levels++;
+ }
+
+end:
+ return decl;
+}
+
+/**
+ * Looks up a class alias within a declaration scope.
+ *
+ * @param scope Declaration scope
+ * @param name Alias name
+ * @param levels Number of levels to dig into (-1 means infinite)
+ * @param copy True to return a copy
+ * @returns Declaration (owned by caller if \p copy is true),
+ * or NULL if not found
+ */
+static
+struct ctf_field_class *ctx_decl_scope_lookup_alias(struct ctf_visitor_generate_ir *ctx,
+ struct ctx_decl_scope *scope, const char *name, int levels,
+ bool copy)
+{
+ return ctx_decl_scope_lookup_prefix_alias(ctx, scope, _PREFIX_ALIAS,
+ name, levels, copy);
+}
+
+/**
+ * Looks up an enumeration within a declaration scope.
+ *
+ * @param scope Declaration scope
+ * @param name Enumeration name
+ * @param levels Number of levels to dig into (-1 means infinite)
+ * @param copy True to return a copy
+ * @returns Declaration (owned by caller if \p copy is true),
+ * or NULL if not found
+ */
+static
+struct ctf_field_class_enum *ctx_decl_scope_lookup_enum(struct ctf_visitor_generate_ir *ctx,
+ struct ctx_decl_scope *scope, const char *name, int levels,
+ bool copy)
+{
+ return ctf_field_class_as_enum(ctx_decl_scope_lookup_prefix_alias(ctx, scope,
+ _PREFIX_ENUM, name, levels, copy));
+}
+
+/**
+ * Looks up a structure within a declaration scope.
+ *
+ * @param scope Declaration scope
+ * @param name Structure name
+ * @param levels Number of levels to dig into (-1 means infinite)
+ * @param copy True to return a copy
+ * @returns Declaration (owned by caller if \p copy is true),
+ * or NULL if not found
+ */
+static
+struct ctf_field_class_struct *ctx_decl_scope_lookup_struct(struct ctf_visitor_generate_ir *ctx,
+ struct ctx_decl_scope *scope, const char *name, int levels,
+ bool copy)
+{
+ return ctf_field_class_as_struct(ctx_decl_scope_lookup_prefix_alias(ctx, scope,
+ _PREFIX_STRUCT, name, levels, copy));
+}
+
+/**
+ * Looks up a variant within a declaration scope.
+ *
+ * @param scope Declaration scope
+ * @param name Variant name
+ * @param levels Number of levels to dig into (-1 means infinite)
+ * @param copy True to return a copy
+ * @returns Declaration (owned by caller if \p copy is true),
+ * or NULL if not found
+ */
+static
+struct ctf_field_class_variant *ctx_decl_scope_lookup_variant(struct ctf_visitor_generate_ir *ctx,
+ struct ctx_decl_scope *scope, const char *name, int levels,
+ bool copy)
+{
+ return ctf_field_class_as_variant(ctx_decl_scope_lookup_prefix_alias(ctx, scope,
+ _PREFIX_VARIANT, name, levels, copy));
+}
+
+/**
+ * Registers a prefixed class alias within a declaration scope.
+ *
+ * @param scope Declaration scope
+ * @param prefix Prefix character
+ * @param name Alias name (non-NULL)
+ * @param decl Field class to register (copied)
+ * @returns 0 if registration went okay, negative value otherwise
+ */
+static
+int ctx_decl_scope_register_prefix_alias(struct ctf_visitor_generate_ir *ctx,
+ struct ctx_decl_scope *scope, char prefix, const char *name,
+ struct ctf_field_class *decl)
+{
+ int ret = 0;
+ GQuark qname = 0;
+
+ BT_ASSERT(scope);
+ BT_ASSERT(name);
+ BT_ASSERT(decl);
+ qname = get_prefixed_named_quark(ctx, prefix, name);
+ if (!qname) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ /* Make sure alias does not exist in local scope */
+ if (ctx_decl_scope_lookup_prefix_alias(ctx, scope, prefix, name, 1,
+ false)) {
+ ret = -EEXIST;
+ goto end;
+ }
+
+ decl = ctf_field_class_copy(decl);
+ BT_ASSERT(decl);
+ g_hash_table_insert(scope->decl_map, GUINT_TO_POINTER(qname), decl);
+
+end:
+ return ret;
+}
+
+/**
+ * Registers a class alias within a declaration scope.
+ *
+ * @param scope Declaration scope
+ * @param name Alias name (non-NULL)
+ * @param decl Field class to register (copied)
+ * @returns 0 if registration went okay, negative value otherwise
+ */
+static
+int ctx_decl_scope_register_alias(struct ctf_visitor_generate_ir *ctx,
+ struct ctx_decl_scope *scope, const char *name, struct ctf_field_class *decl)
+{
+ return ctx_decl_scope_register_prefix_alias(ctx, scope, _PREFIX_ALIAS,
+ name, decl);
+}
+
+/**
+ * Registers an enumeration declaration within a declaration scope.
+ *
+ * @param scope Declaration scope
+ * @param name Enumeration name (non-NULL)
+ * @param decl Enumeration field class to register (copied)
+ * @returns 0 if registration went okay, negative value otherwise
+ */
+static
+int ctx_decl_scope_register_enum(struct ctf_visitor_generate_ir *ctx,
+ struct ctx_decl_scope *scope, const char *name,
+ struct ctf_field_class_enum *decl)
+{
+ return ctx_decl_scope_register_prefix_alias(ctx, scope, _PREFIX_ENUM,
+ name, &decl->base.base.base);
+}
+
+/**
+ * Registers a structure declaration within a declaration scope.
+ *
+ * @param scope Declaration scope
+ * @param name Structure name (non-NULL)
+ * @param decl Structure field class to register (copied)
+ * @returns 0 if registration went okay, negative value otherwise
+ */
+static
+int ctx_decl_scope_register_struct(struct ctf_visitor_generate_ir *ctx,
+ struct ctx_decl_scope *scope, const char *name,
+ struct ctf_field_class_struct *decl)
+{
+ return ctx_decl_scope_register_prefix_alias(ctx, scope, _PREFIX_STRUCT,
+ name, &decl->base);
+}
+
+/**
+ * Registers a variant declaration within a declaration scope.
+ *
+ * @param scope Declaration scope
+ * @param name Variant name (non-NULL)
+ * @param decl Variant field class to register
+ * @returns 0 if registration went okay, negative value otherwise
+ */
+static
+int ctx_decl_scope_register_variant(struct ctf_visitor_generate_ir *ctx,
+ struct ctx_decl_scope *scope, const char *name,
+ struct ctf_field_class_variant *decl)
+{
+ return ctx_decl_scope_register_prefix_alias(ctx, scope, _PREFIX_VARIANT,
+ name, &decl->base);
+}
+
+/**
+ * Destroys a visitor context.
+ *
+ * @param ctx Visitor context to destroy
+ */
+static
+void ctx_destroy(struct ctf_visitor_generate_ir *ctx)
+{
+ struct ctx_decl_scope *scope;
+
+ if (!ctx) {
+ goto end;
+ }
+
+ scope = ctx->current_scope;
+
+ /*
+ * Destroy all scopes, from current one to the root scope.
+ */
+ while (scope) {
+ struct ctx_decl_scope *parent_scope = scope->parent_scope;
+
+ ctx_decl_scope_destroy(scope);
+ scope = parent_scope;
+ }
+
+ bt_trace_class_put_ref(ctx->trace_class);
+
+ if (ctx->ctf_tc) {
+ ctf_trace_class_destroy(ctx->ctf_tc);
+ }
+
+ g_free(ctx);
+
+end:
+ return;
+}
+
+/**
+ * Creates a new visitor context.
+ *
+ * @param trace Associated trace
+ * @returns New visitor context, or NULL on error
+ */
+static
+struct ctf_visitor_generate_ir *ctx_create(
+ const struct ctf_metadata_decoder_config *decoder_config)
+{
+ struct ctf_visitor_generate_ir *ctx = NULL;
+
+ BT_ASSERT(decoder_config);
+
+ ctx = g_new0(struct ctf_visitor_generate_ir, 1);
+ if (!ctx) {
+ BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, decoder_config->log_level,
+ decoder_config->self_comp,
+ "Failed to allocate one visitor context.");
+ goto error;
+ }
+
+ ctx->log_cfg.log_level = decoder_config->log_level;
+ ctx->log_cfg.self_comp = decoder_config->self_comp;
+ ctx->log_cfg.self_comp_class = decoder_config->self_comp_class;
+
+ if (decoder_config->self_comp) {
+ ctx->trace_class = bt_trace_class_create(
+ decoder_config->self_comp);
+ if (!ctx->trace_class) {
+ _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot create empty trace class.");
+ goto error;
+ }
+ }
+
+ ctx->ctf_tc = ctf_trace_class_create();
+ if (!ctx->ctf_tc) {
+ _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot create CTF trace class.");
+ goto error;
+ }
+
+ /* Root declaration scope */
+ ctx->current_scope = ctx_decl_scope_create(ctx, NULL);
+ if (!ctx->current_scope) {
+ _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot create declaration scope.");
+ goto error;
+ }
+
+ ctx->decoder_config = *decoder_config;
+ goto end;
+
+error:
+ ctx_destroy(ctx);
+ ctx = NULL;
+
+end:
+ return ctx;
+}
+
+/**
+ * Pushes a new declaration scope on top of a visitor context's
+ * declaration scope stack.
+ *
+ * @param ctx Visitor context
+ * @returns 0 on success, or a negative value on error
+ */
+static
+int ctx_push_scope(struct ctf_visitor_generate_ir *ctx)
+{
+ int ret = 0;
+ struct ctx_decl_scope *new_scope;
+
+ BT_ASSERT(ctx);
+ new_scope = ctx_decl_scope_create(ctx, ctx->current_scope);
+ if (!new_scope) {
+ _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot create declaration scope.");
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ ctx->current_scope = new_scope;
+
+end:
+ return ret;
+}
+
+static
+void ctx_pop_scope(struct ctf_visitor_generate_ir *ctx)
+{
+ struct ctx_decl_scope *parent_scope = NULL;
+
+ BT_ASSERT(ctx);
+
+ if (!ctx->current_scope) {
+ goto end;
+ }
+
+ parent_scope = ctx->current_scope->parent_scope;
+ ctx_decl_scope_destroy(ctx->current_scope);
+ ctx->current_scope = parent_scope;
+
+end:
+ return;
+}
+
+static
+int visit_field_class_specifier_list(struct ctf_visitor_generate_ir *ctx,
+ struct ctf_node *ts_list, struct ctf_field_class **decl);
+
+static
+int is_unary_string(struct bt_list_head *head)
+{
+ int ret = TRUE;
+ struct ctf_node *node;
+
+ bt_list_for_each_entry(node, head, siblings) {
+ if (node->type != NODE_UNARY_EXPRESSION) {
+ ret = FALSE;
+ }
+
+ if (node->u.unary_expression.type != UNARY_STRING) {
+ ret = FALSE;
+ }
+ }
+
+ return ret;
+}
+
+static
+const char *get_map_clock_name_value(struct bt_list_head *head)
+{
+ int i = 0;
+ struct ctf_node *node;
+ const char *name = NULL;
+
+ bt_list_for_each_entry(node, head, siblings) {
+ char *src_string;
+ int uexpr_type = node->u.unary_expression.type;
+ int uexpr_link = node->u.unary_expression.link;
+ int cond = node->type != NODE_UNARY_EXPRESSION ||
+ uexpr_type != UNARY_STRING ||
+ !((uexpr_link != UNARY_LINK_UNKNOWN) ^ (i == 0));
+ if (cond) {
+ goto error;
+ }
+
+ /* Needs to be chained with . */
+ switch (node->u.unary_expression.link) {
+ case UNARY_DOTLINK:
+ break;
+ case UNARY_ARROWLINK:
+ case UNARY_DOTDOTDOT:
+ goto error;
+ default:
+ break;
+ }
+
+ src_string = node->u.unary_expression.u.string;
+
+ switch (i) {
+ case 0:
+ if (strcmp("clock", src_string)) {
+ goto error;
+ }
+ break;
+ case 1:
+ name = src_string;
+ break;
+ case 2:
+ if (strcmp("value", src_string)) {
+ goto error;
+ }
+ break;
+ default:
+ /* Extra identifier, unknown */
+ goto error;
+ }
+
+ i++;
+ }
+
+ return name;
+
+error:
+ return NULL;
+}
+
+static
+int is_unary_unsigned(struct bt_list_head *head)
+{
+ int ret = TRUE;
+ struct ctf_node *node;
+
+ bt_list_for_each_entry(node, head, siblings) {
+ if (node->type != NODE_UNARY_EXPRESSION) {
+ ret = FALSE;
+ }
+
+ if (node->u.unary_expression.type != UNARY_UNSIGNED_CONSTANT) {
+ ret = FALSE;
+ }
+ }
+
+ return ret;
+}
+
+static
+int get_unary_unsigned(struct ctf_visitor_generate_ir *ctx, struct bt_list_head *head,
+ uint64_t *value)
+{
+ int i = 0;
+ int ret = 0;
+ struct ctf_node *node;
+
+ *value = 0;
+
+ if (bt_list_empty(head)) {
+ ret = -1;
+ goto end;
+ }
+
+ bt_list_for_each_entry(node, head, siblings) {
+ int uexpr_type = node->u.unary_expression.type;
+ int uexpr_link = node->u.unary_expression.link;
+ int cond = node->type != NODE_UNARY_EXPRESSION ||
+ uexpr_type != UNARY_UNSIGNED_CONSTANT ||
+ uexpr_link != UNARY_LINK_UNKNOWN || i != 0;
+ if (cond) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Invalid constant unsigned integer.");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ *value = node->u.unary_expression.u.unsigned_constant;
+ i++;
+ }
+
+end:
+ return ret;
+}
+
+static
+int is_unary_signed(struct bt_list_head *head)
+{
+ int ret = TRUE;
+ struct ctf_node *node;
+
+ bt_list_for_each_entry(node, head, siblings) {
+ if (node->type != NODE_UNARY_EXPRESSION) {
+ ret = FALSE;
+ }
+
+ if (node->u.unary_expression.type != UNARY_SIGNED_CONSTANT) {
+ ret = FALSE;
+ }
+ }
+
+ return ret;
+}
+
+static
+int get_unary_signed(struct bt_list_head *head, int64_t *value)
+{
+ int i = 0;
+ int ret = 0;
+ struct ctf_node *node;
+
+ bt_list_for_each_entry(node, head, siblings) {
+ int uexpr_type = node->u.unary_expression.type;
+ int uexpr_link = node->u.unary_expression.link;
+ int cond = node->type != NODE_UNARY_EXPRESSION ||
+ (uexpr_type != UNARY_UNSIGNED_CONSTANT &&
+ uexpr_type != UNARY_SIGNED_CONSTANT) ||
+ uexpr_link != UNARY_LINK_UNKNOWN || i != 0;
+ if (cond) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ switch (uexpr_type) {
+ case UNARY_UNSIGNED_CONSTANT:
+ *value = (int64_t)
+ node->u.unary_expression.u.unsigned_constant;
+ break;
+ case UNARY_SIGNED_CONSTANT:
+ *value = node->u.unary_expression.u.signed_constant;
+ break;
+ default:
+ ret = -EINVAL;
+ goto end;
+ }
+
+ i++;
+ }
+
+end:
+ return ret;
+}
+
+static
+int get_unary_uuid(struct ctf_visitor_generate_ir *ctx, struct bt_list_head *head,
+ bt_uuid_t uuid)
+{
+ return ctf_ast_get_unary_uuid(head, uuid, ctx->log_cfg.log_level,
+ ctx->log_cfg.self_comp);
+}
+
+static
+int get_boolean(struct ctf_visitor_generate_ir *ctx, struct ctf_node *unary_expr)
+{
+ int ret = 0;
+
+ if (unary_expr->type != NODE_UNARY_EXPRESSION) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(unary_expr,
+ "Expecting unary expression: node-type=%d",
+ unary_expr->type);
+ ret = -EINVAL;
+ goto end;
+ }
+
+ switch (unary_expr->u.unary_expression.type) {
+ case UNARY_UNSIGNED_CONSTANT:
+ ret = (unary_expr->u.unary_expression.u.unsigned_constant != 0);
+ break;
+ case UNARY_SIGNED_CONSTANT:
+ ret = (unary_expr->u.unary_expression.u.signed_constant != 0);
+ break;
+ case UNARY_STRING:
+ {
+ const char *str = unary_expr->u.unary_expression.u.string;
+
+ if (strcmp(str, "true") == 0 || strcmp(str, "TRUE") == 0) {
+ ret = TRUE;
+ } else if (strcmp(str, "false") == 0 || strcmp(str, "FALSE") == 0) {
+ ret = FALSE;
+ } else {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(unary_expr,
+ "Unexpected boolean value: value=\"%s\"", str);
+ ret = -EINVAL;
+ goto end;
+ }
+ break;
+ }
+ default:
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(unary_expr,
+ "Unexpected unary expression type: node-type=%d",
+ unary_expr->u.unary_expression.type);
+ ret = -EINVAL;
+ goto end;
+ }
+
+end:
+ return ret;
+}
+
+static
+enum ctf_byte_order byte_order_from_unary_expr(struct ctf_visitor_generate_ir *ctx,
+ struct ctf_node *unary_expr)
+{
+ const char *str;
+ enum ctf_byte_order bo = CTF_BYTE_ORDER_UNKNOWN;
+
+ if (unary_expr->u.unary_expression.type != UNARY_STRING) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(unary_expr,
+ "\"byte_order\" attribute: expecting `be`, `le`, `network`, or `native`.");
+ goto end;
+ }
+
+ str = unary_expr->u.unary_expression.u.string;
+
+ if (strcmp(str, "be") == 0 || strcmp(str, "network") == 0) {
+ bo = CTF_BYTE_ORDER_BIG;
+ } else if (strcmp(str, "le") == 0) {
+ bo = CTF_BYTE_ORDER_LITTLE;
+ } else if (strcmp(str, "native") == 0) {
+ bo = CTF_BYTE_ORDER_DEFAULT;
+ } else {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(unary_expr,
+ "Unexpected \"byte_order\" attribute value: "
+ "expecting `be`, `le`, `network`, or `native`: value=\"%s\"",
+ str);
+ goto end;
+ }
+
+end:
+ return bo;
+}
+
+static
+enum ctf_byte_order get_real_byte_order(struct ctf_visitor_generate_ir *ctx,
+ struct ctf_node *uexpr)
+{
+ enum ctf_byte_order bo = byte_order_from_unary_expr(ctx, uexpr);
+
+ if (bo == CTF_BYTE_ORDER_DEFAULT) {
+ bo = ctx->ctf_tc->default_byte_order;
+ }
+
+ return bo;
+}
+
+static
+int is_align_valid(uint64_t align)
+{
+ return (align != 0) && !(align & (align - UINT64_C(1)));
+}
+
+static
+int get_class_specifier_name(struct ctf_visitor_generate_ir *ctx,
+ struct ctf_node *cls_specifier, GString *str)
+{
+ int ret = 0;
+
+ if (cls_specifier->type != NODE_TYPE_SPECIFIER) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(cls_specifier,
+ "Unexpected node type: node-type=%d",
+ cls_specifier->type);
+ ret = -EINVAL;
+ goto end;
+ }
+
+ switch (cls_specifier->u.field_class_specifier.type) {
+ case TYPESPEC_VOID:
+ g_string_append(str, "void");
+ break;
+ case TYPESPEC_CHAR:
+ g_string_append(str, "char");
+ break;
+ case TYPESPEC_SHORT:
+ g_string_append(str, "short");
+ break;
+ case TYPESPEC_INT:
+ g_string_append(str, "int");
+ break;
+ case TYPESPEC_LONG:
+ g_string_append(str, "long");
+ break;
+ case TYPESPEC_FLOAT:
+ g_string_append(str, "float");
+ break;
+ case TYPESPEC_DOUBLE:
+ g_string_append(str, "double");
+ break;
+ case TYPESPEC_SIGNED:
+ g_string_append(str, "signed");
+ break;
+ case TYPESPEC_UNSIGNED:
+ g_string_append(str, "unsigned");
+ break;
+ case TYPESPEC_BOOL:
+ g_string_append(str, "bool");
+ break;
+ case TYPESPEC_COMPLEX:
+ g_string_append(str, "_Complex");
+ break;
+ case TYPESPEC_IMAGINARY:
+ g_string_append(str, "_Imaginary");
+ break;
+ case TYPESPEC_CONST:
+ g_string_append(str, "const");
+ break;
+ case TYPESPEC_ID_TYPE:
+ if (cls_specifier->u.field_class_specifier.id_type) {
+ g_string_append(str,
+ cls_specifier->u.field_class_specifier.id_type);
+ }
+ break;
+ case TYPESPEC_STRUCT:
+ {
+ struct ctf_node *node = cls_specifier->u.field_class_specifier.node;
+
+ if (!node->u._struct.name) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Unexpected empty structure field class name.");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ g_string_append(str, "struct ");
+ g_string_append(str, node->u._struct.name);
+ break;
+ }
+ case TYPESPEC_VARIANT:
+ {
+ struct ctf_node *node = cls_specifier->u.field_class_specifier.node;
+
+ if (!node->u.variant.name) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Unexpected empty variant field class name.");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ g_string_append(str, "variant ");
+ g_string_append(str, node->u.variant.name);
+ break;
+ }
+ case TYPESPEC_ENUM:
+ {
+ struct ctf_node *node = cls_specifier->u.field_class_specifier.node;
+
+ if (!node->u._enum.enum_id) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Unexpected empty enumeration field class (`enum`) name.");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ g_string_append(str, "enum ");
+ g_string_append(str, node->u._enum.enum_id);
+ break;
+ }
+ case TYPESPEC_FLOATING_POINT:
+ case TYPESPEC_INTEGER:
+ case TYPESPEC_STRING:
+ default:
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(cls_specifier->u.field_class_specifier.node,
+ "Unexpected field class specifier type: %d",
+ cls_specifier->u.field_class_specifier.type);
+ ret = -EINVAL;
+ goto end;
+ }
+
+end:
+ return ret;
+}
+
+static
+int get_class_specifier_list_name(struct ctf_visitor_generate_ir *ctx,
+ struct ctf_node *cls_specifier_list, GString *str)
+{
+ int ret = 0;
+ struct ctf_node *iter;
+ int alias_item_nr = 0;
+ struct bt_list_head *head =
+ &cls_specifier_list->u.field_class_specifier_list.head;
+
+ bt_list_for_each_entry(iter, head, siblings) {
+ if (alias_item_nr != 0) {
+ g_string_append(str, " ");
+ }
+
+ alias_item_nr++;
+ ret = get_class_specifier_name(ctx, iter, str);
+ if (ret) {
+ goto end;
+ }
+ }
+
+end:
+ return ret;
+}
+
+static
+GQuark create_class_alias_identifier(struct ctf_visitor_generate_ir *ctx,
+ struct ctf_node *cls_specifier_list,
+ struct ctf_node *node_field_class_declarator)
+{
+ int ret;
+ char *str_c;
+ GString *str;
+ GQuark qalias = 0;
+ struct ctf_node *iter;
+ struct bt_list_head *pointers =
+ &node_field_class_declarator->u.field_class_declarator.pointers;
+
+ str = g_string_new("");
+ ret = get_class_specifier_list_name(ctx, cls_specifier_list, str);
+ if (ret) {
+ g_string_free(str, TRUE);
+ goto end;
+ }
+
+ bt_list_for_each_entry(iter, pointers, siblings) {
+ g_string_append(str, " *");
+
+ if (iter->u.pointer.const_qualifier) {
+ g_string_append(str, " const");
+ }
+ }
+
+ str_c = g_string_free(str, FALSE);
+ qalias = g_quark_from_string(str_c);
+ g_free(str_c);
+
+end:
+ return qalias;
+}
+
+static
+int visit_field_class_declarator(struct ctf_visitor_generate_ir *ctx,
+ struct ctf_node *cls_specifier_list,
+ GQuark *field_name, struct ctf_node *node_field_class_declarator,
+ struct ctf_field_class **field_decl,
+ struct ctf_field_class *nested_decl)
+{
+ /*
+ * During this whole function, nested_decl is always OURS,
+ * whereas field_decl is an output which we create, but
+ * belongs to the caller (it is moved).
+ */
+ int ret = 0;
+ *field_decl = NULL;
+
+ /* Validate field class declarator node */
+ if (node_field_class_declarator) {
+ if (node_field_class_declarator->u.field_class_declarator.type ==
+ TYPEDEC_UNKNOWN) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
+ "Unexpected field class declarator type: type=%d",
+ node_field_class_declarator->u.field_class_declarator.type);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* TODO: GCC bitfields not supported yet */
+ if (node_field_class_declarator->u.field_class_declarator.bitfield_len !=
+ NULL) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
+ "GCC bitfields are not supported as of this version.");
+ ret = -EPERM;
+ goto error;
+ }
+ }
+
+ /* Find the right nested declaration if not provided */
+ if (!nested_decl) {
+ struct bt_list_head *pointers =
+ &node_field_class_declarator->u.field_class_declarator.pointers;
+
+ if (node_field_class_declarator && !bt_list_empty(pointers)) {
+ GQuark qalias;
+
+ /*
+ * If we have a pointer declarator, it HAS to
+ * be present in the field class aliases (else
+ * fail).
+ */
+ qalias = create_class_alias_identifier(ctx,
+ cls_specifier_list, node_field_class_declarator);
+ nested_decl =
+ ctx_decl_scope_lookup_alias(ctx,
+ ctx->current_scope,
+ g_quark_to_string(qalias), -1, true);
+ if (!nested_decl) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
+ "Cannot find class alias: name=\"%s\"",
+ g_quark_to_string(qalias));
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (nested_decl->type == CTF_FIELD_CLASS_TYPE_INT) {
+ /* Pointer: force integer's base to 16 */
+ struct ctf_field_class_int *int_fc =
+ ctf_field_class_as_int(nested_decl);
+
+ int_fc->disp_base =
+ BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL;
+ }
+ } else {
+ ret = visit_field_class_specifier_list(ctx,
+ cls_specifier_list, &nested_decl);
+ if (ret) {
+ BT_ASSERT(!nested_decl);
+ goto error;
+ }
+ }
+ }
+
+ BT_ASSERT(nested_decl);
+
+ if (!node_field_class_declarator) {
+ *field_decl = nested_decl;
+ nested_decl = NULL;
+ goto end;
+ }
+
+ if (node_field_class_declarator->u.field_class_declarator.type == TYPEDEC_ID) {
+ if (node_field_class_declarator->u.field_class_declarator.u.id) {
+ const char *id =
+ node_field_class_declarator->u.field_class_declarator.u.id;
+
+ *field_name = g_quark_from_string(id);
+ } else {
+ *field_name = 0;
+ }
+
+ *field_decl = nested_decl;
+ nested_decl = NULL;
+ goto end;
+ } else {
+ struct ctf_node *first;
+ struct ctf_field_class *decl = NULL;
+ struct ctf_field_class *outer_field_decl = NULL;
+ struct bt_list_head *length =
+ &node_field_class_declarator->
+ u.field_class_declarator.u.nested.length;
+
+ /* Create array/sequence, pass nested_decl as child */
+ if (bt_list_empty(length)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
+ "Expecting length field reference or value.");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ first = _BT_LIST_FIRST_ENTRY(length, struct ctf_node, siblings);
+ if (first->type != NODE_UNARY_EXPRESSION) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(first,
+ "Unexpected node type: node-type=%d",
+ first->type);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ switch (first->u.unary_expression.type) {
+ case UNARY_UNSIGNED_CONSTANT:
+ {
+ struct ctf_field_class_array *array_decl = NULL;
+
+ array_decl = ctf_field_class_array_create();
+ BT_ASSERT(array_decl);
+ array_decl->length =
+ first->u.unary_expression.u.unsigned_constant;
+ array_decl->base.elem_fc = nested_decl;
+ nested_decl = NULL;
+ decl = &array_decl->base.base;
+ break;
+ }
+ case UNARY_STRING:
+ {
+ /* Lookup unsigned integer definition, create seq. */
+ struct ctf_field_class_sequence *seq_decl = NULL;
+ char *length_name = ctf_ast_concatenate_unary_strings(length);
+
+ if (!length_name) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
+ "Cannot concatenate unary strings.");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (strncmp(length_name, "env.", 4) == 0) {
+ /* This is, in fact, an array */
+ const char *env_entry_name = &length_name[4];
+ struct ctf_trace_class_env_entry *env_entry =
+ ctf_trace_class_borrow_env_entry_by_name(
+ ctx->ctf_tc, env_entry_name);
+ struct ctf_field_class_array *array_decl;
+
+ if (!env_entry) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
+ "Cannot find environment entry: "
+ "name=\"%s\"", env_entry_name);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (env_entry->type != CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
+ "Wrong environment entry type "
+ "(expecting integer): "
+ "name=\"%s\"", env_entry_name);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (env_entry->value.i < 0) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node_field_class_declarator,
+ "Invalid, negative array length: "
+ "env-entry-name=\"%s\", "
+ "value=%" PRId64,
+ env_entry_name,
+ env_entry->value.i);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ array_decl = ctf_field_class_array_create();
+ BT_ASSERT(array_decl);
+ array_decl->length =
+ (uint64_t) env_entry->value.i;
+ array_decl->base.elem_fc = nested_decl;
+ nested_decl = NULL;
+ decl = &array_decl->base.base;
+ } else {
+ seq_decl = ctf_field_class_sequence_create();
+ BT_ASSERT(seq_decl);
+ seq_decl->base.elem_fc = nested_decl;
+ nested_decl = NULL;
+ g_string_assign(seq_decl->length_ref,
+ length_name);
+ decl = &seq_decl->base.base;
+ }
+
+ g_free(length_name);
+ break;
+ }
+ default:
+ ret = -EINVAL;
+ goto error;
+ }
+
+ BT_ASSERT(!nested_decl);
+ BT_ASSERT(decl);
+ BT_ASSERT(!*field_decl);
+
+ /*
+ * At this point, we found the next nested declaration.
+ * We currently own this (and lost the ownership of
+ * nested_decl in the meantime). Pass this next
+ * nested declaration as the content of the outer
+ * container, MOVING its ownership.
+ */
+ ret = visit_field_class_declarator(ctx, cls_specifier_list,
+ field_name,
+ node_field_class_declarator->
+ u.field_class_declarator.u.nested.field_class_declarator,
+ &outer_field_decl, decl);
+ decl = NULL;
+ if (ret) {
+ BT_ASSERT(!outer_field_decl);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ BT_ASSERT(outer_field_decl);
+ *field_decl = outer_field_decl;
+ outer_field_decl = NULL;
+ }
+
+ BT_ASSERT(*field_decl);
+ goto end;
+
+error:
+ ctf_field_class_destroy(*field_decl);
+ *field_decl = NULL;
+
+ if (ret >= 0) {
+ ret = -1;
+ }
+
+end:
+ ctf_field_class_destroy(nested_decl);
+ nested_decl = NULL;
+ return ret;
+}
+
+static
+int visit_struct_decl_field(struct ctf_visitor_generate_ir *ctx,
+ struct ctf_field_class_struct *struct_decl,
+ struct ctf_node *cls_specifier_list,
+ struct bt_list_head *field_class_declarators)
+{
+ int ret = 0;
+ struct ctf_node *iter;
+ struct ctf_field_class *field_decl = NULL;
+
+ bt_list_for_each_entry(iter, field_class_declarators, siblings) {
+ field_decl = NULL;
+ GQuark qfield_name;
+ const char *field_name;
+
+ ret = visit_field_class_declarator(ctx, cls_specifier_list,
+ &qfield_name, iter, &field_decl, NULL);
+ if (ret) {
+ BT_ASSERT(!field_decl);
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(cls_specifier_list,
+ "Cannot visit field class declarator: ret=%d", ret);
+ goto error;
+ }
+
+ BT_ASSERT(field_decl);
+ field_name = g_quark_to_string(qfield_name);
+
+ /* Check if field with same name already exists */
+ if (ctf_field_class_struct_borrow_member_by_name(
+ struct_decl, field_name)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(cls_specifier_list,
+ "Duplicate field in structure field class: "
+ "field-name=\"%s\"", field_name);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* Add field to structure */
+ ctf_field_class_struct_append_member(struct_decl,
+ field_name, field_decl);
+ field_decl = NULL;
+ }
+
+ return 0;
+
+error:
+ ctf_field_class_destroy(field_decl);
+ field_decl = NULL;
+ return ret;
+}
+
+static
+int visit_variant_decl_field(struct ctf_visitor_generate_ir *ctx,
+ struct ctf_field_class_variant *variant_decl,
+ struct ctf_node *cls_specifier_list,
+ struct bt_list_head *field_class_declarators)
+{
+ int ret = 0;
+ struct ctf_node *iter;
+ struct ctf_field_class *field_decl = NULL;
+
+ bt_list_for_each_entry(iter, field_class_declarators, siblings) {
+ field_decl = NULL;
+ GQuark qfield_name;
+ const char *field_name;
+
+ ret = visit_field_class_declarator(ctx, cls_specifier_list,
+ &qfield_name, iter, &field_decl, NULL);
+ if (ret) {
+ BT_ASSERT(!field_decl);
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(cls_specifier_list,
+ "Cannot visit field class declarator: ret=%d", ret);
+ goto error;
+ }
+
+ BT_ASSERT(field_decl);
+ field_name = g_quark_to_string(qfield_name);
+
+ /* Check if field with same name already exists */
+ if (ctf_field_class_variant_borrow_option_by_name(
+ variant_decl, field_name)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(cls_specifier_list,
+ "Duplicate field in variant field class: "
+ "field-name=\"%s\"", field_name);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* Add field to structure */
+ ctf_field_class_variant_append_option(variant_decl,
+ field_name, field_decl);
+ field_decl = NULL;
+ }
+
+ return 0;
+
+error:
+ ctf_field_class_destroy(field_decl);
+ field_decl = NULL;
+ return ret;
+}
+
+static
+int visit_field_class_def(struct ctf_visitor_generate_ir *ctx,
+ struct ctf_node *cls_specifier_list,
+ struct bt_list_head *field_class_declarators)
+{
+ int ret = 0;
+ GQuark qidentifier;
+ struct ctf_node *iter;
+ struct ctf_field_class *class_decl = NULL;
+
+ bt_list_for_each_entry(iter, field_class_declarators, siblings) {
+ ret = visit_field_class_declarator(ctx, cls_specifier_list,
+ &qidentifier, iter, &class_decl, NULL);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
+ "Cannot visit field class declarator: ret=%d", ret);
+ ret = -EINVAL;
+ goto end;
+ }
+
+ /* Do not allow field class def and alias of untagged variants */
+ if (class_decl->type == CTF_FIELD_CLASS_TYPE_VARIANT) {
+ struct ctf_field_class_variant *var_fc =
+ ctf_field_class_as_variant(class_decl);
+
+ if (var_fc->tag_path.path->len == 0) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
+ "Type definition of untagged variant field class is not allowed.");
+ ret = -EPERM;
+ goto end;
+ }
+ }
+
+ ret = ctx_decl_scope_register_alias(ctx, ctx->current_scope,
+ g_quark_to_string(qidentifier), class_decl);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
+ "Cannot register field class alias: name=\"%s\"",
+ g_quark_to_string(qidentifier));
+ goto end;
+ }
+ }
+
+end:
+ ctf_field_class_destroy(class_decl);
+ class_decl = NULL;
+ return ret;
+}
+
+static
+int visit_field_class_alias(struct ctf_visitor_generate_ir *ctx, struct ctf_node *target,
+ struct ctf_node *alias)
+{
+ int ret = 0;
+ GQuark qalias;
+ struct ctf_node *node;
+ GQuark qdummy_field_name;
+ struct ctf_field_class *class_decl = NULL;
+
+ /* Create target field class */
+ if (bt_list_empty(&target->u.field_class_alias_target.field_class_declarators)) {
+ node = NULL;
+ } else {
+ node = _BT_LIST_FIRST_ENTRY(
+ &target->u.field_class_alias_target.field_class_declarators,
+ struct ctf_node, siblings);
+ }
+
+ ret = visit_field_class_declarator(ctx,
+ target->u.field_class_alias_target.field_class_specifier_list,
+ &qdummy_field_name, node, &class_decl, NULL);
+ if (ret) {
+ BT_ASSERT(!class_decl);
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Cannot visit field class declarator: ret=%d", ret);
+ goto end;
+ }
+
+ /* Do not allow field class def and alias of untagged variants */
+ if (class_decl->type == CTF_FIELD_CLASS_TYPE_VARIANT) {
+ struct ctf_field_class_variant *var_fc =
+ ctf_field_class_as_variant(class_decl);
+
+ if (var_fc->tag_path.path->len == 0) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(target,
+ "Type definition of untagged variant field class is not allowed.");
+ ret = -EPERM;
+ goto end;
+ }
+ }
+
+ /*
+ * The semantic validator does not check whether the target is
+ * abstract or not (if it has an identifier). Check it here.
+ */
+ if (qdummy_field_name != 0) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(target,
+ "Expecting empty identifier: id=\"%s\"",
+ g_quark_to_string(qdummy_field_name));
+ ret = -EINVAL;
+ goto end;
+ }
+
+ /* Create alias identifier */
+ node = _BT_LIST_FIRST_ENTRY(&alias->u.field_class_alias_name.field_class_declarators,
+ struct ctf_node, siblings);
+ qalias = create_class_alias_identifier(ctx,
+ alias->u.field_class_alias_name.field_class_specifier_list, node);
+ ret = ctx_decl_scope_register_alias(ctx, ctx->current_scope,
+ g_quark_to_string(qalias), class_decl);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Cannot register class alias: name=\"%s\"",
+ g_quark_to_string(qalias));
+ goto end;
+ }
+
+end:
+ ctf_field_class_destroy(class_decl);
+ class_decl = NULL;
+ return ret;
+}
+
+static
+int visit_struct_decl_entry(struct ctf_visitor_generate_ir *ctx, struct ctf_node *entry_node,
+ struct ctf_field_class_struct *struct_decl)
+{
+ int ret = 0;
+
+ switch (entry_node->type) {
+ case NODE_TYPEDEF:
+ ret = visit_field_class_def(ctx,
+ entry_node->u.field_class_def.field_class_specifier_list,
+ &entry_node->u.field_class_def.field_class_declarators);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+ "Cannot add field class found in structure field class: ret=%d",
+ ret);
+ goto end;
+ }
+ break;
+ case NODE_TYPEALIAS:
+ ret = visit_field_class_alias(ctx, entry_node->u.field_class_alias.target,
+ entry_node->u.field_class_alias.alias);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+ "Cannot add field class alias found in structure field class: ret=%d",
+ ret);
+ goto end;
+ }
+ break;
+ case NODE_STRUCT_OR_VARIANT_DECLARATION:
+ /* Field */
+ ret = visit_struct_decl_field(ctx, struct_decl,
+ entry_node->u.struct_or_variant_declaration.
+ field_class_specifier_list,
+ &entry_node->u.struct_or_variant_declaration.
+ field_class_declarators);
+ if (ret) {
+ goto end;
+ }
+ break;
+ default:
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+ "Unexpected node type: node-type=%d", entry_node->type);
+ ret = -EINVAL;
+ goto end;
+ }
+
+end:
+ return ret;
+}
+
+static
+int visit_variant_decl_entry(struct ctf_visitor_generate_ir *ctx, struct ctf_node *entry_node,
+ struct ctf_field_class_variant *variant_decl)
+{
+ int ret = 0;
+
+ switch (entry_node->type) {
+ case NODE_TYPEDEF:
+ ret = visit_field_class_def(ctx,
+ entry_node->u.field_class_def.field_class_specifier_list,
+ &entry_node->u.field_class_def.field_class_declarators);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+ "Cannot add field class found in variant field class: ret=%d",
+ ret);
+ goto end;
+ }
+ break;
+ case NODE_TYPEALIAS:
+ ret = visit_field_class_alias(ctx, entry_node->u.field_class_alias.target,
+ entry_node->u.field_class_alias.alias);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+ "Cannot add field class alias found in variant field class: ret=%d",
+ ret);
+ goto end;
+ }
+ break;
+ case NODE_STRUCT_OR_VARIANT_DECLARATION:
+ /* Field */
+ ret = visit_variant_decl_field(ctx, variant_decl,
+ entry_node->u.struct_or_variant_declaration.
+ field_class_specifier_list,
+ &entry_node->u.struct_or_variant_declaration.
+ field_class_declarators);
+ if (ret) {
+ goto end;
+ }
+ break;
+ default:
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+ "Unexpected node type: node-type=%d",
+ entry_node->type);
+ ret = -EINVAL;
+ goto end;
+ }
+
+end:
+ return ret;
+}
+
+static
+int visit_struct_decl(struct ctf_visitor_generate_ir *ctx, const char *name,
+ struct bt_list_head *decl_list, int has_body,
+ struct bt_list_head *min_align,
+ struct ctf_field_class_struct **struct_decl)
+{
+ int ret = 0;
+
+ BT_ASSERT(struct_decl);
+ *struct_decl = NULL;
+
+ /* For named struct (without body), lookup in declaration scope */
+ if (!has_body) {
+ if (!name) {
+ _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Bodyless structure field class: missing name.");
+ ret = -EPERM;
+ goto error;
+ }
+
+ *struct_decl = ctx_decl_scope_lookup_struct(ctx, ctx->current_scope,
+ name, -1, true);
+ if (!*struct_decl) {
+ _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot find structure field class: name=\"struct %s\"",
+ name);
+ ret = -EINVAL;
+ goto error;
+ }
+ } else {
+ struct ctf_node *entry_node;
+ uint64_t min_align_value = 0;
+
+ if (name) {
+ if (ctx_decl_scope_lookup_struct(ctx,
+ ctx->current_scope, name, 1, false)) {
+ _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Structure field class already declared in local scope: "
+ "name=\"struct %s\"", name);
+ ret = -EINVAL;
+ goto error;
+ }
+ }
+
+ if (!bt_list_empty(min_align)) {
+ ret = get_unary_unsigned(ctx, min_align,
+ &min_align_value);
+ if (ret) {
+ _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Unexpected unary expression for structure field class's `align` attribute: "
+ "ret=%d", ret);
+ goto error;
+ }
+ }
+
+ *struct_decl = ctf_field_class_struct_create();
+ BT_ASSERT(*struct_decl);
+
+ if (min_align_value != 0) {
+ (*struct_decl)->base.alignment = min_align_value;
+ }
+
+ _TRY_PUSH_SCOPE_OR_GOTO_ERROR();
+
+ bt_list_for_each_entry(entry_node, decl_list, siblings) {
+ ret = visit_struct_decl_entry(ctx, entry_node,
+ *struct_decl);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+ "Cannot visit structure field class entry: "
+ "ret=%d", ret);
+ ctx_pop_scope(ctx);
+ goto error;
+ }
+ }
+
+ ctx_pop_scope(ctx);
+
+ if (name) {
+ ret = ctx_decl_scope_register_struct(ctx,
+ ctx->current_scope, name, *struct_decl);
+ if (ret) {
+ _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot register structure field class in declaration scope: "
+ "name=\"struct %s\", ret=%d", name, ret);
+ goto error;
+ }
+ }
+ }
+
+ return 0;
+
+error:
+ ctf_field_class_destroy(&(*struct_decl)->base);
+ *struct_decl = NULL;
+ return ret;
+}
+
+static
+int visit_variant_decl(struct ctf_visitor_generate_ir *ctx, const char *name,
+ const char *tag, struct bt_list_head *decl_list,
+ int has_body, struct ctf_field_class_variant **variant_decl)
+{
+ int ret = 0;
+ struct ctf_field_class_variant *untagged_variant_decl = NULL;
+
+ BT_ASSERT(variant_decl);
+ *variant_decl = NULL;
+
+ /* For named variant (without body), lookup in declaration scope */
+ if (!has_body) {
+ if (!name) {
+ _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Bodyless variant field class: missing name.");
+ ret = -EPERM;
+ goto error;
+ }
+
+ untagged_variant_decl =
+ ctx_decl_scope_lookup_variant(ctx, ctx->current_scope,
+ name, -1, true);
+ if (!untagged_variant_decl) {
+ _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot find variant field class: name=\"variant %s\"",
+ name);
+ ret = -EINVAL;
+ goto error;
+ }
+ } else {
+ struct ctf_node *entry_node;
+
+ if (name) {
+ if (ctx_decl_scope_lookup_variant(ctx,
+ ctx->current_scope, name, 1, false)) {
+ _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Variant field class already declared in local scope: "
+ "name=\"variant %s\"", name);
+ ret = -EINVAL;
+ goto error;
+ }
+ }
+
+ untagged_variant_decl = ctf_field_class_variant_create();
+ BT_ASSERT(untagged_variant_decl);
+ _TRY_PUSH_SCOPE_OR_GOTO_ERROR();
+
+ bt_list_for_each_entry(entry_node, decl_list, siblings) {
+ ret = visit_variant_decl_entry(ctx, entry_node,
+ untagged_variant_decl);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+ "Cannot visit variant field class entry: "
+ "ret=%d", ret);
+ ctx_pop_scope(ctx);
+ goto error;
+ }
+ }
+
+ ctx_pop_scope(ctx);
+
+ if (name) {
+ ret = ctx_decl_scope_register_variant(ctx,
+ ctx->current_scope, name,
+ untagged_variant_decl);
+ if (ret) {
+ _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot register variant field class in declaration scope: "
+ "name=\"variant %s\", ret=%d", name, ret);
+ goto error;
+ }
+ }
+ }
+
+ /*
+ * If tagged, create tagged variant and return; otherwise
+ * return untagged variant.
+ */
+ if (!tag) {
+ *variant_decl = untagged_variant_decl;
+ untagged_variant_decl = NULL;
+ } else {
+ /*
+ * At this point, we have a fresh untagged variant; nobody
+ * else owns it. Set its tag now.
+ */
+ g_string_assign(untagged_variant_decl->tag_ref, tag);
+ *variant_decl = untagged_variant_decl;
+ untagged_variant_decl = NULL;
+ }
+
+ BT_ASSERT(!untagged_variant_decl);
+ BT_ASSERT(*variant_decl);
+ return 0;
+
+error:
+ ctf_field_class_destroy(&untagged_variant_decl->base);
+ untagged_variant_decl = NULL;
+ ctf_field_class_destroy(&(*variant_decl)->base);
+ *variant_decl = NULL;
+ return ret;
+}
+
+struct uori {
+ bool is_signed;
+ union {
+ uint64_t u;
+ uint64_t i;
+ } value;
+};
+
+static
+int visit_enum_decl_entry(struct ctf_visitor_generate_ir *ctx, struct ctf_node *enumerator,
+ struct ctf_field_class_enum *enum_decl, struct uori *last)
+{
+ int ret = 0;
+ int nr_vals = 0;
+ struct ctf_node *iter;
+ struct uori start = {
+ .is_signed = false,
+ .value = {
+ .u = 0,
+ },
+ };
+ struct uori end = {
+ .is_signed = false,
+ .value = {
+ .u = 0,
+ },
+ };
+ const char *label = enumerator->u.enumerator.id;
+ struct bt_list_head *values = &enumerator->u.enumerator.values;
+
+ bt_list_for_each_entry(iter, values, siblings) {
+ struct uori *target;
+
+ if (iter->type != NODE_UNARY_EXPRESSION) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
+ "Wrong expression for enumeration field class label: "
+ "node-type=%d, label=\"%s\"", iter->type,
+ label);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (nr_vals == 0) {
+ target = &start;
+ } else {
+ target = &end;
+ }
+
+ switch (iter->u.unary_expression.type) {
+ case UNARY_SIGNED_CONSTANT:
+ target->is_signed = true;
+ target->value.i =
+ iter->u.unary_expression.u.signed_constant;
+ break;
+ case UNARY_UNSIGNED_CONSTANT:
+ target->is_signed = false;
+ target->value.u =
+ iter->u.unary_expression.u.unsigned_constant;
+ break;
+ default:
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
+ "Invalid enumeration field class entry: "
+ "expecting constant signed or unsigned integer: "
+ "node-type=%d, label=\"%s\"",
+ iter->u.unary_expression.type, label);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (nr_vals > 1) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
+ "Invalid enumeration field class entry: label=\"%s\"",
+ label);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ nr_vals++;
+ }
+
+ if (nr_vals == 0) {
+ start = *last;
+ }
+
+ if (nr_vals <= 1) {
+ end = start;
+ }
+
+ if (end.is_signed) {
+ last->value.i = end.value.i + 1;
+ } else {
+ last->value.u = end.value.u + 1;
+ }
+
+ ctf_field_class_enum_map_range(enum_decl, label,
+ start.value.u, end.value.u);
+ return 0;
+
+error:
+ return ret;
+}
+
+static
+int visit_enum_decl(struct ctf_visitor_generate_ir *ctx, const char *name,
+ struct ctf_node *container_cls,
+ struct bt_list_head *enumerator_list,
+ int has_body, struct ctf_field_class_enum **enum_decl)
+{
+ int ret = 0;
+ GQuark qdummy_id;
+ struct ctf_field_class_int *integer_decl = NULL;
+
+ BT_ASSERT(enum_decl);
+ *enum_decl = NULL;
+
+ /* For named enum (without body), lookup in declaration scope */
+ if (!has_body) {
+ if (!name) {
+ _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Bodyless enumeration field class: missing name.");
+ ret = -EPERM;
+ goto error;
+ }
+
+ *enum_decl = ctx_decl_scope_lookup_enum(ctx, ctx->current_scope,
+ name, -1, true);
+ if (!*enum_decl) {
+ _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot find enumeration field class: "
+ "name=\"enum %s\"", name);
+ ret = -EINVAL;
+ goto error;
+ }
+ } else {
+ struct ctf_node *iter;
+ struct uori last_value = {
+ .is_signed = false,
+ .value = {
+ .u = 0,
+ },
+ };
+
+ if (name) {
+ if (ctx_decl_scope_lookup_enum(ctx, ctx->current_scope,
+ name, 1, false)) {
+ _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Enumeration field class already declared in local scope: "
+ "name=\"enum %s\"", name);
+ ret = -EINVAL;
+ goto error;
+ }
+ }
+
+ if (!container_cls) {
+ integer_decl = ctf_field_class_as_int(ctx_decl_scope_lookup_alias(ctx,
+ ctx->current_scope, "int", -1, true));
+ if (!integer_decl) {
+ _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot find implicit `int` field class alias for enumeration field class.");
+ ret = -EINVAL;
+ goto error;
+ }
+ } else {
+ ctf_field_class *decl;
+
+ ret = visit_field_class_declarator(ctx, container_cls,
+ &qdummy_id, NULL, &decl, NULL);
+ if (ret) {
+ BT_ASSERT(!decl);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ integer_decl = ctf_field_class_as_int(decl);
+ }
+
+ BT_ASSERT(integer_decl);
+
+ if (integer_decl->base.base.type != CTF_FIELD_CLASS_TYPE_INT) {
+ _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Container field class for enumeration field class is not an integer field class: "
+ "fc-type=%d", integer_decl->base.base.type);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ *enum_decl = ctf_field_class_enum_create();
+ BT_ASSERT(*enum_decl);
+ (*enum_decl)->base.base.base.alignment =
+ integer_decl->base.base.alignment;
+ ctf_field_class_int_copy_content(
+ &(*enum_decl)->base, integer_decl);
+ last_value.is_signed = (*enum_decl)->base.is_signed;
+
+ bt_list_for_each_entry(iter, enumerator_list, siblings) {
+ ret = visit_enum_decl_entry(ctx, iter, *enum_decl,
+ &last_value);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
+ "Cannot visit enumeration field class entry: "
+ "ret=%d", ret);
+ goto error;
+ }
+ }
+
+ if (name) {
+ ret = ctx_decl_scope_register_enum(ctx,
+ ctx->current_scope, name, *enum_decl);
+ if (ret) {
+ _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot register enumeration field class in declaration scope: "
+ "ret=%d", ret);
+ goto error;
+ }
+ }
+ }
+
+ goto end;
+
+error:
+ ctf_field_class_destroy(&(*enum_decl)->base.base.base);
+ *enum_decl = NULL;
+
+end:
+ ctf_field_class_destroy(&integer_decl->base.base);
+ integer_decl = NULL;
+ return ret;
+}
+
+static
+int visit_field_class_specifier(struct ctf_visitor_generate_ir *ctx,
+ struct ctf_node *cls_specifier_list,
+ struct ctf_field_class **decl)
+{
+ int ret = 0;
+ GString *str = NULL;
+
+ *decl = NULL;
+ str = g_string_new("");
+ ret = get_class_specifier_list_name(ctx, cls_specifier_list, str);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(cls_specifier_list,
+ "Cannot get field class specifier list's name: ret=%d", ret);
+ goto error;
+ }
+
+ *decl = ctx_decl_scope_lookup_alias(ctx, ctx->current_scope, str->str,
+ -1, true);
+ if (!*decl) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(cls_specifier_list,
+ "Cannot find field class alias: name=\"%s\"", str->str);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ goto end;
+
+error:
+ ctf_field_class_destroy(*decl);
+ *decl = NULL;
+
+end:
+ if (str) {
+ g_string_free(str, TRUE);
+ }
+
+ return ret;
+}
+
+static
+int visit_integer_decl(struct ctf_visitor_generate_ir *ctx,
+ struct bt_list_head *expressions,
+ struct ctf_field_class_int **integer_decl)
+{
+ int set = 0;
+ int ret = 0;
+ int signedness = 0;
+ struct ctf_node *expression;
+ uint64_t alignment = 0, size = 0;
+ struct ctf_clock_class *mapped_clock_class = NULL;
+ enum ctf_encoding encoding = CTF_ENCODING_NONE;
+ bt_field_class_integer_preferred_display_base base =
+ BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL;
+ enum ctf_byte_order byte_order = ctx->ctf_tc->default_byte_order;
+
+ *integer_decl = NULL;
+
+ bt_list_for_each_entry(expression, expressions, siblings) {
+ struct ctf_node *left, *right;
+
+ left = _BT_LIST_FIRST_ENTRY(&expression->u.ctf_expression.left,
+ struct ctf_node, siblings);
+ right = _BT_LIST_FIRST_ENTRY(
+ &expression->u.ctf_expression.right, struct ctf_node,
+ siblings);
+
+ if (left->u.unary_expression.type != UNARY_STRING) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(left,
+ "Unexpected unary expression type: type=%d",
+ left->u.unary_expression.type);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (strcmp(left->u.unary_expression.u.string, "signed") == 0) {
+ if (_IS_SET(&set, _INTEGER_SIGNED_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "signed",
+ "integer field class");
+ ret = -EPERM;
+ goto error;
+ }
+
+ signedness = get_boolean(ctx, right);
+ if (signedness < 0) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+ "Invalid boolean value for integer field class's `signed` attribute: "
+ "ret=%d", ret);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ _SET(&set, _INTEGER_SIGNED_SET);
+ } else if (strcmp(left->u.unary_expression.u.string, "byte_order") == 0) {
+ if (_IS_SET(&set, _INTEGER_BYTE_ORDER_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "byte_order",
+ "integer field class");
+ ret = -EPERM;
+ goto error;
+ }
+
+ byte_order = get_real_byte_order(ctx, right);
+ if (byte_order == CTF_BYTE_ORDER_UNKNOWN) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+ "Invalid `byte_order` attribute in integer field class: "
+ "ret=%d", ret);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ _SET(&set, _INTEGER_BYTE_ORDER_SET);
+ } else if (strcmp(left->u.unary_expression.u.string, "size") == 0) {
+ if (_IS_SET(&set, _INTEGER_SIZE_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "size",
+ "integer field class");
+ ret = -EPERM;
+ goto error;
+ }
+
+ if (right->u.unary_expression.type !=
+ UNARY_UNSIGNED_CONSTANT) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+ "Invalid `size` attribute in integer field class: "
+ "expecting unsigned constant integer: "
+ "node-type=%d",
+ right->u.unary_expression.type);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ size = right->u.unary_expression.u.unsigned_constant;
+ if (size == 0) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+ "Invalid `size` attribute in integer field class: "
+ "expecting positive constant integer: "
+ "size=%" PRIu64, size);
+ ret = -EINVAL;
+ goto error;
+ } else if (size > 64) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+ "Invalid `size` attribute in integer field class: "
+ "integer fields over 64 bits are not supported as of this version: "
+ "size=%" PRIu64, size);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ _SET(&set, _INTEGER_SIZE_SET);
+ } else if (strcmp(left->u.unary_expression.u.string, "align") == 0) {
+ if (_IS_SET(&set, _INTEGER_ALIGN_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "align",
+ "integer field class");
+ ret = -EPERM;
+ goto error;
+ }
+
+ if (right->u.unary_expression.type !=
+ UNARY_UNSIGNED_CONSTANT) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+ "Invalid `align` attribute in integer field class: "
+ "expecting unsigned constant integer: "
+ "node-type=%d",
+ right->u.unary_expression.type);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ alignment =
+ right->u.unary_expression.u.unsigned_constant;
+ if (!is_align_valid(alignment)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+ "Invalid `align` attribute in integer field class: "
+ "expecting power of two: "
+ "align=%" PRIu64, alignment);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ _SET(&set, _INTEGER_ALIGN_SET);
+ } else if (strcmp(left->u.unary_expression.u.string, "base") == 0) {
+ if (_IS_SET(&set, _INTEGER_BASE_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "base",
+ "integer field class");
+ ret = -EPERM;
+ goto error;
+ }
+
+ switch (right->u.unary_expression.type) {
+ case UNARY_UNSIGNED_CONSTANT:
+ {
+ uint64_t constant = right->u.unary_expression.
+ u.unsigned_constant;
+
+ switch (constant) {
+ case 2:
+ base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_BINARY;
+ break;
+ case 8:
+ base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL;
+ break;
+ case 10:
+ base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL;
+ break;
+ case 16:
+ base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL;
+ break;
+ default:
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+ "Invalid `base` attribute in integer field class: "
+ "base=%" PRIu64,
+ right->u.unary_expression.u.unsigned_constant);
+ ret = -EINVAL;
+ goto error;
+ }
+ break;
+ }
+ case UNARY_STRING:
+ {
+ char *s_right = ctf_ast_concatenate_unary_strings(
+ &expression->u.ctf_expression.right);
+ if (!s_right) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+ "Unexpected unary expression for integer field class's `base` attribute.");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (strcmp(s_right, "decimal") == 0 ||
+ strcmp(s_right, "dec") == 0 ||
+ strcmp(s_right, "d") == 0 ||
+ strcmp(s_right, "i") == 0 ||
+ strcmp(s_right, "u") == 0) {
+ base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL;
+ } else if (strcmp(s_right, "hexadecimal") == 0 ||
+ strcmp(s_right, "hex") == 0 ||
+ strcmp(s_right, "x") == 0 ||
+ strcmp(s_right, "X") == 0 ||
+ strcmp(s_right, "p") == 0) {
+ base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL;
+ } else if (strcmp(s_right, "octal") == 0 ||
+ strcmp(s_right, "oct") == 0 ||
+ strcmp(s_right, "o") == 0) {
+ base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL;
+ } else if (strcmp(s_right, "binary") == 0 ||
+ strcmp(s_right, "b") == 0) {
+ base = BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_BINARY;
+ } else {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+ "Unexpected unary expression for integer field class's `base` attribute: "
+ "base=\"%s\"", s_right);
+ g_free(s_right);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ g_free(s_right);
+ break;
+ }
+ default:
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+ "Invalid `base` attribute in integer field class: "
+ "expecting unsigned constant integer or unary string.");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ _SET(&set, _INTEGER_BASE_SET);
+ } else if (strcmp(left->u.unary_expression.u.string, "encoding") == 0) {
+ char *s_right;
+
+ if (_IS_SET(&set, _INTEGER_ENCODING_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "encoding",
+ "integer field class");
+ ret = -EPERM;
+ goto error;
+ }
+
+ if (right->u.unary_expression.type != UNARY_STRING) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+ "Invalid `encoding` attribute in integer field class: "
+ "expecting unary string.");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ s_right = ctf_ast_concatenate_unary_strings(
+ &expression->u.ctf_expression.right);
+ if (!s_right) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+ "Unexpected unary expression for integer field class's `encoding` attribute.");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (strcmp(s_right, "UTF8") == 0 ||
+ strcmp(s_right, "utf8") == 0 ||
+ strcmp(s_right, "utf-8") == 0 ||
+ strcmp(s_right, "UTF-8") == 0 ||
+ strcmp(s_right, "ASCII") == 0 ||
+ strcmp(s_right, "ascii") == 0) {
+ encoding = CTF_ENCODING_UTF8;
+ } else if (strcmp(s_right, "none") == 0) {
+ encoding = CTF_ENCODING_NONE;
+ } else {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+ "Invalid `encoding` attribute in integer field class: "
+ "unknown encoding: encoding=\"%s\"",
+ s_right);
+ g_free(s_right);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ g_free(s_right);
+ _SET(&set, _INTEGER_ENCODING_SET);
+ } else if (strcmp(left->u.unary_expression.u.string, "map") == 0) {
+ const char *clock_name;
+
+ if (_IS_SET(&set, _INTEGER_MAP_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "map",
+ "integer field class");
+ ret = -EPERM;
+ goto error;
+ }
+
+ if (right->u.unary_expression.type != UNARY_STRING) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+ "Invalid `map` attribute in integer field class: "
+ "expecting unary string.");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ clock_name =
+ get_map_clock_name_value(
+ &expression->u.ctf_expression.right);
+ if (!clock_name) {
+ char *s_right = ctf_ast_concatenate_unary_strings(
+ &expression->u.ctf_expression.right);
+
+ if (!s_right) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+ "Unexpected unary expression for integer field class's `map` attribute.");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ _BT_COMP_LOGE_NODE(right,
+ "Invalid `map` attribute in integer field class: "
+ "cannot find clock class at this point: name=\"%s\"",
+ s_right);
+ _SET(&set, _INTEGER_MAP_SET);
+ g_free(s_right);
+ continue;
+ }
+
+ mapped_clock_class =
+ ctf_trace_class_borrow_clock_class_by_name(
+ ctx->ctf_tc, clock_name);
+ if (!mapped_clock_class) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+ "Invalid `map` attribute in integer field class: "
+ "cannot find clock class at this point: name=\"%s\"",
+ clock_name);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ _SET(&set, _INTEGER_MAP_SET);
+ } else {
+ _BT_COMP_LOGW_NODE(left,
+ "Unknown attribute in integer field class: "
+ "attr-name=\"%s\"",
+ left->u.unary_expression.u.string);
+ }
+ }
+
+ if (!_IS_SET(&set, _INTEGER_SIZE_SET)) {
+ _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Missing `size` attribute in integer field class.");
+ ret = -EPERM;
+ goto error;
+ }
+
+ if (!_IS_SET(&set, _INTEGER_ALIGN_SET)) {
+ if (size % CHAR_BIT) {
+ /* Bit-packed alignment */
+ alignment = 1;
+ } else {
+ /* Byte-packed alignment */
+ alignment = CHAR_BIT;
+ }
+ }
+
+ *integer_decl = ctf_field_class_int_create();
+ BT_ASSERT(*integer_decl);
+ (*integer_decl)->base.base.alignment = alignment;
+ (*integer_decl)->base.byte_order = byte_order;
+ (*integer_decl)->base.size = size;
+ (*integer_decl)->is_signed = (signedness > 0);
+ (*integer_decl)->disp_base = base;
+ (*integer_decl)->encoding = encoding;
+ (*integer_decl)->mapped_clock_class = mapped_clock_class;
+ return 0;
+
+error:
+ ctf_field_class_destroy(&(*integer_decl)->base.base);
+ *integer_decl = NULL;
+ return ret;
+}
+
+static
+int visit_floating_point_number_decl(struct ctf_visitor_generate_ir *ctx,
+ struct bt_list_head *expressions,
+ struct ctf_field_class_float **float_decl)
+{
+ int set = 0;
+ int ret = 0;
+ struct ctf_node *expression;
+ uint64_t alignment = 1, exp_dig = 0, mant_dig = 0;
+ enum ctf_byte_order byte_order = ctx->ctf_tc->default_byte_order;
+
+ *float_decl = NULL;
+
+ bt_list_for_each_entry(expression, expressions, siblings) {
+ struct ctf_node *left, *right;
+
+ left = _BT_LIST_FIRST_ENTRY(&expression->u.ctf_expression.left,
+ struct ctf_node, siblings);
+ right = _BT_LIST_FIRST_ENTRY(
+ &expression->u.ctf_expression.right, struct ctf_node,
+ siblings);
+
+ if (left->u.unary_expression.type != UNARY_STRING) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(left,
+ "Unexpected unary expression type: type=%d",
+ left->u.unary_expression.type);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (strcmp(left->u.unary_expression.u.string, "byte_order") == 0) {
+ if (_IS_SET(&set, _FLOAT_BYTE_ORDER_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "byte_order",
+ "floating point number field class");
+ ret = -EPERM;
+ goto error;
+ }
+
+ byte_order = get_real_byte_order(ctx, right);
+ if (byte_order == CTF_BYTE_ORDER_UNKNOWN) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+ "Invalid `byte_order` attribute in floating point number field class: "
+ "ret=%d", ret);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ _SET(&set, _FLOAT_BYTE_ORDER_SET);
+ } else if (strcmp(left->u.unary_expression.u.string, "exp_dig") == 0) {
+ if (_IS_SET(&set, _FLOAT_EXP_DIG_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "exp_dig",
+ "floating point number field class");
+ ret = -EPERM;
+ goto error;
+ }
+
+ if (right->u.unary_expression.type !=
+ UNARY_UNSIGNED_CONSTANT) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+ "Invalid `exp_dig` attribute in floating point number field class: "
+ "expecting unsigned constant integer: "
+ "node-type=%d",
+ right->u.unary_expression.type);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ exp_dig = right->u.unary_expression.u.unsigned_constant;
+ _SET(&set, _FLOAT_EXP_DIG_SET);
+ } else if (strcmp(left->u.unary_expression.u.string, "mant_dig") == 0) {
+ if (_IS_SET(&set, _FLOAT_MANT_DIG_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "mant_dig",
+ "floating point number field class");
+ ret = -EPERM;
+ goto error;
+ }
+
+ if (right->u.unary_expression.type !=
+ UNARY_UNSIGNED_CONSTANT) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+ "Invalid `mant_dig` attribute in floating point number field class: "
+ "expecting unsigned constant integer: "
+ "node-type=%d",
+ right->u.unary_expression.type);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ mant_dig = right->u.unary_expression.u.
+ unsigned_constant;
+ _SET(&set, _FLOAT_MANT_DIG_SET);
+ } else if (strcmp(left->u.unary_expression.u.string, "align") == 0) {
+ if (_IS_SET(&set, _FLOAT_ALIGN_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "align",
+ "floating point number field class");
+ ret = -EPERM;
+ goto error;
+ }
+
+ if (right->u.unary_expression.type !=
+ UNARY_UNSIGNED_CONSTANT) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+ "Invalid `align` attribute in floating point number field class: "
+ "expecting unsigned constant integer: "
+ "node-type=%d",
+ right->u.unary_expression.type);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ alignment = right->u.unary_expression.u.
+ unsigned_constant;
+
+ if (!is_align_valid(alignment)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+ "Invalid `align` attribute in floating point number field class: "
+ "expecting power of two: "
+ "align=%" PRIu64, alignment);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ _SET(&set, _FLOAT_ALIGN_SET);
+ } else {
+ _BT_COMP_LOGW_NODE(left,
+ "Unknown attribute in floating point number field class: "
+ "attr-name=\"%s\"",
+ left->u.unary_expression.u.string);
+ }
+ }
+
+ if (!_IS_SET(&set, _FLOAT_MANT_DIG_SET)) {
+ _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Missing `mant_dig` attribute in floating point number field class.");
+ ret = -EPERM;
+ goto error;
+ }
+
+ if (!_IS_SET(&set, _FLOAT_EXP_DIG_SET)) {
+ _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Missing `exp_dig` attribute in floating point number field class.");
+ ret = -EPERM;
+ goto error;
+ }
+
+ if (mant_dig != 24 && mant_dig != 53) {
+ _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("`mant_dig` attribute: expecting 24 or 53.");
+ ret = -EPERM;
+ goto error;
+ }
+
+ if (mant_dig == 24 && exp_dig != 8) {
+ _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("`exp_dig` attribute: expecting 8 because `mant_dig` is 24.");
+ ret = -EPERM;
+ goto error;
+ }
+
+ if (mant_dig == 53 && exp_dig != 11) {
+ _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("`exp_dig` attribute: expecting 11 because `mant_dig` is 53.");
+ ret = -EPERM;
+ goto error;
+ }
+
+ if (!_IS_SET(&set, _INTEGER_ALIGN_SET)) {
+ if ((mant_dig + exp_dig) % CHAR_BIT) {
+ /* Bit-packed alignment */
+ alignment = 1;
+ } else {
+ /* Byte-packed alignment */
+ alignment = CHAR_BIT;
+ }
+ }
+
+ *float_decl = ctf_field_class_float_create();
+ BT_ASSERT(*float_decl);
+ (*float_decl)->base.base.alignment = alignment;
+ (*float_decl)->base.byte_order = byte_order;
+ (*float_decl)->base.size = mant_dig + exp_dig;
+ return 0;
+
+error:
+ ctf_field_class_destroy(&(*float_decl)->base.base);
+ *float_decl = NULL;
+ return ret;
+}
+
+static
+int visit_string_decl(struct ctf_visitor_generate_ir *ctx,
+ struct bt_list_head *expressions,
+ struct ctf_field_class_string **string_decl)
+{
+ int set = 0;
+ int ret = 0;
+ struct ctf_node *expression;
+ enum ctf_encoding encoding = CTF_ENCODING_UTF8;
+
+ *string_decl = NULL;
+
+ bt_list_for_each_entry(expression, expressions, siblings) {
+ struct ctf_node *left, *right;
+
+ left = _BT_LIST_FIRST_ENTRY(&expression->u.ctf_expression.left,
+ struct ctf_node, siblings);
+ right = _BT_LIST_FIRST_ENTRY(
+ &expression->u.ctf_expression.right, struct ctf_node,
+ siblings);
+
+ if (left->u.unary_expression.type != UNARY_STRING) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(left,
+ "Unexpected unary expression type: type=%d",
+ left->u.unary_expression.type);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (strcmp(left->u.unary_expression.u.string, "encoding") == 0) {
+ char *s_right;
+
+ if (_IS_SET(&set, _STRING_ENCODING_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(left, "encoding",
+ "string field class");
+ ret = -EPERM;
+ goto error;
+ }
+
+ if (right->u.unary_expression.type != UNARY_STRING) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+ "Invalid `encoding` attribute in string field class: "
+ "expecting unary string.");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ s_right = ctf_ast_concatenate_unary_strings(
+ &expression->u.ctf_expression.right);
+ if (!s_right) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+ "Unexpected unary expression for string field class's `encoding` attribute.");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (strcmp(s_right, "UTF8") == 0 ||
+ strcmp(s_right, "utf8") == 0 ||
+ strcmp(s_right, "utf-8") == 0 ||
+ strcmp(s_right, "UTF-8") == 0 ||
+ strcmp(s_right, "ASCII") == 0 ||
+ strcmp(s_right, "ascii") == 0) {
+ encoding = CTF_ENCODING_UTF8;
+ } else if (strcmp(s_right, "none") == 0) {
+ encoding = CTF_ENCODING_NONE;
+ } else {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(right,
+ "Invalid `encoding` attribute in string field class: "
+ "unknown encoding: encoding=\"%s\"",
+ s_right);
+ g_free(s_right);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ g_free(s_right);
+ _SET(&set, _STRING_ENCODING_SET);
+ } else {
+ _BT_COMP_LOGW_NODE(left,
+ "Unknown attribute in string field class: "
+ "attr-name=\"%s\"",
+ left->u.unary_expression.u.string);
+ }
+ }
+
+ *string_decl = ctf_field_class_string_create();
+ BT_ASSERT(*string_decl);
+ (*string_decl)->encoding = encoding;
+ return 0;
+
+error:
+ ctf_field_class_destroy(&(*string_decl)->base);
+ *string_decl = NULL;
+ return ret;
+}
+
+static
+int visit_field_class_specifier_list(struct ctf_visitor_generate_ir *ctx,
+ struct ctf_node *ts_list, struct ctf_field_class **decl)
+{
+ int ret = 0;
+ struct ctf_node *first, *node;
+
+ *decl = NULL;
+
+ if (ts_list->type != NODE_TYPE_SPECIFIER_LIST) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(ts_list,
+ "Unexpected node type: node-type=%d", ts_list->type);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ first = _BT_LIST_FIRST_ENTRY(&ts_list->u.field_class_specifier_list.head,
+ struct ctf_node, siblings);
+ if (first->type != NODE_TYPE_SPECIFIER) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(first,
+ "Unexpected node type: node-type=%d", first->type);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ node = first->u.field_class_specifier.node;
+
+ switch (first->u.field_class_specifier.type) {
+ case TYPESPEC_INTEGER: {
+ ctf_field_class_int *int_decl;
+
+ ret = visit_integer_decl(ctx, &node->u.integer.expressions,
+ &int_decl);
+ if (ret) {
+ BT_ASSERT(!int_decl);
+ goto error;
+ }
+
+ *decl = &int_decl->base.base;
+ break;
+ }
+ case TYPESPEC_FLOATING_POINT: {
+ ctf_field_class_float *float_decl;
+
+ ret = visit_floating_point_number_decl(ctx,
+ &node->u.floating_point.expressions, &float_decl);
+ if (ret) {
+ BT_ASSERT(!float_decl);
+ goto error;
+ }
+
+ *decl = &float_decl->base.base;
+ break;
+ }
+ case TYPESPEC_STRING: {
+ ctf_field_class_string *string_decl;
+
+ ret = visit_string_decl(ctx,
+ &node->u.string.expressions, &string_decl);
+ if (ret) {
+ BT_ASSERT(!string_decl);
+ goto error;
+ }
+
+ *decl = &string_decl->base;
+ break;
+ }
+ case TYPESPEC_STRUCT: {
+ ctf_field_class_struct *struct_decl;
+
+ ret = visit_struct_decl(ctx, node->u._struct.name,
+ &node->u._struct.declaration_list,
+ node->u._struct.has_body,
+ &node->u._struct.min_align, &struct_decl);
+ if (ret) {
+ BT_ASSERT(!struct_decl);
+ goto error;
+ }
+
+ *decl = &struct_decl->base;
+ break;
+ }
+ case TYPESPEC_VARIANT: {
+ ctf_field_class_variant *variant_decl;
+
+ ret = visit_variant_decl(ctx, node->u.variant.name,
+ node->u.variant.choice,
+ &node->u.variant.declaration_list,
+ node->u.variant.has_body, &variant_decl);
+ if (ret) {
+ BT_ASSERT(!variant_decl);
+ goto error;
+ }
+
+ *decl = &variant_decl->base;
+ break;
+ }
+ case TYPESPEC_ENUM: {
+ ctf_field_class_enum *enum_decl;
+
+ ret = visit_enum_decl(ctx, node->u._enum.enum_id,
+ node->u._enum.container_field_class,
+ &node->u._enum.enumerator_list,
+ node->u._enum.has_body, &enum_decl);
+ if (ret) {
+ BT_ASSERT(!enum_decl);
+ goto error;
+ }
+
+ *decl = &enum_decl->base.base.base;
+ break;
+ }
+ case TYPESPEC_VOID:
+ case TYPESPEC_CHAR:
+ case TYPESPEC_SHORT:
+ case TYPESPEC_INT:
+ case TYPESPEC_LONG:
+ case TYPESPEC_FLOAT:
+ case TYPESPEC_DOUBLE:
+ case TYPESPEC_SIGNED:
+ case TYPESPEC_UNSIGNED:
+ case TYPESPEC_BOOL:
+ case TYPESPEC_COMPLEX:
+ case TYPESPEC_IMAGINARY:
+ case TYPESPEC_CONST:
+ case TYPESPEC_ID_TYPE:
+ ret = visit_field_class_specifier(ctx, ts_list, decl);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(first,
+ "Cannot visit field class specifier: ret=%d",
+ ret);
+ BT_ASSERT(!*decl);
+ goto error;
+ }
+ break;
+ default:
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(first,
+ "Unexpected field class specifier type: node-type=%d",
+ first->u.field_class_specifier.type);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ BT_ASSERT(*decl);
+ return 0;
+
+error:
+ ctf_field_class_destroy(*decl);
+ *decl = NULL;
+ return ret;
+}
+
+static
+int visit_event_decl_entry(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node,
+ struct ctf_event_class *event_class, uint64_t *stream_id,
+ int *set)
+{
+ int ret = 0;
+ char *left = NULL;
+
+ switch (node->type) {
+ case NODE_TYPEDEF:
+ ret = visit_field_class_def(ctx, node->u.field_class_def.field_class_specifier_list,
+ &node->u.field_class_def.field_class_declarators);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Cannot add field class found in event class.");
+ goto error;
+ }
+ break;
+ case NODE_TYPEALIAS:
+ ret = visit_field_class_alias(ctx, node->u.field_class_alias.target,
+ node->u.field_class_alias.alias);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Cannot add field class alias found in event class.");
+ goto error;
+ }
+ break;
+ case NODE_CTF_EXPRESSION:
+ {
+ left = ctf_ast_concatenate_unary_strings(&node->u.ctf_expression.left);
+ if (!left) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Cannot concatenate unary strings.");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (strcmp(left, "name") == 0) {
+ /* This is already known at this stage */
+ if (_IS_SET(set, _EVENT_NAME_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "name", "event class");
+ ret = -EPERM;
+ goto error;
+ }
+
+ _SET(set, _EVENT_NAME_SET);
+ } else if (strcmp(left, "id") == 0) {
+ int64_t id = -1;
+
+ if (_IS_SET(set, _EVENT_ID_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "id", "event class");
+ ret = -EPERM;
+ goto error;
+ }
+
+ ret = get_unary_unsigned(ctx,
+ &node->u.ctf_expression.right,
+ (uint64_t *) &id);
+ /* Only read "id" if get_unary_unsigned() succeeded. */
+ if (ret || (!ret && id < 0)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Unexpected unary expression for event class's `id` attribute.");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ event_class->id = id;
+ _SET(set, _EVENT_ID_SET);
+ } else if (strcmp(left, "stream_id") == 0) {
+ if (_IS_SET(set, _EVENT_STREAM_ID_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "stream_id",
+ "event class");
+ ret = -EPERM;
+ goto error;
+ }
+
+ ret = get_unary_unsigned(ctx,
+ &node->u.ctf_expression.right, stream_id);
+
+ /*
+ * Only read "stream_id" if get_unary_unsigned()
+ * succeeded.
+ */
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Unexpected unary expression for event class's `stream_id` attribute.");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ _SET(set, _EVENT_STREAM_ID_SET);
+ } else if (strcmp(left, "context") == 0) {
+ if (_IS_SET(set, _EVENT_CONTEXT_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Duplicate `context` entry in event class.");
+ ret = -EPERM;
+ goto error;
+ }
+
+ ret = visit_field_class_specifier_list(ctx,
+ _BT_LIST_FIRST_ENTRY(
+ &node->u.ctf_expression.right,
+ struct ctf_node, siblings),
+ &event_class->spec_context_fc);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Cannot create event class's context field class.");
+ goto error;
+ }
+
+ BT_ASSERT(event_class->spec_context_fc);
+ _SET(set, _EVENT_CONTEXT_SET);
+ } else if (strcmp(left, "fields") == 0) {
+ if (_IS_SET(set, _EVENT_FIELDS_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Duplicate `fields` entry in event class.");
+ ret = -EPERM;
+ goto error;
+ }
+
+ ret = visit_field_class_specifier_list(ctx,
+ _BT_LIST_FIRST_ENTRY(
+ &node->u.ctf_expression.right,
+ struct ctf_node, siblings),
+ &event_class->payload_fc);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Cannot create event class's payload field class.");
+ goto error;
+ }
+
+ BT_ASSERT(event_class->payload_fc);
+ _SET(set, _EVENT_FIELDS_SET);
+ } else if (strcmp(left, "loglevel") == 0) {
+ uint64_t loglevel_value;
+ bool is_log_level_known = true;
+ bt_event_class_log_level log_level;
+
+ if (_IS_SET(set, _EVENT_LOG_LEVEL_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "loglevel",
+ "event class");
+ ret = -EPERM;
+ goto error;
+ }
+
+ ret = get_unary_unsigned(ctx,
+ &node->u.ctf_expression.right, &loglevel_value);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Unexpected unary expression for event class's `loglevel` attribute.");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ switch (loglevel_value) {
+ case 0:
+ log_level = BT_EVENT_CLASS_LOG_LEVEL_EMERGENCY;
+ break;
+ case 1:
+ log_level = BT_EVENT_CLASS_LOG_LEVEL_ALERT;
+ break;
+ case 2:
+ log_level = BT_EVENT_CLASS_LOG_LEVEL_CRITICAL;
+ break;
+ case 3:
+ log_level = BT_EVENT_CLASS_LOG_LEVEL_ERROR;
+ break;
+ case 4:
+ log_level = BT_EVENT_CLASS_LOG_LEVEL_WARNING;
+ break;
+ case 5:
+ log_level = BT_EVENT_CLASS_LOG_LEVEL_NOTICE;
+ break;
+ case 6:
+ log_level = BT_EVENT_CLASS_LOG_LEVEL_INFO;
+ break;
+ case 7:
+ log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM;
+ break;
+ case 8:
+ log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM;
+ break;
+ case 9:
+ log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS;
+ break;
+ case 10:
+ log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE;
+ break;
+ case 11:
+ log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT;
+ break;
+ case 12:
+ log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION;
+ break;
+ case 13:
+ log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG_LINE;
+ break;
+ case 14:
+ log_level = BT_EVENT_CLASS_LOG_LEVEL_DEBUG;
+ break;
+ default:
+ is_log_level_known = false;
+ _BT_COMP_LOGW_NODE(node, "Not setting event class's log level because its value is unknown: "
+ "log-level=%" PRIu64, loglevel_value);
+ }
+
+ if (is_log_level_known) {
+ ctf_event_class_set_log_level(event_class, log_level);
+ }
+
+ _SET(set, _EVENT_LOG_LEVEL_SET);
+ } else if (strcmp(left, "model.emf.uri") == 0) {
+ char *right;
+
+ if (_IS_SET(set, _EVENT_MODEL_EMF_URI_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "model.emf.uri",
+ "event class");
+ ret = -EPERM;
+ goto error;
+ }
+
+ right = ctf_ast_concatenate_unary_strings(
+ &node->u.ctf_expression.right);
+ if (!right) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Unexpected unary expression for event class's `model.emf.uri` attribute.");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (strlen(right) == 0) {
+ _BT_COMP_LOGW_NODE(node,
+ "Not setting event class's EMF URI because it's empty.");
+ } else {
+ g_string_assign(event_class->emf_uri,
+ right);
+ }
+
+ g_free(right);
+ _SET(set, _EVENT_MODEL_EMF_URI_SET);
+ } else {
+ _BT_COMP_LOGW_NODE(node,
+ "Unknown attribute in event class: "
+ "attr-name=\"%s\"", left);
+ }
+
+ g_free(left);
+ left = NULL;
+ break;
+ }
+ default:
+ ret = -EPERM;
+ goto error;
+ }
+
+ goto end;
+
+error:
+ g_free(left);
+
+end:
+ return ret;
+}
+
+static
+char *get_event_decl_name(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node)
+{
+ char *left = NULL;
+ char *name = NULL;
+ struct ctf_node *iter;
+ struct bt_list_head *decl_list = &node->u.event.declaration_list;
+
+ bt_list_for_each_entry(iter, decl_list, siblings) {
+ if (iter->type != NODE_CTF_EXPRESSION) {
+ continue;
+ }
+
+ left = ctf_ast_concatenate_unary_strings(&iter->u.ctf_expression.left);
+ if (!left) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
+ "Cannot concatenate unary strings.");
+ goto error;
+ }
+
+ if (strcmp(left, "name") == 0) {
+ name = ctf_ast_concatenate_unary_strings(
+ &iter->u.ctf_expression.right);
+ if (!name) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
+ "Unexpected unary expression for event class's `name` attribute.");
+ goto error;
+ }
+ }
+
+ g_free(left);
+ left = NULL;
+
+ if (name) {
+ break;
+ }
+ }
+
+ return name;
+
+error:
+ g_free(left);
+ return NULL;
+}
+
+static
+int visit_event_decl(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node)
+{
+ int ret = 0;
+ int set = 0;
+ struct ctf_node *iter;
+ uint64_t stream_id = 0;
+ char *event_name = NULL;
+ struct ctf_event_class *event_class = NULL;
+ struct ctf_stream_class *stream_class = NULL;
+ struct bt_list_head *decl_list = &node->u.event.declaration_list;
+ bool pop_scope = false;
+
+ if (node->visited) {
+ goto end;
+ }
+
+ node->visited = TRUE;
+ event_name = get_event_decl_name(ctx, node);
+ if (!event_name) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Missing `name` attribute in event class.");
+ ret = -EPERM;
+ goto error;
+ }
+
+ event_class = ctf_event_class_create();
+ BT_ASSERT(event_class);
+ g_string_assign(event_class->name, event_name);
+ _TRY_PUSH_SCOPE_OR_GOTO_ERROR();
+ pop_scope = true;
+
+ bt_list_for_each_entry(iter, decl_list, siblings) {
+ ret = visit_event_decl_entry(ctx, iter, event_class,
+ &stream_id, &set);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter, "Cannot visit event class's entry: "
+ "ret=%d", ret);
+ goto error;
+ }
+ }
+
+ if (!_IS_SET(&set, _EVENT_STREAM_ID_SET)) {
+ /*
+ * Allow missing stream_id if there is only a single
+ * stream class.
+ */
+ switch (ctx->ctf_tc->stream_classes->len) {
+ case 0:
+ /* Create implicit stream class if there's none */
+ stream_id = 0;
+ stream_class = ctf_stream_class_create();
+ BT_ASSERT(stream_class);
+ stream_class->id = stream_id;
+ g_ptr_array_add(ctx->ctf_tc->stream_classes,
+ stream_class);
+ break;
+ case 1:
+ /* Single stream class: get its ID */
+ stream_class = (ctf_stream_class *) ctx->ctf_tc->stream_classes->pdata[0];
+ stream_id = stream_class->id;
+ break;
+ default:
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Missing `stream_id` attribute in event class.");
+ ret = -EPERM;
+ goto error;
+ }
+ }
+
+ /* We have the stream ID now; get the stream class if found */
+ if (!stream_class) {
+ stream_class = ctf_trace_class_borrow_stream_class_by_id(
+ ctx->ctf_tc, stream_id);
+ if (!stream_class) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Cannot find stream class at this point: "
+ "id=%" PRId64, stream_id);
+ ret = -EINVAL;
+ goto error;
+ }
+ }
+
+ BT_ASSERT(stream_class);
+
+ if (!_IS_SET(&set, _EVENT_ID_SET)) {
+ /* Allow only one event without ID per stream */
+ if (stream_class->event_classes->len != 0) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Missing `id` attribute in event class.");
+ ret = -EPERM;
+ goto error;
+ }
+
+ /* Automatic ID */
+ event_class->id = 0;
+ }
+
+ if (ctf_stream_class_borrow_event_class_by_id(stream_class,
+ event_class->id)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Duplicate event class (same ID) in the same stream class: "
+ "id=%" PRId64, event_class->id);
+ ret = -EEXIST;
+ goto error;
+ }
+
+ ctf_stream_class_append_event_class(stream_class, event_class);
+ event_class = NULL;
+ goto end;
+
+error:
+ ctf_event_class_destroy(event_class);
+ event_class = NULL;
+
+ if (ret >= 0) {
+ ret = -1;
+ }
+
+end:
+ if (pop_scope) {
+ ctx_pop_scope(ctx);
+ }
+
+ g_free(event_name);
+
+ return ret;
+}
+
+static
+int auto_map_field_to_trace_clock_class(struct ctf_visitor_generate_ir *ctx,
+ struct ctf_field_class *fc)
+{
+ struct ctf_clock_class *clock_class_to_map_to = NULL;
+ struct ctf_field_class_int *int_fc = ctf_field_class_as_int(fc);
+ int ret = 0;
+ uint64_t clock_class_count;
+
+ if (!fc) {
+ goto end;
+ }
+
+ if (fc->type != CTF_FIELD_CLASS_TYPE_INT &&
+ fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
+ goto end;
+ }
+
+ if (int_fc->mapped_clock_class) {
+ /* Already mapped */
+ goto end;
+ }
+
+ clock_class_count = ctx->ctf_tc->clock_classes->len;
+
+ switch (clock_class_count) {
+ case 0:
+ /*
+ * No clock class exists in the trace at this point. Create an
+ * implicit one at 1 GHz, named `default`, and use this clock
+ * class.
+ */
+ clock_class_to_map_to = ctf_clock_class_create();
+ BT_ASSERT(clock_class_to_map_to);
+ clock_class_to_map_to->frequency = UINT64_C(1000000000);
+ g_string_assign(clock_class_to_map_to->name, "default");
+ BT_ASSERT(ret == 0);
+ g_ptr_array_add(ctx->ctf_tc->clock_classes,
+ clock_class_to_map_to);
+ break;
+ case 1:
+ /*
+ * Only one clock class exists in the trace at this point: use
+ * this one.
+ */
+ clock_class_to_map_to = (ctf_clock_class *) ctx->ctf_tc->clock_classes->pdata[0];
+ break;
+ default:
+ /*
+ * Timestamp field not mapped to a clock class and there's more
+ * than one clock class in the trace: this is an error.
+ */
+ _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Timestamp field found with no mapped clock class, "
+ "but there's more than one clock class in the trace at this point.");
+ ret = -1;
+ goto end;
+ }
+
+ BT_ASSERT(clock_class_to_map_to);
+ int_fc->mapped_clock_class = clock_class_to_map_to;
+
+end:
+ return ret;
+}
+
+static
+int auto_map_fields_to_trace_clock_class(struct ctf_visitor_generate_ir *ctx,
+ struct ctf_field_class *root_fc, const char *field_name)
+{
+ int ret = 0;
+ uint64_t i, count;
+ struct ctf_field_class_struct *struct_fc = (ctf_field_class_struct *) root_fc;
+ struct ctf_field_class_variant *var_fc = (ctf_field_class_variant *) root_fc;
+
+ if (!root_fc) {
+ goto end;
+ }
+
+ if (root_fc->type != CTF_FIELD_CLASS_TYPE_STRUCT &&
+ root_fc->type != CTF_FIELD_CLASS_TYPE_VARIANT) {
+ goto end;
+ }
+
+ if (root_fc->type == CTF_FIELD_CLASS_TYPE_STRUCT) {
+ count = struct_fc->members->len;
+ } else {
+ count = var_fc->options->len;
+ }
+
+ for (i = 0; i < count; i++) {
+ struct ctf_named_field_class *named_fc = NULL;
+
+ if (root_fc->type == CTF_FIELD_CLASS_TYPE_STRUCT) {
+ named_fc = ctf_field_class_struct_borrow_member_by_index(
+ struct_fc, i);
+ } else if (root_fc->type == CTF_FIELD_CLASS_TYPE_VARIANT) {
+ named_fc = ctf_field_class_variant_borrow_option_by_index(
+ var_fc, i);
+ } else {
+ bt_common_abort();
+ }
+
+ if (strcmp(named_fc->name->str, field_name) == 0) {
+ ret = auto_map_field_to_trace_clock_class(ctx,
+ named_fc->fc);
+ if (ret) {
+ _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot automatically map field to trace's clock class: "
+ "field-name=\"%s\"", field_name);
+ goto end;
+ }
+ }
+
+ ret = auto_map_fields_to_trace_clock_class(ctx, named_fc->fc,
+ field_name);
+ if (ret) {
+ _BT_COMP_OR_COMP_CLASS_LOGE_APPEND_CAUSE("Cannot automatically map structure or variant field class's fields to trace's clock class: "
+ "field-name=\"%s\", root-field-name=\"%s\"",
+ field_name, named_fc->name->str);
+ goto end;
+ }
+ }
+
+end:
+ return ret;
+}
+
+static
+int visit_stream_decl_entry(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node,
+ struct ctf_stream_class *stream_class, int *set)
+{
+ int ret = 0;
+ char *left = NULL;
+
+ switch (node->type) {
+ case NODE_TYPEDEF:
+ ret = visit_field_class_def(ctx, node->u.field_class_def.field_class_specifier_list,
+ &node->u.field_class_def.field_class_declarators);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Cannot add field class found in stream class.");
+ goto error;
+ }
+ break;
+ case NODE_TYPEALIAS:
+ ret = visit_field_class_alias(ctx, node->u.field_class_alias.target,
+ node->u.field_class_alias.alias);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Cannot add field class alias found in stream class.");
+ goto error;
+ }
+ break;
+ case NODE_CTF_EXPRESSION:
+ {
+ left = ctf_ast_concatenate_unary_strings(&node->u.ctf_expression.left);
+ if (!left) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Cannot concatenate unary strings.");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (strcmp(left, "id") == 0) {
+ int64_t id;
+
+ if (_IS_SET(set, _STREAM_ID_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "id",
+ "stream declaration");
+ ret = -EPERM;
+ goto error;
+ }
+
+ ret = get_unary_unsigned(ctx,
+ &node->u.ctf_expression.right,
+ (uint64_t *) &id);
+
+ /* Only read "id" if get_unary_unsigned() succeeded. */
+ if (ret || (!ret && id < 0)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Unexpected unary expression for stream class's `id` attribute.");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (ctf_trace_class_borrow_stream_class_by_id(
+ ctx->ctf_tc, id)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Duplicate stream class (same ID): id=%" PRId64,
+ id);
+ ret = -EEXIST;
+ goto error;
+ }
+
+ stream_class->id = id;
+ _SET(set, _STREAM_ID_SET);
+ } else if (strcmp(left, "event.header") == 0) {
+ if (_IS_SET(set, _STREAM_EVENT_HEADER_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Duplicate `event.header` entry in stream class.");
+ ret = -EPERM;
+ goto error;
+ }
+
+ ret = visit_field_class_specifier_list(ctx,
+ _BT_LIST_FIRST_ENTRY(
+ &node->u.ctf_expression.right,
+ struct ctf_node, siblings),
+ &stream_class->event_header_fc);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Cannot create stream class's event header field class.");
+ goto error;
+ }
+
+ BT_ASSERT(stream_class->event_header_fc);
+ ret = auto_map_fields_to_trace_clock_class(ctx,
+ stream_class->event_header_fc, "timestamp");
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Cannot automatically map specific event header field class fields named `timestamp` to trace's clock class.");
+ goto error;
+ }
+
+ _SET(set, _STREAM_EVENT_HEADER_SET);
+ } else if (strcmp(left, "event.context") == 0) {
+ if (_IS_SET(set, _STREAM_EVENT_CONTEXT_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Duplicate `event.context` entry in stream class.");
+ ret = -EPERM;
+ goto error;
+ }
+
+ ret = visit_field_class_specifier_list(ctx,
+ _BT_LIST_FIRST_ENTRY(
+ &node->u.ctf_expression.right,
+ struct ctf_node, siblings),
+ &stream_class->event_common_context_fc);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Cannot create stream class's event context field class.");
+ goto error;
+ }
+
+ BT_ASSERT(stream_class->event_common_context_fc);
+ _SET(set, _STREAM_EVENT_CONTEXT_SET);
+ } else if (strcmp(left, "packet.context") == 0) {
+ if (_IS_SET(set, _STREAM_PACKET_CONTEXT_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Duplicate `packet.context` entry in stream class.");
+ ret = -EPERM;
+ goto error;
+ }
+
+ ret = visit_field_class_specifier_list(ctx,
+ _BT_LIST_FIRST_ENTRY(
+ &node->u.ctf_expression.right,
+ struct ctf_node, siblings),
+ &stream_class->packet_context_fc);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Cannot create stream class's packet context field class.");
+ goto error;
+ }
+
+ BT_ASSERT(stream_class->packet_context_fc);
+ ret = auto_map_fields_to_trace_clock_class(ctx,
+ stream_class->packet_context_fc,
+ "timestamp_begin");
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Cannot automatically map specific packet context field class fields named `timestamp_begin` to trace's clock class.");
+ goto error;
+ }
+
+ ret = auto_map_fields_to_trace_clock_class(ctx,
+ stream_class->packet_context_fc,
+ "timestamp_end");
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Cannot automatically map specific packet context field class fields named `timestamp_end` to trace's clock class.");
+ goto error;
+ }
+
+ _SET(set, _STREAM_PACKET_CONTEXT_SET);
+ } else {
+ _BT_COMP_LOGW_NODE(node,
+ "Unknown attribute in stream class: "
+ "attr-name=\"%s\"", left);
+ }
+
+ g_free(left);
+ left = NULL;
+ break;
+ }
+
+ default:
+ ret = -EPERM;
+ goto error;
+ }
+
+ return 0;
+
+error:
+ g_free(left);
+ return ret;
+}
+
+static
+int visit_stream_decl(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node)
+{
+ int set = 0;
+ int ret = 0;
+ struct ctf_node *iter;
+ struct ctf_stream_class *stream_class = NULL;
+ struct bt_list_head *decl_list = &node->u.stream.declaration_list;
+
+ if (node->visited) {
+ goto end;
+ }
+
+ node->visited = TRUE;
+ stream_class = ctf_stream_class_create();
+ BT_ASSERT(stream_class);
+ _TRY_PUSH_SCOPE_OR_GOTO_ERROR();
+
+ bt_list_for_each_entry(iter, decl_list, siblings) {
+ ret = visit_stream_decl_entry(ctx, iter, stream_class, &set);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
+ "Cannot visit stream class's entry: "
+ "ret=%d", ret);
+ ctx_pop_scope(ctx);
+ goto error;
+ }
+ }
+
+ ctx_pop_scope(ctx);
+
+ if (_IS_SET(&set, _STREAM_ID_SET)) {
+ /* Check that packet header has `stream_id` field */
+ struct ctf_named_field_class *named_fc = NULL;
+
+ if (!ctx->ctf_tc->packet_header_fc) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Stream class has a `id` attribute, "
+ "but trace has no packet header field class.");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ named_fc = ctf_field_class_struct_borrow_member_by_name(
+ ctf_field_class_as_struct(ctx->ctf_tc->packet_header_fc), "stream_id");
+ if (!named_fc) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Stream class has a `id` attribute, "
+ "but trace's packet header field class has no `stream_id` field.");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (named_fc->fc->type != CTF_FIELD_CLASS_TYPE_INT &&
+ named_fc->fc->type != CTF_FIELD_CLASS_TYPE_ENUM) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Stream class has a `id` attribute, "
+ "but trace's packet header field class's `stream_id` field is not an integer field class.");
+ ret = -EINVAL;
+ goto error;
+ }
+ } else {
+ /* Allow only _one_ ID-less stream */
+ if (ctx->ctf_tc->stream_classes->len != 0) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Missing `id` attribute in stream class as there's more than one stream class in the trace.");
+ ret = -EPERM;
+ goto error;
+ }
+
+ /* Automatic ID: 0 */
+ stream_class->id = 0;
+ }
+
+ /*
+ * Make sure that this stream class's ID is currently unique in
+ * the trace.
+ */
+ if (ctf_trace_class_borrow_stream_class_by_id(ctx->ctf_tc,
+ stream_class->id)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Duplicate stream class (same ID): id=%" PRId64,
+ stream_class->id);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ g_ptr_array_add(ctx->ctf_tc->stream_classes, stream_class);
+ stream_class = NULL;
+ goto end;
+
+error:
+ ctf_stream_class_destroy(stream_class);
+ stream_class = NULL;
+
+end:
+ return ret;
+}
+
+static
+int visit_trace_decl_entry(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node,
+ int *set)
+{
+ int ret = 0;
+ char *left = NULL;
+ uint64_t val;
+
+ switch (node->type) {
+ case NODE_TYPEDEF:
+ ret = visit_field_class_def(ctx, node->u.field_class_def.field_class_specifier_list,
+ &node->u.field_class_def.field_class_declarators);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Cannot add field class found in trace (`trace` block).");
+ goto error;
+ }
+ break;
+ case NODE_TYPEALIAS:
+ ret = visit_field_class_alias(ctx, node->u.field_class_alias.target,
+ node->u.field_class_alias.alias);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Cannot add field class alias found in trace (`trace` block).");
+ goto error;
+ }
+ break;
+ case NODE_CTF_EXPRESSION:
+ {
+ left = ctf_ast_concatenate_unary_strings(&node->u.ctf_expression.left);
+ if (!left) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Cannot concatenate unary strings.");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (strcmp(left, "major") == 0) {
+ if (_IS_SET(set, _TRACE_MAJOR_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "major", "trace");
+ ret = -EPERM;
+ goto error;
+ }
+
+ ret = get_unary_unsigned(ctx,
+ &node->u.ctf_expression.right, &val);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Unexpected unary expression for trace's `major` attribute.");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (val != 1) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Invalid trace's `minor` attribute: expecting 1.");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ ctx->ctf_tc->major = val;
+ _SET(set, _TRACE_MAJOR_SET);
+ } else if (strcmp(left, "minor") == 0) {
+ if (_IS_SET(set, _TRACE_MINOR_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "minor", "trace");
+ ret = -EPERM;
+ goto error;
+ }
+
+ ret = get_unary_unsigned(ctx,
+ &node->u.ctf_expression.right, &val);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Unexpected unary expression for trace's `minor` attribute.");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (val != 8) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Invalid trace's `minor` attribute: expecting 8.");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ ctx->ctf_tc->minor = val;
+ _SET(set, _TRACE_MINOR_SET);
+ } else if (strcmp(left, "uuid") == 0) {
+ if (_IS_SET(set, _TRACE_UUID_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "uuid", "trace");
+ ret = -EPERM;
+ goto error;
+ }
+
+ ret = get_unary_uuid(ctx,
+ &node->u.ctf_expression.right,
+ ctx->ctf_tc->uuid);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Invalid trace's `uuid` attribute.");
+ goto error;
+ }
+
+ ctx->ctf_tc->is_uuid_set = true;
+ _SET(set, _TRACE_UUID_SET);
+ } else if (strcmp(left, "byte_order") == 0) {
+ /* Default byte order is already known at this stage */
+ if (_IS_SET(set, _TRACE_BYTE_ORDER_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "byte_order",
+ "trace");
+ ret = -EPERM;
+ goto error;
+ }
+
+ BT_ASSERT(ctx->ctf_tc->default_byte_order != CTF_BYTE_ORDER_UNKNOWN);
+ _SET(set, _TRACE_BYTE_ORDER_SET);
+ } else if (strcmp(left, "packet.header") == 0) {
+ if (_IS_SET(set, _TRACE_PACKET_HEADER_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Duplicate `packet.header` entry in trace.");
+ ret = -EPERM;
+ goto error;
+ }
+
+ ret = visit_field_class_specifier_list(ctx,
+ _BT_LIST_FIRST_ENTRY(
+ &node->u.ctf_expression.right,
+ struct ctf_node, siblings),
+ &ctx->ctf_tc->packet_header_fc);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Cannot create trace's packet header field class.");
+ goto error;
+ }
+
+ BT_ASSERT(ctx->ctf_tc->packet_header_fc);
+ _SET(set, _TRACE_PACKET_HEADER_SET);
+ } else {
+ _BT_COMP_LOGW_NODE(node,
+ "Unknown attribute in stream class: "
+ "attr-name=\"%s\"", left);
+ }
+
+ g_free(left);
+ left = NULL;
+ break;
+ }
+ default:
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Unknown expression in trace.");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ return 0;
+
+error:
+ g_free(left);
+ return ret;
+}
+
+static
+int visit_trace_decl(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node)
+{
+ int ret = 0;
+ int set = 0;
+ struct ctf_node *iter;
+ struct bt_list_head *decl_list = &node->u.trace.declaration_list;
+
+ if (node->visited) {
+ goto end;
+ }
+
+ node->visited = TRUE;
+
+ if (ctx->is_trace_visited) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node, "Duplicate trace (`trace` block).");
+ ret = -EEXIST;
+ goto error;
+ }
+
+ _TRY_PUSH_SCOPE_OR_GOTO_ERROR();
+
+ bt_list_for_each_entry(iter, decl_list, siblings) {
+ ret = visit_trace_decl_entry(ctx, iter, &set);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter, "Cannot visit trace's entry (`trace` block): "
+ "ret=%d", ret);
+ ctx_pop_scope(ctx);
+ goto error;
+ }
+ }
+
+ ctx_pop_scope(ctx);
+
+ if (!_IS_SET(&set, _TRACE_MAJOR_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Missing `major` attribute in trace (`trace` block).");
+ ret = -EPERM;
+ goto error;
+ }
+
+ if (!_IS_SET(&set, _TRACE_MINOR_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Missing `minor` attribute in trace (`trace` block).");
+ ret = -EPERM;
+ goto error;
+ }
+
+ if (!_IS_SET(&set, _TRACE_BYTE_ORDER_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Missing `byte_order` attribute in trace (`trace` block).");
+ ret = -EPERM;
+ goto error;
+ }
+
+ ctx->is_trace_visited = true;
+
+end:
+ return 0;
+
+error:
+ return ret;
+}
+
+static
+int visit_env(struct ctf_visitor_generate_ir *ctx, struct ctf_node *node)
+{
+ int ret = 0;
+ char *left = NULL;
+ struct ctf_node *entry_node;
+ struct bt_list_head *decl_list = &node->u.env.declaration_list;
+
+ if (node->visited) {
+ goto end;
+ }
+
+ node->visited = TRUE;
+
+ bt_list_for_each_entry(entry_node, decl_list, siblings) {
+ struct bt_list_head *right_head =
+ &entry_node->u.ctf_expression.right;
+
+ if (entry_node->type != NODE_CTF_EXPRESSION) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+ "Wrong expression in environment entry: "
+ "node-type=%d", entry_node->type);
+ ret = -EPERM;
+ goto error;
+ }
+
+ left = ctf_ast_concatenate_unary_strings(
+ &entry_node->u.ctf_expression.left);
+ if (!left) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+ "Cannot get environment entry's name.");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (is_unary_string(right_head)) {
+ char *right = ctf_ast_concatenate_unary_strings(right_head);
+
+ if (!right) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+ "Unexpected unary expression for environment entry's value: "
+ "name=\"%s\"", left);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (strcmp(left, "tracer_name") == 0) {
+ if (strncmp(right, "lttng", 5) == 0) {
+ BT_COMP_LOGI("Detected LTTng trace from `%s` environment value: "
+ "tracer-name=\"%s\"",
+ left, right);
+ ctx->is_lttng = true;
+ }
+ }
+
+ ctf_trace_class_append_env_entry(ctx->ctf_tc,
+ left, CTF_TRACE_CLASS_ENV_ENTRY_TYPE_STR,
+ right, 0);
+ g_free(right);
+ } else if (is_unary_unsigned(right_head) ||
+ is_unary_signed(right_head)) {
+ int64_t v;
+
+ if (is_unary_unsigned(right_head)) {
+ ret = get_unary_unsigned(ctx, right_head,
+ (uint64_t *) &v);
+ } else {
+ ret = get_unary_signed(right_head, &v);
+ }
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+ "Unexpected unary expression for environment entry's value: "
+ "name=\"%s\"", left);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ ctf_trace_class_append_env_entry(ctx->ctf_tc,
+ left, CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT,
+ NULL, v);
+ } else {
+ _BT_COMP_LOGW_NODE(entry_node,
+ "Environment entry has unknown type: "
+ "name=\"%s\"", left);
+ }
+
+ g_free(left);
+ left = NULL;
+ }
+
+end:
+ return 0;
+
+error:
+ g_free(left);
+ return ret;
+}
+
+static
+int set_trace_byte_order(struct ctf_visitor_generate_ir *ctx, struct ctf_node *trace_node)
+{
+ int ret = 0;
+ int set = 0;
+ char *left = NULL;
+ struct ctf_node *node;
+ struct bt_list_head *decl_list = &trace_node->u.trace.declaration_list;
+
+ bt_list_for_each_entry(node, decl_list, siblings) {
+ if (node->type == NODE_CTF_EXPRESSION) {
+ struct ctf_node *right_node;
+
+ left = ctf_ast_concatenate_unary_strings(
+ &node->u.ctf_expression.left);
+ if (!left) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Cannot concatenate unary strings.");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (strcmp(left, "byte_order") == 0) {
+ enum ctf_byte_order bo;
+
+ if (_IS_SET(&set, _TRACE_BYTE_ORDER_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(node, "byte_order",
+ "trace");
+ ret = -EPERM;
+ goto error;
+ }
+
+ _SET(&set, _TRACE_BYTE_ORDER_SET);
+ right_node = _BT_LIST_FIRST_ENTRY(
+ &node->u.ctf_expression.right,
+ struct ctf_node, siblings);
+ bo = byte_order_from_unary_expr(ctx,
+ right_node);
+ if (bo == CTF_BYTE_ORDER_UNKNOWN) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Invalid `byte_order` attribute in trace (`trace` block): "
+ "expecting `le`, `be`, or `network`.");
+ ret = -EINVAL;
+ goto error;
+ } else if (bo == CTF_BYTE_ORDER_DEFAULT) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Invalid `byte_order` attribute in trace (`trace` block): "
+ "cannot be set to `native` here.");
+ ret = -EPERM;
+ goto error;
+ }
+
+ ctx->ctf_tc->default_byte_order = bo;
+ }
+
+ g_free(left);
+ left = NULL;
+ }
+ }
+
+ if (!_IS_SET(&set, _TRACE_BYTE_ORDER_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(trace_node,
+ "Missing `byte_order` attribute in trace (`trace` block).");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ return 0;
+
+error:
+ g_free(left);
+ return ret;
+}
+
+static
+int visit_clock_decl_entry(struct ctf_visitor_generate_ir *ctx, struct ctf_node *entry_node,
+ struct ctf_clock_class *clock, int *set, int64_t *offset_seconds,
+ uint64_t *offset_cycles)
+{
+ int ret = 0;
+ char *left = NULL;
+
+ if (entry_node->type != NODE_CTF_EXPRESSION) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+ "Unexpected node type: node-type=%d",
+ entry_node->type);
+ ret = -EPERM;
+ goto error;
+ }
+
+ left = ctf_ast_concatenate_unary_strings(&entry_node->u.ctf_expression.left);
+ if (!left) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node, "Cannot concatenate unary strings.");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (strcmp(left, "name") == 0) {
+ char *right;
+
+ if (_IS_SET(set, _CLOCK_NAME_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "name", "clock class");
+ ret = -EPERM;
+ goto error;
+ }
+
+ right = ctf_ast_concatenate_unary_strings(
+ &entry_node->u.ctf_expression.right);
+ if (!right) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+ "Unexpected unary expression for clock class's `name` attribute.");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ g_string_assign(clock->name, right);
+ g_free(right);
+ _SET(set, _CLOCK_NAME_SET);
+ } else if (strcmp(left, "uuid") == 0) {
+ bt_uuid_t uuid;
+
+ if (_IS_SET(set, _CLOCK_UUID_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "uuid", "clock class");
+ ret = -EPERM;
+ goto error;
+ }
+
+ ret = get_unary_uuid(ctx, &entry_node->u.ctf_expression.right,
+ uuid);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+ "Invalid clock class's `uuid` attribute.");
+ goto error;
+ }
+
+ clock->has_uuid = true;
+ bt_uuid_copy(clock->uuid, uuid);
+ _SET(set, _CLOCK_UUID_SET);
+ } else if (strcmp(left, "description") == 0) {
+ char *right;
+
+ if (_IS_SET(set, _CLOCK_DESCRIPTION_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "description",
+ "clock class");
+ ret = -EPERM;
+ goto error;
+ }
+
+ right = ctf_ast_concatenate_unary_strings(
+ &entry_node->u.ctf_expression.right);
+ if (!right) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+ "Unexpected unary expression for clock class's `description` attribute.");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ g_string_assign(clock->description, right);
+ g_free(right);
+ _SET(set, _CLOCK_DESCRIPTION_SET);
+ } else if (strcmp(left, "freq") == 0) {
+ uint64_t freq = UINT64_C(-1);
+
+ if (_IS_SET(set, _CLOCK_FREQ_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "freq", "clock class");
+ ret = -EPERM;
+ goto error;
+ }
+
+ ret = get_unary_unsigned(ctx,
+ &entry_node->u.ctf_expression.right, &freq);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+ "Unexpected unary expression for clock class's `freq` attribute.");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (freq == UINT64_C(-1) || freq == 0) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+ "Invalid clock class frequency: freq=%" PRIu64,
+ freq);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ clock->frequency = freq;
+ _SET(set, _CLOCK_FREQ_SET);
+ } else if (strcmp(left, "precision") == 0) {
+ uint64_t precision;
+
+ if (_IS_SET(set, _CLOCK_PRECISION_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "precision",
+ "clock class");
+ ret = -EPERM;
+ goto error;
+ }
+
+ ret = get_unary_unsigned(ctx,
+ &entry_node->u.ctf_expression.right, &precision);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+ "Unexpected unary expression for clock class's `precision` attribute.");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ clock->precision = precision;
+ _SET(set, _CLOCK_PRECISION_SET);
+ } else if (strcmp(left, "offset_s") == 0) {
+ if (_IS_SET(set, _CLOCK_OFFSET_S_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "offset_s",
+ "clock class");
+ ret = -EPERM;
+ goto error;
+ }
+
+ ret = get_unary_signed(
+ &entry_node->u.ctf_expression.right, offset_seconds);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+ "Unexpected unary expression for clock class's `offset_s` attribute.");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ _SET(set, _CLOCK_OFFSET_S_SET);
+ } else if (strcmp(left, "offset") == 0) {
+ if (_IS_SET(set, _CLOCK_OFFSET_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "offset", "clock class");
+ ret = -EPERM;
+ goto error;
+ }
+
+ ret = get_unary_unsigned(ctx,
+ &entry_node->u.ctf_expression.right, offset_cycles);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+ "Unexpected unary expression for clock class's `offset` attribute.");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ _SET(set, _CLOCK_OFFSET_SET);
+ } else if (strcmp(left, "absolute") == 0) {
+ struct ctf_node *right;
+
+ if (_IS_SET(set, _CLOCK_ABSOLUTE_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_DUP_ATTR(entry_node, "absolute",
+ "clock class");
+ ret = -EPERM;
+ goto error;
+ }
+
+ right = _BT_LIST_FIRST_ENTRY(
+ &entry_node->u.ctf_expression.right,
+ struct ctf_node, siblings);
+ ret = get_boolean(ctx, right);
+ if (ret < 0) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+ "Unexpected unary expression for clock class's `absolute` attribute.");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ clock->is_absolute = ret;
+ _SET(set, _CLOCK_ABSOLUTE_SET);
+ } else {
+ _BT_COMP_LOGW_NODE(entry_node,
+ "Unknown attribute in clock class: attr-name=\"%s\"",
+ left);
+ }
+
+ g_free(left);
+ left = NULL;
+ return 0;
+
+error:
+ g_free(left);
+ return ret;
+}
+
+static inline
+uint64_t cycles_from_ns(uint64_t frequency, uint64_t ns)
+{
+ uint64_t cycles;
+
+ /* 1GHz */
+ if (frequency == UINT64_C(1000000000)) {
+ cycles = ns;
+ } else {
+ cycles = (uint64_t) (((double) ns * (double) frequency) / 1e9);
+ }
+
+ return cycles;
+}
+
+static
+void calibrate_clock_class_offsets(int64_t *offset_seconds,
+ uint64_t *offset_cycles, uint64_t freq)
+{
+ if (*offset_cycles >= freq) {
+ const uint64_t s_in_offset_cycles = *offset_cycles / freq;
+
+ *offset_seconds += (int64_t) s_in_offset_cycles;
+ *offset_cycles -= (s_in_offset_cycles * freq);
+ }
+}
+
+static
+void apply_clock_class_is_absolute(struct ctf_visitor_generate_ir *ctx,
+ struct ctf_clock_class *clock)
+{
+ if (ctx->decoder_config.force_clock_class_origin_unix_epoch) {
+ clock->is_absolute = true;
+ }
+
+ return;
+}
+
+static
+void apply_clock_class_offset(struct ctf_visitor_generate_ir *ctx,
+ struct ctf_clock_class *clock)
+{
+ uint64_t freq;
+ int64_t offset_s_to_apply = ctx->decoder_config.clock_class_offset_s;
+ uint64_t offset_ns_to_apply;
+ int64_t cur_offset_s;
+ uint64_t cur_offset_cycles;
+
+ if (ctx->decoder_config.clock_class_offset_s == 0 &&
+ ctx->decoder_config.clock_class_offset_ns == 0) {
+ goto end;
+ }
+
+ /* Transfer nanoseconds to seconds as much as possible */
+ if (ctx->decoder_config.clock_class_offset_ns < 0) {
+ const int64_t abs_ns = -ctx->decoder_config.clock_class_offset_ns;
+ const int64_t abs_extra_s = abs_ns / INT64_C(1000000000) + 1;
+ const int64_t extra_s = -abs_extra_s;
+ const int64_t offset_ns = ctx->decoder_config.clock_class_offset_ns -
+ (extra_s * INT64_C(1000000000));
+
+ BT_ASSERT(offset_ns > 0);
+ offset_ns_to_apply = (uint64_t) offset_ns;
+ offset_s_to_apply += extra_s;
+ } else {
+ const int64_t extra_s = ctx->decoder_config.clock_class_offset_ns /
+ INT64_C(1000000000);
+ const int64_t offset_ns = ctx->decoder_config.clock_class_offset_ns -
+ (extra_s * INT64_C(1000000000));
+
+ BT_ASSERT(offset_ns >= 0);
+ offset_ns_to_apply = (uint64_t) offset_ns;
+ offset_s_to_apply += extra_s;
+ }
+
+ freq = clock->frequency;
+ cur_offset_s = clock->offset_seconds;
+ cur_offset_cycles = clock->offset_cycles;
+
+ /* Apply offsets */
+ cur_offset_s += offset_s_to_apply;
+ cur_offset_cycles += cycles_from_ns(freq, offset_ns_to_apply);
+
+ /*
+ * Recalibrate offsets because the part in cycles can be greater
+ * than the frequency at this point.
+ */
+ calibrate_clock_class_offsets(&cur_offset_s, &cur_offset_cycles, freq);
+
+ /* Set final offsets */
+ clock->offset_seconds = cur_offset_s;
+ clock->offset_cycles = cur_offset_cycles;
+
+end:
+ return;
+}
+
+static
+int visit_clock_decl(struct ctf_visitor_generate_ir *ctx, struct ctf_node *clock_node)
+{
+ int ret = 0;
+ int set = 0;
+ struct ctf_clock_class *clock;
+ struct ctf_node *entry_node;
+ struct bt_list_head *decl_list = &clock_node->u.clock.declaration_list;
+ const char *clock_class_name;
+ int64_t offset_seconds = 0;
+ uint64_t offset_cycles = 0;
+ uint64_t freq;
+
+ if (clock_node->visited) {
+ return 0;
+ }
+
+ clock_node->visited = TRUE;
+
+ /* CTF 1.8's default frequency for a clock class is 1 GHz */
+ clock = ctf_clock_class_create();
+ if (!clock) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(clock_node,
+ "Cannot create default clock class.");
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ bt_list_for_each_entry(entry_node, decl_list, siblings) {
+ ret = visit_clock_decl_entry(ctx, entry_node, clock, &set,
+ &offset_seconds, &offset_cycles);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(entry_node,
+ "Cannot visit clock class's entry: ret=%d",
+ ret);
+ goto end;
+ }
+ }
+
+ if (!_IS_SET(&set, _CLOCK_NAME_SET)) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(clock_node,
+ "Missing `name` attribute in clock class.");
+ ret = -EPERM;
+ goto end;
+ }
+
+ clock_class_name = clock->name->str;
+ BT_ASSERT(clock_class_name);
+ if (ctx->is_lttng && strcmp(clock_class_name, "monotonic") == 0) {
+ /*
+ * Old versions of LTTng forgot to set its clock class
+ * as absolute, even if it is. This is important because
+ * it's a condition to be able to sort messages
+ * from different sources.
+ */
+ clock->is_absolute = true;
+ }
+
+ /*
+ * Adjust offsets so that the part in cycles is less than the
+ * frequency (move to the part in seconds).
+ */
+ freq = clock->frequency;
+ calibrate_clock_class_offsets(&offset_seconds, &offset_cycles, freq);
+ BT_ASSERT(offset_cycles < clock->frequency);
+ clock->offset_seconds = offset_seconds;
+ clock->offset_cycles = offset_cycles;
+ apply_clock_class_offset(ctx, clock);
+ apply_clock_class_is_absolute(ctx, clock);
+ g_ptr_array_add(ctx->ctf_tc->clock_classes, clock);
+ clock = NULL;
+
+end:
+ if (clock) {
+ ctf_clock_class_destroy(clock);
+ }
+
+ return ret;
+}
+
+static
+int visit_root_decl(struct ctf_visitor_generate_ir *ctx, struct ctf_node *root_decl_node)
+{
+ int ret = 0;
+
+ if (root_decl_node->visited) {
+ goto end;
+ }
+
+ root_decl_node->visited = TRUE;
+
+ switch (root_decl_node->type) {
+ case NODE_TYPEDEF:
+ ret = visit_field_class_def(ctx,
+ root_decl_node->u.field_class_def.field_class_specifier_list,
+ &root_decl_node->u.field_class_def.field_class_declarators);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(root_decl_node,
+ "Cannot add field class found in root scope.");
+ goto end;
+ }
+ break;
+ case NODE_TYPEALIAS:
+ ret = visit_field_class_alias(ctx, root_decl_node->u.field_class_alias.target,
+ root_decl_node->u.field_class_alias.alias);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(root_decl_node,
+ "Cannot add field class alias found in root scope.");
+ goto end;
+ }
+ break;
+ case NODE_TYPE_SPECIFIER_LIST:
+ {
+ struct ctf_field_class *decl = NULL;
+
+ /*
+ * Just add the field class specifier to the root
+ * declaration scope. Put local reference.
+ */
+ ret = visit_field_class_specifier_list(ctx, root_decl_node, &decl);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(root_decl_node,
+ "Cannot visit root scope's field class: "
+ "ret=%d", ret);
+ BT_ASSERT(!decl);
+ goto end;
+ }
+
+ ctf_field_class_destroy(decl);
+ decl = NULL;
+ break;
+ }
+ default:
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(root_decl_node,
+ "Unexpected node type: node-type=%d",
+ root_decl_node->type);
+ ret = -EPERM;
+ goto end;
+ }
+
+end:
+ return ret;
+}
+
+BT_HIDDEN
+struct ctf_visitor_generate_ir *ctf_visitor_generate_ir_create(
+ const struct ctf_metadata_decoder_config *decoder_config)
+{
+ struct ctf_visitor_generate_ir *ctx = NULL;
+
+ /* Create visitor's context */
+ ctx = ctx_create(decoder_config);
+ if (!ctx) {
+ BT_COMP_LOG_CUR_LVL(BT_LOG_ERROR, decoder_config->log_level,
+ decoder_config->self_comp,
+ "Cannot create visitor's context.");
+ goto error;
+ }
+
+ goto end;
+
+error:
+ ctx_destroy(ctx);
+ ctx = NULL;
+
+end:
+ return ctx;
+}
+
+BT_HIDDEN
+void ctf_visitor_generate_ir_destroy(struct ctf_visitor_generate_ir *visitor)
+{
+ ctx_destroy(visitor);
+}
+
+BT_HIDDEN
+bt_trace_class *ctf_visitor_generate_ir_get_ir_trace_class(
+ struct ctf_visitor_generate_ir *ctx)
+{
+ BT_ASSERT_DBG(ctx);
+
+ if (ctx->trace_class) {
+ bt_trace_class_get_ref(ctx->trace_class);
+ }
+
+ return ctx->trace_class;
+}
+
+BT_HIDDEN
+struct ctf_trace_class *ctf_visitor_generate_ir_borrow_ctf_trace_class(
+ struct ctf_visitor_generate_ir *ctx)
+{
+ BT_ASSERT_DBG(ctx);
+ BT_ASSERT_DBG(ctx->ctf_tc);
+ return ctx->ctf_tc;
+}
+
+BT_HIDDEN
+int ctf_visitor_generate_ir_visit_node(struct ctf_visitor_generate_ir *ctx,
+ struct ctf_node *node)
+{
+ int ret = 0;
+
+ BT_COMP_LOGI_STR("Visiting metadata's AST to generate CTF IR objects.");
+
+ switch (node->type) {
+ case NODE_ROOT:
+ {
+ struct ctf_node *iter;
+ bool got_trace_decl = false;
+
+ /*
+ * The first thing we need is the native byte order of
+ * the trace block, because early class aliases can have
+ * a `byte_order` attribute set to `native`. If we don't
+ * have the native byte order yet, and we don't have any
+ * trace block yet, then fail with EINCOMPLETE.
+ */
+ if (ctx->ctf_tc->default_byte_order == CTF_BYTE_ORDER_UNKNOWN) {
+ bt_list_for_each_entry(iter, &node->u.root.trace, siblings) {
+ if (got_trace_decl) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Duplicate trace (`trace` block).");
+ ret = -1;
+ goto end;
+ }
+
+ ret = set_trace_byte_order(ctx, iter);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Cannot set trace's native byte order: "
+ "ret=%d", ret);
+ goto end;
+ }
+
+ got_trace_decl = true;
+ }
+
+ if (!got_trace_decl) {
+ BT_COMP_LOGD_STR("Incomplete AST: need trace (`trace` block).");
+ ret = -EINCOMPLETE;
+ goto end;
+ }
+ }
+
+ BT_ASSERT(ctx->ctf_tc->default_byte_order == CTF_BYTE_ORDER_LITTLE ||
+ ctx->ctf_tc->default_byte_order == CTF_BYTE_ORDER_BIG);
+ BT_ASSERT(ctx->current_scope &&
+ !ctx->current_scope->parent_scope);
+
+ /* Environment */
+ bt_list_for_each_entry(iter, &node->u.root.env, siblings) {
+ ret = visit_env(ctx, iter);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
+ "Cannot visit trace's environment (`env` block) entry: "
+ "ret=%d", ret);
+ goto end;
+ }
+ }
+
+ BT_ASSERT(ctx->current_scope &&
+ !ctx->current_scope->parent_scope);
+
+ /*
+ * Visit clock blocks.
+ */
+ bt_list_for_each_entry(iter, &node->u.root.clock, siblings) {
+ ret = visit_clock_decl(ctx, iter);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
+ "Cannot visit clock class: ret=%d",
+ ret);
+ goto end;
+ }
+ }
+
+ BT_ASSERT(ctx->current_scope &&
+ !ctx->current_scope->parent_scope);
+
+ /*
+ * Visit root declarations next, as they can be used by any
+ * following entity.
+ */
+ bt_list_for_each_entry(iter, &node->u.root.declaration_list,
+ siblings) {
+ ret = visit_root_decl(ctx, iter);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
+ "Cannot visit root entry: ret=%d",
+ ret);
+ goto end;
+ }
+ }
+
+ BT_ASSERT(ctx->current_scope &&
+ !ctx->current_scope->parent_scope);
+
+ /* Callsite blocks are not supported */
+ bt_list_for_each_entry(iter, &node->u.root.callsite, siblings) {
+ _BT_COMP_LOGW_NODE(iter,
+ "\"callsite\" blocks are not supported as of this version.");
+ }
+
+ BT_ASSERT(ctx->current_scope &&
+ !ctx->current_scope->parent_scope);
+
+ /* Trace */
+ bt_list_for_each_entry(iter, &node->u.root.trace, siblings) {
+ ret = visit_trace_decl(ctx, iter);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
+ "Cannot visit trace (`trace` block): "
+ "ret=%d", ret);
+ goto end;
+ }
+ }
+
+ BT_ASSERT(ctx->current_scope &&
+ !ctx->current_scope->parent_scope);
+
+ /* Streams */
+ bt_list_for_each_entry(iter, &node->u.root.stream, siblings) {
+ ret = visit_stream_decl(ctx, iter);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
+ "Cannot visit stream class: ret=%d",
+ ret);
+ goto end;
+ }
+ }
+
+ BT_ASSERT(ctx->current_scope &&
+ !ctx->current_scope->parent_scope);
+
+ /* Events */
+ bt_list_for_each_entry(iter, &node->u.root.event, siblings) {
+ ret = visit_event_decl(ctx, iter);
+ if (ret) {
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(iter,
+ "Cannot visit event class: ret=%d",
+ ret);
+ goto end;
+ }
+ }
+
+ BT_ASSERT(ctx->current_scope &&
+ !ctx->current_scope->parent_scope);
+ break;
+ }
+ default:
+ _BT_COMP_LOGE_APPEND_CAUSE_NODE(node,
+ "Unexpected node type: node-type=%d",
+ node->type);
+ ret = -EINVAL;
+ goto end;
+ }
+
+ /* Update default clock classes */
+ ret = ctf_trace_class_update_default_clock_classes(ctx->ctf_tc,
+ &ctx->log_cfg);
+ if (ret) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ /* Update trace class meanings */
+ ret = ctf_trace_class_update_meanings(ctx->ctf_tc);
+ if (ret) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ /* Update stream class configuration */
+ ret = ctf_trace_class_update_stream_class_config(ctx->ctf_tc);
+ if (ret) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ /* Update text arrays and sequences */
+ ret = ctf_trace_class_update_text_array_sequence(ctx->ctf_tc);
+ if (ret) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ /* Update structure/array/sequence alignments */
+ ret = ctf_trace_class_update_alignments(ctx->ctf_tc);
+ if (ret) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ /* Resolve sequence lengths and variant tags */
+ ret = ctf_trace_class_resolve_field_classes(ctx->ctf_tc, &ctx->log_cfg);
+ if (ret) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ if (ctx->trace_class) {
+ /*
+ * Update "in IR" for field classes.
+ *
+ * If we have no IR trace class, then we'll have no way
+ * to create IR fields anyway, so we leave all the
+ * `in_ir` members false.
+ */
+ ret = ctf_trace_class_update_in_ir(ctx->ctf_tc);
+ if (ret) {
+ ret = -EINVAL;
+ goto end;
+ }
+ }
+
+ /* Update saved value indexes */
+ ret = ctf_trace_class_update_value_storing_indexes(ctx->ctf_tc);
+ if (ret) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ /* Validate what we have so far */
+ ret = ctf_trace_class_validate(ctx->ctf_tc, &ctx->log_cfg);
+ if (ret) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ /*
+ * If there are fields which are not related to the CTF format
+ * itself in the packet header and in event header field
+ * classes, warn about it because they are never translated.
+ */
+ ctf_trace_class_warn_meaningless_header_fields(ctx->ctf_tc,
+ &ctx->log_cfg);
+
+ if (ctx->trace_class) {
+ /* Copy new CTF metadata -> new IR metadata */
+ ret = ctf_trace_class_translate(ctx->log_cfg.self_comp,
+ ctx->trace_class, ctx->ctf_tc);
+ if (ret) {
+ ret = -EINVAL;
+ goto end;
+ }
+ }
+
+end:
+ return ret;
+}