Add `ctf::src::Ctf1MetadataStreamParser` class (TSDL)
authorPhilippe Proulx <eeppeliteloop@gmail.com>
Sun, 10 Dec 2023 05:51:52 +0000 (05:51 +0000)
committerSimon Marchi <simon.marchi@efficios.com>
Wed, 4 Sep 2024 19:05:14 +0000 (15:05 -0400)
This patch 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: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Francis Deslauriers <francis.deslauriers@efficios.com>
Change-Id: I55e0f2f05d64a0acc5949029e315aeefae424dda
Reviewed-on: https://review.lttng.org/c/babeltrace/+/8000
Reviewed-by: Philippe Proulx <eeppeliteloop@gmail.com>
Reviewed-on: https://review.lttng.org/c/babeltrace/+/12734

src/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 f09f733d5e728e4f795aa339ed775d2de2bb12bb..314b7bae1d3138b8a41cbde3c6e1c8718b3ae6a4 100644 (file)
@@ -710,6 +710,8 @@ plugins_ctf_babeltrace_plugin_ctf_la_SOURCES = \
        plugins/ctf/common/src/metadata/metadata-stream-parser.hpp \
        plugins/ctf/common/src/metadata/normalize-clk-offset.cpp \
        plugins/ctf/common/src/metadata/normalize-clk-offset.hpp \
+       plugins/ctf/common/src/metadata/tsdl/ctf-1-metadata-stream-parser.cpp \
+       plugins/ctf/common/src/metadata/tsdl/ctf-1-metadata-stream-parser.hpp \
        plugins/ctf/common/src/metadata/tsdl/metadata-stream-decoder.cpp \
        plugins/ctf/common/src/metadata/tsdl/metadata-stream-decoder.hpp \
        plugins/ctf/common/src/msg-iter.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..678f4c5
