--- /dev/null
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright (c) 2022 Simon Marchi <simon.marchi@efficios.com>
+ * Copyright (c) 2015-2022 Philippe Proulx <pproulx@efficios.com>
+ */
+
+#define BT_CLOG_CFG _mLogCfg
+#define BT_LOG_TAG "PLUGIN/CTF/MSG-ITER"
+#include "cpp-common/cfg-logging-error-reporting-throw.hpp"
+
+#include <algorithm>
+
+#include "cpp-common/data-len.hpp"
+#include "item-seq/item.hpp"
+#include "msg-iter.hpp"
+
+namespace ctf {
+namespace src {
+
+namespace bt2c = bt2_common;
+using namespace bt2c::literals::datalen;
+
+MsgIter::MsgIter(bt_self_message_iterator * const selfMsgIter, const ctf::src::TraceCls& traceCls,
+ nonstd::optional<bt2_common::Uuid> expectedMetadataStreamUuid,
+ const bt2::Stream stream, Medium::UP medium, const MsgIterQuirks& quirks,
+ const bt2c::LogCfg& logCfg) :
+ _mSelfMsgIter {selfMsgIter},
+ _mStream {stream}, _mExpectedMetadataStreamUuid {std::move(expectedMetadataStreamUuid)},
+ _mQuirks {quirks}, _mItemSeqIter {std::move(medium), traceCls, logCfg}, _mLogCfg {logCfg},
+ _mLoggingVisitor {"Handling item", logCfg}
+{
+ BT_CLOGD("Created CTF plugin message iterator: "
+ "addr=%p, trace-cls-addr=%p, log-level=%s",
+ this, &traceCls, bt_common_logging_level_string(logCfg.logLevel()));
+}
+
+nonstd::optional<bt2::ConstMessage::Shared> MsgIter::next()
+{
+ BT_CLOGD("Getting next message: addr=%p", this);
+
+ if (_mIsDone) {
+ return nonstd::nullopt;
+ }
+
+ /*
+ * 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.
+ */
+ const auto item = _mItemSeqIter.next();
+
+ if (!item) {
+ /* No more items: this is the end! */
+ break;
+ }
+
+ /* Handle item if needed */
+ if (!_mSkipItemsUntilScopeEndItem || item->isScopeEndItem()) {
+ this->_handleItem(*item);
+
+ if (auto msg = this->_releaseNextMsg()) {
+ return msg;
+ }
+ }
+ }
+
+ /* We're done! */
+ _mIsDone = true;
+ return bt2::Message::Shared::createWithoutRef(
+ bt_message_stream_end_create(_mSelfMsgIter, _mStream.libObjPtr()));
+ } catch (const bt2c::Error&) {
+ BT_CLOGE_APPEND_CAUSE_AND_RETHROW("Failed to create next message: addr=%p", this);
+ }
+}
+
+void MsgIter::_handleItem(const Item& item)
+{
+ /* Log item details */
+ if (BT_LOG_ON_TRACE) {
+ item.accept(_mLoggingVisitor);
+ }
+
+ /* Defer to specific handler */
+ switch (item.type()) {
+ case Item::Type::PKT_BEGIN:
+ this->_handleItem(static_cast<const PktBeginItem&>(item));
+ break;
+ case Item::Type::PKT_END:
+ this->_handleItem(static_cast<const PktEndItem&>(item));
+ break;
+ case Item::Type::SCOPE_BEGIN:
+ this->_handleItem(static_cast<const ScopeBeginItem&>(item));
+ break;
+ case Item::Type::SCOPE_END:
+ this->_handleItem(static_cast<const ScopeEndItem&>(item));
+ break;
+ case Item::Type::PKT_CONTENT_END:
+ this->_handleItem(static_cast<const PktContentEndItem&>(item));
+ break;
+ case Item::Type::EVENT_RECORD_END:
+ this->_handleItem(static_cast<const EventRecordEndItem&>(item));
+ break;
+ case Item::Type::PKT_MAGIC_NUMBER:
+ this->_handleItem(static_cast<const PktMagicNumberItem&>(item));
+ break;
+ case Item::Type::METADATA_STREAM_UUID:
+ this->_handleItem(static_cast<const MetadataStreamUuidItem&>(item));
+ break;
+ case Item::Type::DATA_STREAM_INFO:
+ this->_handleItem(static_cast<const DataStreamInfoItem&>(item));
+ break;
+ case Item::Type::PKT_INFO:
+ this->_handleItem(static_cast<const PktInfoItem&>(item));
+ break;
+ case Item::Type::EVENT_RECORD_INFO:
+ this->_handleItem(static_cast<const EventRecordInfoItem&>(item));
+ break;
+ case Item::Type::FIXED_LEN_BIT_ARRAY_FIELD:
+ this->_handleItem(static_cast<const FixedLenBitArrayFieldItem&>(item));
+ break;
+ case Item::Type::FIXED_LEN_BOOL_FIELD:
+ this->_handleItem(static_cast<const FixedLenBoolFieldItem&>(item));
+ break;
+ case Item::Type::FIXED_LEN_SINT_FIELD:
+ case Item::Type::FIXED_LEN_SENUM_FIELD:
+ this->_handleItem(static_cast<const FixedLenSIntFieldItem&>(item));
+ break;
+ case Item::Type::FIXED_LEN_UINT_FIELD:
+ case Item::Type::FIXED_LEN_UENUM_FIELD:
+ this->_handleItem(static_cast<const FixedLenUIntFieldItem&>(item));
+ break;
+ case Item::Type::FIXED_LEN_FLOAT_FIELD:
+ this->_handleItem(static_cast<const FixedLenFloatFieldItem&>(item));
+ break;
+ case Item::Type::VAR_LEN_SINT_FIELD:
+ case Item::Type::VAR_LEN_SENUM_FIELD:
+ this->_handleItem(static_cast<const VarLenSIntFieldItem&>(item));
+ break;
+ case Item::Type::VAR_LEN_UINT_FIELD:
+ case Item::Type::VAR_LEN_UENUM_FIELD:
+ this->_handleItem(static_cast<const VarLenUIntFieldItem&>(item));
+ break;
+ case Item::Type::NULL_TERMINATED_STR_FIELD_BEGIN:
+ this->_handleItem(static_cast<const NullTerminatedStrFieldBeginItem&>(item));
+ break;
+ case Item::Type::NULL_TERMINATED_STR_FIELD_END:
+ this->_handleItem(static_cast<const NullTerminatedStrFieldEndItem&>(item));
+ break;
+ case Item::Type::STR_FIELD_SUBSTR:
+ this->_handleItem(static_cast<const StrFieldSubstrItem&>(item));
+ break;
+ case Item::Type::BLOB_FIELD_SECTION:
+ this->_handleItem(static_cast<const BlobFieldSectionItem&>(item));
+ break;
+ case Item::Type::STRUCT_FIELD_BEGIN:
+ this->_handleItem(static_cast<const StructFieldBeginItem&>(item));
+ break;
+ case Item::Type::STRUCT_FIELD_END:
+ this->_handleItem(static_cast<const StructFieldEndItem&>(item));
+ break;
+ case Item::Type::STATIC_LEN_ARRAY_FIELD_BEGIN:
+ this->_handleItem(static_cast<const StaticLenArrayFieldBeginItem&>(item));
+ break;
+ case Item::Type::STATIC_LEN_ARRAY_FIELD_END:
+ case Item::Type::DYN_LEN_ARRAY_FIELD_END:
+ this->_handleItem(static_cast<const ArrayFieldEndItem&>(item));
+ break;
+ case Item::Type::DYN_LEN_ARRAY_FIELD_BEGIN:
+ this->_handleItem(static_cast<const DynLenArrayFieldBeginItem&>(item));
+ break;
+ case Item::Type::STATIC_LEN_BLOB_FIELD_BEGIN:
+ this->_handleItem(static_cast<const StaticLenBlobFieldBeginItem&>(item));
+ break;
+ case Item::Type::STATIC_LEN_BLOB_FIELD_END:
+ case Item::Type::DYN_LEN_BLOB_FIELD_END:
+ this->_handleItem(static_cast<const BlobFieldEndItem&>(item));
+ break;
+ case Item::Type::DYN_LEN_BLOB_FIELD_BEGIN:
+ this->_handleItem(static_cast<const DynLenBlobFieldBeginItem&>(item));
+ break;
+ case Item::Type::STATIC_LEN_STR_FIELD_BEGIN:
+ case Item::Type::DYN_LEN_STR_FIELD_BEGIN:
+ this->_handleItem(static_cast<const NonNullTerminatedStrFieldBeginItem&>(item));
+ break;
+ case Item::Type::STATIC_LEN_STR_FIELD_END:
+ case Item::Type::DYN_LEN_STR_FIELD_END:
+ this->_handleItem(static_cast<const NonNullTerminatedStrFieldEndItem&>(item));
+ break;
+ case Item::Type::VARIANT_FIELD_WITH_SINT_SEL_BEGIN:
+ case Item::Type::VARIANT_FIELD_WITH_UINT_SEL_BEGIN:
+ this->_handleItem(static_cast<const VariantFieldBeginItem&>(item));
+ break;
+ case Item::Type::VARIANT_FIELD_WITH_SINT_SEL_END:
+ case Item::Type::VARIANT_FIELD_WITH_UINT_SEL_END:
+ this->_handleItem(static_cast<const VariantFieldEndItem&>(item));
+ break;
+ case Item::Type::OPTIONAL_FIELD_WITH_BOOL_SEL_BEGIN:
+ case Item::Type::OPTIONAL_FIELD_WITH_SINT_SEL_BEGIN:
+ case Item::Type::OPTIONAL_FIELD_WITH_UINT_SEL_BEGIN:
+ this->_handleItem(static_cast<const OptionalFieldBeginItem&>(item));
+ break;
+ case Item::Type::OPTIONAL_FIELD_WITH_BOOL_SEL_END:
+ case Item::Type::OPTIONAL_FIELD_WITH_SINT_SEL_END:
+ case Item::Type::OPTIONAL_FIELD_WITH_UINT_SEL_END:
+ this->_handleItem(static_cast<const OptionalFieldEndItem&>(item));
+ break;
+ default:
+ BT_CLOGT_STR("Skipping item.");
+ return;
+ }
+}
+
+void MsgIter::_handleItem(const PktBeginItem&)
+{
+ BT_ASSERT_DBG(!this->_curPkt());
+ this->_curPkt(_mStream.createPacket());
+}
+
+bt_message *MsgIter::_createPktEndMsgAndUpdateCurDefClkVal()
+{
+ 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 bt_message_packet_end_create_with_default_clock_snapshot(_mSelfMsgIter,
+ this->_curPkt(), defClkVal);
+ } else {
+ return bt_message_packet_end_create(_mSelfMsgIter, this->_curPkt());
+ }
+}
+
+void MsgIter::_handleItem(const PktEndItem&)
+{
+ BT_ASSERT_DBG(!_mCurMsg);
+ BT_ASSERT_DBG(this->_curPkt());
+
+ /* 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 ir::FieldLocScope::PKT_HEADER:
+ /* Nothing needed from the packet header: fast-forward */
+ _mSkipItemsUntilScopeEndItem = true;
+ break;
+ case ir::FieldLocScope::PKT_CTX:
+ {
+ const auto pkt = this->_curPkt();
+
+ BT_ASSERT_DBG(pkt);
+
+ const auto pktCtxField = bt_packet_borrow_context_field(pkt);
+
+ if (pktCtxField) {
+ _mCurScopeField = bt2::StructureField {pktCtxField};
+ } else {
+ /* Nothing needed from the packet context: fast-forward */
+ _mSkipItemsUntilScopeEndItem = true;
+ }
+
+ break;
+ }
+ case ir::FieldLocScope::EVENT_RECORD_HEADER:
+ /* Nothing needed from the event record header: fast-forward */
+ _mSkipItemsUntilScopeEndItem = true;
+ break;
+ case ir::FieldLocScope::EVENT_RECORD_COMMON_CTX:
+ {
+ const auto event = bt_message_event_borrow_event((*_mCurMsg)->libObjPtr());
+ const auto commonCtxField = bt_event_borrow_common_context_field(event);
+
+ if (commonCtxField) {
+ _mCurScopeField = bt2::StructureField {commonCtxField};
+ } else {
+ /* Nothing needed from the common context: fast-forward */
+ _mSkipItemsUntilScopeEndItem = true;
+ }
+
+ break;
+ }
+ case ir::FieldLocScope::EVENT_RECORD_SPEC_CTX:
+ {
+ const auto event = bt_message_event_borrow_event((*_mCurMsg)->libObjPtr());
+ const auto specCtxField = bt_event_borrow_specific_context_field(event);
+
+ if (specCtxField) {
+ _mCurScopeField = bt2::StructureField {specCtxField};
+ } else {
+ /* Nothing needed from the specific context: fast-forward */
+ _mSkipItemsUntilScopeEndItem = true;
+ }
+
+ break;
+ }
+ case ir::FieldLocScope::EVENT_RECORD_PAYLOAD:
+ {
+ const auto event = bt_message_event_borrow_event((*_mCurMsg)->libObjPtr());
+ const auto payloadField = bt_event_borrow_payload_field(event);
+
+ if (payloadField) {
+ _mCurScopeField = bt2::StructureField {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(this->_curPkt());
+}
+
+void MsgIter::_handleItem(const EventRecordEndItem&)
+{
+ BT_ASSERT_DBG(_mStack.empty());
+ BT_ASSERT_DBG(_mCurMsg);
+
+ /* Emit current message */
+ _mMsgs.emplace(bt2::ConstMessage::Shared::createWithoutRef((_mCurMsg->release())));
+ _mCurMsg.reset();
+}
+
+void MsgIter::_handleItem(const PktMagicNumberItem& item)
+{
+ if (!item.isValid()) {
+ BT_CLOGE_APPEND_CAUSE_AND_THROW(
+ bt2c::Error, "Invalid packet magic number: val=%x, expected-val=%x",
+ static_cast<unsigned int>(item.val()), static_cast<unsigned int>(item.expectedVal()));
+ }
+}
+
+void MsgIter::_handleItem(const MetadataStreamUuidItem& item)
+{
+ BT_ASSERT_DBG(_mExpectedMetadataStreamUuid);
+
+ if (item.uuid() != *_mExpectedMetadataStreamUuid) {
+ BT_CLOGE_APPEND_CAUSE_AND_THROW(
+ bt2c::Error, "Invalid metadata stream UUID: uuid=%s, expected-uuid=%s",
+ item.uuid().str().c_str(), _mExpectedMetadataStreamUuid->str().c_str());
+ }
+}
+
+void MsgIter::_handleItem(const DataStreamInfoItem&)
+{
+ if (!_mEmittedStreamBeginMsg) {
+ this->_addMsgToQueue(
+ bt_message_stream_beginning_create(_mSelfMsgIter, _mStream.libObjPtr()));
+ _mEmittedStreamBeginMsg = true;
+ }
+}
+
+bt_message *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 bt_message_discarded_events_create_with_default_clock_snapshots(
+ _mSelfMsgIter, _mStream.libObjPtr(), *prevPktEndDefClkVal, *_mPktEndDefClkVal);
+ } else {
+ return bt_message_discarded_events_create(_mSelfMsgIter, _mStream.libObjPtr());
+ }
+}
+
+bt_message *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 bt_message_discarded_packets_create_with_default_clock_snapshots(
+ _mSelfMsgIter, _mStream.libObjPtr(), *prevPktEndDefClkVal, *_mPktBeginDefClkVal);
+ } else {
+ return bt_message_discarded_packets_create(_mSelfMsgIter, _mStream.libObjPtr());
+ }
+}
+
+void MsgIter::_emitPktBeginMsg(const _OptUll& defClkVal)
+{
+ /* Create message */
+ const auto msg = [this, &defClkVal] {
+ if (defClkVal) {
+ _mCurDefClkVal = defClkVal;
+ return bt_message_packet_beginning_create_with_default_clock_snapshot(
+ _mSelfMsgIter, this->_curPkt(), *defClkVal);
+ } else {
+ return bt_message_packet_beginning_create(_mSelfMsgIter, this->_curPkt());
+ }
+ }();
+
+ /* Add to queue */
+ this->_addMsgToQueue(msg);
+}
+
+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.
+ */
+ const auto defClkVal = [this, &otherDefClkVal]() -> _OptUll {
+ if (_mPktBeginDefClkVal && otherDefClkVal) {
+ return std::min(*_mPktBeginDefClkVal, *otherDefClkVal);
+ } else if (_mPktBeginDefClkVal) {
+ return _mPktBeginDefClkVal;
+ } else if (otherDefClkVal) {
+ return otherDefClkVal;
+ }
+
+ return nonstd::nullopt;
+ }();
+
+ /* Emit a packet beginning message now */
+ this->_emitPktBeginMsg(defClkVal);
+}
+
+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);
+
+ if (*discErCounterSnap > *_mCurDiscErCounterSnap) {
+ /* Create and initialize the message */
+ const auto msg = this->_createInitDiscEventsMsg(prevPktEndDefClkVal);
+
+ /* Set its count */
+ bt_message_discarded_events_set_count(msg,
+ *discErCounterSnap - *_mCurDiscErCounterSnap);
+
+ /* Add to queue */
+ this->_addMsgToQueue(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);
+
+ if (*_mCurPktSeqNum + 1 < *seqNum) {
+ /* Create and initialize the message */
+ const auto msg = this->_createInitDiscPktsMsg(prevPktEndDefClkVal);
+
+ /* Set its count */
+ bt_message_discarded_packets_set_count(msg, *seqNum - *_mCurPktSeqNum - 1);
+
+ /* Add to queue */
+ this->_addMsgToQueue(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);
+ }
+}
+
+bt_message *MsgIter::_createEventMsg(const bt2::EventClass cls, const _OptUll& defClkVal)
+{
+ if (defClkVal) {
+ if (this->_curPkt()) {
+ return bt_message_event_create_with_packet_and_default_clock_snapshot(
+ _mSelfMsgIter, cls.libObjPtr(), this->_curPkt(), *defClkVal);
+ } else {
+ return bt_message_event_create_with_default_clock_snapshot(
+ _mSelfMsgIter, cls.libObjPtr(), _mStream.libObjPtr(), *defClkVal);
+ }
+ } else {
+ if (this->_curPkt()) {
+ return bt_message_event_create_with_packet(_mSelfMsgIter, cls.libObjPtr(),
+ this->_curPkt());
+ } else {
+ return bt_message_event_create(_mSelfMsgIter, cls.libObjPtr(), _mStream.libObjPtr());
+ }
+ }
+}
+
+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.emplace(bt2::Message::Shared::createWithoutRef(
+ this->_createEventMsg(*item.cls()->libCls(), item.defClkVal())));
+}
+
+void MsgIter::_handleItem(const FixedLenBitArrayFieldItem& item)
+{
+ if (_ignoreFieldItem(item)) {
+ return;
+ }
+
+ this->_stackTopCurSubFieldAndGoToNextSubField().asBitArray() = item.uIntVal();
+}
+
+void MsgIter::_handleItem(const FixedLenBoolFieldItem& item)
+{
+ if (_ignoreFieldItem(item)) {
+ return;
+ }
+
+ this->_stackTopCurSubFieldAndGoToNextSubField().asBool() = 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() = item.val();
+ } else {
+ BT_ASSERT_DBG(item.cls().len() == 64_bits);
+ field.asDoublePrecisionReal() = item.val();
+ }
+}
+
+void MsgIter::_handleItem(const VarLenSIntFieldItem& item)
+{
+ this->_handleSIntFieldItem(item);
+}
+
+void MsgIter::_handleItem(const VarLenUIntFieldItem& item)
+{
+ this->_handleUIntFieldItem(item);
+}
+
+void MsgIter::_handleStrFieldBeginItem()
+{
+ this->_stackTopCurSubField().asString() = "";
+ _mHaveStrFieldSubstrItemNullChar = false;
+}
+
+void MsgIter::_handleStrFieldEndItem()
+{
+ this->_stackTopGoToNextSubField();
+}
+
+void MsgIter::_handleItem(const NullTerminatedStrFieldBeginItem&)
+{
+ this->_handleStrFieldBeginItem();
+}
+
+void MsgIter::_handleItem(const NullTerminatedStrFieldEndItem&)
+{
+ this->_handleStrFieldEndItem();
+}
+
+void MsgIter::_handleItem(const StrFieldSubstrItem& item)
+{
+ if (_mHaveStrFieldSubstrItemNullChar) {
+ /* No more text data */
+ return;
+ }
+
+ const auto end = item.strEnd();
+
+ this->_stackTopCurSubField().asString().append(item.begin(), end - item.begin());
+ _mHaveStrFieldSubstrItemNullChar = end != item.end();
+}
+
+void MsgIter::_handleItem(const BlobFieldSectionItem& item)
+{
+ std::memcpy(&this->_stackTopCurSubField().asBlob().data()[_mCurBlobFieldDataOffset],
+ item.begin(), item.size().bytes());
+ _mCurBlobFieldDataOffset += item.size().bytes();
+}
+
+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& item)
+{
+ _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&)
+{
+ this->_handleStrFieldBeginItem();
+}
+
+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& item)
+{
+ this->_stackPop();
+}
+
+void MsgIter::_addMsgToQueue(bt_message * const msg)
+{
+ _mMsgs.emplace(bt2::ConstMessage::Shared::createWithoutRef(msg));
+}
+
+nonstd::optional<bt2::ConstMessage::Shared> MsgIter::_releaseNextMsg()
+{
+ if (_mMsgs.empty()) {
+ return nonstd::nullopt;
+ }
+
+ auto msg = std::move(_mMsgs.front());
+
+ _mMsgs.pop();
+ return msg;
+}
+
+} /* namespace src */
+} /* namespace ctf */
--- /dev/null
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright (c) 2022 EfficiOS, Inc
+ */
+
+#ifndef CTF_COMMON_SRC_MSG_ITER_HPP
+#define CTF_COMMON_SRC_MSG_ITER_HPP
+
+#include <babeltrace2/babeltrace.h>
+#include <queue>
+#include <stack>
+
+#include "common/assert.h"
+#include "cpp-common/bt2/message.hpp"
+#include "cpp-common/bt2/trace-ir.hpp"
+#include "cpp-common/uuid.hpp"
+#include "item-seq/item-seq-iter.hpp"
+#include "item-seq/item-visitor.hpp"
+#include "item-seq/logging-item-visitor.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 binary 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
+ * `bt2_common::TryAgain` or a medium error.
+ */
+ explicit MsgIter(bt_self_message_iterator *selfMsgIter, const ctf::src::TraceCls& traceCls,
+ nonstd::optional<bt2_common::Uuid> expectedMetadataStreamUuid,
+ bt2::Stream stream, Medium::UP medium, const MsgIterQuirks& quirks,
+ const bt2_common::LogCfg& logCfg);
+
+ /* 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.
+ *
+ * `nonstd::nullopt`:
+ * The iterator is ended.
+ *
+ * May throw whatever Medium::buf() may throw as well as
+ * `bt2_common::Error`.
+ */
+ nonstd::optional<bt2::ConstMessage::Shared> next();
+
+private:
+ /* An optional `unsigned long long` value */
+ using _OptUll = nonstd::optional<unsigned long long>;
+
+ /* 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().size());
+ 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<bt2::StructureField>::value,
+ "`bt2::StructureField` is trivially destructible.");
+ static_assert(std::is_trivially_destructible<bt2::VariantField>::value,
+ "`bt2::VariantField` is trivially destructible.");
+ static_assert(std::is_trivially_destructible<bt2::OptionField>::value,
+ "`bt2::OptionField` is trivially destructible.");
+ static_assert(std::is_trivially_destructible<bt2::ArrayField>::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 BlobFieldSectionItem& item);
+ void _handleItem(const DataStreamInfoItem& item);
+ void _handleItem(const DynLenArrayFieldBeginItem& item);
+ void _handleItem(const DynLenBlobFieldBeginItem& item);
+ void _handleItem(const BlobFieldEndItem& 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 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 _handleItem(const ScopeBeginItem& item);
+ void _handleItem(const ScopeEndItem& item);
+ void _handleItem(const StaticLenArrayFieldBeginItem& item);
+ void _handleItem(const StaticLenBlobFieldBeginItem& item);
+ void _handleItem(const NonNullTerminatedStrFieldBeginItem& item);
+ void _handleItem(const NonNullTerminatedStrFieldEndItem& item);
+ void _handleItem(const StrFieldSubstrItem& item);
+ void _handleItem(const StructFieldBeginItem& item);
+ void _handleItem(const StructFieldEndItem& item);
+ void _handleItem(const VariantFieldBeginItem& item);
+ void _handleItem(const VariantFieldEndItem& item);
+ void _handleItem(const VarLenSEnumFieldItem& item);
+ void _handleItem(const VarLenSIntFieldItem& item);
+ void _handleItem(const VarLenUEnumFieldItem& item);
+ void _handleItem(const VarLenUIntFieldItem& item);
+ void _handleStrFieldBeginItem();
+ void _handleStrFieldEndItem();
+
+ template <typename ItemT>
+ void _handleUIntFieldItem(const ItemT& item)
+ {
+ if (_ignoreFieldItem(item)) {
+ return;
+ }
+
+ const auto field = this->_stackTopCurSubFieldAndGoToNextSubField();
+
+ field.asUnsignedInteger() = item.val();
+ }
+
+ template <typename ItemT>
+ void _handleSIntFieldItem(const ItemT& item)
+ {
+ if (_ignoreFieldItem(item)) {
+ return;
+ }
+
+ const auto field = this->_stackTopCurSubFieldAndGoToNextSubField();
+
+ field.asSignedInteger() = 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 <typename FieldT>
+ void _stackPush(const FieldT field)
+ {
+ _mStack.push(_StackFrame {field});
+ }
+
+ /*
+ * Removes the top stack frame.
+ */
+ void _stackPop()
+ {
+ BT_ASSERT_DBG(!_mStack.empty());
+ _mStack.pop();
+ }
+
+ /*
+ * Returns the current packet (weak) if there's one, or `nullptr`
+ * otherwise.
+ */
+ bt_packet *_curPkt()
+ {
+ if (_mCurPkt) {
+ return (*_mCurPkt)->libObjPtr();
+ }
+
+ return nullptr;
+ }
+
+ /*
+ * Sets the current packet to `pkt`.
+ */
+ void _curPkt(bt2::Packet::Shared pkt)
+ {
+ BT_ASSERT_DBG(!this->_curPkt());
+ _mCurPkt = std::move(pkt);
+ }
+
+ /*
+ * Resets the current packet.
+ */
+ void _resetCurPkt()
+ {
+ BT_ASSERT_DBG(this->_curPkt());
+ _mCurPkt.reset();
+ }
+
+ /*
+ * Creates and returns an initial discarded events message, not
+ * setting any specific count.
+ */
+ bt_message *_createInitDiscEventsMsg(const _OptUll& prevPktEndDefClkVal);
+
+ /*
+ * Creates and returns an initial discarded packets message, not
+ * setting any specific count.
+ */
+ bt_message *_createInitDiscPktsMsg(const _OptUll& prevPktEndDefClkVal);
+
+ /*
+ * Creates a packet end message and, if needed, updates the current
+ * timestamp.
+ */
+ bt_message *_createPktEndMsgAndUpdateCurDefClkVal();
+
+ /*
+ * Creates an event message using the class `cls` and having
+ * `defClkVal` as its timestamp.
+ */
+ bt_message *_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` (ownership is transfered to this method)
+ * to the message queue.
+ */
+ void _addMsgToQueue(bt_message *msg);
+
+ /*
+ * Returns one of:
+ *
+ * A shared libbabeltrace2 message:
+ * The next available message from the message queue,
+ * removing it from the queue.
+ *
+ * `nonstd::nullopt`:
+ * The message queue is empty.
+ */
+ nonstd::optional<bt2::ConstMessage::Shared> _releaseNextMsg();
+
+ /* libbabeltrace2 self message iterator to create messages (weak) */
+ bt_self_message_iterator *_mSelfMsgIter;
+
+ /* Corresponding libbabeltrace2 stream */
+ bt2::Stream _mStream;
+
+ /* Expected metadata stream UUID, if any */
+ nonstd::optional<bt2_common::Uuid> _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<bt2::ConstMessage::Shared> _mMsgs;
+
+ /* Stack */
+ std::stack<_StackFrame> _mStack;
+
+ /* Root field of current scope */
+ nonstd::optional<bt2::StructureField> _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.
+ */
+ nonstd::optional<bt2::Message::Shared> _mCurMsg;
+
+ /*
+ * If set: the current packet.
+ *
+ * Set while handling a `PktBeginItem` and reset while handling a
+ * `PktEndItem`.
+ */
+ nonstd::optional<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 `StrFieldSubstrItem` items, we
+ * got a null character.
+ */
+ bool _mHaveStrFieldSubstrItemNullChar = false;
+
+ /*
+ * Current BLOB field data offset while processing BLOB field
+ * section items.
+ */
+ std::size_t _mCurBlobFieldDataOffset = 0;
+
+ /* Logging configuration */
+ bt2_common::LogCfg _mLogCfg;
+
+ /* Helper to log items */
+ LoggingItemVisitor _mLoggingVisitor;
+};
+
+} /* namespace src */
+} /* namespace ctf */
+
+#endif /* CTF_COMMON_SRC_MSG_ITER_HPP */