Add `ctf::src::Ctf1MetadataStreamParser` class (TSDL)
authorPhilippe Proulx <eeppeliteloop@gmail.com>
Tue, 23 Aug 2022 13:50:13 +0000 (09:50 -0400)
committerSimon Marchi <simon.marchi@efficios.com>
Tue, 23 Aug 2022 16:06:16 +0000 (12:06 -0400)
This patch:

* Adds the `ctf::src::MetadataStreamParser` abstract base class
  to have a common base for CTF 1 and CTF 2 metadata stream parsers.

  Parse a metadata stream section with parseSection() (defers to the
  virtual _parseSection() method).

  Get the resulting, current trace class and metadata stream UUID
  with traceCls() and metadataStreamUuid().

  The constructor accepts an optional self component pointer so that
  parseSection() calls finalizeTraceCls() after calling the
  version-specific _parseSection().

* Adds the `ctf::src::Ctf1MetadataStreamParser` class which is a
  concrete TSDL metadata stream parser inheriting
  `ctf::src::MetadataStreamParser`.

  parseSection() requires one or more complete root blocks of TSDL
  contents.

  `ctf::src::Ctf1MetadataStreamParser` also implements the static
  ctf::src::Ctf1MetadataStreamParser::parse() which considers a whole
  metadata stream. While this static method currently doesn't add
  anything to creating a parser and calling parseSection() once, it
  could eventually contain more validation knowing that the metadata
  stream was completely parsed.

Signed-off-by: Francis Deslauriers <francis.deslauriers@efficios.com>
Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Change-Id: I55e0f2f05d64a0acc5949029e315aeefae424dda
Reviewed-on: https://review.lttng.org/c/babeltrace/+/8000
Reviewed-by: Philippe Proulx <eeppeliteloop@gmail.com>
Change-Id: I24e175447f759220de5943ccb49c7888a5934ce5

src/plugins/ctf/common/src/metadata/tsdl/Makefile.am
src/plugins/ctf/common/src/metadata/tsdl/ctf-1-metadata-stream-parser.cpp [new file with mode: 0644]
src/plugins/ctf/common/src/metadata/tsdl/ctf-1-metadata-stream-parser.hpp [new file with mode: 0644]

index 67e89c3711396ef8766a490ab0741cd7a0164df0..a269b702d7627c3e548380034726416e7e14578e 100644 (file)
@@ -33,6 +33,8 @@ libctf_ast_la_SOURCES = \
        decoder.hpp \
        metadata-stream-decoder.cpp \
        metadata-stream-decoder.hpp \
+       ctf-1-metadata-stream-parser.cpp \
+       ctf-1-metadata-stream-parser.hpp \
        decoder-packetized-file-stream-to-buf.cpp \
        decoder-packetized-file-stream-to-buf.hpp \
        logging.cpp \
