From: Philippe Proulx Date: Wed, 25 Oct 2023 15:41:53 +0000 (+0000) Subject: Add `ctf::src::MetadataStreamParser` abstract base class X-Git-Url: http://drtracing.org/?a=commitdiff_plain;h=8bb4ee66770f1f182f4d7e34a3ac8804633c42bd;p=babeltrace.git Add `ctf::src::MetadataStreamParser` abstract base class This is an abstract base class for any metadata stream parser. Parse a metadata stream section with parseSection() (defers to the virtual _parseSection() method). Get the resulting, current trace class and metadata stream UUID with traceCls() and metadataStreamUuid(). The constructor accepts an optional self component pointer so that parseSection() may finalize the trace class after calling the version-specific _parseSection(). The trace class finalization method finalizes a `ctf::src` CTF IR trace class after its creation or when it gets new data stream classes or event record classes. The method: • Sets the value saving indexes of dependencies (field classes) and the saved value index of dependent (dynamic-length, optional, and variant) field classes. It also links the dependent field classes to their dependencies by weak pointer. • Reconfigures the clock classes of the trace class. • Normalizes the offsets of the clock classes of the trace class so that the cycle part is less than the frequency. • With a self component pointer, translates the contained objects to their trace IR equivalents. Signed-off-by: Philippe Proulx Signed-off-by: Simon Marchi Change-Id: I24e175447f759220de5943ccb49c7888a5934ce5 Reviewed-on: https://review.lttng.org/c/babeltrace/+/12733 --- diff --git a/src/Makefile.am b/src/Makefile.am index 9deaa9ae..f09f733d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -706,6 +706,8 @@ plugins_ctf_babeltrace_plugin_ctf_la_SOURCES = \ plugins/ctf/common/src/metadata/json/strings.hpp \ plugins/ctf/common/src/metadata/json/val-req.cpp \ plugins/ctf/common/src/metadata/json/val-req.hpp \ + plugins/ctf/common/src/metadata/metadata-stream-parser.cpp \ + 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/metadata-stream-decoder.cpp \ diff --git a/src/plugins/ctf/common/src/metadata/metadata-stream-parser.cpp b/src/plugins/ctf/common/src/metadata/metadata-stream-parser.cpp new file mode 100644 index 00000000..d370a87f --- /dev/null +++ b/src/plugins/ctf/common/src/metadata/metadata-stream-parser.cpp @@ -0,0 +1,2037 @@ +/* + * Copyright (c) 2022-2024 Philippe Proulx + * + * SPDX-License-Identifier: MIT + */ + +#include + +#include "common/assert.h" +#include "cpp-common/bt2c/call.hpp" + +#include "metadata-stream-parser.hpp" +#include "normalize-clk-offset.hpp" + +namespace ctf { +namespace src { +namespace { + +using namespace bt2c::literals::datalen; + +/* + * Map of variant field class to the index of the currently + * visited option. + * + * This is used to provide a visiting context to an `FcFinder` instance. + * For example: + * + * Root: Structure FC [0] + * `len`: Fixed-length unsigned integer FC [1] + * `meow`: Dynamic-length array FC [2] + * Element FC: Structure FC [3] + * `tag`: Fixed-length signed integer FC [4] + * `val`: Variant FC [5] + * `boss`: Null-terminated string FC [6] + * `zoom`: Structure FC [7] + * `len`: Variable-length unsigned integer FC [8] + * `data`: Dynamic-length BLOB FC [9] + * `line6`: Structure FC [10] + * `len`: Fixed-length unsigned integer FC [11] + * + * If we're currently visiting [9] to find its keys, then the map + * would contain: + * + * [2] → 0 (visiting current element of `/meow`) + * [5] → 1 (visiting second option (`zoom`) of `/meow/val`) + * + * This means that, if the length field location of [9] is + * `/meow/val/len`, then we must only consider the `zoom` option, not + * the `line6` one, even though both contain a member class named `len`. + */ +using VariantOptIndexes = std::unordered_map; + +/* + * Field class visitor to find field classes from a field location and + * an instance of `VariantOptIndexes` to indicate the current visiting + * context of the dependent field class. + */ +class FcFinder final : public FcVisitor +{ +public: + explicit FcFinder(const FieldLoc::Items& path, const VariantOptIndexes& dynIndexes) : + _mPath {&path}, _mPathIter {path.begin()}, _mVariantOptIndexes {&dynIndexes} + { + } + + /* + * Resulting field class set. + */ + const FcSet& fcs() const noexcept + { + return _mFcs; + } + + void visit(FixedLenBoolFc& fc) override + { + this->_addFc(fc); + } + + void visit(FixedLenUIntFc& fc) override + { + this->_addFc(fc); + } + + void visit(FixedLenSIntFc& fc) override + { + this->_addFc(fc); + } + + void visit(VarLenSIntFc& fc) override + { + this->_addFc(fc); + } + + void visit(VarLenUIntFc& fc) override + { + this->_addFc(fc); + } + + void visit(StaticLenArrayFc& fc) override + { + this->_visit(fc); + } + + void visit(DynLenArrayFc& fc) override + { + this->_visit(fc); + } + + void visit(StructFc& structFc) override + { + BT_ASSERT(_mPathIter != _mPath->end()); + + const auto memberCls = structFc[**_mPathIter]; + + BT_ASSERT(memberCls); + + /* Go to next path item */ + ++_mPathIter; + memberCls->fc().accept(*this); + + /* Restore path item */ + --_mPathIter; + } + + void visit(OptionalWithBoolSelFc& fc) override + { + this->_visit(fc); + } + + void visit(OptionalWithUIntSelFc& fc) override + { + this->_visit(fc); + } + + void visit(OptionalWithSIntSelFc& fc) override + { + this->_visit(fc); + } + + void visit(VariantWithUIntSelFc& fc) override + { + this->_visitVariantFc(fc); + } + + void visit(VariantWithSIntSelFc& fc) override + { + this->_visitVariantFc(fc); + } + +private: + void _addFc(Fc& fc) + { + /* Must be a leaf! */ + BT_ASSERT(_mPathIter == _mPath->end()); + + /* Insert into resulting field class set */ + _mFcs.insert(&fc); + } + + void _visit(ArrayFc& arrayFc) + { + arrayFc.elemFc().accept(*this); + } + + void _visit(OptionalFc& optFc) + { + optFc.fc().accept(*this); + } + + template + void _visitVariantFc(VariantFcT& variantFc) + { + const auto optIndexIt = _mVariantOptIndexes->find(&variantFc); + + if (optIndexIt == _mVariantOptIndexes->end()) { + /* + * Dependent field class isn't within `variantFc`: consider + * all options. + */ + for (auto& opt : variantFc) { + opt.fc().accept(*this); + } + } else { + /* + * Dependent field class is within `variantFc`: follow its + * specific option. + */ + variantFc[optIndexIt->second].fc().accept(*this); + } + } + + const FieldLoc::Items *_mPath; + FieldLoc::Items::const_iterator _mPathIter; + const VariantOptIndexes *_mVariantOptIndexes; + FcSet _mFcs; +}; + +/* + * Returns the field class of the scope `scope` considering the context + * `traceCls`, `dataStreamCls`, and `eventRecordCls`. + */ +Fc& scopeFc(TraceCls& traceCls, DataStreamCls * const dataStreamCls, + EventRecordCls * const eventRecordCls, const Scope scope) noexcept +{ + switch (scope) { + case Scope::PktHeader: + return *traceCls.pktHeaderFc(); + case Scope::PktCtx: + BT_ASSERT(dataStreamCls); + BT_ASSERT(dataStreamCls->pktCtxFc()); + return *dataStreamCls->pktCtxFc(); + case Scope::EventRecordHeader: + BT_ASSERT(dataStreamCls); + BT_ASSERT(dataStreamCls->eventRecordHeaderFc()); + return *dataStreamCls->eventRecordHeaderFc(); + case Scope::CommonEventRecordCtx: + BT_ASSERT(dataStreamCls); + BT_ASSERT(dataStreamCls->commonEventRecordCtxFc()); + return *dataStreamCls->commonEventRecordCtxFc(); + case Scope::SpecEventRecordCtx: + BT_ASSERT(eventRecordCls); + BT_ASSERT(eventRecordCls->specCtxFc()); + return *eventRecordCls->specCtxFc(); + case Scope::EventRecordPayload: + BT_ASSERT(eventRecordCls); + BT_ASSERT(eventRecordCls->payloadFc()); + return *eventRecordCls->payloadFc(); + default: + bt_common_abort(); + } +} + +/* + * Sets the value saving indexes of keys and the saved key value index + * of dependent field classes. + */ +class DependentFcSavedKeyValIndexSetter final : public FcVisitor +{ +public: + explicit DependentFcSavedKeyValIndexSetter(TraceCls& traceCls, + DataStreamCls * const curDataStreamCls, + EventRecordCls * const curEventRecordCls) : + _mTraceCls {&traceCls}, + _mCurDataStreamCls {curDataStreamCls}, _mCurEventRecordCls {curEventRecordCls} + { + } + + void visit(StaticLenArrayFc& fc) override + { + this->_visit(fc); + } + + void visit(DynLenArrayFc& fc) override + { + this->_setSavedKeyValIndex(fc, fc.lenFieldLoc()); + this->_visit(fc); + } + + void visit(DynLenStrFc& fc) override + { + this->_setSavedKeyValIndex(fc, fc.lenFieldLoc()); + } + + void visit(DynLenBlobFc& fc) override + { + this->_setSavedKeyValIndex(fc, fc.lenFieldLoc()); + } + + void visit(StructFc& structFc) override + { + for (auto& memberCls : structFc) { + memberCls.fc().accept(*this); + } + } + + void visit(OptionalWithBoolSelFc& fc) override + { + this->_visit(fc); + } + + void visit(OptionalWithUIntSelFc& fc) override + { + this->_visit(fc); + } + + void visit(OptionalWithSIntSelFc& fc) override + { + this->_visit(fc); + } + + void visit(VariantWithUIntSelFc& fc) override + { + this->_visitVariantFc(fc); + } + + void visit(VariantWithSIntSelFc& fc) override + { + this->_visitVariantFc(fc); + } + +private: + /* + * Sets the saved key value index of the dependent field class `fc`, + * finding the key field classes with `fieldLoc`. + */ + template + void _setSavedKeyValIndex(FcT& fc, const FieldLoc& fieldLoc) + { + /* Find the key field class */ + FcFinder finder {fieldLoc.items(), _mCurVariantOptIndexes}; + + scopeFc(*_mTraceCls, _mCurDataStreamCls, _mCurEventRecordCls, *fieldLoc.origin()) + .accept(finder); + + /* Key value saving index to use */ + const auto keyValSavingIndex = _mTraceCls->savedKeyValCount(); + + /* Update maximum number of saved key values of `*_mTraceCls` */ + _mTraceCls->savedKeyValCount(keyValSavingIndex + 1); + + /* Add key value saving index to all key field classes */ + for (const auto foundFc : finder.fcs()) { + if (foundFc->isFixedLenBool()) { + foundFc->asFixedLenBool().addKeyValSavingIndex(keyValSavingIndex); + } else if (foundFc->isFixedLenInt()) { + foundFc->asFixedLenInt().addKeyValSavingIndex(keyValSavingIndex); + } else { + BT_ASSERT(foundFc->isVarLenInt()); + foundFc->asVarLenInt().addKeyValSavingIndex(keyValSavingIndex); + } + } + + /* Set saved key value index of dependent field class `fc` */ + fc.savedKeyValIndex(keyValSavingIndex); + + /* Set key field classes of dependent field class `fc` */ + fc.keyFcs(finder.fcs()); + } + + void _visit(ArrayFc& arrayFc) + { + arrayFc.elemFc().accept(*this); + } + + void _visit(OptionalFc& optFc) + { + this->_setSavedKeyValIndex(optFc, optFc.selFieldLoc()); + optFc.fc().accept(*this); + } + + template + void _visitVariantFc(VariantFcT& variantFc) + { + this->_setSavedKeyValIndex(variantFc, variantFc.selFieldLoc()); + + for (std::size_t i = 0; i < variantFc.size(); ++i) { + /* + * Mark this option as being visited for this variant field + * class, then visit the field class of the option, then + * finally unmark this option. + */ + _mCurVariantOptIndexes.insert(std::make_pair(&variantFc, i)); + variantFc[i].fc().accept(*this); + _mCurVariantOptIndexes.erase(&variantFc); + } + } + + TraceCls *_mTraceCls; + DataStreamCls *_mCurDataStreamCls; + EventRecordCls *_mCurEventRecordCls; + VariantOptIndexes _mCurVariantOptIndexes; +}; + +/* + * Helper containing context to implement setSavedValIndexes(). + */ +class SavedKeyValIndexesSetter final +{ +public: + explicit SavedKeyValIndexesSetter(TraceCls& traceCls) : _mTraceCls {&traceCls} + { + /* Process the whole trace class */ + this->_setSavedKeyValIndexes(); + } + +private: + /* + * Sets the saved key value indexes within the scope field class + * `structFc`, if it exists. + */ + void _setSavedKeyValIndexes(StructFc * const structFc) + { + if (!structFc) { + /* Scope doesn't exist */ + return; + } + + /* Create setter for dependent field classes */ + DependentFcSavedKeyValIndexSetter setter {*_mTraceCls, _mCurDataStreamCls, + _mCurEventRecordCls}; + + /* + * Visit scope field class. + * + * During the visit, `setter` calls savedKeyValIndex() for each + * dependent field class as well as TraceCls::savedKeyValCount() + * to update the total count of saved values. + */ + structFc->accept(setter); + } + + /* + * Sets the saved key value indexes within `eventRecordCls`. + */ + void _setSavedKeyValIndexes(EventRecordCls& eventRecordCls) + { + if (eventRecordCls.libCls()) { + /* Already done */ + return; + } + + _mCurEventRecordCls = &eventRecordCls; + + /* Process specific context field class */ + this->_setSavedKeyValIndexes(eventRecordCls.specCtxFc()); + + /* Process payload field class */ + this->_setSavedKeyValIndexes(eventRecordCls.payloadFc()); + + /* Not visiting anymore */ + _mCurEventRecordCls = nullptr; + } + + /* + * Sets the saved key value indexes within `dataStreamCls`. + */ + void _setSavedKeyValIndexes(DataStreamCls& dataStreamCls) + { + _mCurDataStreamCls = &dataStreamCls; + + if (!dataStreamCls.libCls()) { + /* Process packet context field class */ + this->_setSavedKeyValIndexes(dataStreamCls.pktCtxFc()); + + /* Process event record header field class */ + this->_setSavedKeyValIndexes(dataStreamCls.eventRecordHeaderFc()); + + /* Process common event record context field class */ + this->_setSavedKeyValIndexes(dataStreamCls.commonEventRecordCtxFc()); + } + + /* Process event record classes */ + for (auto& eventRecordCls : dataStreamCls) { + this->_setSavedKeyValIndexes(*eventRecordCls); + } + + /* Not visiting anymore */ + _mCurDataStreamCls = nullptr; + } + + /* + * Sets the saved key value indexes within `*_mTraceCls`. + */ + void _setSavedKeyValIndexes() + { + if (!_mTraceCls->libCls()) { + /* Process packet header field class */ + this->_setSavedKeyValIndexes(_mTraceCls->pktHeaderFc()); + } + + /* Process data stream classes */ + for (auto& dataStreamCls : *_mTraceCls) { + this->_setSavedKeyValIndexes(*dataStreamCls); + } + } + + /* Trace class on which we're working */ + TraceCls *_mTraceCls; + + /* Current visited data stream class, if any */ + DataStreamCls *_mCurDataStreamCls = nullptr; + + /* Current visited event record class, if any */ + EventRecordCls *_mCurEventRecordCls = nullptr; +}; + +void setSavedKeyValIndexes(TraceCls& traceCls) +{ + SavedKeyValIndexesSetter {traceCls}; +} + +/* + * Visits a field class recursively to check whether or not it contains + * an unsigned integer field class having a given role. + */ +class FcContainsUIntFcWithRole final : public ConstFcVisitor +{ +public: + explicit FcContainsUIntFcWithRole(const UIntFieldRole role) noexcept : _mRole {role} + { + } + + bool result() const noexcept + { + return _mHasRole; + } + + void visit(const FixedLenUIntFc& fc) override + { + this->_updateHasRole(fc); + } + + void visit(const VarLenUIntFc& fc) override + { + this->_updateHasRole(fc); + } + + void visit(const StaticLenArrayFc& fc) override + { + this->_visit(fc); + } + + void visit(const DynLenArrayFc& fc) override + { + this->_visit(fc); + } + + void visit(const StructFc& structFc) override + { + for (auto& memberCls : structFc) { + memberCls.fc().accept(*this); + } + } + + void visit(const OptionalWithBoolSelFc& fc) override + { + this->_visit(fc); + } + + void visit(const OptionalWithUIntSelFc& fc) override + { + this->_visit(fc); + } + + void visit(const OptionalWithSIntSelFc& fc) override + { + this->_visit(fc); + } + + void visit(const VariantWithUIntSelFc& fc) override + { + this->_visitVariantFc(fc); + } + + void visit(const VariantWithSIntSelFc& fc) override + { + this->_visitVariantFc(fc); + } + +private: + template + void _updateHasRole(const FcT& fc) noexcept + { + _mHasRole = _mHasRole || fc.hasRole(_mRole); + } + + void _visit(const ArrayFc& arrayFc) + { + arrayFc.elemFc().accept(*this); + } + + void _visit(const OptionalFc& optFc) + { + optFc.fc().accept(*this); + } + + template + void _visitVariantFc(const VariantFcT& variantFc) + { + for (auto& opt : variantFc) { + opt.fc().accept(*this); + } + } + + UIntFieldRole _mRole; + bool _mHasRole = false; +}; + +bool fcContainsUIntFcWithRole(const Fc& fc, const UIntFieldRole role) noexcept +{ + FcContainsUIntFcWithRole visitor {role}; + + fc.accept(visitor); + return visitor.result(); +} + +bool pktCtxFcContainsUIntFcWithRole(const DataStreamCls& dataStreamCls, + const UIntFieldRole role) noexcept +{ + return dataStreamCls.pktCtxFc() && fcContainsUIntFcWithRole(*dataStreamCls.pktCtxFc(), role); +} + +/* + * Sets the user attributes of the equivalent trace IR object of `obj` + * (`obj.libCls()`) to the attributes of `obj` if `mipVersion` is + * greater than or equal to 1. + */ +template +void trySetLibUserAttrs(ObjT& obj, const unsigned long long mipVersion) noexcept +{ + if (mipVersion >= 1 && obj.attrs()) { + BT_ASSERT(obj.libCls()); + obj.libCls()->userAttributes(*obj.attrs()); + } +} + +/* + * Translates `ranges` to its libbabeltrace2 equivalent (of type + * `LibIntRangeSetT`) and returns it. + */ +template +typename LibIntRangeSetT::Shared libIntRangeSetFromIntRangeSet(const IntRangeSetT& ranges) +{ + auto libRanges = LibIntRangeSetT::create(); + + for (auto& range : ranges) { + libRanges->addRange(range.lower(), range.upper()); + } + + return libRanges; +} + +bt2::UnsignedIntegerRangeSet::Shared libIntRangeSetFromIntRangeSet(const UIntRangeSet& ranges) +{ + return libIntRangeSetFromIntRangeSet(ranges); +} + +bt2::SignedIntegerRangeSet::Shared libIntRangeSetFromIntRangeSet(const SIntRangeSet& ranges) +{ + return libIntRangeSetFromIntRangeSet(ranges); +} + +/* + * Helper containing context to implement libFcFromFc(). + */ +class LibFcFromFcTranslator final : public FcVisitor +{ +public: + explicit LibFcFromFcTranslator(TraceCls& traceCls, const unsigned long long mipVersion) : + _mTraceCls {&traceCls}, _mMipVersion {mipVersion} + { + BT_ASSERT(traceCls.libCls()); + } + + bt2::FieldClass::Shared libFc() noexcept + { + return _mLastTranslatedLibFc; + } + + void visit(FixedLenBitArrayFc& fc) override + { + this->_setLibFc(fc, _mTraceCls->libCls()->createBitArrayFieldClass(*fc.len())); + } + + void visit(FixedLenBitMapFc& fc) override + { + BT_ASSERT(_mMipVersion >= 1); + this->_setLibFc(fc, _mTraceCls->libCls()->createBitArrayFieldClass(*fc.len())); + + /* Set flags */ + for (auto& flag : fc.flags()) { + _mLastTranslatedLibFc->asBitArray().addFlag( + flag.first, *libIntRangeSetFromIntRangeSet(flag.second)); + } + } + + void visit(FixedLenBoolFc& fc) override + { + this->_setLibFc(fc, _mTraceCls->libCls()->createBoolFieldClass()); + } + + void visit(FixedLenFloatFc& fc) override + { + if (fc.len() == 32_bits) { + this->_setLibFc(fc, _mTraceCls->libCls()->createSinglePrecisionRealFieldClass()); + } else { + BT_ASSERT(fc.len() == 64_bits); + this->_setLibFc(fc, _mTraceCls->libCls()->createDoublePrecisionRealFieldClass()); + } + } + + void visit(FixedLenSIntFc& fc) override + { + if (fc.mappings().empty()) { + this->_setLibIntFc<_CreateLibSIntFcFunc>(fc, fc.len()); + } else { + this->_setLibSEnumFc(fc, fc.len()); + } + } + + void visit(FixedLenUIntFc& fc) override + { + if (fc.mappings().empty()) { + this->_setLibUIntFc<_CreateLibUIntFcFunc>(fc, fc.len()); + } else { + this->_setLibUEnumFc(fc, fc.len()); + } + } + + void visit(VarLenSIntFc& fc) override + { + static const auto len = 64_bits; + + if (fc.mappings().empty()) { + this->_setLibIntFc<_CreateLibSIntFcFunc>(fc, len); + } else { + this->_setLibSEnumFc(fc, len); + } + } + + void visit(VarLenUIntFc& fc) override + { + static const auto len = 64_bits; + + if (fc.mappings().empty()) { + this->_setLibUIntFc<_CreateLibUIntFcFunc>(fc, len); + } else { + this->_setLibUEnumFc(fc, len); + } + } + + void visit(NullTerminatedStrFc& fc) override + { + this->_setLibFc(fc, _mTraceCls->libCls()->createStringFieldClass()); + } + + void visit(StaticLenStrFc& fc) override + { + this->_setLibFc(fc, _mTraceCls->libCls()->createStringFieldClass()); + } + + void visit(DynLenStrFc& fc) override + { + this->_setLibFc(fc, _mTraceCls->libCls()->createStringFieldClass()); + } + + void visit(StaticLenBlobFc& fc) override + { + BT_ASSERT(_mMipVersion >= 1); + this->_setLibBlobFc(fc, _mTraceCls->libCls()->createStaticBlobFieldClass(fc.len())); + } + + void visit(DynLenBlobFc& fc) override + { + BT_ASSERT(_mMipVersion >= 1); + + const auto fieldLoc = this->_libFieldLocFromFieldLoc(fc.lenFieldLoc()); + + if (fieldLoc) { + this->_setLibBlobFc( + fc, _mTraceCls->libCls()->createDynamicBlobWithLengthFieldLocationFieldClass( + *fieldLoc)); + } else { + this->_setLibBlobFc( + fc, _mTraceCls->libCls()->createDynamicBlobWithoutLengthFieldLocationFieldClass()); + } + } + + void visit(StaticLenArrayFc& fc) override + { + /* Try to translate element field class */ + this->_visit(fc); + + /* + * `_mLastTranslatedLibFc` is the element field class. + * + * If it's not set, then the element field class itself has no + * trace IR translation, ergo `fc` has no trace IR translation. + */ + if (!_mLastTranslatedLibFc) { + return; + } + + this->_setLibFc(fc, _mTraceCls->libCls()->createStaticArrayFieldClass( + *_mLastTranslatedLibFc, fc.len())); + } + + void visit(DynLenArrayFc& fc) override + { + /* Try to translate element field class */ + this->_visit(fc); + + /* + * `_mLastTranslatedLibFc` is the element field class. + * + * If it's not set, then the element field class itself has no + * trace IR translation, ergo `fc` has no trace IR translation. + */ + if (!_mLastTranslatedLibFc) { + return; + } + + /* Finish translation */ + this->_finishTranslateDynFc<_CreateLibDynLenArrayFcFuncs>(fc, fc.lenFieldLoc()); + } + + void visit(StructFc& structFc) override + { + /* Create empty trace IR structure field class and keep it */ + auto libStructFc = _mTraceCls->libCls()->createStructureFieldClass(); + + /* Assign as translation and set user attributes */ + structFc.libCls(*libStructFc); + trySetLibUserAttrs(structFc, _mMipVersion); + + /* Translate member classes */ + for (auto& memberCls : structFc) { + /* Try to translate field class of member class */ + memberCls.fc().accept(*this); + + /* + * `_mLastTranslatedLibFc` is the field class of this member + * class. + * + * If it's not set, then the member class itself has no + * trace IR translation. + */ + if (!_mLastTranslatedLibFc) { + continue; + } + + /* Append new member class */ + libStructFc->appendMember(memberCls.name(), *_mLastTranslatedLibFc); + + /* Set user attributes of member class, if any */ + if (_mMipVersion >= 1 && memberCls.attrs()) { + (*libStructFc)[libStructFc->length() - 1].userAttributes(*memberCls.attrs()); + } + } + + /* + * Set translated structure field class as last translated field + * class. + */ + _mLastTranslatedLibFc = std::move(libStructFc); + } + + void visit(OptionalWithBoolSelFc& fc) override + { + /* Try to translate optional field class */ + this->_visit(fc); + + /* + * `_mLastTranslatedLibFc` is the optional field class. + * + * If it's not set, then the optional field class itself has no + * trace IR translation, ergo `fc` has no trace IR translation. + */ + if (!_mLastTranslatedLibFc) { + return; + } + + /* Finish translation */ + this->_finishTranslateDynFc<_CreateLibOptWithBoolSelFcFuncs>(fc, fc.selFieldLoc()); + } + + void visit(OptionalWithUIntSelFc& fc) override + { + /* Try to translate optional field class */ + this->_visit(fc); + + /* + * `_mLastTranslatedLibFc` is the optional field class. + * + * If it's not set, then the optional field class itself has no + * trace IR translation, ergo `fc` has no trace IR translation. + */ + if (!_mLastTranslatedLibFc) { + return; + } + + /* Finish translation */ + this->_finishTranslateDynFc<_CreateLibOptWithUIntSelFcFuncs>(fc, fc.selFieldLoc()); + } + + void visit(OptionalWithSIntSelFc& fc) override + { + /* Try to translate optional field class */ + this->_visit(fc); + + /* + * `_mLastTranslatedLibFc` is the optional field class. + * + * If it's not set, then the optional field class itself has no + * trace IR translation, ergo `fc` has no trace IR translation. + */ + if (!_mLastTranslatedLibFc) { + return; + } + + /* Finish translation */ + this->_finishTranslateDynFc<_CreateLibOptWithSIntSelFcFuncs>(fc, fc.selFieldLoc()); + } + + void visit(VariantWithUIntSelFc& fc) override + { + this->_visitVariantFc(fc); + } + + void visit(VariantWithSIntSelFc& fc) override + { + this->_visitVariantFc(fc); + } + +private: + /* + * If the scope of `fieldLoc` is the packet header, the packet + * context, or the event record header: returns an + * empty `bt2::ConstFieldLocation::Shared`. + * + * Otherwise, translates `fieldLoc` to its trace IR equivalent and + * returns it. + */ + bt2::ConstFieldLocation::Shared _libFieldLocFromFieldLoc(const FieldLoc& fieldLoc) const + { + BT_ASSERT(_mMipVersion >= 1); + + if (fieldLoc.origin() == Scope::PktHeader || fieldLoc.origin() == Scope::PktCtx || + fieldLoc.origin() == Scope::EventRecordHeader) { + /* + * We could support referring to a packet context field, but + * because such a field could have a role and therefore not + * have a trace IR translation, we don't take a chance. + */ + return bt2::ConstFieldLocation::Shared {}; + } + + return _mTraceCls->libCls()->createFieldLocation( + bt2c::call([&fieldLoc] { + switch (*fieldLoc.origin()) { + case Scope::CommonEventRecordCtx: + return bt2::ConstFieldLocation::Scope::CommonEventContext; + case Scope::SpecEventRecordCtx: + return bt2::ConstFieldLocation::Scope::SpecificEventContext; + case Scope::EventRecordPayload: + return bt2::ConstFieldLocation::Scope::EventPayload; + default: + bt_common_abort(); + } + }), + bt2c::call([&fieldLoc] { + std::vector items; + + for (auto& item : fieldLoc.items()) { + items.push_back(*item); + } + + return items; + })); + } + + /* + * Sets the trace IR translation of `fc` to `libFc`, sets the user + * attributes of `libFc` from `fc`, and then moves `libFc` as the + * last translated trace IR field class. + */ + template + void _setLibFc(FcT& fc, bt2::FieldClass::Shared libFc) noexcept + { + fc.libCls(*libFc); + trySetLibUserAttrs(fc, _mMipVersion); + _mLastTranslatedLibFc = std::move(libFc); + } + + struct _CreateLibUIntFcFunc final + { + static bt2::IntegerFieldClass::Shared create(bt2::TraceClass traceCls) + { + return traceCls.createUnsignedIntegerFieldClass(); + } + }; + + struct _CreateLibSIntFcFunc final + { + static bt2::IntegerFieldClass::Shared create(bt2::TraceClass traceCls) + { + return traceCls.createSignedIntegerFieldClass(); + } + }; + + struct _CreateLibUEnumFcFunc final + { + static bt2::UnsignedEnumerationFieldClass::Shared create(bt2::TraceClass traceCls) + { + return traceCls.createUnsignedEnumerationFieldClass(); + } + }; + + struct _CreateLibSEnumFcFunc final + { + static bt2::SignedEnumerationFieldClass::Shared create(bt2::TraceClass traceCls) + { + return traceCls.createSignedEnumerationFieldClass(); + } + }; + + /* + * Translates `fc`, an integer field class of which the instances + * have a maximum length of `len`, to its trace IR equivalent, and + * then moves it as the last translated trace IR field class. + * + * Uses `CreateLibIntFcFuncT::create()` to create a trace IR integer + * field class. + */ + template + void _setLibIntFc(FcT& fc, const bt2c::DataLen len) + { + /* Create trace IR field class */ + auto libFc = CreateLibIntFcFuncT::create(*_mTraceCls->libCls()); + + /* Set field value range (bits) */ + libFc->fieldValueRange(*len); + + /* Set preferred display base */ + libFc->preferredDisplayBase([&fc] { + switch (fc.prefDispBase()) { + case DispBase::Bin: + return bt2::DisplayBase::Binary; + case DispBase::Oct: + return bt2::DisplayBase::Octal; + case DispBase::Dec: + return bt2::DisplayBase::Decimal; + case DispBase::Hex: + return bt2::DisplayBase::Hexadecimal; + default: + bt_common_abort(); + } + }()); + + /* Assign as translation */ + this->_setLibFc(fc, std::move(libFc)); + } + + /* + * If `fc`, an unsigned integer field class, has at least one role: + * returns immediately (no translation). + * + * Otherwise, translates `fc`, of which the instances have a maximum + * length of `len`, to its trace IR equivalent, and then moves it as + * the last translated trace IR field class. + * + * Uses `CreateLibIntFcFuncT::create()` to create a trace IR + * unsigned integer field class. + */ + template + void _setLibUIntFc(FcT& fc, const bt2c::DataLen len) + { + if (!fc.roles().empty()) { + /* Field class has a role: don't translate it to trace IR */ + _mLastTranslatedLibFc = bt2::FieldClass::Shared {}; + return; + } + + this->_setLibIntFc(fc, len); + } + + /* + * Sets the mappings of `libFc`, a trace IR enumeration field class, + * to the mappings of `fc`, a CTF IR integer field class with at + * least one mapping. + */ + template + void _setLibEnumFcMappings(const FcT& fc, LibFcT libFc) + { + BT_ASSERT(!fc.mappings().empty()); + + for (auto& mapping : fc.mappings()) { + libFc.addMapping(mapping.first, *libIntRangeSetFromIntRangeSet(mapping.second)); + } + } + + /* + * If `fc`, an unsigned integer field class having at least one + * mapping, has at least one role: returns immediately + * (no translation). + * + * Otherwise, translates `fc`, of which the instances have a maximum + * length of `len`, to its trace IR equivalent, and then moves it as + * the last translated trace IR field class. + */ + template + void _setLibUEnumFc(FcT& fc, const bt2c::DataLen len) + { + this->_setLibUIntFc<_CreateLibUEnumFcFunc>(fc, len); + + if (!_mLastTranslatedLibFc) { + /* Not translated */ + return; + } + + this->_setLibEnumFcMappings(fc, _mLastTranslatedLibFc->asUnsignedEnumeration()); + } + + /* + * Translates `fc`, a signed integer field class having at least one + * mapping and of which the instances have a maximum length of + * `len`, to its trace IR equivalent, and then moves it as the last + * translated trace IR field class. + */ + template + void _setLibSEnumFc(FcT& fc, const bt2c::DataLen len) + { + this->_setLibIntFc<_CreateLibSEnumFcFunc>(fc, len); + BT_ASSERT(_mLastTranslatedLibFc); + this->_setLibEnumFcMappings(fc, _mLastTranslatedLibFc->asSignedEnumeration()); + } + + /* + * Sets the trace IR translation of `fc` to `libFc`, a trace IR BLOB + * field class, also setting the media type of `libFc` from `fc`, + * and then moves `libFc` as the last translated trace IR field + * class. + */ + template + void _setLibBlobFc(FcT& fc, SharedLibFcT libFc) + { + /* Set media type */ + libFc->mediaType(fc.mediaType()); + + /* Assign as translation */ + this->_setLibFc(fc, std::move(libFc)); + } + + struct _CreateLibDynLenArrayFcFuncs final + { + using RetWithout = bt2::ArrayFieldClass::Shared; + using RetWith = bt2::DynamicArrayWithLengthFieldClass::Shared; + + static RetWithout mip0Without(TraceCls& traceCls, DynLenArrayFc&, + const bt2::FieldClass::Shared& lastTranslatedLibFc) + { + BT_ASSERT(lastTranslatedLibFc); + return traceCls.libCls()->createDynamicArrayFieldClass(*lastTranslatedLibFc); + } + + static RetWith mip0With(TraceCls& traceCls, DynLenArrayFc&, + const bt2::FieldClass::Shared& lastTranslatedLibFc, + const bt2::FieldClass libDepFc) + { + BT_ASSERT(lastTranslatedLibFc); + return traceCls.libCls()->createDynamicArrayFieldClass(*lastTranslatedLibFc, + libDepFc.asInteger()); + } + + static RetWithout mip1Without(TraceCls& traceCls, DynLenArrayFc&, + const bt2::FieldClass::Shared& lastTranslatedLibFc) + { + BT_ASSERT(lastTranslatedLibFc); + return traceCls.libCls()->createDynamicArrayWithoutLengthFieldLocationFieldClass( + *lastTranslatedLibFc); + } + + static RetWith mip1With(TraceCls& traceCls, DynLenArrayFc&, + const bt2::FieldClass::Shared& lastTranslatedLibFc, + const bt2::ConstFieldLocation libFieldLoc) + { + BT_ASSERT(lastTranslatedLibFc); + return traceCls.libCls()->createDynamicArrayWithLengthFieldLocationFieldClass( + *lastTranslatedLibFc, libFieldLoc); + } + }; + + struct _CreateLibOptWithBoolSelFcFuncs final + { + using RetWithout = bt2::OptionFieldClass::Shared; + using RetWith = bt2::OptionWithBoolSelectorFieldClass::Shared; + + static RetWithout mip0Without(TraceCls& traceCls, OptionalFc&, + const bt2::FieldClass::Shared& lastTranslatedLibFc) + { + BT_ASSERT(lastTranslatedLibFc); + return traceCls.libCls()->createOptionFieldClass(*lastTranslatedLibFc); + } + + static RetWith mip0With(TraceCls& traceCls, OptionalFc&, + const bt2::FieldClass::Shared& lastTranslatedLibFc, + const bt2::FieldClass libDepFc) + { + BT_ASSERT(lastTranslatedLibFc); + return traceCls.libCls()->createOptionWithBoolSelectorFieldClass(*lastTranslatedLibFc, + libDepFc); + } + + static RetWithout mip1Without(TraceCls& traceCls, OptionalFc&, + const bt2::FieldClass::Shared& lastTranslatedLibFc) + { + BT_ASSERT(lastTranslatedLibFc); + return traceCls.libCls()->createOptionWithoutSelectorFieldLocationFieldClass( + *lastTranslatedLibFc); + } + + static RetWith mip1With(TraceCls& traceCls, OptionalFc&, + const bt2::FieldClass::Shared& lastTranslatedLibFc, + const bt2::ConstFieldLocation libFieldLoc) + { + BT_ASSERT(lastTranslatedLibFc); + return traceCls.libCls()->createOptionWithBoolSelectorFieldLocationFieldClass( + *lastTranslatedLibFc, libFieldLoc); + } + }; + + struct _CreateLibOptWithUIntSelFcFuncs final + { + using RetWithout = bt2::OptionFieldClass::Shared; + using RetWith = bt2::OptionWithUnsignedIntegerSelectorFieldClass::Shared; + + static RetWithout mip0Without(TraceCls& traceCls, OptionalFc&, + const bt2::FieldClass::Shared& lastTranslatedLibFc) + { + BT_ASSERT(lastTranslatedLibFc); + return traceCls.libCls()->createOptionFieldClass(*lastTranslatedLibFc); + } + + static RetWith mip0With(TraceCls& traceCls, OptionalWithUIntSelFc& fc, + const bt2::FieldClass::Shared& lastTranslatedLibFc, + const bt2::FieldClass libDepFc) + { + BT_ASSERT(lastTranslatedLibFc); + return traceCls.libCls()->createOptionWithUnsignedIntegerSelectorFieldClass( + *lastTranslatedLibFc, libDepFc.asInteger(), + *libIntRangeSetFromIntRangeSet(fc.selFieldRanges())); + } + + static RetWithout mip1Without(TraceCls& traceCls, OptionalFc&, + const bt2::FieldClass::Shared& lastTranslatedLibFc) + { + BT_ASSERT(lastTranslatedLibFc); + return traceCls.libCls()->createOptionWithoutSelectorFieldLocationFieldClass( + *lastTranslatedLibFc); + } + + static RetWith mip1With(TraceCls& traceCls, OptionalWithUIntSelFc& fc, + const bt2::FieldClass::Shared& lastTranslatedLibFc, + const bt2::ConstFieldLocation libFieldLoc) + { + BT_ASSERT(lastTranslatedLibFc); + return traceCls.libCls() + ->createOptionWithUnsignedIntegerSelectorFieldLocationFieldClass( + *lastTranslatedLibFc, libFieldLoc, + *libIntRangeSetFromIntRangeSet(fc.selFieldRanges())); + } + }; + + struct _CreateLibOptWithSIntSelFcFuncs final + { + using RetWithout = bt2::OptionFieldClass::Shared; + using RetWith = bt2::OptionWithSignedIntegerSelectorFieldClass::Shared; + + static RetWithout mip0Without(TraceCls& traceCls, OptionalFc&, + const bt2::FieldClass::Shared& lastTranslatedLibFc) + { + BT_ASSERT(lastTranslatedLibFc); + return traceCls.libCls()->createOptionFieldClass(*lastTranslatedLibFc); + } + + static RetWith mip0With(TraceCls& traceCls, OptionalWithSIntSelFc& fc, + const bt2::FieldClass::Shared& lastTranslatedLibFc, + const bt2::FieldClass libDepFc) + { + BT_ASSERT(lastTranslatedLibFc); + return traceCls.libCls()->createOptionWithSignedIntegerSelectorFieldClass( + *lastTranslatedLibFc, libDepFc.asInteger(), + *libIntRangeSetFromIntRangeSet(fc.selFieldRanges())); + } + + static RetWithout mip1Without(TraceCls& traceCls, OptionalFc&, + const bt2::FieldClass::Shared& lastTranslatedLibFc) + { + BT_ASSERT(lastTranslatedLibFc); + return traceCls.libCls()->createOptionWithoutSelectorFieldLocationFieldClass( + *lastTranslatedLibFc); + } + + static RetWith mip1With(TraceCls& traceCls, OptionalWithSIntSelFc& fc, + const bt2::FieldClass::Shared& lastTranslatedLibFc, + const bt2::ConstFieldLocation libFieldLoc) + { + BT_ASSERT(lastTranslatedLibFc); + return traceCls.libCls()->createOptionWithSignedIntegerSelectorFieldLocationFieldClass( + *lastTranslatedLibFc, libFieldLoc, + *libIntRangeSetFromIntRangeSet(fc.selFieldRanges())); + } + }; + + struct _CreateLibVariantWithUIntSelFcFuncs final + { + using FcParam = VariantWithUIntSelFc; + using RetWithout = bt2::VariantFieldClass::Shared; + using RetWith = bt2::VariantWithUnsignedIntegerSelectorFieldClass::Shared; + + static RetWithout mip0Without(TraceCls& traceCls, FcParam&, const bt2::FieldClass::Shared&) + { + return traceCls.libCls()->createVariantFieldClass(); + } + + static RetWith mip0With(TraceCls& traceCls, FcParam&, const bt2::FieldClass::Shared&, + const bt2::FieldClass libDepFc) + { + return traceCls.libCls()->createVariantWithUnsignedIntegerSelectorFieldClass( + libDepFc.asInteger()); + } + + static RetWithout mip1Without(TraceCls& traceCls, FcParam&, const bt2::FieldClass::Shared&) + { + return traceCls.libCls()->createVariantWithoutSelectorFieldLocationFieldClass(); + } + + static RetWith mip1With(TraceCls& traceCls, FcParam&, const bt2::FieldClass::Shared&, + const bt2::ConstFieldLocation libFieldLoc) + { + return traceCls.libCls() + ->createVariantWithUnsignedIntegerSelectorFieldLocationFieldClass(libFieldLoc); + } + }; + + struct _CreateLibVariantWithSIntSelFcFuncs final + { + using FcParam = VariantWithSIntSelFc; + using RetWithout = bt2::VariantFieldClass::Shared; + using RetWith = bt2::VariantWithSignedIntegerSelectorFieldClass::Shared; + + static RetWithout mip0Without(TraceCls& traceCls, FcParam&, const bt2::FieldClass::Shared&) + { + return traceCls.libCls()->createVariantFieldClass(); + } + + static RetWith mip0With(TraceCls& traceCls, FcParam&, const bt2::FieldClass::Shared&, + const bt2::FieldClass libDepFc) + { + return traceCls.libCls()->createVariantWithSignedIntegerSelectorFieldClass( + libDepFc.asInteger()); + } + + static RetWithout mip1Without(TraceCls& traceCls, FcParam&, const bt2::FieldClass::Shared&) + { + return traceCls.libCls()->createVariantWithoutSelectorFieldLocationFieldClass(); + } + + static RetWith mip1With(TraceCls& traceCls, FcParam&, const bt2::FieldClass::Shared&, + const bt2::ConstFieldLocation libFieldLoc) + { + return traceCls.libCls()->createVariantWithSignedIntegerSelectorFieldLocationFieldClass( + libFieldLoc); + } + }; + + /* + * Finishes translating a dynamic field class `fc` to its trace IR + * equivalent using, depending on the effective MIP version and on + * the dependencies, one of the following static methods + * of `CreateLibFcFuncsT`: + * + * mip0Without(): + * Creates and returns a shared dynamic field class for MIP 0 + * without a length/selector. + * + * mip0With(): + * Creates and returns a shared dynamic field class for MIP 0 + * with a length/selector (libbabeltrace2 uses a single field + * class to deduce the field path). + * + * mip1Without(): + * Creates and returns a shared dynamic field class for MIP 1+ + * without a length/selector. + * + * mip1With(): + * Creates and returns a shared dynamic field class for MIP 1+ + * without a length/selector (libbabeltrace2 uses a + * field location). + * + * This method template always calls _setLibFc(). Therefore, after + * calling this method template, you may modify the created trace IR + * dynamic field class through `_mLastTranslatedLibFc`. + */ + template + void _finishTranslateDynFc(FcT& fc, const FieldLoc& fieldLoc) + { + if (_mMipVersion == 0) { + /* MIP 0 only knows field paths */ + BT_ASSERT(fc.keyFcs().size() == 1); + + const auto keyFc = *fc.keyFcs().begin(); + + if (keyFc->libCls()) { + this->_setLibFc(fc, CreateLibFcFuncsT::mip0With( + *_mTraceCls, fc, _mLastTranslatedLibFc, *keyFc->libCls())); + } else { + /* + * Length/selector field class has no trace IR + * translation: translate to a field class without a + * length/selector field. + */ + this->_setLibFc( + fc, CreateLibFcFuncsT::mip0Without(*_mTraceCls, fc, _mLastTranslatedLibFc)); + } + } else { + /* MIP 1+ only knows field locations */ + if (const auto libFieldLoc = this->_libFieldLocFromFieldLoc(fieldLoc)) { + this->_setLibFc(fc, CreateLibFcFuncsT::mip1With( + *_mTraceCls, fc, _mLastTranslatedLibFc, *libFieldLoc)); + } else { + this->_setLibFc( + fc, CreateLibFcFuncsT::mip1Without(*_mTraceCls, fc, _mLastTranslatedLibFc)); + } + } + } + + void _visit(ArrayFc& arrayFc) + { + arrayFc.elemFc().accept(*this); + } + + void _visit(OptionalFc& optFc) + { + optFc.fc().accept(*this); + } + + template + void _appendLibVariantFcOpts(VariantFc& fc) + { + auto libVariantFc = _mLastTranslatedLibFc->asVariant(); + + for (auto& opt : fc.opts()) { + if (opt.fc().libCls()) { + /* Translated to trace IR */ + if (libVariantFc.isVariantWithoutSelector()) { + libVariantFc.asVariantWithoutSelector().appendOption(opt.name(), + *opt.fc().libCls()); + } else { + libVariantFc.as().appendOption( + opt.name(), *opt.fc().libCls(), + *libIntRangeSetFromIntRangeSet(opt.selFieldRanges())); + } + + /* Set user attributes of option, if any */ + if (_mMipVersion >= 1 && opt.attrs()) { + libVariantFc[libVariantFc.length() - 1].userAttributes(*opt.attrs()); + } + } + } + } + + template + void _visitVariantFc(VariantFcT& fc) + { + /* + * Translate options first. + * + * If all options have no translation, then `fc` has + * no translation. + * + * The only purpose of `libOpts` is to keep the translated field + * classes alive until we append the options to the translated + * variant field class (when calling _appendLibVariantFcOpts() + * at the end of this method). + */ + std::vector libOpts; + + for (auto& opt : fc.opts()) { + /* Try to translate field class of option */ + opt.fc().accept(*this); + + /* + * `_mLastTranslatedLibFc` is the field class of + * this option. + * + * If it's not set, then the option itself has no trace + * IR translation. + */ + if (!_mLastTranslatedLibFc) { + continue; + } + + /* Keep this option */ + libOpts.emplace_back(std::move(_mLastTranslatedLibFc)); + _mLastTranslatedLibFc.reset(); + } + + if (libOpts.empty()) { + /* No options mean no trace IR translation */ + return; + } + + /* Finish translation */ + this->_finishTranslateDynFc(fc, fc.selFieldLoc()); + + /* Finally, append options */ + this->_appendLibVariantFcOpts(fc); + } + + TraceCls *_mTraceCls; + unsigned long long _mMipVersion; + bt2::FieldClass::Shared _mLastTranslatedLibFc; +}; + +/* + * Returns the equivalent trace IR field class of `fc` within `traceCls` + * considering the effective MIP version `mipVersion`. + * + * If the return value of this function is set, then for all the field + * classes recursively contained in `fc` which have an equivalent trace + * IR field class, Fc::libCls() returns it. + */ +bt2::FieldClass::Shared libFcFromFc(TraceCls& traceCls, const unsigned long long mipVersion, Fc& fc) +{ + LibFcFromFcTranslator translator {traceCls, mipVersion}; + + fc.accept(translator); + return translator.libFc(); +} + +/* + * Helper containing context to implement libTraceClsFromTraceCls(). + */ +class LibTraceClsFromTraceClsTranslator final +{ +public: + explicit LibTraceClsFromTraceClsTranslator(TraceCls& traceCls, + const bt2::SelfComponent selfComp) : + _mTraceCls {&traceCls}, + _mSelfComp {selfComp}, _mMipVersion {selfComp.graphMipVersion()} + { + /* Translate whole trace class */ + this->_translate(); + } + +private: + /* + * Translates the scope field class `structFc` and returns the + * corresponding trace IR structure field class. + */ + bt2::StructureFieldClass::Shared _translate(StructFc& structFc) + { + /* Translate */ + auto libFc = libFcFromFc(*_mTraceCls, _mMipVersion, structFc); + + /* libFcFromFc() always translates a structure field class */ + BT_ASSERT(libFc); + return libFc->asStructure().shared(); + } + + static constexpr const char *_btUserAttrsNs = "babeltrace.org,2020"; + static constexpr const char *_lttngUserAttrsNs = "lttng.org,2009"; + + static bt2::OptionalBorrowedObject + _userAttr(const bt2::ConstMapValue userAttrs, const char * const ns, + const char * const name) noexcept + { + const auto nsMapVal = userAttrs[ns]; + + if (!nsMapVal || !nsMapVal->isMap()) { + return {}; + } + + return nsMapVal->asMap()[name]; + } + + static bt2::OptionalBorrowedObject + _strUserAttr(const bt2::ConstMapValue userAttrs, const char * const ns, + const char * const name) noexcept + { + const auto val = LibTraceClsFromTraceClsTranslator::_userAttr(userAttrs, ns, name); + + if (!val || !val->isString()) { + return {}; + } + + return val->asString(); + } + + static bt2::OptionalBorrowedObject + _strUserAttr(const bt2::ConstMapValue userAttrs, const char * const name) noexcept + { + if (const auto val = LibTraceClsFromTraceClsTranslator::_strUserAttr( + userAttrs, LibTraceClsFromTraceClsTranslator::_btUserAttrsNs, name)) { + /* From Babeltrace 2 namespace */ + return val; + } + + /* From LTTng namespace */ + return LibTraceClsFromTraceClsTranslator::_strUserAttr( + userAttrs, LibTraceClsFromTraceClsTranslator::_lttngUserAttrsNs, name); + } + + /* + * Translates `eventRecordCls`, adding it to the current data stream + * class if missing. + */ + void _translate(EventRecordCls& eventRecordCls, DataStreamCls& dataStreamCls) + { + if (eventRecordCls.libCls()) { + /* Already done */ + return; + } + + /* Create the trace IR event record class */ + auto libEventRecordCls = dataStreamCls.libCls()->createEventClass(eventRecordCls.id()); + + eventRecordCls.libCls(*libEventRecordCls); + + /* Set namespace (MIP 1+) */ + if (_mMipVersion >= 1 && eventRecordCls.ns()) { + libEventRecordCls->nameSpace(*eventRecordCls.ns()); + } + + /* Set name */ + if (eventRecordCls.name()) { + libEventRecordCls->name(*eventRecordCls.name()); + } + + /* Set UID (MIP 1+) */ + if (_mMipVersion >= 1 && eventRecordCls.uid()) { + libEventRecordCls->uid(*eventRecordCls.uid()); + } + + /* Set log level and EMF URI */ + if (eventRecordCls.attrs()) { + /* Set log level */ + if (const auto userAttr = this->_strUserAttr(*eventRecordCls.attrs(), "log-level")) { + const auto logLevel = bt2c::call([&userAttr]() + -> bt2s::optional { + if (userAttr->value() == MetadataStreamParser::logLevelEmergencyName) { + return bt2::EventClassLogLevel::Emergency; + } else if (userAttr->value() == MetadataStreamParser::logLevelAlertName) { + return bt2::EventClassLogLevel::Alert; + } else if (userAttr->value() == MetadataStreamParser::logLevelCriticalName) { + return bt2::EventClassLogLevel::Critical; + } else if (userAttr->value() == MetadataStreamParser::logLevelErrorName) { + return bt2::EventClassLogLevel::Error; + } else if (userAttr->value() == MetadataStreamParser::logLevelWarningName) { + return bt2::EventClassLogLevel::Warning; + } else if (userAttr->value() == MetadataStreamParser::logLevelNoticeName) { + return bt2::EventClassLogLevel::Notice; + } else if (userAttr->value() == MetadataStreamParser::logLevelInfoName) { + return bt2::EventClassLogLevel::Info; + } else if (userAttr->value() == MetadataStreamParser::logLevelDebugSystemName) { + return bt2::EventClassLogLevel::DebugSystem; + } else if (userAttr->value() == + MetadataStreamParser::logLevelDebugProgramName) { + return bt2::EventClassLogLevel::DebugProgram; + } else if (userAttr->value() == + MetadataStreamParser::logLevelDebugProcessName) { + return bt2::EventClassLogLevel::DebugProcess; + } else if (userAttr->value() == MetadataStreamParser::logLevelDebugModuleName) { + return bt2::EventClassLogLevel::DebugModule; + } else if (userAttr->value() == MetadataStreamParser::logLevelDebugUnitName) { + return bt2::EventClassLogLevel::DebugUnit; + } else if (userAttr->value() == + MetadataStreamParser::logLevelDebugFunctionName) { + return bt2::EventClassLogLevel::DebugFunction; + } else if (userAttr->value() == MetadataStreamParser::logLevelDebugLineName) { + return bt2::EventClassLogLevel::DebugLine; + } else if (userAttr->value() == MetadataStreamParser::logLevelDebugName) { + return bt2::EventClassLogLevel::Debug; + } + + return {}; + }); + + if (logLevel) { + libEventRecordCls->logLevel(*logLevel); + } + } + + /* Set EMF URI */ + if (const auto userAttr = this->_strUserAttr(*eventRecordCls.attrs(), "emf-uri")) { + libEventRecordCls->emfUri(userAttr->value().data()); + } + } + + /* Set user attributes */ + trySetLibUserAttrs(eventRecordCls, _mMipVersion); + + /* Translate specific context field class, if any */ + if (eventRecordCls.specCtxFc()) { + libEventRecordCls->specificContextFieldClass( + *this->_translate(*eventRecordCls.specCtxFc())); + } + + /* Translate payload field class, if any */ + if (eventRecordCls.payloadFc()) { + libEventRecordCls->payloadFieldClass(*this->_translate(*eventRecordCls.payloadFc())); + } + } + + /* + * Translates `clkCls` if not already done. + */ + void _translate(ClkCls& clkCls) + { + if (clkCls.libCls()) { + /* Already done */ + return; + } + + /* Create trace IR clock class */ + clkCls.sharedLibCls(_mSelfComp.createClockClass()); + + /* Set frequency */ + clkCls.libCls()->frequency(clkCls.freq()); + + /* Set namespace (MIP 1+) */ + if (_mMipVersion >= 1 && clkCls.ns()) { + clkCls.libCls()->nameSpace(*clkCls.ns()); + } + + /* Set name */ + if (clkCls.name()) { + clkCls.libCls()->name(*clkCls.name()); + } + + /* Set UID (MIP 1+)*/ + if (_mMipVersion >= 1 && clkCls.uid()) { + clkCls.libCls()->uid(*clkCls.uid()); + } + + /* Set UUID (MIP 0) */ + if (_mMipVersion == 0 && clkCls.uid()) { + /* + * MIP 0 means only CTF 1.8; therefore the UID _is_ a + * UUID string. + */ + clkCls.libCls()->uuid(bt2c::Uuid {*clkCls.uid()}); + } + + /* Set offset from origin */ + clkCls.libCls()->offsetFromOrigin(bt2::ClockOffset {clkCls.offsetFromOrigin().seconds(), + clkCls.offsetFromOrigin().cycles()}); + + /* Set origin */ + if (clkCls.origin()) { + if (clkCls.origin()->isUnixEpoch()) { + /* Unix epoch */ + clkCls.libCls()->setOriginIsUnixEpoch(); + } else if (_mMipVersion >= 1) { + /* Custom (MIP 1+) */ + clkCls.libCls()->origin(clkCls.origin()->ns() ? *clkCls.origin()->ns() : nullptr, + clkCls.origin()->name(), clkCls.origin()->uid()); + } + } else { + /* Unknown */ + clkCls.libCls()->setOriginIsUnknown(); + } + + /* Set precision */ + if (clkCls.precision()) { + clkCls.libCls()->precision(*clkCls.precision()); + } + + /* Set accuracy (MIP 1+) */ + if (clkCls.accuracy()) { + clkCls.libCls()->accuracy(*clkCls.accuracy()); + } + + /* Set description */ + if (clkCls.descr()) { + clkCls.libCls()->description(*clkCls.descr()); + } + + /* Set user attributes */ + trySetLibUserAttrs(clkCls, _mMipVersion); + } + + /* + * Translates `dataStreamCls`, adding it to `_mTraceCls->libCls()` + * if missing. + * + * Also tries to translate all the contained event record classes. + */ + void _translate(DataStreamCls& dataStreamCls) + { + if (!dataStreamCls.libCls()) { + /* Create the trace IR data stream class */ + auto libDataStreamCls = _mTraceCls->libCls()->createStreamClass(dataStreamCls.id()); + + dataStreamCls.libCls(*libDataStreamCls); + + /* Set namespace (MIP 1+) */ + if (_mMipVersion >= 1 && dataStreamCls.ns()) { + libDataStreamCls->nameSpace(*dataStreamCls.ns()); + } + + /* Set name */ + if (dataStreamCls.name()) { + libDataStreamCls->name(*dataStreamCls.name()); + } + + /* Set UID (MIP 1+) */ + if (_mMipVersion >= 1 && dataStreamCls.uid()) { + libDataStreamCls->uid(*dataStreamCls.uid()); + } + + /* Set default clock class, making sure it's translated */ + if (dataStreamCls.defClkCls()) { + this->_translate(*dataStreamCls.defClkCls()); + libDataStreamCls->defaultClockClass(*dataStreamCls.defClkCls()->libCls()); + } + + /* We're working with our own event record class IDs */ + libDataStreamCls->assignsAutomaticEventClassId(false); + + /* We're working with our own data stream IDs */ + libDataStreamCls->assignsAutomaticStreamId(false); + + /* We always support packets */ + libDataStreamCls->supportsPackets( + true, pktCtxFcContainsUIntFcWithRole(dataStreamCls, UIntFieldRole::DefClkTs), + pktCtxFcContainsUIntFcWithRole(dataStreamCls, UIntFieldRole::PktEndDefClkTs)); + + if (pktCtxFcContainsUIntFcWithRole(dataStreamCls, + UIntFieldRole::DiscEventRecordCounterSnap)) { + /* Set that there's discarded event record support */ + libDataStreamCls->supportsDiscardedEvents(true, dataStreamCls.defClkCls()); + } + + if (pktCtxFcContainsUIntFcWithRole(dataStreamCls, UIntFieldRole::PktSeqNum)) { + /* Set that there's discarded packet support */ + libDataStreamCls->supportsDiscardedPackets(true, dataStreamCls.defClkCls()); + } + + /* Set user attributes */ + trySetLibUserAttrs(dataStreamCls, _mMipVersion); + + /* Translate packet context field class, if any */ + if (dataStreamCls.pktCtxFc()) { + const auto fc = this->_translate(*dataStreamCls.pktCtxFc()); + + if (fc->length() > 0) { + libDataStreamCls->packetContextFieldClass(*fc); + } + } + + /* Translate common event record context field class, if any */ + if (dataStreamCls.commonEventRecordCtxFc()) { + libDataStreamCls->commonEventContextFieldClass( + *this->_translate(*dataStreamCls.commonEventRecordCtxFc())); + } + } + + /* Translate event record classes */ + for (auto& eventRecordCls : dataStreamCls) { + this->_translate(*eventRecordCls, dataStreamCls); + } + } + + /* + * Translate `*_mTraceCls`, setting `_mTraceCls->libCls()` + * if missing. + * + * Also tries to translate all the contained data stream classes. + */ + void _translate() + { + if (!_mTraceCls->libCls()) { + /* Create trace IR trace class */ + _mTraceCls->sharedLibCls(_mSelfComp.createTraceClass()); + + /* We're working with our own data stream class IDs */ + _mTraceCls->libCls()->assignsAutomaticStreamClassId(false); + + /* Set user attributes */ + trySetLibUserAttrs(*_mTraceCls, _mMipVersion); + } + + /* Translate data stream classes */ + for (auto& dataStreamCls : *_mTraceCls) { + this->_translate(*dataStreamCls); + } + } + + /* Trace class on which we're working */ + TraceCls *_mTraceCls; + + /* Our source component */ + bt2::SelfComponent _mSelfComp; + + /* Effective MIP version */ + unsigned long long _mMipVersion; +}; + +/* + * Returns the number clock cycles which correspond to `ns` ns + * considering the frequency `freq` Hz. + */ +unsigned long long cyclesFromNs(const unsigned long long freq, const unsigned long long ns) +{ + return (freq == 1000000000ULL) ? + ns : + static_cast( + (static_cast(ns) * static_cast(freq)) / 1e9); +} + +/* + * Normalizes the offset of `clkCls` so that the cycle part is less than + * the frequency of `clkCls`. + */ +void normalizeClkClsOffsetFromOrigin(ClkCls& clkCls) noexcept +{ + const auto offsetParts = normalizeClkOffset(clkCls.offsetFromOrigin().seconds(), + clkCls.offsetFromOrigin().cycles(), clkCls.freq()); + + clkCls.offsetFromOrigin(ClkOffset {offsetParts.first, offsetParts.second}); +} + +} /* namespace */ + +const char * const MetadataStreamParser::logLevelEmergencyName = "emergency"; +const char * const MetadataStreamParser::logLevelAlertName = "alert"; +const char * const MetadataStreamParser::logLevelCriticalName = "critical"; +const char * const MetadataStreamParser::logLevelErrorName = "error"; +const char * const MetadataStreamParser::logLevelWarningName = "warning"; +const char * const MetadataStreamParser::logLevelNoticeName = "notice"; +const char * const MetadataStreamParser::logLevelInfoName = "info"; +const char * const MetadataStreamParser::logLevelDebugSystemName = "debug:system"; +const char * const MetadataStreamParser::logLevelDebugProgramName = "debug:program"; +const char * const MetadataStreamParser::logLevelDebugProcessName = "debug:process"; +const char * const MetadataStreamParser::logLevelDebugModuleName = "debug:module"; +const char * const MetadataStreamParser::logLevelDebugUnitName = "debug:unit"; +const char * const MetadataStreamParser::logLevelDebugFunctionName = "debug:function"; +const char * const MetadataStreamParser::logLevelDebugLineName = "debug:line"; +const char * const MetadataStreamParser::logLevelDebugName = "debug"; + +MetadataStreamParser::MetadataStreamParser( + const bt2::OptionalBorrowedObject selfComp, + const ClkClsCfg& clkClsCfg) noexcept : + _mClkClsCfg(clkClsCfg), + _mSelfComp {selfComp} +{ +} + +void MetadataStreamParser::parseSection(const bt2c::ConstBytes buffer) +{ + this->_parseSection(buffer); + this->_finalizeTraceCls(); +} + +void MetadataStreamParser::_adjustClkClsOffsetFromOrigin(ClkCls& clkCls) noexcept +{ + auto offsetSeconds = static_cast(_mClkClsCfg.offsetSec); + auto offsetNs = static_cast(_mClkClsCfg.offsetNanoSec); + + if (offsetSeconds == 0 && offsetNs == 0) { + return; + } + + /* Transfer nanoseconds to seconds as much as possible */ + { + static constexpr auto nsPerSecond = 1000000000LL; + + if (offsetNs < 0) { + const auto absNs = -offsetNs; + const auto absExtraSeconds = absNs / nsPerSecond + 1; + const auto extraSeconds = -absExtraSeconds; + + offsetNs -= extraSeconds * nsPerSecond; + BT_ASSERT(offsetNs > 0); + offsetSeconds += extraSeconds; + } else { + const auto extraSeconds = offsetNs / nsPerSecond; + + offsetNs -= (extraSeconds * nsPerSecond); + BT_ASSERT(offsetNs >= 0); + offsetSeconds += extraSeconds; + } + } + + /* Set final offsets */ + clkCls.offsetFromOrigin( + ClkOffset {clkCls.offsetFromOrigin().seconds() + offsetSeconds, + clkCls.offsetFromOrigin().cycles() + cyclesFromNs(clkCls.freq(), offsetNs)}); +} + +void MetadataStreamParser::_adjustClkCls(ClkCls& clkCls) noexcept +{ + if (_mClkClsCfg.forceOriginIsUnixEpoch) { + clkCls.origin(ClkOrigin {}); + } + + this->_adjustClkClsOffsetFromOrigin(clkCls); +} + +void MetadataStreamParser::_finalizeTraceCls() +{ + if (!_mTraceCls) { + /* No trace class yet */ + return; + } + + /* + * Set the key value saving indexes of key field classes and the + * saved key value index of dependent (dynamic-length, optional, and + * variant) field classes. + */ + setSavedKeyValIndexes(*_mTraceCls); + + /* Adjust clock classes, if needed */ + for (const auto& dataStreamCls : *_mTraceCls) { + const auto clkCls = dataStreamCls->defClkCls(); + + if (!clkCls) { + /* Data stream class has no default clock class */ + continue; + } + + if (_mAdjustedClkClasses.find(clkCls) != _mAdjustedClkClasses.end()) { + /* Already done */ + continue; + } + + /* Adjust and normalize */ + this->_adjustClkCls(*clkCls); + normalizeClkClsOffsetFromOrigin(*clkCls); + + /* This one is now done */ + _mAdjustedClkClasses.insert(clkCls); + } + + /* Translates CTF IR objects to their trace IR equivalents */ + if (_mSelfComp) { + LibTraceClsFromTraceClsTranslator {*_mTraceCls, *_mSelfComp}; + } +} + +} /* namespace src */ +} /* namespace ctf */ diff --git a/src/plugins/ctf/common/src/metadata/metadata-stream-parser.hpp b/src/plugins/ctf/common/src/metadata/metadata-stream-parser.hpp new file mode 100644 index 00000000..7ea2f73c --- /dev/null +++ b/src/plugins/ctf/common/src/metadata/metadata-stream-parser.hpp @@ -0,0 +1,164 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (c) 2022-2024 Philippe Proulx + */ + +#ifndef BABELTRACE_PLUGINS_CTF_COMMON_SRC_METADATA_METADATA_STREAM_PARSER_HPP +#define BABELTRACE_PLUGINS_CTF_COMMON_SRC_METADATA_METADATA_STREAM_PARSER_HPP + +#include +#include +#include + +#include "cpp-common/bt2/self-component-port.hpp" +#include "cpp-common/bt2c/aliases.hpp" +#include "cpp-common/bt2c/uuid.hpp" + +#include "../clk-cls-cfg.hpp" +#include "ctf-ir.hpp" + +namespace ctf { +namespace src { + +/* + * Abstract base CTF metadata stream parser class. + */ +class MetadataStreamParser +{ +public: + using UP = std::unique_ptr; + + /* + * Common return type of a static parse() method of a derived class. + */ + struct ParseRet final + { + std::unique_ptr traceCls; + bt2s::optional uuid; + }; + +protected: + explicit MetadataStreamParser(bt2::OptionalBorrowedObject selfComp, + const ClkClsCfg& clkClsCfg) noexcept; + +public: + virtual ~MetadataStreamParser() = default; + + /* + * Parses the section of metadata stream in `buffer` possibly + * creating or updating the current trace class (as returned + * by traceCls()). + */ + void parseSection(bt2c::ConstBytes buffer); + + /* + * Current trace class, or `nullptr` if none exists at this point. + */ + const TraceCls *traceCls() const noexcept + { + return _mTraceCls.get(); + } + + /* + * Releases the current trace class. + */ + std::unique_ptr releaseTraceCls() noexcept + { + return std::move(_mTraceCls); + } + + /* + * Current metadata stream UUID, or `bt2s::nullopt` if none exists + * at this point. + */ + const bt2s::optional& metadataStreamUuid() const noexcept + { + return _mMetadataStreamUuid; + } + + /* Log level names for user attributes */ + static const char * const logLevelEmergencyName; + static const char * const logLevelAlertName; + static const char * const logLevelCriticalName; + static const char * const logLevelErrorName; + static const char * const logLevelWarningName; + static const char * const logLevelNoticeName; + static const char * const logLevelInfoName; + static const char * const logLevelDebugSystemName; + static const char * const logLevelDebugProgramName; + static const char * const logLevelDebugProcessName; + static const char * const logLevelDebugModuleName; + static const char * const logLevelDebugUnitName; + static const char * const logLevelDebugFunctionName; + static const char * const logLevelDebugLineName; + static const char * const logLevelDebugName; + +protected: + /* + * Self component access for derived classes. + */ + bt2::OptionalBorrowedObject _selfComp() const noexcept + { + return _mSelfComp; + } + +private: + virtual void _parseSection(bt2c::ConstBytes buffer) = 0; + + /* + * Finalizes `*_mTraceCls` after its creation or when it gets new + * data stream classes or event record classes. + * + * This function: + * + * • Sets the key value saving indexes of key field classes and the + * saved key value index of dependent (dynamic-length, optional, + * and variant) field classes. + * + * • Reconfigures the clock classes of `*_mTraceCls` + * using `_mClkClsCfg`. + * + * • Normalizes the offsets of the clock classes of `*_mTraceCls` so + * that the cycle part is less than the frequency. + * + * • If `_mSelfComp` exists, then translates the contained objects + * to their trace IR equivalents. + */ + void _finalizeTraceCls(); + + /* + * Applies the clock offset of `_mClkClsCfg` to `clkCls`. + */ + void _adjustClkClsOffsetFromOrigin(ClkCls& clkCls) noexcept; + + /* + * Reconfigures `clkCls` using `_mClkClsCfg`. + */ + void _adjustClkCls(ClkCls& clkCls) noexcept; + +protected: + /* Trace class */ + std::unique_ptr _mTraceCls; + + /* Metadata stream UUID */ + bt2s::optional _mMetadataStreamUuid; + +private: + /* Clock class configuration */ + ClkClsCfg _mClkClsCfg; + + /* + * Clock classes to which we have already applied the config and + * normalized. + */ + std::unordered_set _mAdjustedClkClasses; + + /* Self component, used to finalize `*_mTraceCls` */ + bt2::OptionalBorrowedObject _mSelfComp; +}; + +} /* namespace src */ +} /* namespace ctf */ + +#endif /* BABELTRACE_PLUGINS_CTF_COMMON_SRC_METADATA_METADATA_STREAM_PARSER_HPP */