From af6b76faf16e949af1fd9639eb1303c58980e2a6 Mon Sep 17 00:00:00 2001 From: Simon Marchi Date: Wed, 22 May 2024 12:00:59 -0400 Subject: [PATCH] `ctf` plugin: add `ctf::src::MsgIter` class MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Original patch by Simon. Philippe's changes over original patch (no functional change intended): • Conform to the Babeltrace 2 coding style for new C++11 code and to some naming conventions of the common part of the `ctf` plugin. • Add many public and internal comments. • Rename `Quirks` class to `MsgIterQuirks`. Also, rename the quirks to the generic `pktEndDefClkValZero`, `eventRecordDefClkValGtNextPktBeginDefClkVal`, and `eventRecordDefClkValLtPktBeginDefClkVal` names to decouple this API from specific tracers. • Remove `MsgIterItemVisitor`. Instead, MsgIter::_handleItem() dispatches the item to a specific handling method based on its numeric type. Move the relevant members of `MsgIterItemVisitor` into `MsgIter` itself. • In `_StackFrame`, rename everything named "field" to "sub-field" so as to remove some ambiguity. • Add MsgIter::_stackTop*() helper methods which defer to the corresponding method on the top stack frame. • Add MsgIter::_stackPush() and MsgIter::_stackPop() helper methods. • Add MsgIter::_addMsgToQueue() helper method. • Only log item details when the `TRACE` level is enabled. • Handle `PktMagicNumberItem`: validate the magic number value. • Handle `MetadataStreamUuidItem`: validate the UUID. The `MsgIter` constructor now accepts an `expectedMetadataStreamUuid` parameter (optional UUID) which is the expected metadata stream UUID to compare to. • Handle variable-length integer field items, adding the MsgIter::_handleUIntFieldItem() and MsgIter::_handleSIntFieldItem() helper method templates to do so. • Handle BLOB field items. A new data member, `_mCurBlobFieldDataOffset`, tracks the current BLOB field data offset to write to when handling the next `BlobFieldSectionItem` (we can't append to a BLOB field like when handling `StrFieldSubstrItem`). • Handle optional field items, adding a member to `MsgIter::_StackFrame::_Field`. • In MsgIter::_StackFrame::goToNextSubField(), unconditionally increment `_mSubFieldIndex`: curSubField() doesn't care about `_mSubFieldIndex` for the variant/option field cases anyway. Therefore, make curSubFieldAndGoToNextSubField() reuse curSubField() and goToNextSubField() to remove some almost duplicate code. • Handle UTF-16 and UTF-32 string data. Change-Id: If2dd82b43f4d19a16ec4420c13c9a3686034766d Reviewed-on: https://review.lttng.org/c/babeltrace/+/8227 Reviewed-by: Philippe Proulx Signed-off-by: Simon Marchi Reviewed-on: https://review.lttng.org/c/babeltrace/+/12716 --- src/Makefile.am | 5 +- src/cpp-common/bt2c/uuid.hpp | 5 + src/plugins/ctf/common/src/msg-iter.cpp | 904 ++++++++++++++++++++++++ src/plugins/ctf/common/src/msg-iter.hpp | 598 ++++++++++++++++ 4 files changed, 1511 insertions(+), 1 deletion(-) create mode 100644 src/plugins/ctf/common/src/msg-iter.cpp create mode 100644 src/plugins/ctf/common/src/msg-iter.hpp diff --git a/src/Makefile.am b/src/Makefile.am index 1fffd088..dcbc8700 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -703,6 +703,8 @@ plugins_ctf_babeltrace_plugin_ctf_la_SOURCES = \ plugins/ctf/common/src/metadata/ctf-ir.hpp \ plugins/ctf/common/src/metadata/tsdl/metadata-stream-decoder.cpp \ plugins/ctf/common/src/metadata/tsdl/metadata-stream-decoder.hpp \ + plugins/ctf/common/src/msg-iter.cpp \ + plugins/ctf/common/src/msg-iter.hpp \ plugins/ctf/common/src/msg-iter/msg-iter.cpp \ plugins/ctf/common/src/msg-iter/msg-iter.hpp \ plugins/ctf/common/src/null-cp-finder.hpp \ @@ -749,7 +751,8 @@ plugins_ctf_babeltrace_plugin_ctf_la_LDFLAGS = \ plugins_ctf_babeltrace_plugin_ctf_la_LIBADD = \ plugins/ctf/common/metadata/libctf-parser.la \ plugins/ctf/common/metadata/libctf-ast.la \ - plugins/common/param-validation/libparam-validation.la + plugins/common/param-validation/libparam-validation.la \ + cpp-common/libcpp-common.la if BABELTRACE_BUILD_WITH_MINGW plugins_ctf_babeltrace_plugin_ctf_la_LIBADD += -lws2_32 diff --git a/src/cpp-common/bt2c/uuid.hpp b/src/cpp-common/bt2c/uuid.hpp index 30f30144..3bc1d7a4 100644 --- a/src/cpp-common/bt2c/uuid.hpp +++ b/src/cpp-common/bt2c/uuid.hpp @@ -248,6 +248,11 @@ inline UuidView::operator Uuid() const noexcept return Uuid {*this}; } +static inline std::string format_as(const bt2c::Uuid& uuid) +{ + return uuid.str(); +} + } /* namespace bt2c */ #endif /* BABELTRACE_CPP_COMMON_BT2C_UUID_HPP */ diff --git a/src/plugins/ctf/common/src/msg-iter.cpp b/src/plugins/ctf/common/src/msg-iter.cpp new file mode 100644 index 00000000..36e2088b --- /dev/null +++ b/src/plugins/ctf/common/src/msg-iter.cpp @@ -0,0 +1,904 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (c) 2022 Simon Marchi + * Copyright (c) 2015-2024 Philippe Proulx + */ + +#include + +#include "common/assert.h" +#include "common/common.h" +#include "cpp-common/bt2/message.hpp" +#include "cpp-common/bt2/trace-ir.hpp" +#include "cpp-common/bt2c/aliases.hpp" +#include "cpp-common/bt2c/call.hpp" +#include "cpp-common/bt2c/fmt.hpp" +#include "cpp-common/vendor/fmt/format.h" + +#include "item-seq/item.hpp" +#include "msg-iter.hpp" +#include "plugins/ctf/common/src/metadata/ctf-ir.hpp" + +namespace ctf { +namespace src { + +using namespace bt2c::literals::datalen; + +MsgIter::MsgIter(const bt2::SelfMessageIterator selfMsgIter, const ctf::src::TraceCls& traceCls, + bt2s::optional expectedMetadataStreamUuid, const bt2::Stream stream, + Medium::UP medium, const MsgIterQuirks& quirks, const bt2c::Logger& parentLogger) : + _mLogger {parentLogger, "PLUGIN/CTF/MSG-ITER"}, + _mSelfMsgIter {selfMsgIter}, _mStream {stream}, + _mExpectedMetadataStreamUuid {std::move(expectedMetadataStreamUuid)}, _mQuirks {quirks}, + _mItemSeqIter {std::move(medium), traceCls, _mLogger}, _mUnicodeConv {_mLogger}, + _mLoggingVisitor {"Handling item", _mLogger} +{ + BT_CPPLOGD("Created CTF plugin message iterator: " + "addr={}, trace-cls-addr={}, log-level={}", + fmt::ptr(this), fmt::ptr(&traceCls), _mLogger.level()); +} + +bt2::ConstMessage::Shared MsgIter::next() +{ + BT_CPPLOGD("Getting next message: addr={}", fmt::ptr(this)); + + if (_mIsDone) { + return bt2::ConstMessage::Shared {}; + } + + /* + * Return any message that's already in the queue (one iteration of + * the underlying item sequence iterator may yield more than one + * message, but we return one at a time). + */ + if (auto msg = this->_releaseNextMsg()) { + return msg; + } + + try { + while (true) { + /* + * Get the next item from the underlying item + * sequence iterator. + */ + if (const auto item = _mItemSeqIter.next()) { + /* Handle item if needed */ + if (!_mSkipItemsUntilScopeEndItem || item->isScopeEnd()) { + this->_handleItem(*item); + + if (auto msg = this->_releaseNextMsg()) { + return msg; + } + } + } else { + /* No more items: this is the end! */ + break; + } + } + + /* We're done! */ + _mIsDone = true; + return _mSelfMsgIter.createStreamEndMessage(_mStream); + } catch (const bt2c::Error&) { + BT_CPPLOGE_APPEND_CAUSE_AND_RETHROW("Failed to create the next message: addr={}", + fmt::ptr(this)); + } +} + +void MsgIter::_handleItem(const Item& item) +{ + /* Log item details */ + if (_mLogger.wouldLogT()) { + item.accept(_mLoggingVisitor); + } + + /* Defer to specific handler */ + switch (item.type()) { + case Item::Type::PktBegin: + this->_handleItem(item.asPktBegin()); + break; + case Item::Type::PktEnd: + this->_handleItem(item.asPktEnd()); + break; + case Item::Type::ScopeBegin: + this->_handleItem(item.asScopeBegin()); + break; + case Item::Type::ScopeEnd: + this->_handleItem(item.asScopeEnd()); + break; + case Item::Type::PktContentEnd: + this->_handleItem(item.asPktContentEnd()); + break; + case Item::Type::EventRecordEnd: + this->_handleItem(item.asEventRecordEnd()); + break; + case Item::Type::PktMagicNumber: + this->_handleItem(item.asPktMagicNumber()); + break; + case Item::Type::MetadataStreamUuid: + this->_handleItem(item.asMetadataStreamUuid()); + break; + case Item::Type::DataStreamInfo: + this->_handleItem(item.asDataStreamInfo()); + break; + case Item::Type::PktInfo: + this->_handleItem(item.asPktInfo()); + break; + case Item::Type::EventRecordInfo: + this->_handleItem(item.asEventRecordInfo()); + break; + case Item::Type::FixedLenBitArrayField: + case Item::Type::FixedLenBitMapField: + this->_handleItem(item.asFixedLenBitArrayField()); + break; + case Item::Type::FixedLenBoolField: + this->_handleItem(item.asFixedLenBoolField()); + break; + case Item::Type::FixedLenSIntField: + this->_handleItem(item.asFixedLenSIntField()); + break; + case Item::Type::FixedLenUIntField: + this->_handleItem(item.asFixedLenUIntField()); + break; + case Item::Type::FixedLenFloatField: + this->_handleItem(item.asFixedLenFloatField()); + break; + case Item::Type::VarLenSIntField: + this->_handleItem(item.asVarLenSIntField()); + break; + case Item::Type::VarLenUIntField: + this->_handleItem(item.asVarLenUIntField()); + break; + case Item::Type::NullTerminatedStrFieldBegin: + this->_handleItem(item.asNullTerminatedStrFieldBegin()); + break; + case Item::Type::NullTerminatedStrFieldEnd: + this->_handleItem(item.asNullTerminatedStrFieldEnd()); + break; + case Item::Type::RawData: + this->_handleItem(item.asRawData()); + break; + case Item::Type::StructFieldBegin: + this->_handleItem(item.asStructFieldBegin()); + break; + case Item::Type::StructFieldEnd: + this->_handleItem(item.asStructFieldEnd()); + break; + case Item::Type::StaticLenArrayFieldBegin: + this->_handleItem(item.asStaticLenArrayFieldBegin()); + break; + case Item::Type::StaticLenArrayFieldEnd: + case Item::Type::DynLenArrayFieldEnd: + this->_handleItem(item.asArrayFieldEnd()); + break; + case Item::Type::DynLenArrayFieldBegin: + this->_handleItem(item.asDynLenArrayFieldBegin()); + break; + case Item::Type::StaticLenBlobFieldBegin: + this->_handleItem(item.asStaticLenBlobFieldBegin()); + break; + case Item::Type::StaticLenBlobFieldEnd: + case Item::Type::DynLenBlobFieldEnd: + this->_handleItem(item.asBlobFieldEnd()); + break; + case Item::Type::DynLenBlobFieldBegin: + this->_handleItem(item.asDynLenBlobFieldBegin()); + break; + case Item::Type::StaticLenStrFieldBegin: + case Item::Type::DynLenStrFieldBegin: + this->_handleItem(item.asNonNullTerminatedStrFieldBegin()); + break; + case Item::Type::StaticLenStrFieldEnd: + case Item::Type::DynLenStrFieldEnd: + this->_handleItem(item.asNonNullTerminatedStrFieldEnd()); + break; + case Item::Type::VariantFieldWithSIntSelBegin: + case Item::Type::VariantFieldWithUIntSelBegin: + this->_handleItem(item.asVariantFieldBegin()); + break; + case Item::Type::VariantFieldWithSIntSelEnd: + case Item::Type::VariantFieldWithUIntSelEnd: + this->_handleItem(item.asVariantFieldEnd()); + break; + case Item::Type::OptionalFieldWithBoolSelBegin: + case Item::Type::OptionalFieldWithSIntSelBegin: + case Item::Type::OptionalFieldWithUIntSelBegin: + this->_handleItem(item.asOptionalFieldBegin()); + break; + case Item::Type::OptionalFieldWithBoolSelEnd: + case Item::Type::OptionalFieldWithSIntSelEnd: + case Item::Type::OptionalFieldWithUIntSelEnd: + this->_handleItem(item.asOptionalFieldEnd()); + break; + default: + BT_CPPLOGT("Skipping item."); + return; + } +} + +void MsgIter::_handleItem(const PktBeginItem&) +{ + BT_ASSERT_DBG(!_mCurPkt); + this->_curPkt(_mStream.createPacket()); +} + +bt2::Message::Shared MsgIter::_createPktEndMsgAndUpdateCurDefClkVal() +{ + BT_ASSERT_DBG(_mCurPkt); + + if (_mPktEndDefClkVal) { + const auto pktEndDefClkValZeroBug = _mQuirks.pktEndDefClkValZero && _mPktBeginDefClkVal && + _mPktEndDefClkVal && *_mPktBeginDefClkVal != 0 && + *_mPktEndDefClkVal == 0; + const auto eventRecordDefClkValGtNextPktBeginDefClkValBug = + _mQuirks.eventRecordDefClkValGtNextPktBeginDefClkVal && _mCurDefClkVal && + _mPktEndDefClkVal && *_mPktEndDefClkVal < _mCurDefClkVal; + const auto anyBug = + pktEndDefClkValZeroBug || eventRecordDefClkValGtNextPktBeginDefClkValBug; + const auto defClkVal = anyBug ? *_mCurDefClkVal : *_mPktEndDefClkVal; + + if (!anyBug) { + _mCurDefClkVal = _mPktEndDefClkVal; + } + + return _mSelfMsgIter.createPacketEndMessage(*_mCurPkt, defClkVal); + } else { + return _mSelfMsgIter.createPacketEndMessage(*_mCurPkt); + } +} + +void MsgIter::_handleItem(const PktEndItem&) +{ + BT_ASSERT_DBG(!_mCurMsg); + BT_ASSERT_DBG(_mCurPkt); + + /* Emit a packet beginning message now if required to fix a quirk */ + if (_mDelayPktBeginMsgEmission) { + this->_emitDelayedPktBeginMsg(_mPktEndDefClkVal); + } + + /* Emit a packet end message */ + this->_addMsgToQueue(this->_createPktEndMsgAndUpdateCurDefClkVal()); + + /* No more current packet */ + this->_resetCurPkt(); +} + +void MsgIter::_handleItem(const ScopeBeginItem& item) +{ + BT_ASSERT(_mStack.empty()); + BT_ASSERT(!_mCurScopeField); + + /* Handle specific scope */ + switch (item.scope()) { + case Scope::PktHeader: + /* Nothing needed from the packet header: fast-forward */ + _mSkipItemsUntilScopeEndItem = true; + break; + case Scope::PktCtx: + { + BT_ASSERT_DBG(_mCurPkt); + + if (const auto pktCtxField = _mCurPkt->contextField()) { + _mCurScopeField = pktCtxField; + } else { + /* Nothing needed from the packet context: fast-forward */ + _mSkipItemsUntilScopeEndItem = true; + } + + break; + } + case Scope::EventRecordHeader: + /* Nothing needed from the event record header: fast-forward */ + _mSkipItemsUntilScopeEndItem = true; + break; + case Scope::CommonEventRecordCtx: + { + BT_ASSERT_DBG(_mCurMsg); + + if (const auto commonCtxField = _mCurMsg->asEvent().event().commonContextField()) { + _mCurScopeField = commonCtxField; + } else { + /* Nothing needed from the common context: fast-forward */ + _mSkipItemsUntilScopeEndItem = true; + } + + break; + } + case Scope::SpecEventRecordCtx: + { + BT_ASSERT_DBG(_mCurMsg); + + if (const auto specCtxField = _mCurMsg->asEvent().event().specificContextField()) { + _mCurScopeField = specCtxField; + } else { + /* Nothing needed from the specific context: fast-forward */ + _mSkipItemsUntilScopeEndItem = true; + } + + break; + } + case Scope::EventRecordPayload: + { + BT_ASSERT_DBG(_mCurMsg); + + if (const auto payloadField = _mCurMsg->asEvent().event().payloadField()) { + _mCurScopeField = payloadField; + } else { + /* Nothing needed from the payload: fast-forward */ + _mSkipItemsUntilScopeEndItem = true; + } + + break; + } + default: + bt_common_abort(); + } +} + +void MsgIter::_handleItem(const ScopeEndItem&) +{ + /* + * The last stack frame was removed by the `StructFieldEndItem` + * handler. + */ + BT_ASSERT_DBG(_mStack.empty()); + + /* No more current scope root field */ + _mCurScopeField.reset(); + + /* Reset this flag */ + _mSkipItemsUntilScopeEndItem = false; +} + +void MsgIter::_handleItem(const PktContentEndItem&) +{ + BT_ASSERT_DBG(_mCurPkt); +} + +void MsgIter::_handleItem(const EventRecordEndItem&) +{ + BT_ASSERT_DBG(_mStack.empty()); + BT_ASSERT_DBG(_mCurMsg); + + /* Emit current message (move to message queue) */ + _mMsgs.emplace(std::move(_mCurMsg)); + BT_ASSERT_DBG(!_mCurMsg); +} + +void MsgIter::_handleItem(const PktMagicNumberItem& item) +{ + if (!item.isValid()) { + BT_CPPLOGE_APPEND_CAUSE_AND_THROW( + bt2c::Error, "Invalid packet magic number: val={:#x}, expected-val={:#x}", item.val(), + item.expectedVal()); + } +} + +void MsgIter::_handleItem(const MetadataStreamUuidItem& item) +{ + BT_ASSERT_DBG(_mExpectedMetadataStreamUuid); + + if (item.uuid() != *_mExpectedMetadataStreamUuid) { + BT_CPPLOGE_APPEND_CAUSE_AND_THROW(bt2c::Error, + "Invalid metadata stream UUID: uuid={}, expected-uuid={}", + item.uuid(), *_mExpectedMetadataStreamUuid); + } +} + +void MsgIter::_handleItem(const DataStreamInfoItem& item) +{ + /* + * `_mItemSeqIter` doesn't care about contiguous packets from the + * same medium belonging to different data streams, but this message + * iterator does because it manages a single libbabeltrace2 stream. + */ + BT_ASSERT_DBG(item.cls()); + + if (item.cls()->id() != _mStream.cls().id()) { + BT_CPPLOGE_APPEND_CAUSE_AND_THROW( + bt2c::Error, + "Two contiguous packets belong to data streams having different classes: " + "expected-data-stream-class-class-id={}, data-stream-class-id={}", + item.cls()->id(), _mStream.cls().id()); + } + + if (item.id() && *item.id() != _mStream.id()) { + BT_CPPLOGE_APPEND_CAUSE_AND_THROW( + bt2c::Error, + "Two contiguous packets belong to different data streams: " + "expected-data-stream-id={}, data-stream-id={}", + *item.id(), _mStream.id()); + } + + if (!_mEmittedStreamBeginMsg) { + this->_addMsgToQueue(_mSelfMsgIter.createStreamBeginningMessage(_mStream)); + _mEmittedStreamBeginMsg = true; + } +} + +bt2::Message::Shared MsgIter::_createInitDiscEventsMsg(const _OptUll& prevPktEndDefClkVal) +{ + if (_mStream.cls().discardedEventsHaveDefaultClockSnapshots()) { + /* + * We know there was a previous packet since we can't reach this + * point for the first packet. + */ + BT_ASSERT_DBG(prevPktEndDefClkVal); + return _mSelfMsgIter.createDiscardedEventsMessage(_mStream, *prevPktEndDefClkVal, + *_mPktEndDefClkVal); + } else { + return _mSelfMsgIter.createDiscardedEventsMessage(_mStream); + } +} + +bt2::Message::Shared MsgIter::_createInitDiscPktsMsg(const _OptUll& prevPktEndDefClkVal) +{ + if (_mStream.cls().discardedPacketsHaveDefaultClockSnapshots()) { + /* + * We know there was a previous packet since we can't reach this + * point for the first packet. + */ + BT_ASSERT_DBG(prevPktEndDefClkVal); + return _mSelfMsgIter.createDiscardedPacketsMessage(_mStream, *prevPktEndDefClkVal, + *_mPktBeginDefClkVal); + } else { + return _mSelfMsgIter.createDiscardedPacketsMessage(_mStream); + } +} + +void MsgIter::_emitPktBeginMsg(const _OptUll& defClkVal) +{ + BT_ASSERT_DBG(_mCurPkt); + + /* Add new message to queue */ + this->_addMsgToQueue(bt2c::call([this, &defClkVal] { + if (defClkVal) { + _mCurDefClkVal = defClkVal; + return _mSelfMsgIter.createPacketBeginningMessage(*_mCurPkt, *defClkVal); + } else { + return _mSelfMsgIter.createPacketBeginningMessage(*_mCurPkt); + } + })); +} + +void MsgIter::_emitDelayedPktBeginMsg(const _OptUll& otherDefClkVal) +{ + BT_ASSERT_DBG(_mDelayPktBeginMsgEmission); + + /* Reset the flag */ + _mDelayPktBeginMsgEmission = false; + + /* + * Only fix the beginning timestamp of the packet if it's larger + * than the timestamp of its first event record. + * + * Emit a packet beginning message now. + */ + this->_emitPktBeginMsg(bt2c::call([this, &otherDefClkVal]() -> _OptUll { + if (_mPktBeginDefClkVal && otherDefClkVal) { + return std::min(*_mPktBeginDefClkVal, *otherDefClkVal); + } else if (_mPktBeginDefClkVal) { + return _mPktBeginDefClkVal; + } else if (otherDefClkVal) { + return otherDefClkVal; + } + + return bt2s::nullopt; + })); +} + +void MsgIter::_handleItem(const PktInfoItem& item) +{ + /* + * Save the packet beginning and end timestamps. + * + * Also keep the end timestamp of the previous packet: we might need + * it if there are discarded event records. + */ + const auto prevPktEndDefClkVal = _mPktEndDefClkVal; + + _mPktBeginDefClkVal = item.beginDefClkVal(); + _mPktEndDefClkVal = item.endDefClkVal(); + + /* + * Emit a discarded events message if the count of discarded event + * records went up since the previous packet. + * + * For the first packet, `_mCurDiscErCounterSnap` isn't set: we + * don't have anything to compare to. + */ + { + const auto& discErCounterSnap = item.discEventRecordCounterSnap(); + + if (_mCurDiscErCounterSnap) { + /* + * If the previous packet of this same stream had a discarded + * event record counter snapshot, then this one must have one + * too. + */ + BT_ASSERT_DBG(discErCounterSnap); + + // TODO: handle `*discErCounterSnap` being <= `*_mCurDiscErCounterSnap` + if (*discErCounterSnap > *_mCurDiscErCounterSnap) { + /* Create and initialize the message */ + auto msg = this->_createInitDiscEventsMsg(prevPktEndDefClkVal); + + /* Set its count */ + msg->asDiscardedEvents().count(*discErCounterSnap - *_mCurDiscErCounterSnap); + + /* Add to queue */ + this->_addMsgToQueue(std::move(msg)); + } + } + + /* Set new current discarded event record counter snapshot */ + _mCurDiscErCounterSnap = discErCounterSnap; + } + + /* + * Emit a discarded packets message if there's a gap between the + * previous packet sequence number and the sequence number of this + * new packet. + */ + { + const auto& seqNum = item.seqNum(); + + if (_mCurPktSeqNum) { + /* + * If the previous packet of this same stream had a sequence + * number, then this one must have one too. + */ + BT_ASSERT_DBG(seqNum); + + // TODO: handle `*seqNum` being <= `*_mCurPktSeqNum` + if (*_mCurPktSeqNum + 1 < *seqNum) { + /* Create and initialize the message */ + const auto msg = this->_createInitDiscPktsMsg(prevPktEndDefClkVal); + + /* Set its count */ + msg->asDiscardedPackets().count(*seqNum - *_mCurPktSeqNum - 1); + + /* Add to queue */ + this->_addMsgToQueue(std::move(msg)); + } + } + + /* Set new packet sequence number */ + _mCurPktSeqNum = seqNum; + } + + /* There's no pending message */ + BT_ASSERT_DBG(!_mCurMsg); + + /* + * Depending on a quirk to handle, emit a packet beginning message + * now or delay said emission. + */ + if (_mQuirks.eventRecordDefClkValLtPktBeginDefClkVal) { + _mDelayPktBeginMsgEmission = true; + } else { + /* No quirk to handle: emit the message now */ + this->_emitPktBeginMsg(_mPktBeginDefClkVal); + } +} + +bt2::Message::Shared MsgIter::_createEventMsg(const bt2::EventClass cls, const _OptUll& defClkVal) +{ + if (defClkVal) { + if (_mCurPkt) { + return _mSelfMsgIter.createEventMessage(cls, *_mCurPkt, *defClkVal); + } else { + return _mSelfMsgIter.createEventMessage(cls, _mStream, *defClkVal); + } + } else { + if (_mCurPkt) { + return _mSelfMsgIter.createEventMessage(cls, *_mCurPkt); + } else { + return _mSelfMsgIter.createEventMessage(cls, _mStream); + } + } +} + +void MsgIter::_handleItem(const EventRecordInfoItem& item) +{ + // TODO: Test having a trace with only event record headers + BT_ASSERT_DBG(item.cls()); + BT_ASSERT_DBG(item.cls()->libCls()); + BT_ASSERT_DBG(!_mCurMsg); + + /* Emit a packet beginning message now if required to fix a quirk */ + if (_mDelayPktBeginMsgEmission) { + this->_emitDelayedPktBeginMsg(item.defClkVal()); + } + + /* Update the default clock value if needed */ + if (item.defClkVal()) { + _mCurDefClkVal = *item.defClkVal(); + } + + /* + * Set as current message. + * + * The following items will gradually fill this message. + * + * This message will be emitted (added to the message queue) when + * handling the next `EventRecordEndItem`. + */ + _mCurMsg = this->_createEventMsg(*item.cls()->libCls(), item.defClkVal()); +} + +void MsgIter::_handleItem(const FixedLenBitArrayFieldItem& item) +{ + if (_ignoreFieldItem(item)) { + return; + } + + this->_stackTopCurSubFieldAndGoToNextSubField().asBitArray().valueAsInteger(item.uIntVal()); +} + +void MsgIter::_handleItem(const FixedLenBoolFieldItem& item) +{ + if (_ignoreFieldItem(item)) { + return; + } + + this->_stackTopCurSubFieldAndGoToNextSubField().asBool().value(item.val()); +} + +void MsgIter::_handleItem(const FixedLenSIntFieldItem& item) +{ + this->_handleSIntFieldItem(item); +} + +void MsgIter::_handleItem(const FixedLenUIntFieldItem& item) +{ + this->_handleUIntFieldItem(item); +} + +void MsgIter::_handleItem(const FixedLenFloatFieldItem& item) +{ + const auto field = this->_stackTopCurSubFieldAndGoToNextSubField(); + + if (item.cls().len() == 32_bits) { + field.asSinglePrecisionReal().value(item.val()); + } else { + BT_ASSERT_DBG(item.cls().len() == 64_bits); + field.asDoublePrecisionReal().value(item.val()); + } +} + +void MsgIter::_handleItem(const VarLenSIntFieldItem& item) +{ + this->_handleSIntFieldItem(item); +} + +void MsgIter::_handleItem(const VarLenUIntFieldItem& item) +{ + this->_handleUIntFieldItem(item); +} + +void MsgIter::_handleStrFieldBeginItem(const FieldItem& item) +{ + this->_stackTopCurSubField().asString().value(""); + _mHaveNullChar = false; + _mUtf16NullCpFinder = NullCpFinder<2> {}; + _mUtf32NullCpFinder = NullCpFinder<4> {}; + _mStrBuf.clear(); + _mCurStrFieldEncoding = item.cls().asStr().encoding(); +} + +void MsgIter::_handleStrFieldEndItem() +{ + switch (_mCurStrFieldEncoding) { + case StrEncoding::Utf16Be: + case StrEncoding::Utf16Le: + case StrEncoding::Utf32Be: + case StrEncoding::Utf32Le: + { + /* Convert to UTF-8 */ + const auto utf8Str = bt2c::call([this] { + bt2c::ConstBytes inBytes {_mStrBuf.begin(), _mStrBuf.end()}; + + switch (_mCurStrFieldEncoding) { + case StrEncoding::Utf16Be: + return _mUnicodeConv.utf8FromUtf16Be(inBytes); + case StrEncoding::Utf16Le: + return _mUnicodeConv.utf8FromUtf16Le(inBytes); + case StrEncoding::Utf32Be: + return _mUnicodeConv.utf8FromUtf32Be(inBytes); + case StrEncoding::Utf32Le: + return _mUnicodeConv.utf8FromUtf32Le(inBytes); + default: + bt_common_abort(); + } + }); + const auto endIt = + !utf8Str.empty() && utf8Str.back() == 0 ? utf8Str.end() - 1 : utf8Str.end(); + + /* Append */ + this->_stackTopCurSubField().asString().append( + reinterpret_cast(utf8Str.data()), endIt - utf8Str.begin()); + } + + default: + break; + } + + this->_stackTopGoToNextSubField(); +} + +void MsgIter::_handleItem(const NullTerminatedStrFieldBeginItem& item) +{ + this->_handleStrFieldBeginItem(item); +} + +void MsgIter::_handleItem(const NullTerminatedStrFieldEndItem&) +{ + this->_handleStrFieldEndItem(); +} + +void MsgIter::_handleBlobRawDataItem(const RawDataItem& item) +{ + std::memcpy(&this->_stackTopCurSubField().asBlob().data()[_mCurBlobFieldDataOffset], + item.data().begin(), item.data().size()); + _mCurBlobFieldDataOffset += item.data().size(); +} + +void MsgIter::_handleStrRawDataItem(const RawDataItem& item) +{ + if (_mHaveNullChar) { + /* No more text data */ + return; + } + + if (_mCurStrFieldEncoding == StrEncoding::Utf8) { + /* Try to find the first U+0000 codepoint */ + const auto endIt = std::find(item.data().begin(), item.data().end(), 0); + _mHaveNullChar = endIt != item.data().end(); + + /* Append to current string field */ + this->_stackTopCurSubField().asString().append( + reinterpret_cast(item.data().data()), endIt - item.data().begin()); + } else { + /* Try to find the first U+0000 codepoint */ + auto endIt = item.data().end(); + const auto afterNullCpIt = bt2c::call([this, &item] { + if (_mCurStrFieldEncoding == StrEncoding::Utf16Be || + _mCurStrFieldEncoding == StrEncoding::Utf16Le) { + return _mUtf16NullCpFinder.findNullCp(item.data()); + } else { + BT_ASSERT_DBG(_mCurStrFieldEncoding == StrEncoding::Utf32Be || + _mCurStrFieldEncoding == StrEncoding::Utf32Le); + return _mUtf32NullCpFinder.findNullCp(item.data()); + } + }); + + if (afterNullCpIt) { + /* Found U+0000 */ + endIt = *afterNullCpIt; + _mHaveNullChar = true; + } + + /* Append to current string buffer */ + _mStrBuf.insert(_mStrBuf.end(), item.data().begin(), endIt); + } +} + +void MsgIter::_handleItem(const RawDataItem& item) +{ + if (this->_stackTopCurSubField().isString()) { + this->_handleStrRawDataItem(item); + } else { + BT_ASSERT_DBG(this->_stackTopCurSubField().isBlob()); + this->_handleBlobRawDataItem(item); + } +} + +void MsgIter::_handleItem(const StructFieldBeginItem&) +{ + if (_mStack.empty()) { + /* This is the root field of the current scope */ + BT_ASSERT_DBG(_mCurScopeField); + this->_stackPush(*_mCurScopeField); + } else { + /* Use sub-field */ + this->_stackPush(this->_stackTopCurSubFieldAndGoToNextSubField().asStructure()); + } +} + +void MsgIter::_handleItem(const StructFieldEndItem&) +{ + this->_stackPop(); +} + +void MsgIter::_handleItem(const StaticLenArrayFieldBeginItem&) +{ + this->_stackPush(this->_stackTopCurSubFieldAndGoToNextSubField().asArray()); +} + +void MsgIter::_handleItem(const DynLenArrayFieldBeginItem& item) +{ + auto arrayField = this->_stackTopCurSubFieldAndGoToNextSubField().asDynamicArray(); + + arrayField.length(item.len()); + this->_stackPush(arrayField); +} + +void MsgIter::_handleItem(const ArrayFieldEndItem&) +{ + this->_stackPop(); +} + +void MsgIter::_handleItem(const StaticLenBlobFieldBeginItem&) +{ + _mCurBlobFieldDataOffset = 0; +} + +void MsgIter::_handleItem(const DynLenBlobFieldBeginItem& item) +{ + this->_stackTopCurSubField().asDynamicBlob().length(item.len().bytes()); + _mCurBlobFieldDataOffset = 0; +} + +void MsgIter::_handleItem(const BlobFieldEndItem&) +{ + this->_stackTopGoToNextSubField(); +} + +void MsgIter::_handleItem(const NonNullTerminatedStrFieldBeginItem& item) +{ + this->_handleStrFieldBeginItem(item); +} + +void MsgIter::_handleItem(const NonNullTerminatedStrFieldEndItem&) +{ + this->_handleStrFieldEndItem(); +} + +void MsgIter::_handleItem(const VariantFieldBeginItem& item) +{ + auto field = this->_stackTopCurSubFieldAndGoToNextSubField().asVariant(); + + field.selectOption(item.selectedOptIndex()); + this->_stackPush(field); +} + +void MsgIter::_handleItem(const VariantFieldEndItem&) +{ + this->_stackPop(); +} + +void MsgIter::_handleItem(const OptionalFieldBeginItem& item) +{ + auto field = this->_stackTopCurSubFieldAndGoToNextSubField().asOption(); + + field.hasField(item.isEnabled()); + this->_stackPush(field); +} + +void MsgIter::_handleItem(const OptionalFieldEndItem&) +{ + this->_stackPop(); +} + +void MsgIter::_addMsgToQueue(bt2::ConstMessage::Shared msg) +{ + _mMsgs.emplace(std::move(msg)); +} + +bt2::ConstMessage::Shared MsgIter::_releaseNextMsg() +{ + if (_mMsgs.empty()) { + return bt2::ConstMessage::Shared {}; + } + + auto msg = std::move(_mMsgs.front()); + + _mMsgs.pop(); + return msg; +} + +} /* namespace src */ +} /* namespace ctf */ diff --git a/src/plugins/ctf/common/src/msg-iter.hpp b/src/plugins/ctf/common/src/msg-iter.hpp new file mode 100644 index 00000000..13fa881e --- /dev/null +++ b/src/plugins/ctf/common/src/msg-iter.hpp @@ -0,0 +1,598 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (c) 2022-2024 EfficiOS, Inc + */ + +#ifndef BABELTRACE_PLUGINS_CTF_COMMON_SRC_MSG_ITER_HPP +#define BABELTRACE_PLUGINS_CTF_COMMON_SRC_MSG_ITER_HPP + +#include +#include + +#include + +#include "common/assert.h" +#include "cpp-common/bt2/message.hpp" +#include "cpp-common/bt2/self-message-iterator.hpp" +#include "cpp-common/bt2/trace-ir.hpp" +#include "cpp-common/bt2c/aliases.hpp" +#include "cpp-common/bt2c/unicode-conv.hpp" + +#include "item-seq/item-seq-iter.hpp" +#include "item-seq/item-visitor.hpp" +#include "item-seq/logging-item-visitor.hpp" +#include "null-cp-finder.hpp" +#include "plugins/ctf/common/src/metadata/ctf-ir.hpp" + +namespace ctf { +namespace src { + +/* + * Various quirks that a CTF message iterator can work around. + */ +struct MsgIterQuirks final +{ + /* Packet end timestamps set to zero */ + bool pktEndDefClkValZero = false; + + /* + * Timestamp of last event record of a packet is greater than the + * beginning timestamp of the next packet. + */ + bool eventRecordDefClkValGtNextPktBeginDefClkVal = false; + + /* + * Timestamp of the first event record of a packet is less than the + * beginning timestamp of its packet. + */ + bool eventRecordDefClkValLtPktBeginDefClkVal = false; +}; + +/* + * CTF message iterator. + * + * Such an iterator essentially converts the items of an underlying item + * sequence iterator to corresponding libbabeltrace2 messages. + * + * Therefore, as a user, you provide: + * + * • A medium, which provides data stream data to the iterator. + * + * • A CTF IR trace class, which describes how to decode said data. + * + * • A libbabeltrace2 self message iterator and stream, which the + * iterator needs to create libbabeltrace2 messages. + * + * A CTF message iterator may automatically fix some common quirks + * (see `MsgIterQuirks`). + */ +class MsgIter final +{ +public: + /* + * Builds a CTF message iterator, using `traceCls` and `medium` to + * decode a data stream identified by `stream`, and `selfMsgIter` + * and `stream` to create libbabeltrace2 messages. + * + * `quirks` indicates which quirks to fix. + * + * It's guaranteed that this constructor doesn't throw + * `bt2c::TryAgain` or a medium error. + */ + explicit MsgIter(bt2::SelfMessageIterator selfMsgIter, const ctf::src::TraceCls& traceCls, + bt2s::optional expectedMetadataStreamUuid, bt2::Stream stream, + Medium::UP medium, const MsgIterQuirks& quirks, + const bt2c::Logger& parentLogger); + + /* Disable copy/move operations */ + MsgIter(const MsgIter&) = delete; + MsgIter& operator=(const MsgIter&) = delete; + + /* + * Advances the iterator to the next message, returning: + * + * A libbabeltrace2 message: + * Said next message. + * + * `bt2s::nullopt`: + * The iterator is ended. + * + * May throw whatever Medium::buf() may throw as well + * as `bt2c::Error`. + */ + bt2::ConstMessage::Shared next(); + +private: + /* An optional `unsigned long long` value */ + using _OptUll = bt2s::optional; + + /* Single frame of a message iterator stack */ + class _StackFrame + { + public: + explicit _StackFrame(const bt2::StructureField field) noexcept : + _mFieldType {_FieldType::Struct}, _mField {field} + { + } + + explicit _StackFrame(const bt2::VariantField field) noexcept : + _mFieldType {_FieldType::Variant}, _mField(field) + { + } + + explicit _StackFrame(const bt2::OptionField field) noexcept : + _mFieldType {_FieldType::Option}, _mField(field) + { + } + + explicit _StackFrame(const bt2::ArrayField field) noexcept : + _mFieldType(_FieldType::Array), _mField(field) + { + } + + bt2::StructureField structureField() const noexcept + { + BT_ASSERT_DBG(_mFieldType == _FieldType::Struct); + return _mField.structure; + } + + bt2::VariantField variantField() const noexcept + { + BT_ASSERT_DBG(_mFieldType == _FieldType::Variant); + return _mField.variant; + } + + bt2::OptionField optionField() const noexcept + { + BT_ASSERT_DBG(_mFieldType == _FieldType::Option); + return _mField.option; + } + + bt2::ArrayField arrayField() const noexcept + { + BT_ASSERT_DBG(_mFieldType == _FieldType::Array); + return _mField.array; + } + + unsigned long long subFieldIndex() const noexcept + { + return _mSubFieldIndex; + } + + void goToNextSubField() noexcept + { + /* + * We unconditionally increment `_mSubFieldIndex`, even if + * the current field is a variant/option field, because + * curSubField() doesn't care about `_mSubFieldIndex` for + * those cases anyway. + * + * In practice, `_mSubFieldIndex` will reach one with a + * variant/option field, but curSubField() will never be + * called with `_mSubFieldIndex` being something else + * than zero. + */ + ++_mSubFieldIndex; + } + + bt2::Field curSubField() noexcept + { + switch (_mFieldType) { + case _FieldType::Struct: + BT_ASSERT_DBG(_mSubFieldIndex < _mField.structure.cls().length()); + return _mField.structure[_mSubFieldIndex]; + + case _FieldType::Variant: + BT_ASSERT_DBG(_mSubFieldIndex == 0); + return _mField.variant.selectedOptionField(); + + case _FieldType::Option: + BT_ASSERT_DBG(_mSubFieldIndex == 0); + BT_ASSERT_DBG(_mField.option.hasField()); + return *_mField.option.field(); + + case _FieldType::Array: + BT_ASSERT_DBG(_mSubFieldIndex < _mField.array.length()); + return _mField.array[_mSubFieldIndex]; + + default: + bt_common_abort(); + } + } + + bt2::Field curSubFieldAndGoToNextSubField() noexcept + { + const auto field = this->curSubField(); + + this->goToNextSubField(); + return field; + } + + private: + /* Selector of `_mField` below */ + enum class _FieldType + { + /* Selects `structure` */ + Struct = 1, + + /* Selects `variant` */ + Variant, + + /* Selects `option` */ + Option, + + /* Selects `array` */ + Array, + } _mFieldType; + + /* Field of this frame, selected by `_mFieldType` above */ + union _Field + { + explicit _Field(const bt2::StructureField field) noexcept + { + new (&structure) bt2::StructureField {field}; + } + + explicit _Field(const bt2::VariantField field) noexcept + { + new (&variant) bt2::VariantField {field}; + } + + explicit _Field(const bt2::OptionField field) noexcept + { + new (&option) bt2::OptionField {field}; + } + + explicit _Field(const bt2::ArrayField field) noexcept + { + new (&array) bt2::ArrayField {field}; + } + + static_assert(std::is_trivially_destructible::value, + "`bt2::StructureField` is trivially destructible."); + static_assert(std::is_trivially_destructible::value, + "`bt2::VariantField` is trivially destructible."); + static_assert(std::is_trivially_destructible::value, + "`bt2::OptionField` is trivially destructible."); + static_assert(std::is_trivially_destructible::value, + "`bt2::ArrayField` is trivially destructible."); + + bt2::StructureField structure; + bt2::VariantField variant; + bt2::OptionField option; + bt2::ArrayField array; + } _mField; + + /* + * Index of, depending on `_mFieldType` above: + * + * `_FieldType::Struct`: + * The current member of `_mField.structure`. + * + * `_FieldType::Array`: + * The current element field of `_mField.array`. + * + * `_FieldType::Variant`: + * `_FieldType::Option`: + * Not applicable. + */ + unsigned long long _mSubFieldIndex = 0; + }; + + /* + * Returns whether or not to ignore the field of `item`. + */ + static bool _ignoreFieldItem(const FieldItem& item) noexcept + { + return !item.cls().libCls(); + } + + /* + * Handles the item `item`, changing the state accordingly, possibly + * adding one or more messages to `_mMsgs`. + */ + void _handleItem(const Item& item); + + /* Specific item handlers below */ + void _handleItem(const ArrayFieldEndItem& item); + void _handleItem(const BlobFieldEndItem& item); + void _handleItem(const DataStreamInfoItem& item); + void _handleItem(const DynLenArrayFieldBeginItem& item); + void _handleItem(const DynLenBlobFieldBeginItem& item); + void _handleItem(const EventRecordEndItem& item); + void _handleItem(const EventRecordInfoItem& item); + void _handleItem(const FixedLenBitArrayFieldItem& item); + void _handleItem(const FixedLenBoolFieldItem& item); + void _handleItem(const FixedLenFloatFieldItem& item); + void _handleItem(const FixedLenSIntFieldItem& item); + void _handleItem(const FixedLenUIntFieldItem& item); + void _handleItem(const MetadataStreamUuidItem& item); + void _handleItem(const NonNullTerminatedStrFieldBeginItem& item); + void _handleItem(const NonNullTerminatedStrFieldEndItem& item); + void _handleItem(const NullTerminatedStrFieldBeginItem& item); + void _handleItem(const NullTerminatedStrFieldEndItem& item); + void _handleItem(const OptionalFieldBeginItem& item); + void _handleItem(const OptionalFieldEndItem& item); + void _handleItem(const PktBeginItem& item); + void _handleItem(const PktContentEndItem& item); + void _handleItem(const PktEndItem& item); + void _handleItem(const PktInfoItem& item); + void _handleItem(const PktMagicNumberItem& item); + void _handleBlobRawDataItem(const RawDataItem& item); + void _handleStrRawDataItem(const RawDataItem& item); + void _handleItem(const RawDataItem& item); + void _handleItem(const ScopeBeginItem& item); + void _handleItem(const ScopeEndItem& item); + void _handleItem(const StaticLenArrayFieldBeginItem& item); + void _handleItem(const StaticLenBlobFieldBeginItem& item); + void _handleItem(const StructFieldBeginItem& item); + void _handleItem(const StructFieldEndItem& item); + void _handleItem(const VariantFieldBeginItem& item); + void _handleItem(const VariantFieldEndItem& item); + void _handleItem(const VarLenSIntFieldItem& item); + void _handleItem(const VarLenUIntFieldItem& item); + void _handleStrFieldBeginItem(const FieldItem& item); + void _handleStrFieldEndItem(); + + template + void _handleUIntFieldItem(const ItemT& item) + { + if (_ignoreFieldItem(item)) { + return; + } + + const auto field = this->_stackTopCurSubFieldAndGoToNextSubField(); + + field.asUnsignedInteger().value(item.val()); + } + + template + void _handleSIntFieldItem(const ItemT& item) + { + if (_ignoreFieldItem(item)) { + return; + } + + const auto field = this->_stackTopCurSubFieldAndGoToNextSubField(); + + field.asSignedInteger().value(item.val()); + } + + /* + * Calls goToNextSubField() for the top stack frame. + */ + void _stackTopGoToNextSubField() + { + BT_ASSERT_DBG(!_mStack.empty()); + _mStack.top().goToNextSubField(); + } + + /* + * Returns curSubField() for the top stack frame. + */ + bt2::Field _stackTopCurSubField() + { + BT_ASSERT_DBG(!_mStack.empty()); + return _mStack.top().curSubField(); + } + + /* + * Returns curSubFieldAndGoToNextSubField() for the top stack frame. + */ + bt2::Field _stackTopCurSubFieldAndGoToNextSubField() + { + BT_ASSERT_DBG(!_mStack.empty()); + return _mStack.top().curSubFieldAndGoToNextSubField(); + } + + /* + * Pushes a stack frame managing `field` on the stack. + */ + template + void _stackPush(const FieldT field) + { + _mStack.push(_StackFrame {field}); + } + + /* + * Removes the top stack frame. + */ + void _stackPop() + { + BT_ASSERT_DBG(!_mStack.empty()); + _mStack.pop(); + } + + /* + * Sets the current packet to `pkt`. + */ + void _curPkt(bt2::Packet::Shared pkt) + { + BT_ASSERT_DBG(!_mCurPkt); + _mCurPkt = std::move(pkt); + } + + /* + * Resets the current packet. + */ + void _resetCurPkt() + { + BT_ASSERT_DBG(_mCurPkt); + _mCurPkt.reset(); + } + + /* + * Creates and returns an initial discarded events message, not + * setting any specific count. + */ + bt2::Message::Shared _createInitDiscEventsMsg(const _OptUll& prevPktEndDefClkVal); + + /* + * Creates and returns an initial discarded packets message, not + * setting any specific count. + */ + bt2::Message::Shared _createInitDiscPktsMsg(const _OptUll& prevPktEndDefClkVal); + + /* + * Creates a packet end message and, if needed, updates the current + * timestamp. + */ + bt2::Message::Shared _createPktEndMsgAndUpdateCurDefClkVal(); + + /* + * Creates an event message using the class `cls` and having + * `defClkVal` as its timestamp. + */ + bt2::Message::Shared _createEventMsg(bt2::EventClass cls, const _OptUll& defClkVal); + + /* + * Emits a packet beginning message having `defClkVal`, if set, as + * its default clock snapshot. + */ + void _emitPktBeginMsg(const _OptUll& defClkVal); + + /* + * Emits a delayed packet beginning message, considering the other + * timestamp `otherDefClkVal`. + */ + void _emitDelayedPktBeginMsg(const _OptUll& otherDefClkVal); + + /* + * Adds the message `msg` to the message queue. + */ + void _addMsgToQueue(bt2::ConstMessage::Shared msg); + + /* + * Returns one of: + * + * A shared libbabeltrace2 message: + * The next available message from the message queue, removing + * it from the queue. + * + * `bt2s::nullopt`: + * The message queue is empty. + */ + bt2::ConstMessage::Shared _releaseNextMsg(); + + /* Logger */ + bt2c::Logger _mLogger; + + /* libbabeltrace2 self message iterator to create messages (weak) */ + bt2::SelfMessageIterator _mSelfMsgIter; + + /* Corresponding libbabeltrace2 stream */ + bt2::Stream _mStream; + + /* Expected metadata stream UUID, if any */ + bt2s::optional _mExpectedMetadataStreamUuid; + + /* Quirks to fix */ + MsgIterQuirks _mQuirks; + + /* Underlying item sequence iterator to decode the data stream */ + ItemSeqIter _mItemSeqIter; + + /* Whether or not the iterator is ended */ + bool _mIsDone = false; + + /* + * Queue of already created messages. + * + * We're using a queue instead of keeping a single message because + * because one item (from `_mItemSeqIter`) may correspond to more + * than one libbabeltrace2 message. + */ + std::queue _mMsgs; + + /* Stack */ + std::stack<_StackFrame> _mStack; + + /* Root field of current scope */ + bt2::OptionalBorrowedObject _mCurScopeField; + + /* + * Whether or not to skip items until reaching the end of the + * current scope. + */ + bool _mSkipItemsUntilScopeEndItem = false; + + /* + * If set: a message that we're building, that's not yet ready to be + * returned. + */ + bt2::Message::Shared _mCurMsg; + + /* + * If set: the current packet. + * + * Set while handling a `PktBeginItem` and reset while handling a + * `PktEndItem`. + */ + bt2::Packet::Shared _mCurPkt; + + /* Current packet sequence number, if any */ + _OptUll _mCurPktSeqNum; + + /* Current default clock value, if any */ + _OptUll _mCurDefClkVal; + + /* Current discarded event record counter snapshot, if any */ + _OptUll _mCurDiscErCounterSnap; + + /* + * Values of the beginning and end timestamps of the current packet. + * + * Set while handling a `PktInfoItem` and reset while handling a + * `PktEndItem`. + */ + _OptUll _mPktBeginDefClkVal; + _OptUll _mPktEndDefClkVal; + + /* + * Whether or not a stream beginning message was provided to the + * user. + */ + bool _mEmittedStreamBeginMsg = false; + + /* + * Whether or not to delay the emission of a packet beginning + * message. + */ + bool _mDelayPktBeginMsgEmission = false; + + /* + * Whether or not, while processing `RawDataItem` items, we got a + * null character. + */ + bool _mHaveNullChar = false; + + /* Null codepoint finders for UTF-16 and UTF-32 */ + NullCpFinder<2> _mUtf16NullCpFinder; + NullCpFinder<4> _mUtf32NullCpFinder; + + /* Unicode converter to decode UTF-16 and UTF-32 strings */ + bt2c::UnicodeConv _mUnicodeConv; + + /* Buffer holding the string to convert to UTF-8 */ + std::vector _mStrBuf; + + /* + * Current BLOB field data offset while processing BLOB field + * section items. + */ + std::size_t _mCurBlobFieldDataOffset = 0; + + /* + * Current string field encoding, if any. + */ + StrEncoding _mCurStrFieldEncoding = StrEncoding::Utf8; + + /* Helper to log items */ + LoggingItemVisitor _mLoggingVisitor; +}; + +} /* namespace src */ +} /* namespace ctf */ + +#endif /* BABELTRACE_PLUGINS_CTF_COMMON_SRC_MSG_ITER_HPP */ -- 2.34.1