From d2505ef390cb17cbbbd17b5cde440158d6c4b188 Mon Sep 17 00:00:00 2001 From: Thorsten de Buhr Date: Wed, 2 Sep 2026 14:55:26 +0200 Subject: [PATCH] feat(ctrace): recognize PMU trace-on-overflow packets --- tools/ctrace/src/decode/DwtPacketDecoder.cpp | 20 ++++++++++++---- tools/ctrace/src/model/TraceEvent.h | 6 ++--- .../test/integration/src/CtraceIntegTests.cpp | 22 +++++++++++++++++ .../src/decode/CortexMPostDecoderTests.cpp | 24 +++++++++++++++++++ .../unit/src/decode/DecodePipelineTests.cpp | 2 +- .../unit/src/decode/DwtPacketDecoderTests.cpp | 11 +++++---- 6 files changed, 70 insertions(+), 15 deletions(-) diff --git a/tools/ctrace/src/decode/DwtPacketDecoder.cpp b/tools/ctrace/src/decode/DwtPacketDecoder.cpp index c59b85811..6f0677568 100644 --- a/tools/ctrace/src/decode/DwtPacketDecoder.cpp +++ b/tools/ctrace/src/decode/DwtPacketDecoder.cpp @@ -22,7 +22,7 @@ enum class DwtPacketSource : std::uint8_t { EventCounter = 0U, ExceptionTrace = 1U, PeriodicPcSample = 2U, - PmuOverflow = 3U, + PmuTraceOnOverflow = 3U, }; /** @brief Identifies address and value variants of DWT data-trace packets. */ @@ -50,6 +50,7 @@ constexpr std::uint8_t kDataTracePacketTypeShift = 3U; constexpr std::uint32_t kExceptionNumberMask = 0x1ffU; constexpr std::uint32_t kExceptionActionMask = 0x3U; constexpr std::uint32_t kExceptionActionShift = 12U; +constexpr std::uint32_t kPmuOverflowMask = 0xffU; constexpr std::uint8_t kArmv7MFullPcBytes = 4U; constexpr std::uint8_t kArmv7MAddressOffsetBytes = 2U; @@ -60,11 +61,20 @@ std::vector DwtPacketDecoder::decode(const DwtPayloadPacket& payload const auto discriminator = payload.discriminator; const auto source = static_cast(discriminator); - if (source == DwtPacketSource::EventCounter || source == DwtPacketSource::PmuOverflow) { + if (source == DwtPacketSource::EventCounter) { output = flush(payload.quality, payload.tcyc); - TraceEvent packet = source == DwtPacketSource::EventCounter - ? TraceEvent(DwtEventTraceEvent{discriminator, payload.size, payload.value}) - : TraceEvent(PmuTraceEvent{discriminator, payload.size, payload.value}); + TraceEvent packet{DwtEventTraceEvent{discriminator, payload.size, payload.value}}; + packet.index = payload.index; + packet.traceBusId = payload.traceBusId; + packet.tcyc = payload.tcyc; + packet.quality = payload.quality; + output.push_back(std::move(packet)); + return output; + } + + if (source == DwtPacketSource::PmuTraceOnOverflow) { + output = flush(payload.quality, payload.tcyc); + TraceEvent packet{PmuTraceEvent{static_cast(payload.value & kPmuOverflowMask)}}; packet.index = payload.index; packet.traceBusId = payload.traceBusId; packet.tcyc = payload.tcyc; diff --git a/tools/ctrace/src/model/TraceEvent.h b/tools/ctrace/src/model/TraceEvent.h index ad3a75f7a..17c7fb570 100644 --- a/tools/ctrace/src/model/TraceEvent.h +++ b/tools/ctrace/src/model/TraceEvent.h @@ -132,11 +132,9 @@ struct DwtEventTraceEvent { std::uint32_t value = 0; }; -/** @brief Contains a decoded PMU overflow packet. */ +/** @brief Contains the OVn counter mask from a decoded PMU trace-on-overflow packet. */ struct PmuTraceEvent { - std::uint32_t discriminator = 0; - std::uint8_t size = 0; - std::uint32_t value = 0; + std::uint8_t overflowMask = 0; }; /** @brief Contains a periodic DWT PC sample or its processor-sleep indication. */ diff --git a/tools/ctrace/test/integration/src/CtraceIntegTests.cpp b/tools/ctrace/test/integration/src/CtraceIntegTests.cpp index d180e588f..c5b4a8862 100644 --- a/tools/ctrace/test/integration/src/CtraceIntegTests.cpp +++ b/tools/ctrace/test/integration/src/CtraceIntegTests.cpp @@ -139,6 +139,28 @@ TEST_F(CtraceIntegTests, GeneratesAllOutputs) expectNonEmptyFile(workDirectory() / "Minimal.SWO.traceanalysis.xml"); } +TEST_F(CtraceIntegTests, IgnoresPmuPacketsUntilOutputSemanticsExist) +{ + writeFile(workDirectory() / "Pmu.ctrace-run.yml", R"yml(ctrace-run: + ctrace-setup: + - timestamps: + clock: 400000000 + ctrace-refs: [] +)yml"); + + const std::string raw{"\0\0\0\0\0\x80\x1d\x81\x09\x41", 10U}; + writeFile(workDirectory() / "Pmu.SWO.raw", raw); + + const auto result = run({"ctrace", workDirectory().string(), "--target", "Pmu", "--all"}); + EXPECT_EQ(0, result.exitCode) << result.stderrText; + EXPECT_EQ("cycles,stream,type,source,value,pc,offset,note\n" + "0,,itm,1,0x41,,,\n", + readTextFile(workDirectory() / "Pmu.SWO.csv")); + expectNonEmptyFile(workDirectory() / "Pmu.ctf" / "metadata"); + expectNonEmptyFile(workDirectory() / "Pmu.ctf" / "stream_0"); + expectNonEmptyFile(workDirectory() / "Pmu.SWO.traceanalysis.xml"); +} + TEST_F(CtraceIntegTests, RejectsInvalidOptionCombination) { const auto result = run({"ctrace", "--version", "--type", "DWT"}); diff --git a/tools/ctrace/test/unit/src/decode/CortexMPostDecoderTests.cpp b/tools/ctrace/test/unit/src/decode/CortexMPostDecoderTests.cpp index 198dbc171..95b1edef3 100644 --- a/tools/ctrace/test/unit/src/decode/CortexMPostDecoderTests.cpp +++ b/tools/ctrace/test/unit/src/decode/CortexMPostDecoderTests.cpp @@ -101,3 +101,27 @@ TEST(CtraceUnitTests, testCortexMPostDecoderMapsDiscontinuityTimestamp) ASSERT_TRUE(sink.events().size() == 3) << "timestamp discontinuity event count mismatch"; ASSERT_TRUE(sink.events()[2].tcyc == 207U) << "post-decoder explicit discontinuity timestamp mismatch"; } + +TEST(CtraceUnitTests, testCortexMPostDecoderLabelsPmuTraceOnOverflowPacket) +{ + CollectingEventSink sink; + CortexMPostDecoder decoder(sink); + + decoder.append(openCsdTimestampElement(100U, 10U, 5U)); + auto pmuElement = openCsdElement(OpenCsdTraceElement::Kind::Hardware, 24U, 5U); + pmuElement.discriminator = 3U; + pmuElement.size = 1U; + pmuElement.value = 0x81U; + decoder.append(pmuElement); + decoder.finish(); + + ASSERT_TRUE(sink.events().size() == 2U) << "post-decoder PMU event count mismatch"; + const auto& packet = sink.events().back(); + const auto* pmu = traceEventPayload(packet); + ASSERT_TRUE(pmu != nullptr && pmu->overflowMask == 0x81U) + << "post-decoder must label discriminator 3 as a PMU trace-on-overflow event"; + ASSERT_TRUE(packet.index == 24U && packet.traceBusId == 5U && packet.tcyc == 100U) + << "post-decoder PMU event context mismatch"; + ASSERT_TRUE(packet.quality.has_value() && packet.quality->timestampReliable) + << "post-decoder PMU timestamp quality mismatch"; +} diff --git a/tools/ctrace/test/unit/src/decode/DecodePipelineTests.cpp b/tools/ctrace/test/unit/src/decode/DecodePipelineTests.cpp index 7964fe09a..7df52b8e9 100644 --- a/tools/ctrace/test/unit/src/decode/DecodePipelineTests.cpp +++ b/tools/ctrace/test/unit/src/decode/DecodePipelineTests.cpp @@ -575,7 +575,7 @@ TEST(CtraceUnitTests, testDecodePipelinePreservesDwtEventAndPmuPackets) const auto* pmu = traceEventPayload(packet); foundEvent = foundEvent || (event != nullptr && event->discriminator == 0U && event->size == 1U && event->value == 0x21U); - foundPmu = foundPmu || (pmu != nullptr && pmu->discriminator == 3U && pmu->size == 1U && pmu->value == 0x81U); + foundPmu = foundPmu || (pmu != nullptr && pmu->overflowMask == 0x81U); } ASSERT_TRUE(foundEvent) << "OpenCSD DWT event-counter packet must survive post-decoding"; ASSERT_TRUE(foundPmu) << "OpenCSD PMU-overflow packet must survive post-decoding"; diff --git a/tools/ctrace/test/unit/src/decode/DwtPacketDecoderTests.cpp b/tools/ctrace/test/unit/src/decode/DwtPacketDecoderTests.cpp index 12871cb9f..784771cf8 100644 --- a/tools/ctrace/test/unit/src/decode/DwtPacketDecoderTests.cpp +++ b/tools/ctrace/test/unit/src/decode/DwtPacketDecoderTests.cpp @@ -95,11 +95,12 @@ TEST(CtraceUnitTests, testDwtCounterPacketsArePreservedUntilOutputSemanticsExist ASSERT_TRUE((discriminator == 0U && event != nullptr && pmu == nullptr) || (discriminator == 3U && event == nullptr && pmu != nullptr)) << "DWT counter semantic event type mismatch"; - const auto actualDiscriminator = event != nullptr ? event->discriminator : pmu->discriminator; - const auto actualSize = event != nullptr ? event->size : pmu->size; - const auto actualValue = event != nullptr ? event->value : pmu->value; - ASSERT_TRUE(actualDiscriminator == discriminator) << "DWT counter discriminator mismatch"; - ASSERT_TRUE(actualSize == 1U && actualValue == 0x21U) << "DWT counter payload mismatch"; + if (event != nullptr) { + ASSERT_TRUE(event->discriminator == discriminator && event->size == 1U && event->value == 0x21U) + << "DWT event-counter payload mismatch"; + } else { + ASSERT_TRUE(pmu->overflowMask == 0x21U) << "PMU OVn counter mask mismatch"; + } ASSERT_TRUE(packet.index == 23U && packet.traceBusId == 4U) << "DWT counter identity mismatch"; ASSERT_TRUE(packet.tcyc == 949339100U) << "DWT counter timestamp mismatch"; ASSERT_TRUE(packet.quality.has_value() && packet.quality->overflow) << "DWT counter overflow status mismatch";