diff --git a/src/plugins/ctf/common/src/metadata/tsdl/ctf-1-metadata-stream-parser.cpp b/src/plugins/ctf/common/src/metadata/tsdl/ctf-1-metadata-stream-parser.cpp
new file mode 100644 (file)
index 0000000..78e8862
--- /dev/null
@@ -0,0 +1,710 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2022 Francis Deslauriers <francis.deslauriers@efficios.com>
+ */
+#define BT_CLOG_CFG (_mLogCfg)
+#define BT_LOG_TAG  "PLUGIN/CTF/CTF-1-META-STREAM-PARSER"
+
+#include "compat/memstream.h"
+#include "cpp-common/cfg-logging-error-reporting-throw.hpp"
+#include "cpp-common/exc.hpp"
+#include "cpp-common/optional.hpp"
+#include "ctf-1-metadata-stream-parser.hpp"
+
+namespace ctf {
+namespace src {
+
+namespace bt2c = bt2_common;
+
+namespace {
+
+ir::DispBase
+dispBaseFromIrDispBase(const bt_field_class_integer_preferred_display_base dispBase) noexcept
+{
+    return static_cast<ir::DispBase>(dispBase);
+}
+
+ir::ByteOrder byteOrderFromOrigByteOrder(const ctf_byte_order origByteOrder)
+{
+    switch (origByteOrder) {
+    case CTF_BYTE_ORDER_LITTLE:
+        return ir::ByteOrder::LITTLE;
+    case CTF_BYTE_ORDER_BIG:
+        return ir::ByteOrder::BIG;
+    default:
+        bt_common_abort();
+    }
+}
+
+nonstd::optional<ir::UIntFieldRole>
+roleFromOrigMeaning(const ctf_field_class_meaning meaning) noexcept
+{
+    switch (meaning) {
+    case CTF_FIELD_CLASS_MEANING_PACKET_BEGINNING_TIME:
+        return {ir::UIntFieldRole::DEF_CLK_TS};
+    case CTF_FIELD_CLASS_MEANING_PACKET_END_TIME:
+        return {ir::UIntFieldRole::PKT_END_DEF_CLK_TS};
+    case CTF_FIELD_CLASS_MEANING_EVENT_CLASS_ID:
+        return {ir::UIntFieldRole::EVENT_RECORD_CLS_ID};
+    case CTF_FIELD_CLASS_MEANING_STREAM_CLASS_ID:
+        return {ir::UIntFieldRole::DATA_STREAM_CLS_ID};
+    case CTF_FIELD_CLASS_MEANING_DATA_STREAM_ID:
+        return {ir::UIntFieldRole::DATA_STREAM_ID};
+    case CTF_FIELD_CLASS_MEANING_MAGIC:
+        return {ir::UIntFieldRole::PKT_MAGIC_NUMBER};
+    case CTF_FIELD_CLASS_MEANING_PACKET_COUNTER_SNAPSHOT:
+        return {ir::UIntFieldRole::PKT_SEQ_NUM};
+    case CTF_FIELD_CLASS_MEANING_DISC_EV_REC_COUNTER_SNAPSHOT:
+        return {ir::UIntFieldRole::DISC_EVENT_RECORD_COUNTER_SNAP};
+    case CTF_FIELD_CLASS_MEANING_EXP_PACKET_TOTAL_SIZE:
+        return {ir::UIntFieldRole::PKT_TOTAL_LEN};
+    case CTF_FIELD_CLASS_MEANING_EXP_PACKET_CONTENT_SIZE:
+        return {ir::UIntFieldRole::PKT_CONTENT_LEN};
+    case CTF_FIELD_CLASS_MEANING_UUID:
+    case CTF_FIELD_CLASS_MEANING_NONE:
+        return nonstd::nullopt;
+    default:
+        bt_common_abort();
+    }
+}
+
+/*
+ * Returns the integer field class roles which correspond to the meaning
+ * of the original CTF IR integer field class `origIntFc`.
+ */
+ir::UIntFieldRoles rolesFromOrigIntFc(const ctf_field_class_int& origIntFc)
+{
+    ir::UIntFieldRoles roles;
+
+    const auto role = roleFromOrigMeaning(origIntFc.meaning);
+
+    if (role) {
+        roles.insert(*role);
+    }
+
+    const auto hasPktEndDefClkTsRole = role && *role == ir::UIntFieldRole::PKT_END_DEF_CLK_TS;
+
+    if (!hasPktEndDefClkTsRole && origIntFc.mapped_clock_class) {
+        roles.insert(ir::UIntFieldRole::DEF_CLK_TS);
+    }
+
+    return roles;
+}
+
+/*
+ * Translates the original CTF IR integer field class `origFc` and
+ * returns the translated object.
+ */
+Fc::UP fcFromOrigFc(const ctf_field_class_int& oldFc)
+{
+    if (oldFc.is_signed) {
+        return createFixedLenSIntFc(oldFc.base.base.alignment,
+                                    bt2_common::DataLen::fromBits(oldFc.base.size),
+                                    byteOrderFromOrigByteOrder(oldFc.base.byte_order),
+                                    dispBaseFromIrDispBase(oldFc.disp_base));
+    } else {
+        auto roles = rolesFromOrigIntFc(oldFc);
+
+        return createFixedLenUIntFc(oldFc.base.base.alignment,
+                                    bt2_common::DataLen::fromBits(oldFc.base.size),
+                                    byteOrderFromOrigByteOrder(oldFc.base.byte_order),
+                                    dispBaseFromIrDispBase(oldFc.disp_base), std::move(roles));
+    }
+}
+
+/*
+ * Translates the mappings of the original CTF IR enumeration field
+ * class `origFc` and returns the translated objects.
+ */
+template <typename EnumT>
+static typename EnumT::Mappings enumFcMappingsFromOrigEnumFc(const ctf_field_class_enum& origFc)
+{
+    using Mappings = typename EnumT::Mappings;
+    using RangeSet = typename Mappings::mapped_type;
+
+    Mappings mappings;
+
+    for (std::size_t mappingIndex = 0; mappingIndex < origFc.mappings->len; ++mappingIndex) {
+        auto& origMapping =
+            *ctf_field_class_enum_borrow_mapping_by_index_const(&origFc, mappingIndex);
+
+        typename RangeSet::Set ranges;
+
+        for (std::size_t rangeIdx = 0; rangeIdx < origMapping.ranges->len; ++rangeIdx) {
+            auto& origRange =
+                *ctf_field_class_enum_mapping_borrow_range_by_index_const(&origMapping, rangeIdx);
+
+            ranges.emplace(static_cast<typename EnumT::Val>(origRange.lower.u),
+                           static_cast<typename EnumT::Val>(origRange.upper.u));
+        }
+
+        typename Mappings::mapped_type rangeSet {ranges};
+
+        mappings.emplace(std::make_pair(origMapping.label->str, RangeSet {ranges}));
+    }
+
+    return mappings;
+}
+
+/*
+ * Translates the original CTF IR enumeration field class `origFc` and
+ * returns the translated object.
+ */
+Fc::UP fcFromOrigFc(const ctf_field_class_enum& origFc)
+{
+    const auto byteOrder = byteOrderFromOrigByteOrder(origFc.base.base.byte_order);
+    const auto dispBase = dispBaseFromIrDispBase(origFc.base.disp_base);
+
+    if (origFc.base.is_signed) {
+        return createFixedLenSEnumFc(
+            origFc.base.base.base.alignment, bt2_common::DataLen::fromBits(origFc.base.base.size),
+            byteOrder, enumFcMappingsFromOrigEnumFc<FixedLenSEnumFc>(origFc), dispBase);
+    } else {
+        return createFixedLenUEnumFc(
+            origFc.base.base.base.alignment, bt2_common::DataLen::fromBits(origFc.base.base.size),
+            byteOrder, enumFcMappingsFromOrigEnumFc<FixedLenUEnumFc>(origFc), dispBase,
+            rolesFromOrigIntFc(origFc.base));
+    }
+}
+
+/*
+ * Translates the original CTF IR floating-point number field class
+ * `origFc` and returns the translated object.
+ */
+Fc::UP fcFromOrigFc(const ctf_field_class_float& origFc)
+{
+    return createFixedLenFloatFc(origFc.base.base.alignment,
+                                 bt2_common::DataLen::fromBits(origFc.base.size),
+                                 byteOrderFromOrigByteOrder(origFc.base.byte_order));
+}
+
+/*
+ * Returns the event record class log level name which corresponds to
+ * the original CTF IR event record class log level `origLogLevel`.
+ */
+const char *eventRecordClsLogLevelNameFromOrigLogLevel(const bt_event_class_log_level origLogLevel)
+{
+    switch (origLogLevel) {
+    case BT_EVENT_CLASS_LOG_LEVEL_EMERGENCY:
+        return "emergency";
+    case BT_EVENT_CLASS_LOG_LEVEL_ALERT:
+        return "alert";
+    case BT_EVENT_CLASS_LOG_LEVEL_CRITICAL:
+        return "critical";
+    case BT_EVENT_CLASS_LOG_LEVEL_ERROR:
+        return "error";
+    case BT_EVENT_CLASS_LOG_LEVEL_WARNING:
+        return "warning";
+    case BT_EVENT_CLASS_LOG_LEVEL_NOTICE:
+        return "notice";
+    case BT_EVENT_CLASS_LOG_LEVEL_INFO:
+        return "info";
+    case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM:
+        return "debug:system";
+    case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM:
+        return "debug:program";
+    case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS:
+        return "debug:process";
+    case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE:
+        return "debug:module";
+    case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT:
+        return "debug:unit";
+    case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION:
+        return "debug:function";
+    case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_LINE:
+        return "debug:line";
+    case BT_EVENT_CLASS_LOG_LEVEL_DEBUG:
+        return "debug";
+    default:
+        bt_common_abort();
+    }
+}
+
+/*
+ * Returns the event record class user attributes which correspond to
+ * the log level and EMF URI properties of the original CTF IR event
+ * record class `origEventRecordCls`.
+ */
+ir::OptUserAttrs
+eventRecordClsBtUserAttrsFromOrigEventRecordCls(const ctf_event_class& origEventRecordCls)
+{
+    if (origEventRecordCls.emf_uri->len == 0 && !origEventRecordCls.is_log_level_set) {
+        /* No log level and no EMF URI: no user attributes */
+        return nonstd::nullopt;
+    }
+
+    auto userAttrs = bt2::MapValue::create();
+    auto nsMapVal = userAttrs->insertEmptyMap("babeltrace.org,2020");
+
+    if (origEventRecordCls.emf_uri->len) {
+        /* Set EMF URI user attribute */
+        nsMapVal.insert("emf-uri", origEventRecordCls.emf_uri->str);
+    }
+
+    if (origEventRecordCls.is_log_level_set) {
+        /* Set log level user attribute */
+        nsMapVal.insert("log-level",
+                        eventRecordClsLogLevelNameFromOrigLogLevel(origEventRecordCls.log_level));
+    }
+
+    return userAttrs;
+}
+
+/*
+ * Translates the environment entries of the original CTF IR trace class
+ * `origTraceCls` to a map value and returns it.
+ */
+bt2::ConstMapValue::Shared envMapValFromOrigTraceCls(const ctf_trace_class& origTraceCls)
+{
+    auto envMapVal = bt2::MapValue::create();
+
+    for (std::size_t i = 0; i < origTraceCls.env_entries->len; ++i) {
+        auto& origEnvEntry = *ctf_trace_class_borrow_env_entry_by_index(
+            const_cast<ctf_trace_class *>(&origTraceCls), i);
+
+        if (origEnvEntry.type == CTF_TRACE_CLASS_ENV_ENTRY_TYPE_INT) {
+            envMapVal->insert(origEnvEntry.name->str, origEnvEntry.value.i);
+        } else {
+            BT_ASSERT(origEnvEntry.type == CTF_TRACE_CLASS_ENV_ENTRY_TYPE_STR);
+            envMapVal->insert(origEnvEntry.name->str, origEnvEntry.value.str->str);
+        }
+    }
+
+    return envMapVal;
+}
+
+} /* namespace */
+
+Fc::UP Ctf1MetadataStreamParser::_fcFromOrigFc(const ctf_field_class_struct& origFc)
+{
+    StructFc::MemberClasses memberClasses;
+
+    for (std::size_t i = 0; i < origFc.members->len; ++i) {
+        auto& origMemberCls = *ctf_field_class_struct_borrow_member_by_index_const(&origFc, i);
+
+        memberClasses.emplace_back(createStructFieldMemberCls(
+            origMemberCls.name->str, this->_fcFromOrigFc(*origMemberCls.fc)));
+    }
+
+    return createStructFc(std::move(memberClasses), origFc.base.alignment);
+}
+
+Fc::UP Ctf1MetadataStreamParser::_fcFromOrigFc(const ctf_field_class_array& origFc)
+{
+    if (origFc.base.is_text) {
+        return createStaticLenStrFc(origFc.length);
+    }
+
+    return createStaticLenArrayFc(origFc.length, this->_fcFromOrigFc(*origFc.base.elem_fc),
+                                  origFc.base.base.alignment, nonstd::nullopt,
+                                  origFc.meaning == CTF_FIELD_CLASS_MEANING_UUID);
+}
+
+FieldLoc Ctf1MetadataStreamParser::_fieldLocFromOrigFieldPath(const ctf_field_path& origFieldPath)
+{
+    /* Get original CTF IR root field class and CTF IR scope */
+    const auto origFcAndScope = [this, &origFieldPath] {
+        switch (origFieldPath.root) {
+        case CTF_SCOPE_PACKET_HEADER:
+            return std::make_pair(_mFcTranslationCtx.origTraceCls->packet_header_fc,
+                                  ir::FieldLocScope::PKT_HEADER);
+        case CTF_SCOPE_PACKET_CONTEXT:
+            return std::make_pair(_mFcTranslationCtx.origDataStreamCls->packet_context_fc,
+                                  ir::FieldLocScope::PKT_CTX);
+        case CTF_SCOPE_EVENT_HEADER:
+            return std::make_pair(_mFcTranslationCtx.origDataStreamCls->event_header_fc,
+                                  ir::FieldLocScope::EVENT_RECORD_HEADER);
+        case CTF_SCOPE_EVENT_COMMON_CONTEXT:
+            return std::make_pair(_mFcTranslationCtx.origDataStreamCls->event_common_context_fc,
+                                  ir::FieldLocScope::EVENT_RECORD_COMMON_CTX);
+        case CTF_SCOPE_EVENT_SPECIFIC_CONTEXT:
+            return std::make_pair(_mFcTranslationCtx.origEventRecordCls->spec_context_fc,
+                                  ir::FieldLocScope::EVENT_RECORD_SPEC_CTX);
+        case CTF_SCOPE_EVENT_PAYLOAD:
+            return std::make_pair(_mFcTranslationCtx.origEventRecordCls->payload_fc,
+                                  ir::FieldLocScope::EVENT_RECORD_PAYLOAD);
+        default:
+            bt_common_abort();
+        }
+    }();
+
+    /* Translate field path to field scope */
+    FieldLoc::Items items;
+    auto origFc = origFcAndScope.first;
+
+    for (std::size_t i = 0; i < origFieldPath.path->len; ++i) {
+        switch (origFc->type) {
+        case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+        case CTF_FIELD_CLASS_TYPE_ARRAY:
+        {
+            const auto childIndex = ctf_field_path_borrow_index_by_index(&origFieldPath, i);
+
+            BT_ASSERT(childIndex == -1);
+            origFc = ctf_field_class_as_array_base(origFc)->elem_fc;
+            break;
+        }
+        case CTF_FIELD_CLASS_TYPE_STRUCT:
+        {
+            const auto childIndex = ctf_field_path_borrow_index_by_index(&origFieldPath, i);
+            const auto origChildFc =
+                ctf_field_class_compound_borrow_named_field_class_by_index(origFc, childIndex);
+
+            BT_ASSERT(origChildFc);
+            items.emplace_back(origChildFc->name->str);
+            origFc = origChildFc->fc;
+            break;
+        }
+        case CTF_FIELD_CLASS_TYPE_VARIANT:
+        {
+            const auto childIndex = ctf_field_path_borrow_index_by_index(&origFieldPath, i);
+            const auto origChildFc =
+                ctf_field_class_compound_borrow_named_field_class_by_index(origFc, childIndex);
+
+            BT_ASSERT_DBG(origChildFc);
+
+            /*
+             * Variant field class option names aren't part of a CTF IR
+             * field location: like for the current element of an array
+             * field, a dependency which is part of a variant field F is
+             * always within the current option of F.
+             */
+            origFc = origChildFc->fc;
+            break;
+        }
+        default:
+            break;
+        }
+    }
+
+    return createFieldLoc(origFcAndScope.second, std::move(items));
+}
+
+Fc::UP Ctf1MetadataStreamParser::_fcFromOrigFc(const ctf_field_class_sequence& origFc)
+{
+    auto lenFieldLoc = this->_fieldLocFromOrigFieldPath(origFc.length_path);
+
+    if (origFc.base.is_text) {
+        return createDynLenStrFc(std::move(lenFieldLoc));
+    }
+
+    return createDynLenArrayFc(std::move(lenFieldLoc), this->_fcFromOrigFc(*origFc.base.elem_fc));
+}
+
+Fc::UP Ctf1MetadataStreamParser::_fcFromOrigFc(const ctf_field_class_variant& origFc)
+{
+    auto selFieldLoc = this->_fieldLocFromOrigFieldPath(origFc.tag_path);
+
+    if (origFc.tag_fc->base.is_signed) {
+        return createVariantFc(this->_variantOptsFromOrigVariantFc<VariantWithSIntSelFc>(origFc),
+                               std::move(selFieldLoc));
+    } else {
+        return createVariantFc(this->_variantOptsFromOrigVariantFc<VariantWithUIntSelFc>(origFc),
+                               std::move(selFieldLoc));
+    }
+}
+
+Fc::UP Ctf1MetadataStreamParser::_fcFromOrigFc(const ctf_field_class& origFc)
+{
+    /*
+     * The ctf_field_class_as_*() functions only accept non-const
+     * pointers.
+     */
+    auto& nonConstOrigFc = const_cast<ctf_field_class&>(origFc);
+
+    switch (origFc.type) {
+    case CTF_FIELD_CLASS_TYPE_INT:
+        return fcFromOrigFc(*ctf_field_class_as_int(&nonConstOrigFc));
+    case CTF_FIELD_CLASS_TYPE_ENUM:
+        return fcFromOrigFc(*ctf_field_class_as_enum(&nonConstOrigFc));
+    case CTF_FIELD_CLASS_TYPE_FLOAT:
+        return fcFromOrigFc(*ctf_field_class_as_float(&nonConstOrigFc));
+    case CTF_FIELD_CLASS_TYPE_STRING:
+        return createNullTerminatedStrFc();
+    case CTF_FIELD_CLASS_TYPE_STRUCT:
+        return this->_fcFromOrigFc(*ctf_field_class_as_struct(&nonConstOrigFc));
+    case CTF_FIELD_CLASS_TYPE_ARRAY:
+        return this->_fcFromOrigFc(*ctf_field_class_as_array(&nonConstOrigFc));
+    case CTF_FIELD_CLASS_TYPE_SEQUENCE:
+        return this->_fcFromOrigFc(*ctf_field_class_as_sequence(&nonConstOrigFc));
+    case CTF_FIELD_CLASS_TYPE_VARIANT:
+        return this->_fcFromOrigFc(*ctf_field_class_as_variant(&nonConstOrigFc));
+    default:
+        bt_common_abort();
+    }
+}
+
+bt2c::FileUP Ctf1MetadataStreamParser::_fileUpFromStr(const std::string& str)
+{
+    const auto fp = bt_fmemopen(const_cast<char *>(str.data()), str.size(), "rb");
+
+    if (!fp) {
+        BT_CLOGE_APPEND_CAUSE_AND_THROW(bt2c::Error, "bt_fmemopen() failed.");
+    }
+
+    return bt2c::FileUP {fp};
+}
+
+void Ctf1MetadataStreamParser::_parseSection(const uint8_t * const begin, const uint8_t * const end)
+{
+    const auto plaintextMetadata =
+        _mStreamDecoder.decode(begin, bt2c::DataLen::fromBytes(end - begin));
+    auto plaintextFile = this->_fileUpFromStr(plaintextMetadata);
+
+    /* Append the metadata text content to the TSDL scanner */
+    {
+        const auto ret = ctf_scanner_append_ast(_mScanner.get(), plaintextFile.get());
+
+        if (ret) {
+            BT_CLOGE_APPEND_CAUSE_AND_THROW(
+                bt2_common::Error, "Cannot create the metadata stream AST from TSDL text: ret=%d",
+                ret);
+        }
+    }
+
+    /* Make some basic AST node validation */
+    {
+        const auto ret = ctf_visitor_semantic_check(0, &_mScanner.get()->ast->root, _mLogCfg);
+
+        if (ret) {
+            BT_CLOGE_APPEND_CAUSE_AND_THROW(
+                bt2_common::Error, "Failed to validate metadata stream AST nodes: ret=%d", ret);
+        }
+    }
+
+    /* Convert AST nodes to original CTF IR objects */
+    {
+        const auto ret = ctf_visitor_generate_ir_visit_node(_mOrigCtfIrGenerator.get(),
+                                                            &_mScanner.get()->ast->root);
+
+        switch (ret) {
+        case 0:
+            /* Success */
+            break;
+        case -EINCOMPLETE:
+            BT_CLOGE_APPEND_CAUSE_AND_THROW(bt2_common::Error,
+                                            "Incomplete metadata stream section.");
+        default:
+            BT_CLOGE_APPEND_CAUSE_AND_THROW(
+                bt2_common::Error,
+                "Failed to create original CTF IR objects from metadata stream AST nodes: ret=%d",
+                ret);
+        }
+    }
+
+    /* Translate original CTF IR objects to current CTF IR ones */
+    this->_tryTranslate(*_mOrigCtfIrGenerator->ctf_tc);
+}
+
+void Ctf1MetadataStreamParser::_tryTranslate(ctf_trace_class& origTraceCls)
+{
+    _mFcTranslationCtx.origTraceCls = &origTraceCls;
+
+    if (!_mTraceCls) {
+        /* No trace class yet: translate original CTF IR trace class */
+        _mTraceCls = this->_translateTraceCls(origTraceCls);
+    }
+
+    /* Try to translate data stream classes and event record classes */
+    for (std::size_t iDataStreamCls = 0; iDataStreamCls < origTraceCls.stream_classes->len;
+         ++iDataStreamCls) {
+        auto& origDataStreamCls =
+            *static_cast<ctf_stream_class *>(origTraceCls.stream_classes->pdata[iDataStreamCls]);
+
+        _mFcTranslationCtx.origDataStreamCls = &origDataStreamCls;
+        _mFcTranslationCtx.origEventRecordCls = nullptr;
+        _mFcTranslationCtx.dataStreamCls = &this->_tryTranslateDataStreamCls(origDataStreamCls);
+
+        for (std::size_t iEventRecordCls = 0;
+             iEventRecordCls < origDataStreamCls.event_classes->len; iEventRecordCls++) {
+            auto& origEventRecordCls = *static_cast<ctf_event_class *>(
+                origDataStreamCls.event_classes->pdata[iEventRecordCls]);
+
+            _mFcTranslationCtx.origEventRecordCls = &origEventRecordCls;
+            this->_tryTranslateEventRecordCls(origEventRecordCls);
+        }
+    }
+}
+
+std::unique_ptr<TraceCls>
+Ctf1MetadataStreamParser::_translateTraceCls(ctf_trace_class& origTraceCls)
+{
+    BT_ASSERT(!origTraceCls.is_translated);
+
+    /* Translate packet header field class */
+    Fc::UP pktHeaderFc;
+
+    if (origTraceCls.packet_header_fc) {
+        pktHeaderFc = this->_fcFromOrigFc(*origTraceCls.packet_header_fc);
+    }
+
+    /* UUID */
+    nonstd::optional<bt2_common::Uuid> uuid;
+
+    if (origTraceCls.is_uuid_set) {
+        uuid = origTraceCls.uuid;
+    }
+
+    /* Create trace class */
+    auto traceCls = createTraceCls(std::move(uuid), envMapValFromOrigTraceCls(origTraceCls),
+                                   std::move(pktHeaderFc));
+
+    /* Mark original CTF IR trace class as translated */
+    origTraceCls.is_translated = true;
+
+    /* Return created trace class */
+    return traceCls;
+}
+
+ClkCls::SP Ctf1MetadataStreamParser::_clkClsFromOrigClkCls(const ctf_clock_class& origClkCls)
+{
+    /* Try to find a corresponding clock class for `origClkCls`*/
+    const auto it = _mClkClsMap.find(&origClkCls);
+
+    if (it != _mClkClsMap.end()) {
+        /* Found it */
+        return it->second;
+    }
+
+    /* Translate clock class */
+    {
+        /* Description */
+        nonstd::optional<std::string> descr;
+
+        if (origClkCls.description->len > 0) {
+            descr = origClkCls.description->str;
+        }
+
+        /* UUID */
+        nonstd::optional<bt2c::Uuid> uuid;
+
+        if (origClkCls.has_uuid) {
+            uuid = origClkCls.uuid;
+        }
+
+        /* Create clock class */
+        auto clkCls = createClkCls(
+            origClkCls.name->str, origClkCls.frequency,
+            ir::ClkOffset {origClkCls.offset_seconds, origClkCls.offset_cycles},
+            origClkCls.is_absolute, std::move(descr), origClkCls.precision, std::move(uuid));
+
+        /* Add to map of translated clock classes */
+        _mClkClsMap.emplace(&origClkCls, clkCls);
+
+        /* Return created clock class */
+        return clkCls;
+    }
+}
+
+DataStreamCls&
+Ctf1MetadataStreamParser::_tryTranslateDataStreamCls(ctf_stream_class& origDataStreamCls)
+{
+    if (origDataStreamCls.is_translated) {
+        /* Already translated: return it */
+        return *(*_mTraceCls)[origDataStreamCls.id];
+    }
+
+    /* Translate packet context field class */
+    Fc::UP pktCtxFc;
+
+    if (origDataStreamCls.packet_context_fc) {
+        pktCtxFc = this->_fcFromOrigFc(*origDataStreamCls.packet_context_fc);
+    }
+
+    /* Translate event record header field class */
+    Fc::UP eventRecordHeaderFc;
+
+    if (origDataStreamCls.event_header_fc) {
+        eventRecordHeaderFc = this->_fcFromOrigFc(*origDataStreamCls.event_header_fc);
+    }
+
+    /* Translate common event record context field class */
+    Fc::UP commonEventRecordCtxFc;
+
+    if (origDataStreamCls.event_common_context_fc) {
+        commonEventRecordCtxFc = this->_fcFromOrigFc(*origDataStreamCls.event_common_context_fc);
+    }
+
+    /* Translate default clock class */
+    ClkCls::SP defClkCls;
+
+    if (origDataStreamCls.default_clock_class) {
+        defClkCls = this->_clkClsFromOrigClkCls(*origDataStreamCls.default_clock_class);
+    }
+
+    /* Create data stream class */
+    auto dataStreamClsSp = createDataStreamCls(
+        origDataStreamCls.id, nonstd::nullopt, nonstd::nullopt, std::move(pktCtxFc),
+        std::move(eventRecordHeaderFc), std::move(commonEventRecordCtxFc), std::move(defClkCls));
+    auto& dataStreamCls = *dataStreamClsSp;
+
+    /* Add to trace class */
+    _mTraceCls->addDataStreamCls(std::move(dataStreamClsSp));
+
+    /* Mark original CTF IR data stream class as translated */
+    origDataStreamCls.is_translated = true;
+
+    /* Return created data stream class */
+    return dataStreamCls;
+}
+
+void Ctf1MetadataStreamParser::_tryTranslateEventRecordCls(ctf_event_class& origEventRecordCls)
+{
+    if (origEventRecordCls.is_translated) {
+        /* Already translated */
+        return;
+    }
+
+    /* Translate specific context field class, if any */
+    Fc::UP specCtxFc;
+
+    if (origEventRecordCls.spec_context_fc) {
+        specCtxFc = this->_fcFromOrigFc(*origEventRecordCls.spec_context_fc);
+    }
+
+    /* Translate payload field class, if any */
+    Fc::UP payloadFc;
+
+    if (origEventRecordCls.payload_fc) {
+        payloadFc = this->_fcFromOrigFc(*origEventRecordCls.payload_fc);
+    }
+
+    /* Create event record class */
+    auto eventRecordCls = createEventRecordCls(
+        origEventRecordCls.id, nonstd::nullopt, origEventRecordCls.name->str, std::move(specCtxFc),
+        std::move(payloadFc), eventRecordClsBtUserAttrsFromOrigEventRecordCls(origEventRecordCls));
+
+    /* Add to data stream class */
+    BT_ASSERT(_mFcTranslationCtx.dataStreamCls);
+    _mFcTranslationCtx.dataStreamCls->addEventRecordCls(std::move(eventRecordCls));
+
+    /* Mark original CTF IR event record class as translated */
+    origEventRecordCls.is_translated = true;
+}
+
+Ctf1MetadataStreamParser::Ctf1MetadataStreamParser(const ClkClsCfg& clkClsCfg,
+                                                   bt_self_component * const selfComp,
+                                                   const bt2c::LogCfg& logCfg) :
+    MetadataStreamParser {clkClsCfg, selfComp},
+    _mLogCfg {logCfg}, _mScanner {ctf_scanner_alloc()}, _mStreamDecoder {logCfg}
+{
+    ctf_metadata_decoder_config metadataCfg {logCfg};
+
+    metadataCfg.clkClsCfg = clkClsCfg;
+    _mOrigCtfIrGenerator = ctf_visitor_generate_ir_create(&metadataCfg);
+}
+
+MetadataStreamParser::ParseRet Ctf1MetadataStreamParser::parse(const ClkClsCfg& clkClsCfg,
+                                                               bt_self_component * const selfComp,
+                                                               const uint8_t * const begin,
+                                                               const uint8_t * const end,
+                                                               const bt2_common::LogCfg& logCfg)
+{
+    Ctf1MetadataStreamParser parser {clkClsCfg, selfComp, logCfg};
+
+    parser.parseSection(begin, end);
+    return std::make_pair(parser.releaseTraceCls(), parser.metadataStreamUuid());
+}
+
+} /* namespace src */
+} /* namespace ctf */
diff --git a/src/plugins/ctf/common/src/metadata/tsdl/ctf-1-metadata-stream-parser.hpp b/src/plugins/ctf/common/src/metadata/tsdl/ctf-1-metadata-stream-parser.hpp
new file mode 100644 (file)
index 0000000..7d2242c
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2022 Francis Deslauriers <francis.deslauriers@efficios.com>
+ */
+
+#ifndef _CTF_SRC_METADATA_TSDL_CTF_1_METADATA_STREAM_PARSER_HPP
+#define _CTF_SRC_METADATA_TSDL_CTF_1_METADATA_STREAM_PARSER_HPP
+
+#include "cpp-common/optional.hpp"
+#include "cpp-common/libc-up.hpp"
+#include "cpp-common/log-cfg.hpp"
+#include "../../../metadata/ctf-ir.hpp"
+#include "ctf-meta.hpp"
+#include "metadata-stream-decoder.hpp"
+#include "scanner.hpp"
+#include "../ctf-ir.hpp"
+#include "../metadata-stream-parser.hpp"
+
+namespace ctf {
+namespace src {
+
+/*
+ * CTF 1 metadata stream (TSDL) parser.
+ *
+ * Build an instance of `Ctf1MetadataStreamParser`, and then call
+ * parseSection() as often as needed with one or more complete TSDL root
+ * blocks.
+ */
+class Ctf1MetadataStreamParser final : public MetadataStreamParser
+{
+public:
+    /*
+     * Builds a CTF 1 metadata stream parser.
+     *
+     * If `selfComp` isn't `nullptr`, then the parser uses it each time
+     * you call parseSection() to finalize its current trace class.
+     */
+    explicit Ctf1MetadataStreamParser(const ClkClsCfg& clkClsCfg, bt_self_component *selfComp,
+                                      const bt2_common::LogCfg& logCfg);
+
+    /*
+     * Parses the whole CTF 1 metadata stream between `begin` and `end`
+     * and returns the resulting trace class and optional metadata
+     * stream UUID on success, or appends a cause to the error of the
+     * current thread and throws `bt2_common::Error` otherwise.
+     */
+    static ParseRet parse(const ClkClsCfg& clkClsCfg, bt_self_component *selfComp,
+                          const uint8_t *begin, const uint8_t *end,
+                          const bt2_common::LogCfg& logCfg);
+
+private:
+    void _parseSection(const uint8_t *begin, const uint8_t *end) override;
+
+    /*
+     * Translates the original CTF IR field class `origFc` and returns
+     * the translated object.
+     */
+    Fc::UP _fcFromOrigFc(const ctf_field_class& origFc);
+
+    /*
+     * Translates the original CTF IR structure field class `origFc` and
+     * returns the translated object.
+     */
+    Fc::UP _fcFromOrigFc(const ctf_field_class_struct& origFc);
+
+    /*
+     * Translates the original CTF IR static-length array field class
+     * `origFc` and returns the translated object.
+     */
+    Fc::UP _fcFromOrigFc(const ctf_field_class_array& origFc);
+
+    /*
+     * Translates the original CTF IR field path `origFieldPath` to a
+     * field location and returns the translated object.
+     */
+    FieldLoc _fieldLocFromOrigFieldPath(const ctf_field_path& origFieldPath);
+
+    /*
+     * Translates the original CTF IR dynamic-length array field class
+     * `origFc` and returns the translated object.
+     */
+    Fc::UP _fcFromOrigFc(const ctf_field_class_sequence& origFc);
+
+    /*
+     * Translates the options of the original CTF IR variant field class
+     * `origFc` and returns the translated objects.
+     */
+    template <typename VariantFcT>
+    typename VariantFcT::Opts _variantOptsFromOrigVariantFc(const ctf_field_class_variant& origFc)
+    {
+        typename VariantFcT::Opts opts;
+
+        for (std::size_t iOpt = 0; iOpt < origFc.options->len; ++iOpt) {
+            auto& origOpt = *ctf_field_class_variant_borrow_option_by_index_const(&origFc, iOpt);
+
+            /*
+             * In an original CTF IR variant field class FC, the
+             * `ranges` member contains an array of integer ranges, each
+             * one associated to a specific option of FC.
+             *
+             * Only add to `ranges` below the ones for the current
+             * option.
+             */
+            typename VariantFcT::SelFieldRanges::Set ranges;
+
+            for (std::size_t iRange = 0; iRange < origFc.ranges->len; ++iRange) {
+                auto& origRange =
+                    *ctf_field_class_variant_borrow_range_by_index_const(&origFc, iRange);
+
+                if (origRange.option_index == iOpt) {
+                    ranges.emplace(
+                        static_cast<typename VariantFcT::SelVal>(origRange.range.lower.u),
+                        static_cast<typename VariantFcT::SelVal>(origRange.range.upper.u));
+                }
+            }
+
+            /* Create and add variant field class option */
+            opts.emplace_back(createVariantFcOpt(
+                this->_fcFromOrigFc(*origOpt.fc),
+                typename VariantFcT::SelFieldRanges {std::move(ranges)}, origOpt.name->str));
+        }
+
+        return opts;
+    }
+
+    /*
+     * Translates the original CTF IR variant field class `origFc` and
+     * returns the translated object.
+     */
+    Fc::UP _fcFromOrigFc(const ctf_field_class_variant& origFc);
+
+    /*
+     * Translates the original CTF IR clock class `origClkCls` and
+     * returns the translated object.
+     */
+    ClkCls::SP _clkClsFromOrigClkCls(const ctf_clock_class& origClkCls);
+
+    /*
+     * Tries to translate the original CTF IR event record class
+     * `origEventRecordCls`, adding the translated object to the current
+     * data stream class and marking it as translated on success.
+     */
+    void _tryTranslateEventRecordCls(ctf_event_class& origEventRecordCls);
+
+    /*
+     * Tries to translate the original CTF IR data stream class
+     * `origDataStreamCls`, adding the translated object to the current
+     * trace class and marking it as translated on success.
+     *
+     * Returns the translated data stream class.
+     */
+    DataStreamCls& _tryTranslateDataStreamCls(ctf_stream_class& origDataStreamCls);
+
+    /*
+     * Translates the original CTF IR trace class `origTraceCls` and
+     * returns it.
+     */
+    std::unique_ptr<TraceCls> _translateTraceCls(ctf_trace_class& origTraceCls);
+
+    /*
+     * Tries to translate the original CTF IR trace class `origTraceCls`
+     * as well as all its data stream and event record classes.
+     */
+    void _tryTranslate(ctf_trace_class& origTraceCls);
+
+    /*
+     * Returns an `std::FILE` unique pointer from the string `str`.
+     *
+     * `str` must remain alive and not change while you use the returned
+     * object.
+     */
+    bt2_common::FileUP _fileUpFromStr(const std::string& str);
+
+    /*
+     * Deleter for a unique pointer to CTF scanner.
+     */
+    struct _CtfScannerDeleter final
+    {
+        void operator()(ctf_scanner * const scanner) noexcept
+        {
+            ctf_scanner_free(scanner);
+        }
+    };
+
+    /* Logging configuration */
+    bt2_common::LogCfg _mLogCfg;
+
+    /* Map of original CTF IR clock classes to clock classes */
+    std::unordered_map<const ctf_clock_class *, ClkCls::SP> _mClkClsMap;
+
+    /* Field class translation context */
+    struct
+    {
+        /* Current data stream class */
+        DataStreamCls *dataStreamCls = nullptr;
+
+        /* Original CTF IR trace class */
+        const ctf_trace_class *origTraceCls = nullptr;
+
+        /* Current original CTF IR data stream class */
+        const ctf_stream_class *origDataStreamCls = nullptr;
+
+        /* Current original CTF IR event record class */
+        const ctf_event_class *origEventRecordCls = nullptr;
+    } _mFcTranslationCtx;
+
+    /* Original CTF IR generator (from AST nodes) */
+    ctf_visitor_generate_ir::UP _mOrigCtfIrGenerator;
+
+    /* TSDL scanner */
+    std::unique_ptr<ctf_scanner, _CtfScannerDeleter> _mScanner;
+
+    /* Metadata stream decoder */
+    MetadataStreamDecoder _mStreamDecoder;
+};
+
+} /* namespace src */
+} /* namespace ctf */
+
+#endif /* _CTF_SRC_METADATA_TSDL_CTF_1_METADATA_STREAM_PARSER_HPP */
This page took 0.032842 seconds and 5 git commands to generate.