From 12af5c06c063ca22ef5ecdfa0f253fb8b31a5e54 Mon Sep 17 00:00:00 2001 From: Thorsten de Buhr Date: Thu, 3 Sep 2026 16:42:50 +0200 Subject: [PATCH] feat(ctrace): output DWT and PMU event counters --- tools/ctrace/docs/architecture.md | 13 +- tools/ctrace/docs/todo.md | 3 +- tools/ctrace/src/decode/DwtPacketDecoder.cpp | 56 ++++++- .../src/diagnostics/TraceIssueReporter.cpp | 2 + tools/ctrace/src/model/TraceEvent.h | 68 +++++++- tools/ctrace/src/model/TraceSelection.cpp | 8 +- tools/ctrace/src/output/csv/CsvRowMapper.cpp | 18 +++ tools/ctrace/src/output/ctf/CtfEncoder.cpp | 45 ++++++ tools/ctrace/src/output/ctf/CtfEncoder.h | 4 + .../src/output/ctf/CtfMetadataWriter.cpp | 56 +++++++ tools/ctrace/src/output/ctf/CtfSchema.h | 64 ++++++++ .../src/output/ctf/TraceCompassXmlWriter.cpp | 91 +++++++++++ tools/ctrace/test/data/README.md | 9 ++ .../trace-event/trace-event.ctrace-run.yml | 5 + .../test/data/trace-event/trace-event.raw | Bin 0 -> 19999 bytes tools/ctrace/test/integration/CMakeLists.txt | 4 + .../test/integration/src/CtraceIntegTests.cpp | 146 +++++++++++++++++- .../test/unit/src/cli/CliParserTests.cpp | 6 +- .../unit/src/decode/DecodePipelineTests.cpp | 3 +- .../unit/src/decode/DwtPacketDecoderTests.cpp | 140 +++++++++++++---- .../unit/src/output/csv/CsvRowMapperTests.cpp | 7 +- .../unit/src/output/ctf/CtfEncoderTests.cpp | 71 +++++++++ .../src/output/ctf/CtfMetadataWriterTests.cpp | 29 +++- .../unit/src/output/ctf/CtfSchemaTests.cpp | 28 +++- .../ctrace/test/unit/support/CtfTestSupport.h | 6 + 25 files changed, 816 insertions(+), 66 deletions(-) create mode 100644 tools/ctrace/test/data/trace-event/trace-event.ctrace-run.yml create mode 100644 tools/ctrace/test/data/trace-event/trace-event.raw diff --git a/tools/ctrace/docs/architecture.md b/tools/ctrace/docs/architecture.md index 57a5b03cd..1ad660677 100644 --- a/tools/ctrace/docs/architecture.md +++ b/tools/ctrace/docs/architecture.md @@ -13,11 +13,14 @@ backend-independent semantic events. Output backends consume these events to cre The first release profile supports SWO data containing ITM and DWT packets. The command line accepts the stable type names `itm`, `dwt`, `event`, `pmu`, `exception`, `pcsample`, `global_ts`, `overflow`, and `error`. Output semantics are -currently implemented for `itm`, `dwt`, `exception`, `pcsample`, `global_ts`, `overflow`, and `error`. DWT event-counter -and PMU packets are retained internally but are not mapped to their selectors yet. Periodic PC samples reach CSV and -CTF as semantic events; the CTF event distinguishes a sampled PC from a processor-sleep indication, and Trace Compass -shows processor-sleep intervals as a timeline. Trace Bus input is discovered so that a complete trace directory can be -inspected, but `*.TB.raw` files are reported and skipped until a decoder is implemented. +currently implemented for every listed type. Valid DWT event-counter and PMU trace-on-overflow packets reach CSV as +one row containing the hardware mask. The CTF backend expands each mask into one timestamped record per set bit so +Trace Compass can show exact table rows and labeled one-microsecond visualization pulses. DWT records use their fixed +architectural counter names; PMU records provisionally use `Event0` through `Event7` until trace-run configuration can +resolve the programmable counter assignments. Periodic PC samples reach CSV and CTF as semantic events; the CTF event +distinguishes a sampled PC from a processor-sleep indication, and Trace Compass shows processor-sleep intervals as a +timeline. Trace Bus input is discovered so that a complete trace directory can be inspected, but `*.TB.raw` files are +reported and skipped until a decoder is implemented. The architecture separates protocol decoding, semantic interpretation, and output generation. This keeps output formats independent of OpenCSD and allows another raw trace channel to reuse the event model and output backends. diff --git a/tools/ctrace/docs/todo.md b/tools/ctrace/docs/todo.md index f4a82d41c..0e14bd217 100644 --- a/tools/ctrace/docs/todo.md +++ b/tools/ctrace/docs/todo.md @@ -10,8 +10,7 @@ - [ ] Preserve DWT reference, group, and setup-binding identities when expanding source arrays. - [ ] Complete Armv7-M linked-comparator, range, and value-match decoding. - [ ] Add Armv8-M and Armv8.1-M DWT decoding. -- [ ] Add `event` output in its own PR. -- [ ] Add `pmu` output in its own PR. +- [ ] Resolve programmable PMU event-counter names from trace-run configuration. ## Multiple streams diff --git a/tools/ctrace/src/decode/DwtPacketDecoder.cpp b/tools/ctrace/src/decode/DwtPacketDecoder.cpp index 6f0677568..562f77dd7 100644 --- a/tools/ctrace/src/decode/DwtPacketDecoder.cpp +++ b/tools/ctrace/src/decode/DwtPacketDecoder.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +56,24 @@ constexpr std::uint32_t kPmuOverflowMask = 0xffU; constexpr std::uint8_t kArmv7MFullPcBytes = 4U; constexpr std::uint8_t kArmv7MAddressOffsetBytes = 2U; +/** @brief Describes an invalid DWT event-counter payload. */ +static std::string invalidEventCounterMessage(const DwtPayloadPacket& payload) +{ + std::ostringstream message; + message << "unsupported DWT event-counter payload: size " << static_cast(payload.size) << ", value 0x" + << std::hex << payload.value << "; expected a non-zero 1-byte mask using bits 0..5 only"; + return message.str(); +} + +/** @brief Describes an invalid PMU trace-on-overflow payload. */ +static std::string invalidPmuEventCounterMessage(const DwtPayloadPacket& payload) +{ + std::ostringstream message; + message << "unsupported PMU event-counter payload: size " << static_cast(payload.size) << ", value 0x" + << std::hex << payload.value << "; expected a non-zero 1-byte mask using bits 0..7"; + return message.str(); +} + std::vector DwtPacketDecoder::decode(const DwtPayloadPacket& payload) { std::vector output; @@ -63,7 +82,24 @@ std::vector DwtPacketDecoder::decode(const DwtPayloadPacket& payload const auto source = static_cast(discriminator); if (source == DwtPacketSource::EventCounter) { output = flush(payload.quality, payload.tcyc); - TraceEvent packet{DwtEventTraceEvent{discriminator, payload.size, payload.value}}; + const auto validPayload = payload.size == 1U && payload.value != 0U && + (payload.value & ~static_cast(kDwtEventCounterValidMask)) == 0U; + if (!validPayload) { + TraceEvent error{TraceIssueEvent{ + TraceIssueCode::UnsupportedDwtEventCounterPayload, + TraceIssueSeverity::Error, + invalidEventCounterMessage(payload), + std::nullopt, + std::nullopt, + }}; + error.index = payload.index; + error.traceBusId = payload.traceBusId; + error.tcyc = payload.tcyc; + error.quality = payload.quality; + output.push_back(std::move(error)); + return output; + } + TraceEvent packet{DwtEventTraceEvent{static_cast(payload.value)}}; packet.index = payload.index; packet.traceBusId = payload.traceBusId; packet.tcyc = payload.tcyc; @@ -74,7 +110,23 @@ std::vector DwtPacketDecoder::decode(const DwtPayloadPacket& payload if (source == DwtPacketSource::PmuTraceOnOverflow) { output = flush(payload.quality, payload.tcyc); - TraceEvent packet{PmuTraceEvent{static_cast(payload.value & kPmuOverflowMask)}}; + const auto validPayload = payload.size == 1U && payload.value != 0U && (payload.value & ~kPmuOverflowMask) == 0U; + if (!validPayload) { + TraceEvent error{TraceIssueEvent{ + TraceIssueCode::UnsupportedPmuEventCounterPayload, + TraceIssueSeverity::Error, + invalidPmuEventCounterMessage(payload), + std::nullopt, + std::nullopt, + }}; + error.index = payload.index; + error.traceBusId = payload.traceBusId; + error.tcyc = payload.tcyc; + error.quality = payload.quality; + output.push_back(std::move(error)); + return output; + } + TraceEvent packet{PmuTraceEvent{static_cast(payload.value)}}; packet.index = payload.index; packet.traceBusId = payload.traceBusId; packet.tcyc = payload.tcyc; diff --git a/tools/ctrace/src/diagnostics/TraceIssueReporter.cpp b/tools/ctrace/src/diagnostics/TraceIssueReporter.cpp index 4c4424c82..bc01c8182 100644 --- a/tools/ctrace/src/diagnostics/TraceIssueReporter.cpp +++ b/tools/ctrace/src/diagnostics/TraceIssueReporter.cpp @@ -44,6 +44,8 @@ static std::string displayErrorMessage(const TraceEvent& event, const TraceIssue return "OpenCSD initialization failed"; case TraceIssueCode::DecodeError: case TraceIssueCode::InvalidExceptionAction: + case TraceIssueCode::UnsupportedDwtEventCounterPayload: + case TraceIssueCode::UnsupportedPmuEventCounterPayload: case TraceIssueCode::UnsupportedDwtAddressPayload: case TraceIssueCode::UnsupportedDwtPcSamplePayload: case TraceIssueCode::OpenCsdDecodeError: diff --git a/tools/ctrace/src/model/TraceEvent.h b/tools/ctrace/src/model/TraceEvent.h index 17c7fb570..16fd27e1a 100644 --- a/tools/ctrace/src/model/TraceEvent.h +++ b/tools/ctrace/src/model/TraceEvent.h @@ -8,6 +8,7 @@ #ifndef CTRACE_SRC_MODEL_TRACEEVENT_H #define CTRACE_SRC_MODEL_TRACEEVENT_H +#include #include #include #include @@ -39,6 +40,8 @@ enum class TraceIssueCode { DecodeError, DataLoss, InvalidExceptionAction, + UnsupportedDwtEventCounterPayload, + UnsupportedPmuEventCounterPayload, UnsupportedDwtAddressPayload, UnsupportedDwtPcSamplePayload, OpenCsdDecodeError, @@ -125,13 +128,70 @@ struct ExceptionTraceEvent { ExceptionAction action = ExceptionAction::Unknown; }; -/** @brief Contains a decoded DWT event-counter packet. */ +/** @brief Identifies one counter reported by a DWT event-counter packet. */ +enum class DwtEventCounter : std::uint8_t { + Cpi = 0U, + Exception = 1U, + Sleep = 2U, + LoadStore = 3U, + Fold = 4U, + Cycle = 5U, +}; + +inline constexpr std::array kDwtEventCounters{{ + DwtEventCounter::Cpi, + DwtEventCounter::Exception, + DwtEventCounter::Sleep, + DwtEventCounter::LoadStore, + DwtEventCounter::Fold, + DwtEventCounter::Cycle, +}}; + +/** @brief Returns the wire bit represented by one DWT event counter. */ +constexpr std::uint8_t dwtEventCounterBit(DwtEventCounter counter) +{ + return static_cast(1U << static_cast(counter)); +} + +inline constexpr std::uint8_t kDwtEventCounterValidMask = + dwtEventCounterBit(DwtEventCounter::Cpi) | dwtEventCounterBit(DwtEventCounter::Exception) | + dwtEventCounterBit(DwtEventCounter::Sleep) | dwtEventCounterBit(DwtEventCounter::LoadStore) | + dwtEventCounterBit(DwtEventCounter::Fold) | dwtEventCounterBit(DwtEventCounter::Cycle); + +/** @brief Contains the counter-overflow mask from one decoded DWT event packet. */ struct DwtEventTraceEvent { - std::uint32_t discriminator = 0; - std::uint8_t size = 0; - std::uint32_t value = 0; + std::uint8_t counterMask = 0; +}; + +/** @brief Identifies one programmable counter reported by a PMU trace packet. */ +enum class PmuEventCounter : std::uint8_t { + Event0 = 0U, + Event1 = 1U, + Event2 = 2U, + Event3 = 3U, + Event4 = 4U, + Event5 = 5U, + Event6 = 6U, + Event7 = 7U, }; +inline constexpr std::array kPmuEventCounters{{ + PmuEventCounter::Event0, + PmuEventCounter::Event1, + PmuEventCounter::Event2, + PmuEventCounter::Event3, + PmuEventCounter::Event4, + PmuEventCounter::Event5, + PmuEventCounter::Event6, + PmuEventCounter::Event7, +}}; + +/** @brief Returns the wire bit represented by one programmable PMU event counter. */ +constexpr std::uint8_t pmuEventCounterBit(PmuEventCounter counter) +{ + return static_cast(1U << static_cast(counter)); +} + /** @brief Contains the OVn counter mask from a decoded PMU trace-on-overflow packet. */ struct PmuTraceEvent { std::uint8_t overflowMask = 0; diff --git a/tools/ctrace/src/model/TraceSelection.cpp b/tools/ctrace/src/model/TraceSelection.cpp index c766a5afc..2fafbb4b5 100644 --- a/tools/ctrace/src/model/TraceSelection.cpp +++ b/tools/ctrace/src/model/TraceSelection.cpp @@ -42,16 +42,16 @@ static std::optional typeFor(const ExceptionTraceEvent&) return TraceEventType::Exception; } -/** @brief Keeps DWT counter payloads disabled until their output semantics are implemented. */ +/** @brief Exposes DWT event-counter payloads through their public output selector. */ static std::optional typeFor(const DwtEventTraceEvent&) { - return std::nullopt; + return TraceEventType::Event; } -/** @brief Keeps PMU payloads disabled until their output semantics are implemented. */ +/** @brief Exposes PMU trace-on-overflow payloads through their public output selector. */ static std::optional typeFor(const PmuTraceEvent&) { - return std::nullopt; + return TraceEventType::Pmu; } /** @brief Exposes periodic DWT PC samples through their public output selector. */ diff --git a/tools/ctrace/src/output/csv/CsvRowMapper.cpp b/tools/ctrace/src/output/csv/CsvRowMapper.cpp index e4f9b6fd2..f5b5d557d 100644 --- a/tools/ctrace/src/output/csv/CsvRowMapper.cpp +++ b/tools/ctrace/src/output/csv/CsvRowMapper.cpp @@ -122,6 +122,20 @@ static std::string_view exceptionActionCsvValue(ExceptionAction action) return "0x0"; } +/** @brief Writes one DWT event-counter packet to the CSV event columns. */ +static void writeDwtEvent(CsvRow& row, const DwtEventTraceEvent& event) +{ + row[column(CsvColumn::Source)] = "0"; + row[column(CsvColumn::Value)] = hexValue(event.counterMask, 1U); +} + +/** @brief Writes one PMU trace-on-overflow packet to the CSV event columns. */ +static void writePmuEvent(CsvRow& row, const PmuTraceEvent& event) +{ + row[column(CsvColumn::Source)] = "3"; + row[column(CsvColumn::Value)] = hexValue(event.overflowMask, 1U); +} + /** @brief Maps one semantic trace event to all CSV columns. */ static CsvRow eventToCsvRow(const TraceEvent& event) { @@ -159,6 +173,10 @@ static CsvRow eventToCsvRow(const TraceEvent& event) } else if (const auto* exception = traceEventPayload(event)) { row[column(CsvColumn::Source)] = std::to_string(exception->number); row[column(CsvColumn::Value)] = exceptionActionCsvValue(exception->action); + } else if (const auto* counter = traceEventPayload(event)) { + writeDwtEvent(row, *counter); + } else if (const auto* counter = traceEventPayload(event)) { + writePmuEvent(row, *counter); } else if (const auto* sample = traceEventPayload(event)) { if (!sample->sleeping) { row[column(CsvColumn::Pc)] = hexValue(sample->pc, 4); diff --git a/tools/ctrace/src/output/ctf/CtfEncoder.cpp b/tools/ctrace/src/output/ctf/CtfEncoder.cpp index 817d4eedc..e83475920 100644 --- a/tools/ctrace/src/output/ctf/CtfEncoder.cpp +++ b/tools/ctrace/src/output/ctf/CtfEncoder.cpp @@ -200,6 +200,14 @@ void CtfEncoder::writeEvent(const TraceEvent& event) if (selected) { writeDwtAddrEvent(event, *address); } + } else if (const auto* counters = traceEventPayload(event)) { + if (selected) { + writeDwtEvent(event, *counters); + } + } else if (const auto* counters = traceEventPayload(event)) { + if (selected) { + writePmuEvent(event, *counters); + } } else if (const auto* sample = traceEventPayload(event)) { if (selected) { writePcSampleEvent(event, *sample); @@ -355,6 +363,43 @@ void CtfEncoder::writeDwtAddrEvent(const TraceEvent& event, const DwtAddressTrac }); } +void CtfEncoder::writeDwtEvent(const TraceEvent& event, const DwtEventTraceEvent& counters) +{ + constexpr auto payloadSize = 1U + 1U + 4U; + const auto eventTimestamp = allocateEventTimestamp(event.traceBusId); + const auto quality = computeSampleQuality(event); + for (const auto counter : kDwtEventCounters) { + const auto counterBit = dwtEventCounterBit(counter); + if ((counters.counterMask & counterBit) == 0U) { + continue; + } + m_stream.writeRecord(CtfSchema::value(CtfSchema::EventId::DwtEvent), eventTimestamp, event.traceBusId, payloadSize, + [&](CtfStreamWriter::Record& record) { + record.writeU8(CtfSchema::value(counter)); + record.writeU8(quality.first); + record.writeU32(quality.second); + }); + } +} + +void CtfEncoder::writePmuEvent(const TraceEvent& event, const PmuTraceEvent& counters) +{ + constexpr auto payloadSize = 1U + 1U + 4U; + const auto eventTimestamp = allocateEventTimestamp(event.traceBusId); + const auto quality = computeSampleQuality(event); + for (const auto counter : kPmuEventCounters) { + if ((counters.overflowMask & pmuEventCounterBit(counter)) == 0U) { + continue; + } + m_stream.writeRecord(CtfSchema::value(CtfSchema::EventId::PmuEvent), eventTimestamp, event.traceBusId, payloadSize, + [&](CtfStreamWriter::Record& record) { + record.writeU8(CtfSchema::value(counter)); + record.writeU8(quality.first); + record.writeU32(quality.second); + }); + } +} + void CtfEncoder::writeGlobalTimestampEvent(const TraceEvent& event, const GlobalTimestampTraceEvent& timestamp) { constexpr auto payloadSize = 8U + 1U; diff --git a/tools/ctrace/src/output/ctf/CtfEncoder.h b/tools/ctrace/src/output/ctf/CtfEncoder.h index 6034dcf20..594fcff89 100644 --- a/tools/ctrace/src/output/ctf/CtfEncoder.h +++ b/tools/ctrace/src/output/ctf/CtfEncoder.h @@ -75,6 +75,10 @@ class CtfEncoder final { void reportDwtSizeMismatch(const TraceEvent& event, const DwtDataTraceEvent& data, const ResolvedTraceSource* source); /** @brief Encodes one DWT address event. */ void writeDwtAddrEvent(const TraceEvent& event, const DwtAddressTraceEvent& address); + /** @brief Expands one DWT event-counter mask into individual CTF records. */ + void writeDwtEvent(const TraceEvent& event, const DwtEventTraceEvent& counters); + /** @brief Expands one PMU trace-on-overflow mask into individual CTF records. */ + void writePmuEvent(const TraceEvent& event, const PmuTraceEvent& counters); /** @brief Encodes one periodic PC-sample or processor-sleep event. */ void writePcSampleEvent(const TraceEvent& event, const PcSampleTraceEvent& sample); /** @brief Encodes one reconstructed global timestamp event. */ diff --git a/tools/ctrace/src/output/ctf/CtfMetadataWriter.cpp b/tools/ctrace/src/output/ctf/CtfMetadataWriter.cpp index 0dfdca39e..68e132ff0 100644 --- a/tools/ctrace/src/output/ctf/CtfMetadataWriter.cpp +++ b/tools/ctrace/src/output/ctf/CtfMetadataWriter.cpp @@ -302,6 +302,20 @@ typealias enum : uint8_t { << static_cast(CtfSchema::value(CtfSchema::ExceptionOrigin::Synthetic)) << R"( } := cmsis_exception_origin_t; typealias enum : uint8_t { +)"; + for (const auto counter : kDwtEventCounters) { + out << " \"" << CtfSchema::dwtEventCounterName(counter) << "\" = " + << static_cast(CtfSchema::value(counter)) << ",\n"; + } + out << R"(} := cmsis_dwt_event_counter_t; +typealias enum : uint8_t { +)"; + for (const auto counter : kPmuEventCounters) { + out << " \"" << CtfSchema::pmuEventCounterName(counter) << "\" = " + << static_cast(CtfSchema::value(counter)) << ",\n"; + } + out << R"(} := cmsis_pmu_event_counter_t; +typealias enum : uint8_t { )"; std::set itmLabels; for (std::uint32_t channel = 1U; channel < 32U; ++channel) { @@ -426,6 +440,46 @@ event { )"; } +/** @brief Writes the DWT event-counter declaration. */ +static void writeDwtEvent(std::ostream& out) +{ + out << R"( +event { + id = )" + << CtfSchema::value(CtfSchema::EventId::DwtEvent) << R"(; + name = ")" + << CtfSchema::eventName(CtfSchema::EventId::DwtEvent) << R"("; + stream_id = )" + << CtfSchema::SwoStreamId << R"(; + fields := struct { + cmsis_dwt_event_counter_t cmsis_dwt_event_counter; + uint8_t cmsis_sample_flags; + uint32_t cmsis_overflow_count; + }; +}; +)"; +} + +/** @brief Writes the programmable PMU event-counter declaration. */ +static void writePmuEvent(std::ostream& out) +{ + out << R"( +event { + id = )" + << CtfSchema::value(CtfSchema::EventId::PmuEvent) << R"(; + name = ")" + << CtfSchema::eventName(CtfSchema::EventId::PmuEvent) << R"("; + stream_id = )" + << CtfSchema::SwoStreamId << R"(; + fields := struct { + cmsis_pmu_event_counter_t cmsis_pmu_event_counter; + uint8_t cmsis_sample_flags; + uint32_t cmsis_overflow_count; + }; +}; +)"; +} + /** @brief Writes the periodic PC-sample event declaration. */ static void writePcSampleEvent(std::ostream& out) { @@ -511,6 +565,8 @@ void CtfMetadataWriter::write(const std::filesystem::path& outputDir, const std: writeItmEvent(out); writeDwtValueEvent(out); writeDwtAddressEvent(out); + writeDwtEvent(out); + writePmuEvent(out); writeStatusEvents(out); writePcSampleEvent(out); out.close(); diff --git a/tools/ctrace/src/output/ctf/CtfSchema.h b/tools/ctrace/src/output/ctf/CtfSchema.h index ad5a6f307..343602045 100644 --- a/tools/ctrace/src/output/ctf/CtfSchema.h +++ b/tools/ctrace/src/output/ctf/CtfSchema.h @@ -8,6 +8,8 @@ #ifndef CTRACE_SRC_OUTPUT_CTF_CTFSCHEMA_H #define CTRACE_SRC_OUTPUT_CTF_CTFSCHEMA_H +#include "TraceEvent.h" + #include #include #include @@ -27,6 +29,8 @@ enum class EventId : std::uint32_t { Exception = 4U, GlobalTimestamp = 5U, PcSample = 6U, + DwtEvent = 7U, + PmuEvent = 8U, }; /** @brief Classifies CTF trace-status records. */ @@ -173,6 +177,62 @@ constexpr std::uint8_t value(ExceptionOrigin origin) return static_cast(origin); } +/** @brief Returns the CTF enumeration value for one DWT event counter. */ +constexpr std::uint8_t value(DwtEventCounter counter) +{ + return static_cast(counter); +} + +/** @brief Returns the public counter name used by CTF metadata and Trace Compass. */ +constexpr std::string_view dwtEventCounterName(DwtEventCounter counter) +{ + switch (counter) { + case DwtEventCounter::Cpi: + return "CPICNT"; + case DwtEventCounter::Exception: + return "EXCCNT"; + case DwtEventCounter::Sleep: + return "SLEEPCNT"; + case DwtEventCounter::LoadStore: + return "LSUCNT"; + case DwtEventCounter::Fold: + return "FOLDCNT"; + case DwtEventCounter::Cycle: + return "CYCCNT"; + } + return "UNKNOWN"; +} + +/** @brief Returns the CTF enumeration value for one programmable PMU event counter. */ +constexpr std::uint8_t value(PmuEventCounter counter) +{ + return static_cast(counter); +} + +/** @brief Returns the provisional PMU counter name used until trace-run configuration can resolve it. */ +constexpr std::string_view pmuEventCounterName(PmuEventCounter counter) +{ + switch (counter) { + case PmuEventCounter::Event0: + return "Event0"; + case PmuEventCounter::Event1: + return "Event1"; + case PmuEventCounter::Event2: + return "Event2"; + case PmuEventCounter::Event3: + return "Event3"; + case PmuEventCounter::Event4: + return "Event4"; + case PmuEventCounter::Event5: + return "Event5"; + case PmuEventCounter::Event6: + return "Event6"; + case PmuEventCounter::Event7: + return "Event7"; + } + return "UNKNOWN"; +} + /** @brief Returns the stable schema name of a CTF event ID. */ constexpr std::string_view eventName(EventId id) { @@ -191,6 +251,10 @@ constexpr std::string_view eventName(EventId id) return "GLOBAL_TIMESTAMP"; case EventId::PcSample: return "PC_SAMPLE"; + case EventId::DwtEvent: + return "DWT_EVENT"; + case EventId::PmuEvent: + return "PMU_EVENT"; } return "UNKNOWN"; } diff --git a/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp b/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp index d9e839aaf..a8a59b5b3 100644 --- a/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp +++ b/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp @@ -9,6 +9,8 @@ #include "CtfSchema.h" +#include +#include #include #include #include @@ -16,8 +18,11 @@ #include #include #include +#include constexpr const char* kTraceCompassAnalysisVersionPlaceholder = "__SWO_ANALYSIS_VERSION__"; +// Stack depth keeps overlapping visual pulses active until their last scheduled pop. +constexpr std::uint64_t kEventPulseNanoseconds = 1000U; /** @brief Reads the analysis version encoded in generated Trace Compass XML. */ static std::uint32_t traceCompassAnalysisVersion(const std::string& xml) @@ -76,6 +81,58 @@ static std::string valueHandlers(CtfSchema::EventId eventId, const char* prefix, return handlers.str(); } +/** @brief Generates visible pulses for one family of event counters. */ +template +static std::string eventCounterHandlers(CtfSchema::EventId eventId, std::string_view field, + const std::array& counters, CounterName counterName) +{ + std::ostringstream handlers; + for (const auto counter : counters) { + const auto name = counterName(counter); + const auto value = static_cast(CtfSchema::value(counter)); + handlers << R"( + + + + + + + + + + + + + + + + + + + + + + + + + + + + +)"; + } + return handlers.str(); +} + /** @brief Generates the Trace Compass state-provider definition. */ static std::string stateProviderXml() { @@ -102,6 +159,16 @@ static std::string stateProviderXml() )" << valueHandlers(CtfSchema::EventId::Itm, "itm", "cmsis_itm_channel", "value") + << R"( + +)" << eventCounterHandlers(CtfSchema::EventId::DwtEvent, "cmsis_dwt_event_counter", kDwtEventCounters, + CtfSchema::dwtEventCounterName) + << R"( + +)" << eventCounterHandlers(CtfSchema::EventId::PmuEvent, "cmsis_pmu_event_counter", kPmuEventCounters, + CtfSchema::pmuEventCounterName) << R"( @@ -313,6 +380,30 @@ static std::string viewsXml() << CtfSchema::eventName(CtfSchema::EventId::DwtAddress) << '/' << R"(*"> + + + +