--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */