From 87b4b2e2834c7450b98dfb1dbccfe521a7cec75c Mon Sep 17 00:00:00 2001 From: Thorsten de Buhr Date: Tue, 1 Sep 2026 10:37:18 +0200 Subject: [PATCH 1/8] fix(ctrace): preserve exception returns in CTF --- tools/ctrace/src/output/ctf/CtfEncoder.cpp | 14 ++++++++++---- .../src/output/ctf/CtfExceptionLaneTracker.cpp | 18 +++++++++++------- .../src/output/ctf/CtfExceptionLaneTracker.h | 9 +++++---- .../src/output/ctf/CtfMetadataWriter.cpp | 4 +++- tools/ctrace/src/output/ctf/CtfSchema.h | 3 ++- .../src/output/ctf/TraceCompassXmlWriter.cpp | 15 +++++++++++++++ .../src/output/ctf/CtfBundleOutputTests.cpp | 4 +++- .../src/output/ctf/CtfMetadataWriterTests.cpp | 1 + .../unit/src/output/ctf/CtfSchemaTests.cpp | 17 +++++++++++------ 9 files changed, 61 insertions(+), 24 deletions(-) diff --git a/tools/ctrace/src/output/ctf/CtfEncoder.cpp b/tools/ctrace/src/output/ctf/CtfEncoder.cpp index 6d570e96f..1794f4c4f 100644 --- a/tools/ctrace/src/output/ctf/CtfEncoder.cpp +++ b/tools/ctrace/src/output/ctf/CtfEncoder.cpp @@ -379,9 +379,13 @@ void CtfEncoder::writeExceptionEvent(std::uint8_t traceBusId, const ExceptionTra void CtfEncoder::emitExceptionRecord(std::uint8_t traceBusId, std::uint32_t number, CtfExceptionLaneTracker::RecordAction action) { + const auto semanticAction = + action == CtfExceptionLaneTracker::RecordAction::Enter + ? ExceptionAction::Entered + : action == CtfExceptionLaneTracker::RecordAction::Exit ? ExceptionAction::Exited : ExceptionAction::Returned; TraceEvent selectionEvent{ExceptionTraceEvent{ number, - action == CtfExceptionLaneTracker::RecordAction::Enter ? ExceptionAction::Entered : ExceptionAction::Exited, + semanticAction, }}; selectionEvent.traceBusId = traceBusId; if (!traceEventSelectedForOutput(selectionEvent, m_config.selection)) { @@ -389,9 +393,11 @@ void CtfEncoder::emitExceptionRecord(std::uint8_t traceBusId, std::uint32_t numb } constexpr auto payloadSize = 2U + 1U + 2U; const auto eventTimestamp = allocateEventTimestamp(traceBusId); - const auto encodedAction = - CtfSchema::value(action == CtfExceptionLaneTracker::RecordAction::Enter ? CtfSchema::ExceptionAction::Entered - : CtfSchema::ExceptionAction::Exited); + const auto encodedAction = CtfSchema::value( + action == CtfExceptionLaneTracker::RecordAction::Enter + ? CtfSchema::ExceptionAction::Entered + : action == CtfExceptionLaneTracker::RecordAction::Exit ? CtfSchema::ExceptionAction::Exited + : CtfSchema::ExceptionAction::Returned); m_stream.writeRecord(CtfSchema::value(CtfSchema::EventId::Exception), eventTimestamp, traceBusId, payloadSize, [&](CtfStreamWriter::Record& record) { record.writeU16(static_cast(number & 0xffffU)); diff --git a/tools/ctrace/src/output/ctf/CtfExceptionLaneTracker.cpp b/tools/ctrace/src/output/ctf/CtfExceptionLaneTracker.cpp index 4b9ee03ef..ca62896db 100644 --- a/tools/ctrace/src/output/ctf/CtfExceptionLaneTracker.cpp +++ b/tools/ctrace/src/output/ctf/CtfExceptionLaneTracker.cpp @@ -15,7 +15,7 @@ void CtfExceptionLaneTracker::startThreadMode(const RecordEmitter& emit) { - setActiveContext(kThreadModeNumber, emit); + setActiveContext(kThreadModeNumber, RecordAction::Enter, emit); } void CtfExceptionLaneTracker::resetForDiscontinuity(const RecordEmitter& emit) @@ -36,11 +36,12 @@ void CtfExceptionLaneTracker::consume(const ExceptionTraceEvent& event, const Re break; case ExceptionAction::Returned: returnToContext(number); - break; + updateActiveContext(RecordAction::Return, emit); + return; case ExceptionAction::Unknown: return; } - updateActiveContext(emit); + updateActiveContext(RecordAction::Enter, emit); } const std::vector& CtfExceptionLaneTracker::observedExceptionNumbers() const @@ -48,15 +49,18 @@ const std::vector& CtfExceptionLaneTracker::observedExceptionNumb return m_observedExceptionNumbers; } -void CtfExceptionLaneTracker::setActiveContext(std::uint32_t number, const RecordEmitter& emit) +void CtfExceptionLaneTracker::setActiveContext(std::uint32_t number, RecordAction action, const RecordEmitter& emit) { if (m_activeContextNumber.has_value() && *m_activeContextNumber == number) { + if (action == RecordAction::Return) { + emitRecord(number, action, emit); + } return; } if (m_activeContextNumber.has_value()) { emitRecord(*m_activeContextNumber, RecordAction::Exit, emit); } - emitRecord(number, RecordAction::Enter, emit); + emitRecord(number, action, emit); m_activeContextNumber = number; } @@ -69,9 +73,9 @@ void CtfExceptionLaneTracker::closeActiveContext(const RecordEmitter& emit) m_activeContextNumber.reset(); } -void CtfExceptionLaneTracker::updateActiveContext(const RecordEmitter& emit) +void CtfExceptionLaneTracker::updateActiveContext(RecordAction action, const RecordEmitter& emit) { - setActiveContext(m_contextStack.empty() ? kThreadModeNumber : m_contextStack.back().number, emit); + setActiveContext(m_contextStack.empty() ? kThreadModeNumber : m_contextStack.back().number, action, emit); } void CtfExceptionLaneTracker::emitRecord(std::uint32_t number, RecordAction action, const RecordEmitter& emit) diff --git a/tools/ctrace/src/output/ctf/CtfExceptionLaneTracker.h b/tools/ctrace/src/output/ctf/CtfExceptionLaneTracker.h index 67aa813d6..bcd27fd57 100644 --- a/tools/ctrace/src/output/ctf/CtfExceptionLaneTracker.h +++ b/tools/ctrace/src/output/ctf/CtfExceptionLaneTracker.h @@ -18,10 +18,11 @@ /** @brief Tracks active Cortex-M exception contexts for Trace Compass lanes. */ class CtfExceptionLaneTracker { public: - /** @brief Selects whether an emitted lane record enters or exits a context. */ + /** @brief Selects whether an emitted lane record enters, exits, or returns to a context. */ enum class RecordAction { Enter, Exit, + Return, }; /** @brief Emits one exception-lane record. */ @@ -51,12 +52,12 @@ class CtfExceptionLaneTracker { ContextState state = ContextState::Running; }; - /** @brief Switches the emitted active lane to one context. */ - void setActiveContext(std::uint32_t number, const RecordEmitter& emit); + /** @brief Switches the emitted active lane to one context with the requested activation action. */ + void setActiveContext(std::uint32_t number, RecordAction action, const RecordEmitter& emit); /** @brief Closes the currently emitted active lane. */ void closeActiveContext(const RecordEmitter& emit); /** @brief Reconciles emitted lane state with the context stack. */ - void updateActiveContext(const RecordEmitter& emit); + void updateActiveContext(RecordAction action, const RecordEmitter& emit); /** @brief Emits and records one lane transition. */ void emitRecord(std::uint32_t number, RecordAction action, const RecordEmitter& emit); /** @brief Pushes or reactivates an entered exception context. */ diff --git a/tools/ctrace/src/output/ctf/CtfMetadataWriter.cpp b/tools/ctrace/src/output/ctf/CtfMetadataWriter.cpp index a869ccc29..04b8276b0 100644 --- a/tools/ctrace/src/output/ctf/CtfMetadataWriter.cpp +++ b/tools/ctrace/src/output/ctf/CtfMetadataWriter.cpp @@ -291,7 +291,9 @@ typealias enum : uint8_t { "entered" = )" << static_cast(CtfSchema::value(CtfSchema::ExceptionAction::Entered)) << R"(, "exited" = )" - << static_cast(CtfSchema::value(CtfSchema::ExceptionAction::Exited)) << R"( + << static_cast(CtfSchema::value(CtfSchema::ExceptionAction::Exited)) << R"(, + "returned" = )" + << static_cast(CtfSchema::value(CtfSchema::ExceptionAction::Returned)) << R"( } := cmsis_exception_action_t; typealias enum : uint8_t { )"; diff --git a/tools/ctrace/src/output/ctf/CtfSchema.h b/tools/ctrace/src/output/ctf/CtfSchema.h index d5d436fa9..2d585f60d 100644 --- a/tools/ctrace/src/output/ctf/CtfSchema.h +++ b/tools/ctrace/src/output/ctf/CtfSchema.h @@ -43,10 +43,11 @@ enum class DwtAccess : std::uint8_t { Write = 1U, }; -/** @brief Encodes exception entry and exit actions in CTF records. */ +/** @brief Encodes exception entry, exit, and return actions in CTF records. */ enum class ExceptionAction : std::uint8_t { Entered = 0U, Exited = 1U, + Returned = 2U, }; /** @brief Identifies the supported CTF sample value encodings. */ diff --git a/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp b/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp index 2de1a1ce3..524c7e816 100644 --- a/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp +++ b/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp @@ -133,6 +133,21 @@ static std::string stateProviderXml() + + + + + + + + + + + + + + diff --git a/tools/ctrace/test/unit/src/output/ctf/CtfBundleOutputTests.cpp b/tools/ctrace/test/unit/src/output/ctf/CtfBundleOutputTests.cpp index 1413abcb7..09f482d63 100644 --- a/tools/ctrace/test/unit/src/output/ctf/CtfBundleOutputTests.cpp +++ b/tools/ctrace/test/unit/src/output/ctf/CtfBundleOutputTests.cpp @@ -155,6 +155,7 @@ TEST(CtraceUnitTests, testCtfBundleOutputExceptionContext) ASSERT_TRUE(metadata.find("\"entered\" = 0") != std::string::npos) << "CTF exception entered label mismatch"; ASSERT_TRUE(metadata.find("\"exited\" = 1") != std::string::npos) << "CTF exception exited label mismatch"; + ASSERT_TRUE(metadata.find("\"returned\" = 2") != std::string::npos) << "CTF exception returned label mismatch"; ASSERT_TRUE(records == std::vector({ "0:0", @@ -164,6 +165,7 @@ TEST(CtraceUnitTests, testCtfBundleOutputExceptionContext) "54:0", "54:1", "15:0", + "15:2", "15:1", "0:0", })) @@ -339,7 +341,7 @@ TEST(CtraceUnitTests, testCtfWarningsRemainVisibleWithoutResettingContext) dataLoss.writeEvent(exceptionPacket(15U, ExceptionAction::Returned, 20U)); dataLoss.stop(); ASSERT_TRUE(readCtfExceptionRecords(dataLossDir / "stream_0") == - std::vector({"0:0", "0:1", "15:0", "15:1", "15:0"})) + std::vector({"0:0", "0:1", "15:0", "15:1", "15:2"})) << "filtered data-loss must still reset the CTF exception context"; } diff --git a/tools/ctrace/test/unit/src/output/ctf/CtfMetadataWriterTests.cpp b/tools/ctrace/test/unit/src/output/ctf/CtfMetadataWriterTests.cpp index 43962bdd8..3c475282c 100644 --- a/tools/ctrace/test/unit/src/output/ctf/CtfMetadataWriterTests.cpp +++ b/tools/ctrace/test/unit/src/output/ctf/CtfMetadataWriterTests.cpp @@ -90,6 +90,7 @@ TEST(CtraceUnitTests, testTraceCompassXmlUsesCurrentCtfEvents) for (const auto eventId : visualizedEvents) { EXPECT_NE(xml.find("eventName=\"" + std::string(CtfSchema::eventName(eventId)) + "\""), std::string::npos); } + EXPECT_NE(xml.find("value=\"returned\""), std::string::npos); } TEST(CtraceUnitTests, testCtfTextWritersReportDeviceWriteFailures) diff --git a/tools/ctrace/test/unit/src/output/ctf/CtfSchemaTests.cpp b/tools/ctrace/test/unit/src/output/ctf/CtfSchemaTests.cpp index 45cd49180..7b37411aa 100644 --- a/tools/ctrace/test/unit/src/output/ctf/CtfSchemaTests.cpp +++ b/tools/ctrace/test/unit/src/output/ctf/CtfSchemaTests.cpp @@ -33,9 +33,10 @@ TEST(CtraceUnitTests, testCtfSchemaUsesDenseIdentifiers) CtfSchema::value(CtfSchema::TraceStatusReason::DecodeError), CtfSchema::value(CtfSchema::TraceStatusReason::DataLoss), }; - constexpr std::array exceptionActions{ + constexpr std::array exceptionActions{ CtfSchema::value(CtfSchema::ExceptionAction::Entered), CtfSchema::value(CtfSchema::ExceptionAction::Exited), + CtfSchema::value(CtfSchema::ExceptionAction::Returned), }; constexpr std::array valueTags{ CtfSchema::value(CtfSchema::ValueTag::Signed8), CtfSchema::value(CtfSchema::ValueTag::Unsigned8), @@ -98,8 +99,10 @@ TEST(CtraceUnitTests, testCtfExceptionLaneTracker) CtfExceptionLaneTracker tracker; std::vector records; const auto emit = [&records](std::uint32_t number, CtfExceptionLaneTracker::RecordAction action) { - records.push_back(std::to_string(number) + - (action == CtfExceptionLaneTracker::RecordAction::Enter ? ":enter" : ":exit")); + const auto suffix = action == CtfExceptionLaneTracker::RecordAction::Enter + ? ":enter" + : action == CtfExceptionLaneTracker::RecordAction::Exit ? ":exit" : ":return"; + records.push_back(std::to_string(number) + suffix); }; tracker.startThreadMode(emit); @@ -124,6 +127,7 @@ TEST(CtraceUnitTests, testCtfExceptionLaneTracker) "54:enter", "54:exit", "3:enter", + "3:return", "3:exit", "0:enter", })) @@ -152,14 +156,15 @@ TEST(CtraceUnitTests, testCtfExceptionLaneTracker) << "CtfExceptionLaneTracker must not exit a preempted context before its return packet"; resumedTracker.consume(ExceptionTraceEvent{3, ExceptionAction::Returned}, emit); resumedTracker.consume(ExceptionTraceEvent{3, ExceptionAction::Exited}, emit); - ASSERT_TRUE(records.size() == preemptedRecordCount + 2U && records[records.size() - 2U] == "3:exit" && + ASSERT_TRUE(records.size() == preemptedRecordCount + 3U && records[records.size() - 3U] == "3:return" && + records[records.size() - 2U] == "3:exit" && records.back() == "0:enter") - << "CtfExceptionLaneTracker must exit a resumed context"; + << "CtfExceptionLaneTracker must preserve return and exit the resumed context"; CtfExceptionLaneTracker returnTracker; records.clear(); returnTracker.consume(ExceptionTraceEvent{3, ExceptionAction::Entered}, emit); returnTracker.consume(ExceptionTraceEvent{15, ExceptionAction::Entered}, emit); returnTracker.consume(ExceptionTraceEvent{3, ExceptionAction::Returned}, emit); - EXPECT_EQ(records.back(), "3:enter"); + EXPECT_EQ(records.back(), "3:return"); } From a947ea0ca5d650b55ea216deb62fa7c546faced1 Mon Sep 17 00:00:00 2001 From: Thorsten de Buhr Date: Tue, 1 Sep 2026 11:38:57 +0200 Subject: [PATCH 2/8] feat(ctrace): visualize exception return intervals --- tools/ctrace/src/output/ctf/CtfEncoder.cpp | 42 ++++++---- tools/ctrace/src/output/ctf/CtfEncoder.h | 3 +- .../output/ctf/CtfExceptionLaneTracker.cpp | 43 ++++++----- .../src/output/ctf/CtfExceptionLaneTracker.h | 23 ++++-- .../src/output/ctf/CtfMetadataWriter.cpp | 7 ++ tools/ctrace/src/output/ctf/CtfSchema.h | 12 +++ .../src/output/ctf/TraceCompassXmlWriter.cpp | 77 +++++++++++++++++++ .../src/output/ctf/CtfBundleOutputTests.cpp | 30 ++++---- .../unit/src/output/ctf/CtfEncoderTests.cpp | 13 ++-- .../src/output/ctf/CtfMetadataWriterTests.cpp | 5 ++ .../unit/src/output/ctf/CtfSchemaTests.cpp | 50 +++++++----- .../ctrace/test/unit/support/CtfTestSupport.h | 6 +- 12 files changed, 224 insertions(+), 87 deletions(-) diff --git a/tools/ctrace/src/output/ctf/CtfEncoder.cpp b/tools/ctrace/src/output/ctf/CtfEncoder.cpp index 1794f4c4f..38a326611 100644 --- a/tools/ctrace/src/output/ctf/CtfEncoder.cpp +++ b/tools/ctrace/src/output/ctf/CtfEncoder.cpp @@ -346,6 +346,18 @@ void CtfEncoder::writeGlobalTimestampEvent(const TraceEvent& event, const Global void CtfEncoder::writeTraceStatusEvent(std::uint8_t reason, std::uint8_t traceBusId, bool emitEvent) { + if (reason == CtfSchema::value(CtfSchema::TraceStatusReason::Overflow) || + reason == CtfSchema::value(CtfSchema::TraceStatusReason::DataLoss)) { + const auto lane = m_exceptionLanes.find(traceBusId); + if (lane != m_exceptionLanes.end()) { + lane->second.resetForDiscontinuity( + [this, traceBusId](std::uint32_t number, CtfExceptionLaneTracker::RecordAction action, + CtfExceptionLaneTracker::RecordOrigin origin) { + emitExceptionRecord(traceBusId, number, action, origin); + }); + } + } + if (emitEvent) { constexpr auto payloadSize = 1U + 4U; const auto eventTimestamp = allocateEventTimestamp(traceBusId); @@ -355,29 +367,20 @@ void CtfEncoder::writeTraceStatusEvent(std::uint8_t reason, std::uint8_t traceBu record.writeU32(ctfOverflowCount(m_streamStates[traceBusId].overflowCount)); }); } - - if (reason == CtfSchema::value(CtfSchema::TraceStatusReason::Overflow) || - reason == CtfSchema::value(CtfSchema::TraceStatusReason::DataLoss)) { - const auto lane = m_exceptionLanes.find(traceBusId); - if (lane != m_exceptionLanes.end()) { - lane->second.resetForDiscontinuity( - [this, traceBusId](std::uint32_t number, CtfExceptionLaneTracker::RecordAction action) { - emitExceptionRecord(traceBusId, number, action); - }); - } - } } void CtfEncoder::writeExceptionEvent(std::uint8_t traceBusId, const ExceptionTraceEvent& exception) { exceptionLane(traceBusId) - .consume(exception, [this, traceBusId](std::uint32_t number, CtfExceptionLaneTracker::RecordAction action) { - emitExceptionRecord(traceBusId, number, action); + .consume(exception, [this, traceBusId](std::uint32_t number, CtfExceptionLaneTracker::RecordAction action, + CtfExceptionLaneTracker::RecordOrigin origin) { + emitExceptionRecord(traceBusId, number, action, origin); }); } void CtfEncoder::emitExceptionRecord(std::uint8_t traceBusId, std::uint32_t number, - CtfExceptionLaneTracker::RecordAction action) + CtfExceptionLaneTracker::RecordAction action, + CtfExceptionLaneTracker::RecordOrigin origin) { const auto semanticAction = action == CtfExceptionLaneTracker::RecordAction::Enter @@ -391,18 +394,22 @@ void CtfEncoder::emitExceptionRecord(std::uint8_t traceBusId, std::uint32_t numb if (!traceEventSelectedForOutput(selectionEvent, m_config.selection)) { return; } - constexpr auto payloadSize = 2U + 1U + 2U; + constexpr auto payloadSize = 2U + 1U + 2U + 1U; const auto eventTimestamp = allocateEventTimestamp(traceBusId); const auto encodedAction = CtfSchema::value( action == CtfExceptionLaneTracker::RecordAction::Enter ? CtfSchema::ExceptionAction::Entered : action == CtfExceptionLaneTracker::RecordAction::Exit ? CtfSchema::ExceptionAction::Exited : CtfSchema::ExceptionAction::Returned); + const auto encodedOrigin = CtfSchema::value(origin == CtfExceptionLaneTracker::RecordOrigin::Trace + ? CtfSchema::ExceptionOrigin::Trace + : CtfSchema::ExceptionOrigin::Synthetic); m_stream.writeRecord(CtfSchema::value(CtfSchema::EventId::Exception), eventTimestamp, traceBusId, payloadSize, [&](CtfStreamWriter::Record& record) { record.writeU16(static_cast(number & 0xffffU)); record.writeU8(encodedAction); record.writeU16(static_cast(number & 0xffffU)); + record.writeU8(encodedOrigin); }); } @@ -411,8 +418,9 @@ CtfExceptionLaneTracker& CtfEncoder::exceptionLane(std::uint8_t traceBusId) const auto [lane, inserted] = m_exceptionLanes.try_emplace(traceBusId); if (inserted) { lane->second.startThreadMode( - [this, traceBusId](std::uint32_t number, CtfExceptionLaneTracker::RecordAction action) { - emitExceptionRecord(traceBusId, number, action); + [this, traceBusId](std::uint32_t number, CtfExceptionLaneTracker::RecordAction action, + CtfExceptionLaneTracker::RecordOrigin origin) { + emitExceptionRecord(traceBusId, number, action, origin); }); } return lane->second; diff --git a/tools/ctrace/src/output/ctf/CtfEncoder.h b/tools/ctrace/src/output/ctf/CtfEncoder.h index d7659eb39..3f25d92e1 100644 --- a/tools/ctrace/src/output/ctf/CtfEncoder.h +++ b/tools/ctrace/src/output/ctf/CtfEncoder.h @@ -80,7 +80,8 @@ class CtfEncoder final { /** @brief Applies one exception transition to its CTF lane state. */ void writeExceptionEvent(std::uint8_t traceBusId, const ExceptionTraceEvent& exception); /** @brief Emits one concrete exception lane record. */ - void emitExceptionRecord(std::uint8_t traceBusId, std::uint32_t number, CtfExceptionLaneTracker::RecordAction action); + void emitExceptionRecord(std::uint8_t traceBusId, std::uint32_t number, CtfExceptionLaneTracker::RecordAction action, + CtfExceptionLaneTracker::RecordOrigin origin); /** @brief Returns the exception tracker for one stream. */ CtfExceptionLaneTracker& exceptionLane(std::uint8_t traceBusId); /** @brief Computes CTF sample flags and saturated overflow count. */ diff --git a/tools/ctrace/src/output/ctf/CtfExceptionLaneTracker.cpp b/tools/ctrace/src/output/ctf/CtfExceptionLaneTracker.cpp index ca62896db..cd14daeda 100644 --- a/tools/ctrace/src/output/ctf/CtfExceptionLaneTracker.cpp +++ b/tools/ctrace/src/output/ctf/CtfExceptionLaneTracker.cpp @@ -15,13 +15,13 @@ void CtfExceptionLaneTracker::startThreadMode(const RecordEmitter& emit) { - setActiveContext(kThreadModeNumber, RecordAction::Enter, emit); + setActiveContext(kThreadModeNumber, RecordAction::Enter, RecordOrigin::Synthetic, emit); } void CtfExceptionLaneTracker::resetForDiscontinuity(const RecordEmitter& emit) { m_contextStack.clear(); - closeActiveContext(emit); + closeActiveContext(RecordOrigin::Synthetic, emit); } void CtfExceptionLaneTracker::consume(const ExceptionTraceEvent& event, const RecordEmitter& emit) @@ -30,18 +30,20 @@ void CtfExceptionLaneTracker::consume(const ExceptionTraceEvent& event, const Re switch (event.action) { case ExceptionAction::Entered: enterContext(number); - break; + updateActiveContext(RecordAction::Enter, RecordOrigin::Trace, emit); + return; case ExceptionAction::Exited: - exitContext(number); - break; + if (exitContext(number)) { + closeActiveContext(RecordOrigin::Trace, emit); + } + return; case ExceptionAction::Returned: returnToContext(number); - updateActiveContext(RecordAction::Return, emit); + updateActiveContext(RecordAction::Return, RecordOrigin::Trace, emit); return; case ExceptionAction::Unknown: return; } - updateActiveContext(RecordAction::Enter, emit); } const std::vector& CtfExceptionLaneTracker::observedExceptionNumbers() const @@ -49,42 +51,44 @@ const std::vector& CtfExceptionLaneTracker::observedExceptionNumb return m_observedExceptionNumbers; } -void CtfExceptionLaneTracker::setActiveContext(std::uint32_t number, RecordAction action, const RecordEmitter& emit) +void CtfExceptionLaneTracker::setActiveContext(std::uint32_t number, RecordAction action, RecordOrigin origin, + const RecordEmitter& emit) { if (m_activeContextNumber.has_value() && *m_activeContextNumber == number) { if (action == RecordAction::Return) { - emitRecord(number, action, emit); + emitRecord(number, action, origin, emit); } return; } if (m_activeContextNumber.has_value()) { - emitRecord(*m_activeContextNumber, RecordAction::Exit, emit); + emitRecord(*m_activeContextNumber, RecordAction::Exit, RecordOrigin::Synthetic, emit); } - emitRecord(number, action, emit); + emitRecord(number, action, origin, emit); m_activeContextNumber = number; } -void CtfExceptionLaneTracker::closeActiveContext(const RecordEmitter& emit) +void CtfExceptionLaneTracker::closeActiveContext(RecordOrigin origin, const RecordEmitter& emit) { if (!m_activeContextNumber.has_value()) { return; } - emitRecord(*m_activeContextNumber, RecordAction::Exit, emit); + emitRecord(*m_activeContextNumber, RecordAction::Exit, origin, emit); m_activeContextNumber.reset(); } -void CtfExceptionLaneTracker::updateActiveContext(RecordAction action, const RecordEmitter& emit) +void CtfExceptionLaneTracker::updateActiveContext(RecordAction action, RecordOrigin origin, const RecordEmitter& emit) { - setActiveContext(m_contextStack.empty() ? kThreadModeNumber : m_contextStack.back().number, action, emit); + setActiveContext(m_contextStack.empty() ? kThreadModeNumber : m_contextStack.back().number, action, origin, emit); } -void CtfExceptionLaneTracker::emitRecord(std::uint32_t number, RecordAction action, const RecordEmitter& emit) +void CtfExceptionLaneTracker::emitRecord(std::uint32_t number, RecordAction action, RecordOrigin origin, + const RecordEmitter& emit) { if (std::find(m_observedExceptionNumbers.begin(), m_observedExceptionNumbers.end(), number) == m_observedExceptionNumbers.end()) { m_observedExceptionNumbers.push_back(number); } - emit(number, action); + emit(number, action, origin); } void CtfExceptionLaneTracker::enterContext(std::uint32_t number) @@ -95,13 +99,14 @@ void CtfExceptionLaneTracker::enterContext(std::uint32_t number) m_contextStack.push_back({number, ContextState::Running}); } -void CtfExceptionLaneTracker::exitContext(std::uint32_t number) +bool CtfExceptionLaneTracker::exitContext(std::uint32_t number) { if (m_contextStack.empty() || m_contextStack.back().number != number || m_contextStack.back().state != ContextState::Running) { - return; + return false; } m_contextStack.pop_back(); + return true; } void CtfExceptionLaneTracker::returnToContext(std::uint32_t number) diff --git a/tools/ctrace/src/output/ctf/CtfExceptionLaneTracker.h b/tools/ctrace/src/output/ctf/CtfExceptionLaneTracker.h index bcd27fd57..466058f9b 100644 --- a/tools/ctrace/src/output/ctf/CtfExceptionLaneTracker.h +++ b/tools/ctrace/src/output/ctf/CtfExceptionLaneTracker.h @@ -25,8 +25,15 @@ class CtfExceptionLaneTracker { Return, }; + /** @brief Identifies whether a record came from trace input or lane reconstruction. */ + enum class RecordOrigin { + Trace, + Synthetic, + }; + /** @brief Emits one exception-lane record. */ - using RecordEmitter = std::function; + using RecordEmitter = + std::function; /** @brief Starts the initial thread-mode context. */ void startThreadMode(const RecordEmitter& emit); @@ -53,17 +60,17 @@ class CtfExceptionLaneTracker { }; /** @brief Switches the emitted active lane to one context with the requested activation action. */ - void setActiveContext(std::uint32_t number, RecordAction action, const RecordEmitter& emit); + void setActiveContext(std::uint32_t number, RecordAction action, RecordOrigin origin, const RecordEmitter& emit); /** @brief Closes the currently emitted active lane. */ - void closeActiveContext(const RecordEmitter& emit); - /** @brief Reconciles emitted lane state with the context stack. */ - void updateActiveContext(RecordAction action, const RecordEmitter& emit); + void closeActiveContext(RecordOrigin origin, const RecordEmitter& emit); + /** @brief Activates the context selected by the stack after an enter or return. */ + void updateActiveContext(RecordAction action, RecordOrigin origin, const RecordEmitter& emit); /** @brief Emits and records one lane transition. */ - void emitRecord(std::uint32_t number, RecordAction action, const RecordEmitter& emit); + void emitRecord(std::uint32_t number, RecordAction action, RecordOrigin origin, const RecordEmitter& emit); /** @brief Pushes or reactivates an entered exception context. */ void enterContext(std::uint32_t number); - /** @brief Removes an exited exception context. */ - void exitContext(std::uint32_t number); + /** @brief Removes an exited running exception context. */ + bool exitContext(std::uint32_t number); /** @brief Returns the stack to a previously active context. */ void returnToContext(std::uint32_t number); diff --git a/tools/ctrace/src/output/ctf/CtfMetadataWriter.cpp b/tools/ctrace/src/output/ctf/CtfMetadataWriter.cpp index 04b8276b0..6d3b7f1f9 100644 --- a/tools/ctrace/src/output/ctf/CtfMetadataWriter.cpp +++ b/tools/ctrace/src/output/ctf/CtfMetadataWriter.cpp @@ -295,6 +295,12 @@ typealias enum : uint8_t { "returned" = )" << static_cast(CtfSchema::value(CtfSchema::ExceptionAction::Returned)) << R"( } := cmsis_exception_action_t; +typealias enum : uint8_t { + "trace" = )" + << static_cast(CtfSchema::value(CtfSchema::ExceptionOrigin::Trace)) << R"(, + "synthetic" = )" + << static_cast(CtfSchema::value(CtfSchema::ExceptionOrigin::Synthetic)) << R"( +} := cmsis_exception_origin_t; typealias enum : uint8_t { )"; std::set itmLabels; @@ -448,6 +454,7 @@ event { cmsis_exception_number_t cmsis_exception_number; cmsis_exception_action_t cmsis_exception_action; uint16_t cmsis_exception_number_value; + cmsis_exception_origin_t cmsis_exception_origin; }; }; diff --git a/tools/ctrace/src/output/ctf/CtfSchema.h b/tools/ctrace/src/output/ctf/CtfSchema.h index 2d585f60d..2eef071f2 100644 --- a/tools/ctrace/src/output/ctf/CtfSchema.h +++ b/tools/ctrace/src/output/ctf/CtfSchema.h @@ -50,6 +50,12 @@ enum class ExceptionAction : std::uint8_t { Returned = 2U, }; +/** @brief Identifies whether an exception record came from trace input or lane reconstruction. */ +enum class ExceptionOrigin : std::uint8_t { + Trace = 0U, + Synthetic = 1U, +}; + /** @brief Identifies the supported CTF sample value encodings. */ enum class ValueTag : std::uint8_t { Signed8 = 0U, @@ -148,6 +154,12 @@ constexpr std::uint8_t value(ExceptionAction action) return static_cast(action); } +/** @brief Returns the integer representation of an exception record origin. */ +constexpr std::uint8_t value(ExceptionOrigin origin) +{ + return static_cast(origin); +} + /** @brief Returns the stable schema name of a CTF event ID. */ constexpr std::string_view eventName(EventId id) { diff --git a/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp b/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp index 524c7e816..0af19461b 100644 --- a/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp +++ b/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp @@ -118,6 +118,20 @@ static std::string stateProviderXml() + + + + + + + + + + + + + + @@ -133,6 +147,26 @@ static std::string stateProviderXml() + + + + + + + + + + + + + + + + + + + + @@ -148,6 +182,20 @@ static std::string stateProviderXml() + + + + + + + + + + + + + + @@ -157,6 +205,34 @@ static std::string stateProviderXml() + + + + + + + + + + + + + + + + + + + + + + + + + + + + )"; @@ -194,6 +270,7 @@ static std::string viewsXml() +