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