--- /dev/null
@@ -0,0 +1,717 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2022 Francis Deslauriers <francis.deslauriers@efficios.com>
+ * Copyright 2024 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#include <babeltrace2/babeltrace.h>
+
+#include "common/common.h"
+#include "compat/memstream.h"
+#include "cpp-common/bt2c/aliases.hpp"
+
+#include "ctf-1-metadata-stream-parser.hpp"
+#include "plugins/ctf/common/src/metadata/ctf-ir.hpp"
+
+namespace ctf {
+namespace src {
+namespace {
+
+DispBase
+dispBaseFromIrDispBase(const bt_field_class_integer_preferred_display_base dispBase) noexcept
+{
+    switch (dispBase) {
+    case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_BINARY:
+        return DispBase::Bin;
+    case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL:
+        return DispBase::Oct;
+    case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL:
+        return DispBase::Dec;
+    case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL:
+        return DispBase::Hex;
+    default:
+        bt_common_abort();
+    }
+}
+
+ByteOrder byteOrderFromOrigByteOrder(const ctf_byte_order origByteOrder)
+{
+    switch (origByteOrder) {
+    case CTF_BYTE_ORDER_LITTLE:
+        return ByteOrder::Little;
+    case CTF_BYTE_ORDER_BIG:
+        return ByteOrder::Big;
+    default:
+        bt_common_abort();
+    }
+}
+
+bt2s::optional<UIntFieldRole> roleFromOrigMeaning(const ctf_field_class_meaning meaning) noexcept
+{
+    switch (meaning) {
+    case CTF_FIELD_CLASS_MEANING_PACKET_BEGINNING_TIME:
+        return {UIntFieldRole::DefClkTs};
+    case CTF_FIELD_CLASS_MEANING_PACKET_END_TIME:
+        return {UIntFieldRole::PktEndDefClkTs};
+    case CTF_FIELD_CLASS_MEANING_EVENT_CLASS_ID:
+        return {UIntFieldRole::EventRecordClsId};
+    case CTF_FIELD_CLASS_MEANING_STREAM_CLASS_ID:
+        return {UIntFieldRole::DataStreamClsId};
+    case CTF_FIELD_CLASS_MEANING_DATA_STREAM_ID:
+        return {UIntFieldRole::DataStreamId};
+    case CTF_FIELD_CLASS_MEANING_MAGIC:
+        return {UIntFieldRole::PktMagicNumber};
+    case CTF_FIELD_CLASS_MEANING_PACKET_COUNTER_SNAPSHOT:
+        return {UIntFieldRole::PktSeqNum};
+    case CTF_FIELD_CLASS_MEANING_DISC_EV_REC_COUNTER_SNAPSHOT:
+        return {UIntFieldRole::DiscEventRecordCounterSnap};
+    case CTF_FIELD_CLASS_MEANING_EXP_PACKET_TOTAL_SIZE:
+        return {UIntFieldRole::PktTotalLen};
+    case CTF_FIELD_CLASS_MEANING_EXP_PACKET_CONTENT_SIZE:
+        return {UIntFieldRole::PktContentLen};
+    case CTF_FIELD_CLASS_MEANING_UUID:
+    case CTF_FIELD_CLASS_MEANING_NONE:
+        return bt2s::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`.
+ */
+UIntFieldRoles rolesFromOrigIntFc(const ctf_field_class_int& origIntFc)
+{
+    UIntFieldRoles roles;
+
+    const auto role = roleFromOrigMeaning(origIntFc.meaning);
+
+    if (role) {
+        roles.insert(*role);
+    }
+
+    {
+        const auto hasPktEndDefClkTsRole = role && *role == UIntFieldRole::PktEndDefClkTs;
+
+        if (!hasPktEndDefClkTsRole && origIntFc.mapped_clock_class) {
+            roles.insert(UIntFieldRole::DefClkTs);
+        }
+    }
+
+    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,
+                                    bt2c::DataLen::fromBits(oldFc.base.size),
+                                    byteOrderFromOrigByteOrder(oldFc.base.byte_order),
+                                    bt2s::nullopt, dispBaseFromIrDispBase(oldFc.disp_base));
+    } else {
+        return createFixedLenUIntFc(
+            oldFc.base.base.alignment, bt2c::DataLen::fromBits(oldFc.base.size),
+            byteOrderFromOrigByteOrder(oldFc.base.byte_order), bt2s::nullopt,
+            dispBaseFromIrDispBase(oldFc.disp_base), {}, rolesFromOrigIntFc(oldFc));
+    }
+}
+
+/*
+ * Translates the mappings of the original CTF IR enumeration field
+ * class `origFc` and returns the translated objects.
+ */
+template <typename IntFcT>
+static typename IntFcT::Mappings intFcMappingsFromOrigEnumFc(const ctf_field_class_enum& origFc)
+{
+    using Mappings = typename IntFcT::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 IntFcT::Val>(origRange.lower.u),
+                           static_cast<typename IntFcT::Val>(origRange.upper.u));
+        }
+
+        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 createFixedLenSIntFc(origFc.base.base.base.alignment,
+                                    bt2c::DataLen::fromBits(origFc.base.base.size), byteOrder,
+                                    bt2s::nullopt, dispBase,
+                                    intFcMappingsFromOrigEnumFc<FixedLenSIntFc>(origFc));
+    } else {
+        return createFixedLenUIntFc(
+            origFc.base.base.base.alignment, bt2c::DataLen::fromBits(origFc.base.base.size),
+            byteOrder, bt2s::nullopt, dispBase, intFcMappingsFromOrigEnumFc<FixedLenUIntFc>(origFc),
+            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,
+                                 bt2c::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 MetadataStreamParser::logLevelEmergencyName;
+    case BT_EVENT_CLASS_LOG_LEVEL_ALERT:
+        return MetadataStreamParser::logLevelAlertName;
+    case BT_EVENT_CLASS_LOG_LEVEL_CRITICAL:
+        return MetadataStreamParser::logLevelCriticalName;
+    case BT_EVENT_CLASS_LOG_LEVEL_ERROR:
+        return MetadataStreamParser::logLevelErrorName;
+    case BT_EVENT_CLASS_LOG_LEVEL_WARNING:
+        return MetadataStreamParser::logLevelWarningName;
+    case BT_EVENT_CLASS_LOG_LEVEL_NOTICE:
+        return MetadataStreamParser::logLevelNoticeName;
+    case BT_EVENT_CLASS_LOG_LEVEL_INFO:
+        return MetadataStreamParser::logLevelInfoName;
+    case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_SYSTEM:
+        return MetadataStreamParser::logLevelDebugSystemName;
+    case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROGRAM:
+        return MetadataStreamParser::logLevelDebugProgramName;
+    case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_PROCESS:
+        return MetadataStreamParser::logLevelDebugProcessName;
+    case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_MODULE:
+        return MetadataStreamParser::logLevelDebugModuleName;
+    case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_UNIT:
+        return MetadataStreamParser::logLevelDebugUnitName;
+    case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_FUNCTION:
+        return MetadataStreamParser::logLevelDebugFunctionName;
+    case BT_EVENT_CLASS_LOG_LEVEL_DEBUG_LINE:
+        return MetadataStreamParser::logLevelDebugLineName;
+    case BT_EVENT_CLASS_LOG_LEVEL_DEBUG:
+        return MetadataStreamParser::logLevelDebugName;
+    default:
+        bt_common_abort();
+    }
+}
+
+/*
+ * Returns the event record class attributes which correspond to the log
+ * level and EMF URI properties of the original CTF IR event record
+ * class `origEventRecordCls`.
+ */
+OptAttrs eventRecordClsBtAttrsFromOrigEventRecordCls(const ctf_event_class& origEventRecordCls)
+{
+    if (origEventRecordCls.emf_uri->len == 0 && !origEventRecordCls.is_log_level_set) {
+        /* No log level and no EMF URI: no attributes */
+        return OptAttrs {};
+    }
+
+    auto attrs = bt2::MapValue::create();
+    auto nsMapVal = attrs->insertEmptyMap("babeltrace.org,2020");
+
+    if (origEventRecordCls.emf_uri->len) {
+        /* Set EMF URI attribute */
+        nsMapVal.insert("emf-uri", origEventRecordCls.emf_uri->str);
+    }
+
+    if (origEventRecordCls.is_log_level_set) {
+        /* Set log level attribute */
+        nsMapVal.insert("log-level",
+                        eventRecordClsLogLevelNameFromOrigLogLevel(origEventRecordCls.log_level));
+    }
+
+    return attrs;
+}
+
+/*
+ * 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 bt2::ConstMapValue::Shared::createWithoutRef(envMapVal.release().libObjPtr());
+}
+
+} /* 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,
+                                  origFc.meaning == CTF_FIELD_CLASS_MEANING_UUID, OptAttrs {});
+}
+
+FieldLoc Ctf1MetadataStreamParser::_fieldLocFromOrigFieldPath(const ctf_field_path& origFieldPath)
+{
+    /* Get original CTF IR root field class and CTF IR scope */
+    const auto origFcAndScope = bt2c::call([this, &origFieldPath] {
+        switch (origFieldPath.root) {
+        case CTF_SCOPE_PACKET_HEADER:
+            return std::make_pair(_mFcTranslationCtx.origTraceCls->packet_header_fc,
+                                  Scope::PktHeader);
+        case CTF_SCOPE_PACKET_CONTEXT:
+            return std::make_pair(_mFcTranslationCtx.origDataStreamCls->packet_context_fc,
+                                  Scope::PktCtx);
+        case CTF_SCOPE_EVENT_HEADER:
+            return std::make_pair(_mFcTranslationCtx.origDataStreamCls->event_header_fc,
+                                  Scope::EventRecordHeader);
+        case CTF_SCOPE_EVENT_COMMON_CONTEXT:
+            return std::make_pair(_mFcTranslationCtx.origDataStreamCls->event_common_context_fc,
+                                  Scope::CommonEventRecordCtx);
+        case CTF_SCOPE_EVENT_SPECIFIC_CONTEXT:
+            return std::make_pair(_mFcTranslationCtx.origEventRecordCls->spec_context_fc,
+                                  Scope::SpecEventRecordCtx);
+        case CTF_SCOPE_EVENT_PAYLOAD:
+            return std::make_pair(_mFcTranslationCtx.origEventRecordCls->payload_fc,
+                                  Scope::EventRecordPayload);
+        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:
+        {
+            BT_ASSERT(ctf_field_path_borrow_index_by_index(&origFieldPath, i) == -1);
+            origFc = ctf_field_class_as_array_base(origFc)->elem_fc;
+            break;
+        }
+        case CTF_FIELD_CLASS_TYPE_STRUCT:
+        {
+            const auto origChildFc = ctf_field_class_compound_borrow_named_field_class_by_index(
+                origFc, ctf_field_path_borrow_index_by_index(&origFieldPath, i));
+
+            BT_ASSERT(origChildFc);
+            items.emplace_back(origChildFc->name->str);
+            origFc = origChildFc->fc;
+            break;
+        }
+        case CTF_FIELD_CLASS_TYPE_VARIANT:
+        {
+            const auto origChildFc = ctf_field_class_compound_borrow_named_field_class_by_index(
+                origFc, ctf_field_path_borrow_index_by_index(&origFieldPath, i));
+
+            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)
+{
+    if (const auto fp = bt_fmemopen(const_cast<char *>(str.data()), str.size(), "rb")) {
+        return bt2c::FileUP {fp};
+    }
+
+    BT_CPPLOGE_APPEND_CAUSE_AND_THROW(bt2c::Error, "bt_fmemopen() failed.");
+}
+
+void Ctf1MetadataStreamParser::_parseSection(const bt2c::ConstBytes buffer)
+{
+    {
+        const auto metadataStr = _mStreamDecoder.decode(buffer);
+        const auto plaintextFile = this->_fileUpFromStr(metadataStr);
+
+        /* Append the metadata text content to the TSDL scanner */
+        if (const auto ret = ctf_scanner_append_ast(_mScanner.get(), plaintextFile.get())) {
+            BT_CPPLOGE_APPEND_CAUSE_AND_THROW(
+                bt2c::Error, "Cannot create the metadata stream AST from TSDL text: ret={}", ret);
+        }
+    }
+
+    /* Make some basic AST node validation */
+    if (const auto ret = ctf_visitor_semantic_check(0, &_mScanner.get()->ast->root, _mLogger)) {
+        BT_CPPLOGE_APPEND_CAUSE_AND_THROW(
+            bt2c::Error, "Failed to validate metadata stream AST nodes: ret={}", 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_CPPLOGE_APPEND_CAUSE_AND_THROW(bt2c::Error, "Incomplete metadata stream section.");
+        default:
+            BT_CPPLOGE_APPEND_CAUSE_AND_THROW(
+                bt2c::Error,
+                "Failed to create original CTF IR objects from metadata stream AST nodes: ret={}",
+                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);
+    }
+
+    /* UID */
+    bt2s::optional<std::string> uid;
+
+    if (origTraceCls.is_uuid_set) {
+        uid = bt2c::UuidView {origTraceCls.uuid}.str();
+
+        /*
+         * For CTF 1, the trace class UUID is also the metadata
+         * stream UUID.
+         */
+        _mMetadataStreamUuid = origTraceCls.uuid;
+    }
+
+    /* Create trace class */
+    auto traceCls = createTraceCls(bt2s::nullopt, bt2s::nullopt, std::move(uid),
+                                   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 */
+        bt2s::optional<std::string> descr;
+
+        if (origClkCls.description->len > 0) {
+            descr = origClkCls.description->str;
+        }
+
+        /* UID from UUID */
+        bt2s::optional<std::string> uid;
+
+        if (origClkCls.has_uuid) {
+            uid = bt2c::UuidView {origClkCls.uuid}.str();
+        }
+
+        /* Clock origin */
+        bt2s::optional<ClkOrigin> origin;
+
+        if (origClkCls.is_absolute) {
+            /* Unix epoch */
+            origin = ClkOrigin {};
+        }
+
+        /* Create clock class */
+        auto clkCls = createClkCls(origClkCls.name->str, origClkCls.frequency, bt2s::nullopt,
+                                   origClkCls.name->str, std::move(uid),
+                                   ClkOffset {origClkCls.offset_seconds, origClkCls.offset_cycles},
+                                   std::move(origin), std::move(descr), origClkCls.precision);
+
+        /* 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, bt2s::nullopt, bt2s::nullopt, bt2s::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, bt2s::nullopt, origEventRecordCls.name->str,
+                             bt2s::nullopt, std::move(specCtxFc), std::move(payloadFc),
+                             eventRecordClsBtAttrsFromOrigEventRecordCls(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 bt2::OptionalBorrowedObject<bt2::SelfComponent> selfComp, const ClkClsCfg& clkClsCfg,
+    const bt2c::Logger& parentLogger) :
+    MetadataStreamParser {selfComp, clkClsCfg},
+    _mLogger {parentLogger, "PLUGIN/CTF/CTF-1-META-STREAM-PARSER"},
+    _mScanner {ctf_scanner_alloc(_mLogger)}, _mStreamDecoder {_mLogger}
+{
+    ctf_metadata_decoder_config metadataCfg {_mLogger};
+
+    metadataCfg.clkClsCfg = clkClsCfg;
+    _mOrigCtfIrGenerator = ctf_visitor_generate_ir_create(&metadataCfg);
+}
+
+MetadataStreamParser::ParseRet
+Ctf1MetadataStreamParser::parse(const bt2::OptionalBorrowedObject<bt2::SelfComponent> selfComp,
+                                const ClkClsCfg& clkClsCfg, const bt2c::ConstBytes buffer,
+                                const bt2c::Logger& parentLogger)
+{
+    Ctf1MetadataStreamParser parser {selfComp, clkClsCfg, parentLogger};
+
+    parser.parseSection(buffer);
+    return {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..e289b0e
--- /dev/null
@@ -0,0 +1,288 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2022 Francis Deslauriers <francis.deslauriers@efficios.com>
+ * Copyright 2024 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#ifndef BABELTRACE_PLUGINS_CTF_COMMON_SRC_METADATA_TSDL_CTF_1_METADATA_STREAM_PARSER_HPP
+#define BABELTRACE_PLUGINS_CTF_COMMON_SRC_METADATA_TSDL_CTF_1_METADATA_STREAM_PARSER_HPP
+
+#include "cpp-common/bt2c/aliases.hpp"
+#include "cpp-common/bt2c/libc-up.hpp"
+
+#include "../../../metadata/ctf-ir.hpp"
+#include "../ctf-ir.hpp"
+#include "../metadata-stream-parser.hpp"
+#include "ctf-meta.hpp"
+#include "metadata-stream-decoder.hpp"
+#include "scanner.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
+ * packetized or plain text TSDL root blocks.
+ *
+ * You may also call the static Ctf1MetadataStreamParser::parse() method
+ * to parse a whole packetized or plain text CTF 1 metadata stream.
+ *
+ * IMPLEMENTATION
+ * ━━━━━━━━━━━━━━
+ * The current parsing strategy is to reuse the C parser, which was
+ * written for Babeltrace 2.0, almost as is.
+ *
+ * The output of said legacy parser is a `ctf_trace_class` instance.
+ * When parsing more metadata stream data, the current legacy (original)
+ * trace class (`_mOrigCtfIrGenerator->ctf_tc`) gets updated. This means
+ * potentially adding more clock classes, data classes, and event
+ * classes to `_mOrigCtfIrGenerator->ctf_tc`.
+ *
+ * The top-level legacy structures contain an `is_translated` member
+ * which indicates whether or not a `Ctf1MetadataStreamParser` instance
+ * translated from legacy CTF IR to woke CTF IR (the classes
+ * of `ctf::src`).
+ *
+ * All in all, this is the data flow from packetized or plain text
+ * metadata stream bytes to woke CTF IR instances:
+ *
+ *          ┌───────────────────────┐
+ *          │ Metadata stream bytes │
+ *          │ (possibly packetized) │
+ *          └───────────────────────┘
+ *                      ↓
+ *         ╔═════════════════════════╗
+ *         ║ Metadata stream decoder ║
+ *         ║   (`_mStreamDecoder`)   ║
+ *         ╚═════════════════════════╝
+ *                      ↓
+ *     ┌──────────────────────────────────┐
+ *     │ Plain text metadata stream bytes │
+ *     └──────────────────────────────────┘
+ *                      ↓
+ *              ╔═══════════════╗ ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┐
+ *              ║  AST scanner  ║               ┊
+ *              ║ (`*_mScanner`)║               ┊
+ *              ╚═══════════════╝               ┊
+ *                      ↓                       ┊
+ *          ┌───────────────────────┐           ┊
+ *          │       AST nodes       │           ┊
+ *          │ (within `*_mScanner`) │           ┊
+ *          └───────────────────────┘           ┊
+ *                      ↓                       ├┈ Legacy code
+ *        ╔═══════════════════════════╗         ┊
+ *        ║      AST node parser      ║         ┊
+ *        ║ (`*_mOrigCtfIrGenerator`) ║         ┊
+ *        ╚═══════════════════════════╝         ┊
+ *                      ↓                       ┊
+ *     ┌──────────────────────────────────┐     ┊
+ *     │         Original CTF IR          │     ┊
+ *     │ (`_mOrigCtfIrGenerator->ctf_tc`) │     ┊
+ *     └──────────────────────────────────┘ ┈┈┈┈┘
+ *                      ↓
+ *       ╔══════════════════════════════╗
+ *       ║         This parser          ║
+ *       ║ (`Ctf1MetadataStreamParser`) ║
+ *       ╚══════════════════════════════╝
+ *                      ↓
+ *              ┏━━━━━━━━━━━━━━┓
+ *              ┃ Woke CTF IR  ┃
+ *              ┃ (traceCls()) ┃
+ *              ┗━━━━━━━━━━━━━━┛
+ */
+class Ctf1MetadataStreamParser final : public MetadataStreamParser
+{
+public:
+    /*
+     * Builds a CTF 1 metadata stream parser.
+     *
+     * If `selfComp` exists, then the parser uses it each time you call
+     * parseSection() to finalize its current trace class.
+     */
+    explicit Ctf1MetadataStreamParser(bt2::OptionalBorrowedObject<bt2::SelfComponent> selfComp,
+                                      const ClkClsCfg& clkClsCfg, const bt2c::Logger& parentLogger);
+
+    /*
+     * Parses the whole packetized or plain text CTF 1 metadata stream
+     * in `buffer` 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 `bt2c::Error` otherwise.
+     */
+    static ParseRet parse(bt2::OptionalBorrowedObject<bt2::SelfComponent> selfComp,
+                          const ClkClsCfg& clkClsCfg, bt2c::ConstBytes buffer,
+                          const bt2c::Logger& parentLogger);
+
+private:
+    void _parseSection(bt2c::ConstBytes buffer) 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.
+     */
+    bt2c::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 */
+    bt2c::Logger _mLogger;
+
+    /* 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 /* BABELTRACE_PLUGINS_CTF_COMMON_SRC_METADATA_TSDL_CTF_1_METADATA_STREAM_PARSER_HPP */
This page took 0.034236 seconds and 4 git commands to generate.