From d20624643c17725affe2e17e85712712d31d5d21 Mon Sep 17 00:00:00 2001 From: Thorsten de Buhr Date: Fri, 4 Sep 2026 16:24:04 +0200 Subject: [PATCH 1/6] feat(ctrace): preserve DWT address offset widths --- tools/ctrace/src/decode/DwtPacketDecoder.cpp | 37 +++++++------ tools/ctrace/src/decode/DwtPacketDecoder.h | 4 +- tools/ctrace/src/model/TraceEvent.h | 27 +++++++--- tools/ctrace/src/output/csv/CsvRowMapper.cpp | 6 +-- tools/ctrace/src/output/ctf/CtfEncoder.cpp | 52 ++++++++++++++----- .../src/output/ctf/CtfMetadataWriter.cpp | 34 +++++++++--- tools/ctrace/src/output/ctf/CtfSchema.h | 39 ++++++++++++++ .../src/output/ctf/TraceCompassXmlWriter.cpp | 40 +++++++++++--- .../unit/src/decode/DwtPacketDecoderTests.cpp | 31 +++++++++-- .../src/output/csv/CsvFileOutputTests.cpp | 2 +- .../unit/src/output/csv/CsvRowMapperTests.cpp | 9 ++-- .../unit/src/output/ctf/CtfEncoderTests.cpp | 35 ++++++++++--- .../src/output/ctf/CtfMetadataWriterTests.cpp | 7 +++ .../unit/src/output/ctf/CtfSchemaTests.cpp | 5 ++ .../ctrace/test/unit/support/CtfTestSupport.h | 28 ++++++++-- 15 files changed, 280 insertions(+), 76 deletions(-) diff --git a/tools/ctrace/src/decode/DwtPacketDecoder.cpp b/tools/ctrace/src/decode/DwtPacketDecoder.cpp index b64dfa165..95c709fdf 100644 --- a/tools/ctrace/src/decode/DwtPacketDecoder.cpp +++ b/tools/ctrace/src/decode/DwtPacketDecoder.cpp @@ -54,10 +54,15 @@ constexpr std::uint32_t kExceptionActionShift = 12U; constexpr std::uint32_t kPmuOverflowMask = 0xffU; constexpr std::uint8_t kArmv7MFullPcBytes = 4U; -constexpr std::uint8_t kArmv7MAddressOffsetBytes = 2U; constexpr std::uint8_t kArmv8MMatchBytes = 1U; constexpr std::uint32_t kArmv8MMatchValue = 1U; +/** @brief Returns whether a raw DWT address fragment width can be preserved. */ +static bool isSupportedAddressOffsetSize(std::uint8_t size) +{ + return size == 1U || size == 2U || size == 4U; +} + /** @brief Describes an invalid DWT event-counter payload. */ static std::string invalidEventCounterMessage(const DwtPayloadPacket& payload) { @@ -260,17 +265,17 @@ void DwtPacketDecoder::decodeDataTrace(const DwtPayloadPacket& payload, std::vec output.push_back(std::move(match)); return; } - const auto expectedSize = secondarySubtype ? kArmv7MAddressOffsetBytes : kArmv7MFullPcBytes; - if (payload.size != expectedSize) { + const auto supportedSize = + secondarySubtype ? isSupportedAddressOffsetSize(payload.size) : payload.size == kArmv7MFullPcBytes; + if (!supportedSize) { auto flushed = flush(payload.quality, payload.tcyc); output.insert(output.end(), std::make_move_iterator(flushed.begin()), std::make_move_iterator(flushed.end())); TraceEvent error{TraceIssueEvent{ TraceIssueCode::UnsupportedDwtAddressPayload, TraceIssueSeverity::Error, - "unsupported DWT " + std::string(secondarySubtype ? "address" : "PC or match") + " payload size " + - std::to_string(payload.size) + - "; the current ctrace-run format does not provide the " - "architecture and reconstruction data needed to decode it safely", + "unsupported DWT " + std::string(secondarySubtype ? "address offset" : "PC or match") + + " payload size " + std::to_string(payload.size) + + (secondarySubtype ? "; expected 1, 2, or 4 bytes" : "; expected a 4-byte PC or 1-byte match"), std::nullopt, std::nullopt, }}; @@ -282,8 +287,8 @@ void DwtPacketDecoder::decodeDataTrace(const DwtPayloadPacket& payload, std::vec return; } if (secondarySubtype) { - event.addressLo16 = payload.value; - event.hasAddressLo16 = true; + event.offset = DwtAddressOffset{payload.size, payload.value}; + event.hasOffset = true; } else { event.pc = payload.value; event.hasPc = true; @@ -313,18 +318,18 @@ void DwtPacketDecoder::sendDataTraceEvent(std::uint32_t comparator, const Pendin // The individual short-circuit permutations are an implementation detail; // repeated and complementary fragments are covered as complete behaviors. const auto repeatsFragmentKind = (pending->hasPc && event.hasPc) || - (pending->hasAddressLo16 && event.hasAddressLo16) || + (pending->hasOffset && event.hasOffset) || (pending->hasValue && event.hasValue); if (!repeatsFragmentKind) { pending->index = event.index; pending->traceBusId = event.traceBusId; pending->pc = event.hasPc ? event.pc : pending->pc; - pending->addressLo16 = event.hasAddressLo16 ? event.addressLo16 : pending->addressLo16; + pending->offset = event.hasOffset ? event.offset : pending->offset; pending->value = event.hasValue ? event.value : pending->value; pending->size = event.hasValue ? event.size : pending->size; pending->isRead = event.hasValue ? event.isRead : pending->isRead; pending->hasPc = pending->hasPc || event.hasPc; - pending->hasAddressLo16 = pending->hasAddressLo16 || event.hasAddressLo16; + pending->hasOffset = pending->hasOffset || event.hasOffset; pending->hasValue = pending->hasValue || event.hasValue; pending->quality.overflow = pending->quality.overflow || event.quality.overflow; pending->quality.timestampReliable = pending->quality.timestampReliable && event.quality.timestampReliable; @@ -351,14 +356,14 @@ void DwtPacketDecoder::flushPending(std::uint32_t comparator, const TraceQuality pending->size, pending->value, pending->isRead ? AccessType::Read : AccessType::Write, - pending->hasAddressLo16 ? std::optional(pending->addressLo16) : std::nullopt, + pending->hasOffset ? std::optional(pending->offset) : std::nullopt, pending->hasPc ? std::optional(pending->pc) : std::nullopt, }); } - DwtAddressTraceLocation location = DwtOffsetTraceLocation{pending->addressLo16}; - if (pending->hasPc && pending->hasAddressLo16) { - location = DwtPcAndOffsetTraceLocation{pending->pc, pending->addressLo16}; + DwtAddressTraceLocation location = DwtOffsetTraceLocation{pending->offset}; + if (pending->hasPc && pending->hasOffset) { + location = DwtPcAndOffsetTraceLocation{pending->pc, pending->offset}; } else if (pending->hasPc) { location = DwtPcTraceLocation{pending->pc}; } diff --git a/tools/ctrace/src/decode/DwtPacketDecoder.h b/tools/ctrace/src/decode/DwtPacketDecoder.h index 1ec2fb9d1..60b100a96 100644 --- a/tools/ctrace/src/decode/DwtPacketDecoder.h +++ b/tools/ctrace/src/decode/DwtPacketDecoder.h @@ -45,12 +45,12 @@ class DwtPacketDecoder { std::uint64_t index = 0; std::uint8_t traceBusId = 0U; std::uint32_t pc = 0; - std::uint32_t addressLo16 = 0; + DwtAddressOffset offset; std::uint32_t value = 0; std::uint8_t size = 4; bool isRead = false; bool hasPc = false; - bool hasAddressLo16 = false; + bool hasOffset = false; bool hasValue = false; TraceQuality quality; }; diff --git a/tools/ctrace/src/model/TraceEvent.h b/tools/ctrace/src/model/TraceEvent.h index 5e6706171..cd4499355 100644 --- a/tools/ctrace/src/model/TraceEvent.h +++ b/tools/ctrace/src/model/TraceEvent.h @@ -60,13 +60,24 @@ struct SoftwareTraceEvent { std::uint32_t value = 0; }; +/** @brief Stores the raw address fragment and width carried by a DWT packet. */ +struct DwtAddressOffset { + std::uint8_t size = 0; + std::uint32_t value = 0; +}; + +constexpr bool operator==(const DwtAddressOffset& left, const DwtAddressOffset& right) +{ + return left.size == right.size && left.value == right.value; +} + /** @brief Contains a reconstructed DWT data access event. */ struct DwtDataTraceEvent { std::uint32_t comparator = 0; std::uint8_t size = 0; std::uint32_t value = 0; AccessType access = AccessType::Read; - std::optional addressLo16 = std::nullopt; + std::optional offset = std::nullopt; std::optional pc = std::nullopt; }; @@ -75,15 +86,15 @@ struct DwtPcTraceLocation { std::uint32_t pc; }; -/** @brief Identifies a DWT address event by its low address bits. */ +/** @brief Identifies a DWT address event by its raw address fragment. */ struct DwtOffsetTraceLocation { - std::uint32_t addressLo16; + DwtAddressOffset offset; }; /** @brief Identifies a DWT address event by program counter and address offset. */ struct DwtPcAndOffsetTraceLocation { std::uint32_t pc; - std::uint32_t addressLo16; + DwtAddressOffset offset; }; /** @brief Stores one of the supported DWT address location representations. */ @@ -112,14 +123,14 @@ inline std::optional dwtAddressPc(const DwtAddressTraceEvent& eve return std::nullopt; } -/** @brief Returns the address offset carried by a DWT address event, if present. */ -inline std::optional dwtAddressOffset(const DwtAddressTraceEvent& event) +/** @brief Returns the raw address fragment carried by a DWT address event, if present. */ +inline std::optional dwtAddressOffset(const DwtAddressTraceEvent& event) { if (const auto* offset = std::get_if(&event.location)) { - return offset->addressLo16; + return offset->offset; } if (const auto* combined = std::get_if(&event.location)) { - return combined->addressLo16; + return combined->offset; } return std::nullopt; } diff --git a/tools/ctrace/src/output/csv/CsvRowMapper.cpp b/tools/ctrace/src/output/csv/CsvRowMapper.cpp index c2ad981fe..eca02f48a 100644 --- a/tools/ctrace/src/output/csv/CsvRowMapper.cpp +++ b/tools/ctrace/src/output/csv/CsvRowMapper.cpp @@ -159,8 +159,8 @@ static CsvRow eventToCsvRow(const TraceEvent& event) if (data->pc.has_value()) { row[column(CsvColumn::Pc)] = hexValue(*data->pc, 4); } - if (data->addressLo16.has_value()) { - row[column(CsvColumn::Offset)] = hexValue(*data->addressLo16, 2); + if (data->offset.has_value()) { + row[column(CsvColumn::Offset)] = hexValue(data->offset->value, data->offset->size); } } else if (const auto* address = traceEventPayload(event)) { row[column(CsvColumn::Source)] = std::to_string(address->comparator); @@ -168,7 +168,7 @@ static CsvRow eventToCsvRow(const TraceEvent& event) row[column(CsvColumn::Pc)] = hexValue(*pc, 4); } if (const auto offset = dwtAddressOffset(*address)) { - row[column(CsvColumn::Offset)] = hexValue(*offset, 2); + row[column(CsvColumn::Offset)] = hexValue(offset->value, offset->size); } } else if (const auto* match = traceEventPayload(event)) { row[column(CsvColumn::Source)] = std::to_string(match->comparator); diff --git a/tools/ctrace/src/output/ctf/CtfEncoder.cpp b/tools/ctrace/src/output/ctf/CtfEncoder.cpp index 3f0494bac..b49428c5e 100644 --- a/tools/ctrace/src/output/ctf/CtfEncoder.cpp +++ b/tools/ctrace/src/output/ctf/CtfEncoder.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -111,6 +112,34 @@ static void writeVariantValue(CtfStreamWriter::Record& record, std::uint32_t dat } } +/** @brief Resolves the CTF representation of an optional raw DWT address offset. */ +static const CtfSchema::DwtOffsetVariant& dwtOffsetVariant(const std::optional& offset) +{ + if (!offset.has_value()) { + return CtfSchema::DwtOffsetVariants.front(); + } + const auto* variant = CtfSchema::dwtOffsetVariantForSize(offset->size); + if (variant == nullptr) { + throw std::runtime_error("CTF DWT address offset has an invalid SWO payload size"); + } + return *variant; +} + +/** @brief Writes an optional raw DWT address offset using its exact SWO width. */ +static void writeDwtOffset(CtfStreamWriter::Record& record, const std::optional& offset, + const CtfSchema::DwtOffsetVariant& variant) +{ + record.writeU8(CtfSchema::value(variant.tag)); + const auto value = offset.has_value() ? offset->value : 0U; + if (variant.byteSize == 1U) { + record.writeU8(static_cast(value & 0xffU)); + } else if (variant.byteSize == 2U) { + record.writeU16(static_cast(value & 0xffffU)); + } else { + record.writeU32(value); + } +} + CtfEncoder::CtfEncoder(CtfEncoderConfig config) : m_config(std::move(config)) { @@ -298,8 +327,9 @@ void CtfEncoder::writeDwtValueEvent(const TraceEvent& event, const DwtDataTraceE reportDwtSizeMismatch(event, data, source); const auto& variant = dwtValueVariant(source, data.comparator); const auto hasPc = data.pc.has_value() ? 1U : 0U; - const auto hasAddress = data.addressLo16.has_value() ? 1U : 0U; - const auto payloadSize = 1U + 1U + 1U + variant.byteSize + 1U + hasPc * 4U + 1U + hasAddress * 2U + 1U + 4U; + const auto& offsetVariant = dwtOffsetVariant(data.offset); + const auto payloadSize = + 1U + 1U + 1U + variant.byteSize + 1U + hasPc * 4U + 1U + offsetVariant.byteSize + 1U + 4U; const auto eventTimestamp = allocateEventTimestamp(event.traceBusId); const auto quality = computeSampleQuality(event); m_stream.writeRecord(CtfSchema::value(CtfSchema::EventId::DwtValue), eventTimestamp, event.traceBusId, payloadSize, @@ -314,10 +344,7 @@ void CtfEncoder::writeDwtValueEvent(const TraceEvent& event, const DwtDataTraceE if (hasPc != 0U) { record.writeU32(*data.pc); } - record.writeU8(static_cast(hasAddress)); - if (hasAddress != 0U) { - record.writeU16(static_cast(*data.addressLo16 & 0xffffU)); - } + writeDwtOffset(record, data.offset, offsetVariant); record.writeU8(quality.first); record.writeU32(quality.second); }); @@ -348,20 +375,21 @@ void CtfEncoder::reportDwtSizeMismatch(const TraceEvent& event, const DwtDataTra void CtfEncoder::writeDwtAddrEvent(const TraceEvent& event, const DwtAddressTraceEvent& address) { - constexpr auto payloadSize = 1U + 1U + 1U + 4U + 2U + 1U + 4U; const auto eventTimestamp = allocateEventTimestamp(event.traceBusId); const auto quality = computeSampleQuality(event); const auto pc = dwtAddressPc(address); - const auto addressOffset = dwtAddressOffset(address); + const auto offset = dwtAddressOffset(address); + const auto& offsetVariant = dwtOffsetVariant(offset); const auto hasPc = pc.has_value() ? 1U : 0U; - const auto hasAddress = addressOffset.has_value() ? 1U : 0U; + const auto payloadSize = 1U + 1U + hasPc * 4U + 1U + offsetVariant.byteSize + 1U + 4U; m_stream.writeRecord(CtfSchema::value(CtfSchema::EventId::DwtAddress), eventTimestamp, event.traceBusId, payloadSize, [&](CtfStreamWriter::Record& record) { record.writeU8(static_cast(address.comparator & 0xffU)); record.writeU8(static_cast(hasPc)); - record.writeU8(static_cast(hasAddress)); - record.writeU32(pc.value_or(0U)); - record.writeU16(static_cast(addressOffset.value_or(0U) & 0xffffU)); + if (hasPc != 0U) { + record.writeU32(*pc); + } + writeDwtOffset(record, offset, offsetVariant); record.writeU8(quality.first); record.writeU32(quality.second); }); diff --git a/tools/ctrace/src/output/ctf/CtfMetadataWriter.cpp b/tools/ctrace/src/output/ctf/CtfMetadataWriter.cpp index 60d115c9c..3c2d82b4d 100644 --- a/tools/ctrace/src/output/ctf/CtfMetadataWriter.cpp +++ b/tools/ctrace/src/output/ctf/CtfMetadataWriter.cpp @@ -170,6 +170,28 @@ static std::string ctfValueFields(const std::string_view& prefix) return out.str(); } +/** @brief Generates the width-tagged TSDL field for a raw DWT address offset. */ +static std::string ctfDwtOffsetFields() +{ + std::ostringstream out; + out << " enum : uint8_t { "; + for (std::size_t index = 0; index < CtfSchema::DwtOffsetVariants.size(); ++index) { + const auto& variant = CtfSchema::DwtOffsetVariants[index]; + if (index > 0U) { + out << ", "; + } + out << variant.name << " = " << static_cast(CtfSchema::value(variant.tag)); + } + out << " } cmsis_dwt_offset_type;\n" + << " variant {\n"; + for (const auto& variant : CtfSchema::DwtOffsetVariants) { + const auto type = variant.byteSize == 1U ? "uint8_t" : variant.byteSize == 2U ? "uint16_t" : "uint32_t"; + out << " " << type << " " << variant.name << ";\n"; + } + out << " } cmsis_dwt_offset;\n"; + return out.str(); +} + /** @brief Stores source labels and exception lanes emitted into CTF metadata. */ struct MetadataSymbols { std::map dwtValueTypes; @@ -407,9 +429,8 @@ event { )" << ctfValueFields("dwt") << R"( uint8_t cmsis_has_pc; uint32_t cmsis_pc[cmsis_has_pc]; - uint8_t cmsis_has_address_lo16; - uint16_t cmsis_address_lo16[cmsis_has_address_lo16]; - uint8_t cmsis_sample_flags; +)" << ctfDwtOffsetFields() + << R"( uint8_t cmsis_sample_flags; uint32_t cmsis_overflow_count; }; }; @@ -430,10 +451,9 @@ event { fields := struct { cmsis_dwt_comparator_t cmsis_dwt_comparator; uint8_t cmsis_has_pc; - uint8_t cmsis_has_address_lo16; - uint32_t cmsis_pc; - uint16_t cmsis_address_lo16; - uint8_t cmsis_sample_flags; + uint32_t cmsis_pc[cmsis_has_pc]; +)" << ctfDwtOffsetFields() + << R"( uint8_t cmsis_sample_flags; uint32_t cmsis_overflow_count; }; }; diff --git a/tools/ctrace/src/output/ctf/CtfSchema.h b/tools/ctrace/src/output/ctf/CtfSchema.h index a71427484..e1d7f43c9 100644 --- a/tools/ctrace/src/output/ctf/CtfSchema.h +++ b/tools/ctrace/src/output/ctf/CtfSchema.h @@ -79,6 +79,28 @@ enum class ValueTag : std::uint8_t { Float32 = 6U, }; +/** @brief Identifies the width of a raw DWT address offset in CTF. */ +enum class DwtOffsetTag : std::uint8_t { + None = 0U, + U8 = 1U, + U16 = 2U, + U32 = 4U, +}; + +/** @brief Describes one CTF DWT address-offset representation. */ +struct DwtOffsetVariant { + DwtOffsetTag tag; + std::string_view name; + std::uint8_t byteSize; +}; + +inline constexpr std::array DwtOffsetVariants{{ + {DwtOffsetTag::None, "none", 1U}, + {DwtOffsetTag::U8, "u8", 1U}, + {DwtOffsetTag::U16, "u16", 2U}, + {DwtOffsetTag::U32, "u32", 4U}, +}}; + /** @brief Describes one supported CTF sample value encoding. */ struct ValueVariant { ValueTag tag; @@ -109,6 +131,17 @@ constexpr const ValueVariant& valueVariant(ValueTag tag) return ValueVariants[static_cast(tag)]; } +/** @brief Resolves a DWT address-offset payload width to its CTF representation. */ +constexpr const DwtOffsetVariant* dwtOffsetVariantForSize(std::uint8_t byteSize) +{ + for (const auto& variant : DwtOffsetVariants) { + if (variant.tag != DwtOffsetTag::None && variant.byteSize == byteSize) { + return &variant; + } + } + return nullptr; +} + /** @brief Resolves trace-run type metadata to a supported CTF value encoding. */ constexpr const ValueVariant* valueVariantForTraceRunType(const std::string_view& typeName, std::uint64_t byteSize) { @@ -154,6 +187,12 @@ constexpr std::uint8_t value(ValueTag tag) return static_cast(tag); } +/** @brief Returns the integer representation of a DWT offset tag. */ +constexpr std::uint8_t value(DwtOffsetTag tag) +{ + return static_cast(tag); +} + /** @brief Returns the integer representation of a DWT access type. */ constexpr std::uint8_t value(DwtAccess access) { diff --git a/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp b/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp index 6315b0c06..90cf3ec06 100644 --- a/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp +++ b/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp @@ -157,6 +157,36 @@ static std::string dwtMatchHandler() return handler.str(); } +/** @brief Generates one DWT address handler for each encoded offset width. */ +static std::string dwtAddressHandlers() +{ + std::ostringstream handlers; + for (const auto& variant : CtfSchema::DwtOffsetVariants) { + if (variant.tag == CtfSchema::DwtOffsetTag::None) { + continue; + } + handlers << R"( + + + + + + + + + + + + + +)"; + } + return handlers.str(); +} + /** @brief Generates the Trace Compass state-provider definition. */ static std::string stateProviderXml() { @@ -172,14 +202,8 @@ static std::string stateProviderXml() xml << R"( - - - - - - - +)" << dwtAddressHandlers() + << R"( )" << valueHandlers(CtfSchema::EventId::Itm, "itm", "cmsis_itm_channel", "value") diff --git a/tools/ctrace/test/unit/src/decode/DwtPacketDecoderTests.cpp b/tools/ctrace/test/unit/src/decode/DwtPacketDecoderTests.cpp index b3cc33290..d96db9364 100644 --- a/tools/ctrace/test/unit/src/decode/DwtPacketDecoderTests.cpp +++ b/tools/ctrace/test/unit/src/decode/DwtPacketDecoderTests.cpp @@ -267,8 +267,9 @@ TEST(CtraceUnitTests, testDwtPacketDecoderPreservesRepeatedAddressFragments) dwtAddressPc(*secondAddress) == std::optional(secondValue)) << "repeated DWT PC fragments were overwritten"; } else { - ASSERT_TRUE(firstOffset == std::optional(firstValue) && - dwtAddressOffset(*secondAddress) == std::optional(secondValue)) + ASSERT_TRUE(firstOffset == std::optional(DwtAddressOffset{size, firstValue}) && + dwtAddressOffset(*secondAddress) == + std::optional(DwtAddressOffset{size, secondValue})) << "repeated DWT offset fragments were overwritten"; } }; @@ -338,7 +339,27 @@ TEST(CtraceUnitTests, testDwtPacketDecoderRejectsUnsupportedAddressWidths) }; verify(8U, 1U); - verify(9U, 4U); + verify(8U, 2U); + verify(9U, 3U); +} + +TEST(CtraceUnitTests, testDwtPacketDecoderPreservesRawAddressOffsetWidths) +{ + const auto verify = [](std::uint8_t size, std::uint32_t value, const char* expectedCsv) { + DwtPacketDecoder decoder; + EXPECT_TRUE(decoder.decode(dwtPayload(9U, size, value, 17U, 3U, 99U)).empty()); + + const auto packets = decoder.flush({}, 100U); + ASSERT_EQ(packets.size(), 1U); + const auto* address = traceEventPayload(packets.front()); + ASSERT_NE(address, nullptr); + EXPECT_EQ(dwtAddressOffset(*address), std::optional(DwtAddressOffset{size, value})); + EXPECT_EQ(CsvRowMapper::row(packets.front()), expectedCsv); + }; + + verify(1U, 0x58U, "100,3,dwt,0,,,0x58,"); + verify(2U, 0x7858U, "100,3,dwt,0,,,0x7858,"); + verify(4U, 0x20007858U, "100,3,dwt,0,,,0x20007858,"); } TEST(CtraceUnitTests, testDwtPacketDecoderMapsAllExceptionActions) @@ -406,7 +427,7 @@ TEST(CtraceUnitTests, testDwtPacketDecoderCombinesPcOffsetAndValue) ASSERT_NE(data, nullptr); EXPECT_EQ(data->access, AccessType::Read); EXPECT_EQ(data->pc, std::optional(0x08001234U)); - EXPECT_EQ(data->addressLo16, std::optional(0x20U)); + EXPECT_EQ(data->offset, std::optional(DwtAddressOffset{2U, 0x20U})); DwtPacketDecoder addressDecoder; EXPECT_TRUE(addressDecoder.decode(pc).empty()); @@ -416,5 +437,5 @@ TEST(CtraceUnitTests, testDwtPacketDecoderCombinesPcOffsetAndValue) const auto* address = traceEventPayload(addresses.front()); ASSERT_NE(address, nullptr); EXPECT_EQ(dwtAddressPc(*address), std::optional(0x08001234U)); - EXPECT_EQ(dwtAddressOffset(*address), std::optional(0x20U)); + EXPECT_EQ(dwtAddressOffset(*address), std::optional(DwtAddressOffset{2U, 0x20U})); } diff --git a/tools/ctrace/test/unit/src/output/csv/CsvFileOutputTests.cpp b/tools/ctrace/test/unit/src/output/csv/CsvFileOutputTests.cpp index 0a7e99e56..37ddd6cbf 100644 --- a/tools/ctrace/test/unit/src/output/csv/CsvFileOutputTests.cpp +++ b/tools/ctrace/test/unit/src/output/csv/CsvFileOutputTests.cpp @@ -128,7 +128,7 @@ TEST(CtraceUnitTests, testCsvFileOutputMatchesSpecification) 4U, 0xfffffdf9U, AccessType::Read, - 0xfdf9U, + DwtAddressOffset{2U, 0xfdf9U}, 0x08001234U, }}, 949338400U)); diff --git a/tools/ctrace/test/unit/src/output/csv/CsvRowMapperTests.cpp b/tools/ctrace/test/unit/src/output/csv/CsvRowMapperTests.cpp index fd133d728..04edc0876 100644 --- a/tools/ctrace/test/unit/src/output/csv/CsvRowMapperTests.cpp +++ b/tools/ctrace/test/unit/src/output/csv/CsvRowMapperTests.cpp @@ -78,10 +78,13 @@ TEST(CtraceUnitTests, testCsvRowMapperAndTraceEventSchema) TEST(CtraceUnitTests, testCsvRowMapperCoversAddressAndExceptionVariants) { - EXPECT_EQ(CsvRowMapper::row(TraceEvent{DwtAddressTraceEvent{2U, DwtOffsetTraceLocation{0xabcdU}}}), + EXPECT_EQ(CsvRowMapper::row(TraceEvent{DwtAddressTraceEvent{2U, DwtOffsetTraceLocation{{2U, 0xabcdU}}}}), ",,dwt,2,,,0xabcd,"); - EXPECT_EQ(CsvRowMapper::row(TraceEvent{DwtAddressTraceEvent{3U, DwtPcAndOffsetTraceLocation{0x1234U, 0x56U}}}), - ",,dwt,3,,0x00001234,0x0056,"); + EXPECT_EQ( + CsvRowMapper::row(TraceEvent{DwtAddressTraceEvent{3U, DwtPcAndOffsetTraceLocation{0x1234U, {1U, 0x56U}}}}), + ",,dwt,3,,0x00001234,0x56,"); + EXPECT_EQ(CsvRowMapper::row(TraceEvent{DwtAddressTraceEvent{1U, DwtOffsetTraceLocation{{4U, 0x20007858U}}}}), + ",,dwt,1,,,0x20007858,"); EXPECT_EQ(CsvRowMapper::row(TraceEvent{ExceptionTraceEvent{1U, ExceptionAction::Exited}}), ",,exception,1,0x2,,,"); EXPECT_EQ(CsvRowMapper::row(TraceEvent{ExceptionTraceEvent{1U, ExceptionAction::Returned}}), ",,exception,1,0x3,,,"); EXPECT_EQ(CsvRowMapper::row(TraceEvent{ExceptionTraceEvent{1U, ExceptionAction::Unknown}}), ",,exception,1,,,,"); diff --git a/tools/ctrace/test/unit/src/output/ctf/CtfEncoderTests.cpp b/tools/ctrace/test/unit/src/output/ctf/CtfEncoderTests.cpp index 120b2a3ec..c5c5c598f 100644 --- a/tools/ctrace/test/unit/src/output/ctf/CtfEncoderTests.cpp +++ b/tools/ctrace/test/unit/src/output/ctf/CtfEncoderTests.cpp @@ -310,22 +310,38 @@ TEST(CtraceUnitTests, testCtfEncoderDwtAddressEncoding) encoder.start(outputDirectory); encoder.writeEvent(atCycle(TraceEvent{DwtAddressTraceEvent{ 3U, - DwtPcAndOffsetTraceLocation{0x12345678U, 0x0000abcdU}, + DwtPcAndOffsetTraceLocation{0x12345678U, {2U, 0x0000abcdU}}, }}, 99U)); + encoder.writeEvent( + atCycle(TraceEvent{DwtAddressTraceEvent{1U, DwtOffsetTraceLocation{{1U, 0x58U}}}}, 100U)); + encoder.writeEvent( + atCycle(TraceEvent{DwtAddressTraceEvent{2U, DwtOffsetTraceLocation{{4U, 0x20007858U}}}}, 101U)); + encoder.writeEvent(atCycle(TraceEvent{DwtAddressTraceEvent{0U, DwtPcTraceLocation{0x08001234U}}}, 102U)); encoder.stop(); const auto records = readCtfRecords(outputDirectory / "stream_0"); - ASSERT_EQ(records.size(), 1U); + ASSERT_EQ(records.size(), 4U); const auto& record = records.front(); ASSERT_TRUE(record.id == CtfSchema::value(CtfSchema::EventId::DwtAddress)) << "CTF DWT address event ID mismatch"; ASSERT_TRUE(record.timestamp == 99U) << "CTF DWT address timestamp mismatch"; ASSERT_TRUE(record.payload.size() == 14U) << "CTF DWT address event payload size mismatch"; - ASSERT_TRUE(record.payload[0U] == 3U && record.payload[1U] == 1U && record.payload[2U] == 1U) - << "CTF DWT address comparator or presence flags mismatch"; - ASSERT_TRUE(readLe32(record.payload, 3U) == 0x12345678U && readLe16(record.payload, 7U) == 0xabcdU) + ASSERT_TRUE(record.payload[0U] == 3U && record.payload[1U] == 1U && + record.payload[6U] == CtfSchema::value(CtfSchema::DwtOffsetTag::U16)) + << "CTF DWT address comparator, PC flag, or offset tag mismatch"; + ASSERT_TRUE(readLe32(record.payload, 2U) == 0x12345678U && readLe16(record.payload, 7U) == 0xabcdU) << "CTF DWT PC/address payload mismatch"; + EXPECT_EQ(records[1].payload.size(), 9U); + EXPECT_EQ(records[1].payload[2U], CtfSchema::value(CtfSchema::DwtOffsetTag::U8)); + EXPECT_EQ(records[1].payload[3U], 0x58U); + EXPECT_EQ(records[2].payload.size(), 12U); + EXPECT_EQ(records[2].payload[2U], CtfSchema::value(CtfSchema::DwtOffsetTag::U32)); + EXPECT_EQ(readLe32(records[2].payload, 3U), 0x20007858U); + EXPECT_EQ(records[3].payload.size(), 13U); + EXPECT_EQ(records[3].payload[6U], CtfSchema::value(CtfSchema::DwtOffsetTag::None)); + EXPECT_EQ(records[3].payload[7U], 0U); + encoder.abort(); } @@ -350,6 +366,8 @@ TEST(CtraceUnitTests, testCtfEncoderRejectsInvalidClockAndPayloadMetadata) invalidDwt.start(temporaryPath.path()); EXPECT_THROW(invalidDwt.writeEvent(onStream(TraceEvent{DwtDataTraceEvent{0U, 1U, 0U, AccessType::Read}}, 1U)), std::runtime_error); + EXPECT_THROW(invalidDwt.writeEvent(TraceEvent{DwtAddressTraceEvent{0U, DwtOffsetTraceLocation{{3U, 0U}}}}), + std::runtime_error); invalidDwt.abort(); } @@ -368,7 +386,10 @@ TEST(CtraceUnitTests, testCtfEncoderWritesAllDwtValueVariants) encoder.start(temporaryPath.path()); auto signed16 = atCycle( - onStream(TraceEvent{DwtDataTraceEvent{0U, 2U, 0xff80U, AccessType::Write, 0x1234U, 0x08000000U}}, 1U), 10U); + onStream(TraceEvent{DwtDataTraceEvent{ + 0U, 2U, 0xff80U, AccessType::Write, DwtAddressOffset{2U, 0x1234U}, 0x08000000U}}, + 1U), + 10U); signed16.quality = TraceQuality{false, true, 0U}; encoder.writeEvent(signed16); @@ -392,7 +413,7 @@ TEST(CtraceUnitTests, testCtfEncoderWritesAllDwtValueVariants) EXPECT_EQ(readLe16(records[0].payload, 3U), 0xff80U); EXPECT_EQ(records[0].payload[5U], 1U); EXPECT_EQ(readLe32(records[0].payload, 6U), 0x08000000U); - EXPECT_EQ(records[0].payload[10U], 1U); + EXPECT_EQ(records[0].payload[10U], CtfSchema::value(CtfSchema::DwtOffsetTag::U16)); EXPECT_EQ(readLe16(records[0].payload, 11U), 0x1234U); EXPECT_EQ(records[1].timestamp, 11U); diff --git a/tools/ctrace/test/unit/src/output/ctf/CtfMetadataWriterTests.cpp b/tools/ctrace/test/unit/src/output/ctf/CtfMetadataWriterTests.cpp index c2610f09c..812fe5a6b 100644 --- a/tools/ctrace/test/unit/src/output/ctf/CtfMetadataWriterTests.cpp +++ b/tools/ctrace/test/unit/src/output/ctf/CtfMetadataWriterTests.cpp @@ -60,6 +60,9 @@ TEST(CtraceUnitTests, testCtfMetadataWriterEscapesAndDeduplicatesSourceLabels) EXPECT_NE(metadata.find("\"Event0\" = 0"), std::string::npos); EXPECT_NE(metadata.find("\"Event7\" = 7"), std::string::npos); EXPECT_NE(metadata.find("cmsis_pmu_event_counter_t cmsis_pmu_event_counter;"), std::string::npos); + EXPECT_NE(metadata.find("none = 0, u8 = 1, u16 = 2, u32 = 4"), std::string::npos); + EXPECT_NE(metadata.find("variant "), std::string::npos); + EXPECT_NE(metadata.find("uint32_t u32;"), std::string::npos); } TEST(CtraceUnitTests, testCtfMetadataWriterRejectsMissingOutputDirectory) @@ -150,6 +153,10 @@ TEST(CtraceUnitTests, testTraceCompassXmlUsesCurrentCtfEvents) const auto matchPulseEnd = xml.find("value=\"timestamp + 1000\"", matchHandler); ASSERT_NE(matchPulseEnd, std::string::npos); EXPECT_LT(matchPulseEnd, matchHandlerEnd); + EXPECT_NE(xml.find("value=\"cmsis_dwt_offset_type\""), std::string::npos); + EXPECT_NE(xml.find("value=\"cmsis_dwt_offset.u8\" forcedType=\"long\""), std::string::npos); + EXPECT_NE(xml.find("value=\"cmsis_dwt_offset.u16\" forcedType=\"long\""), std::string::npos); + EXPECT_NE(xml.find("value=\"cmsis_dwt_offset.u32\" forcedType=\"long\""), std::string::npos); const auto threadModeEntry = xml.find("path=\"EXCEPTION/Thread Mode\""); const auto returnEntry = xml.find("path=\"EXCEPTION_RETURN/*\" displayText=\"true\""); const auto interruptEntries = xml.find("path=\"EXCEPTION/(?!Thread Mode).+\""); diff --git a/tools/ctrace/test/unit/src/output/ctf/CtfSchemaTests.cpp b/tools/ctrace/test/unit/src/output/ctf/CtfSchemaTests.cpp index 1daf27bad..86441344c 100644 --- a/tools/ctrace/test/unit/src/output/ctf/CtfSchemaTests.cpp +++ b/tools/ctrace/test/unit/src/output/ctf/CtfSchemaTests.cpp @@ -132,6 +132,11 @@ TEST(CtraceUnitTests, testCtfValueTypes) << "incompatible CTF float width was accepted"; ASSERT_TRUE(CtfSchema::valueVariantForTraceRunType("unsigned", 8U) == nullptr) << "invalid CTF data size was accepted"; + + EXPECT_EQ(CtfSchema::dwtOffsetVariantForSize(1U)->tag, CtfSchema::DwtOffsetTag::U8); + EXPECT_EQ(CtfSchema::dwtOffsetVariantForSize(2U)->tag, CtfSchema::DwtOffsetTag::U16); + EXPECT_EQ(CtfSchema::dwtOffsetVariantForSize(4U)->tag, CtfSchema::DwtOffsetTag::U32); + EXPECT_EQ(CtfSchema::dwtOffsetVariantForSize(3U), nullptr); } TEST(CtraceUnitTests, testCtfExceptionLaneTracker) diff --git a/tools/ctrace/test/unit/support/CtfTestSupport.h b/tools/ctrace/test/unit/support/CtfTestSupport.h index 57c95a409..77748194e 100644 --- a/tools/ctrace/test/unit/support/CtfTestSupport.h +++ b/tools/ctrace/test/unit/support/CtfTestSupport.h @@ -100,6 +100,23 @@ inline std::size_t ctfValueSize(std::uint8_t tag) return sizes[tag]; } +/** @brief Returns the encoded payload size of a width-tagged DWT offset. */ +inline std::size_t ctfDwtOffsetSize(std::uint8_t tag) +{ + if (tag == CtfSchema::value(CtfSchema::DwtOffsetTag::None) || + tag == CtfSchema::value(CtfSchema::DwtOffsetTag::U8)) { + return 1U; + } + if (tag == CtfSchema::value(CtfSchema::DwtOffsetTag::U16)) { + return 2U; + } + if (tag == CtfSchema::value(CtfSchema::DwtOffsetTag::U32)) { + return 4U; + } + require(false, "CTF test parser encountered an invalid DWT offset tag"); + return 0U; +} + /** @brief Determines one encoded CTF event payload size. */ inline std::size_t ctfPayloadSize(const std::vector& bytes, std::size_t payloadOffset, std::size_t contentEnd, std::uint32_t eventId) @@ -121,9 +138,7 @@ inline std::size_t ctfPayloadSize(const std::vector& bytes, std:: require(hasPc <= 1U, "CTF test parser encountered an invalid DWT PC presence flag"); size += 1U + (hasPc != 0U ? 4U : 0U); requirePayload(size + 1U); - const auto hasAddress = bytes[payloadOffset + size]; - require(hasAddress <= 1U, "CTF test parser encountered an invalid DWT address presence flag"); - size += 1U + (hasAddress != 0U ? 2U : 0U); + size += 1U + ctfDwtOffsetSize(bytes[payloadOffset + size]); return size + 5U; } if (eventId == CtfSchema::value(CtfSchema::EventId::TraceStatus)) { @@ -133,7 +148,12 @@ inline std::size_t ctfPayloadSize(const std::vector& bytes, std:: return 6U; } if (eventId == CtfSchema::value(CtfSchema::EventId::DwtAddress)) { - return 14U; + requirePayload(2U); + const auto hasPc = bytes[payloadOffset + 1U]; + require(hasPc <= 1U, "CTF test parser encountered an invalid DWT PC presence flag"); + const auto offsetTagPosition = 2U + (hasPc != 0U ? 4U : 0U); + requirePayload(offsetTagPosition + 1U); + return offsetTagPosition + 1U + ctfDwtOffsetSize(bytes[payloadOffset + offsetTagPosition]) + 5U; } if (eventId == CtfSchema::value(CtfSchema::EventId::GlobalTimestamp)) { return 9U; From 2c1264a4a60e432c3ace2ff6c12f155a78c895bb Mon Sep 17 00:00:00 2001 From: Thorsten de Buhr Date: Mon, 7 Sep 2026 12:02:02 +0200 Subject: [PATCH 2/6] feat(ctrace): preserve compressed DWT PC widths --- tools/ctrace/src/decode/DwtPacketDecoder.cpp | 17 +++---- tools/ctrace/src/decode/DwtPacketDecoder.h | 4 +- tools/ctrace/src/model/TraceEvent.h | 20 ++++---- tools/ctrace/src/output/csv/CsvRowMapper.cpp | 26 +++++----- tools/ctrace/src/output/ctf/CtfEncoder.cpp | 46 ++++++++--------- .../src/output/ctf/CtfMetadataWriter.cpp | 25 ++++------ tools/ctrace/src/output/ctf/CtfSchema.h | 32 ++++++------ .../src/output/ctf/TraceCompassXmlWriter.cpp | 4 +- .../unit/src/decode/DecodePipelineTests.cpp | 28 ++++++++++- .../unit/src/decode/DwtPacketDecoderTests.cpp | 42 +++++++++++----- .../src/output/csv/CsvFileOutputTests.cpp | 4 +- .../unit/src/output/csv/CsvRowMapperTests.cpp | 6 +-- .../unit/src/output/ctf/CtfEncoderTests.cpp | 50 ++++++++++++------- .../src/output/ctf/CtfMetadataWriterTests.cpp | 1 + .../unit/src/output/ctf/CtfSchemaTests.cpp | 8 +-- .../ctrace/test/unit/support/CtfTestSupport.h | 26 ++++------ 16 files changed, 192 insertions(+), 147 deletions(-) diff --git a/tools/ctrace/src/decode/DwtPacketDecoder.cpp b/tools/ctrace/src/decode/DwtPacketDecoder.cpp index 95c709fdf..940906550 100644 --- a/tools/ctrace/src/decode/DwtPacketDecoder.cpp +++ b/tools/ctrace/src/decode/DwtPacketDecoder.cpp @@ -53,12 +53,11 @@ 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 kArmv8MMatchBytes = 1U; constexpr std::uint32_t kArmv8MMatchValue = 1U; /** @brief Returns whether a raw DWT address fragment width can be preserved. */ -static bool isSupportedAddressOffsetSize(std::uint8_t size) +static bool isSupportedAddressFragmentSize(std::uint8_t size) { return size == 1U || size == 2U || size == 4U; } @@ -265,8 +264,7 @@ void DwtPacketDecoder::decodeDataTrace(const DwtPayloadPacket& payload, std::vec output.push_back(std::move(match)); return; } - const auto supportedSize = - secondarySubtype ? isSupportedAddressOffsetSize(payload.size) : payload.size == kArmv7MFullPcBytes; + const auto supportedSize = isSupportedAddressFragmentSize(payload.size); if (!supportedSize) { auto flushed = flush(payload.quality, payload.tcyc); output.insert(output.end(), std::make_move_iterator(flushed.begin()), std::make_move_iterator(flushed.end())); @@ -275,7 +273,7 @@ void DwtPacketDecoder::decodeDataTrace(const DwtPayloadPacket& payload, std::vec TraceIssueSeverity::Error, "unsupported DWT " + std::string(secondarySubtype ? "address offset" : "PC or match") + " payload size " + std::to_string(payload.size) + - (secondarySubtype ? "; expected 1, 2, or 4 bytes" : "; expected a 4-byte PC or 1-byte match"), + "; expected 1, 2, or 4 bytes", std::nullopt, std::nullopt, }}; @@ -286,11 +284,12 @@ void DwtPacketDecoder::decodeDataTrace(const DwtPayloadPacket& payload, std::vec output.push_back(std::move(error)); return; } + const DwtAddressFragment fragment{payload.size, payload.value}; if (secondarySubtype) { - event.offset = DwtAddressOffset{payload.size, payload.value}; + event.offset = fragment; event.hasOffset = true; } else { - event.pc = payload.value; + event.pc = fragment; event.hasPc = true; } sendDataTraceEvent(comparator, event, payload.quality, payload.tcyc, output); @@ -356,8 +355,8 @@ void DwtPacketDecoder::flushPending(std::uint32_t comparator, const TraceQuality pending->size, pending->value, pending->isRead ? AccessType::Read : AccessType::Write, - pending->hasOffset ? std::optional(pending->offset) : std::nullopt, - pending->hasPc ? std::optional(pending->pc) : std::nullopt, + pending->hasOffset ? std::optional(pending->offset) : std::nullopt, + pending->hasPc ? std::optional(pending->pc) : std::nullopt, }); } diff --git a/tools/ctrace/src/decode/DwtPacketDecoder.h b/tools/ctrace/src/decode/DwtPacketDecoder.h index 60b100a96..4c3e08da1 100644 --- a/tools/ctrace/src/decode/DwtPacketDecoder.h +++ b/tools/ctrace/src/decode/DwtPacketDecoder.h @@ -44,8 +44,8 @@ class DwtPacketDecoder { struct PendingDataTrace { std::uint64_t index = 0; std::uint8_t traceBusId = 0U; - std::uint32_t pc = 0; - DwtAddressOffset offset; + DwtAddressFragment pc; + DwtAddressFragment offset; std::uint32_t value = 0; std::uint8_t size = 4; bool isRead = false; diff --git a/tools/ctrace/src/model/TraceEvent.h b/tools/ctrace/src/model/TraceEvent.h index cd4499355..f83cb4f1c 100644 --- a/tools/ctrace/src/model/TraceEvent.h +++ b/tools/ctrace/src/model/TraceEvent.h @@ -61,12 +61,12 @@ struct SoftwareTraceEvent { }; /** @brief Stores the raw address fragment and width carried by a DWT packet. */ -struct DwtAddressOffset { +struct DwtAddressFragment { std::uint8_t size = 0; std::uint32_t value = 0; }; -constexpr bool operator==(const DwtAddressOffset& left, const DwtAddressOffset& right) +constexpr bool operator==(const DwtAddressFragment& left, const DwtAddressFragment& right) { return left.size == right.size && left.value == right.value; } @@ -77,24 +77,24 @@ struct DwtDataTraceEvent { std::uint8_t size = 0; std::uint32_t value = 0; AccessType access = AccessType::Read; - std::optional offset = std::nullopt; - std::optional pc = std::nullopt; + std::optional offset = std::nullopt; + std::optional pc = std::nullopt; }; /** @brief Identifies a DWT address event by program counter. */ struct DwtPcTraceLocation { - std::uint32_t pc; + DwtAddressFragment pc; }; /** @brief Identifies a DWT address event by its raw address fragment. */ struct DwtOffsetTraceLocation { - DwtAddressOffset offset; + DwtAddressFragment offset; }; /** @brief Identifies a DWT address event by program counter and address offset. */ struct DwtPcAndOffsetTraceLocation { - std::uint32_t pc; - DwtAddressOffset offset; + DwtAddressFragment pc; + DwtAddressFragment offset; }; /** @brief Stores one of the supported DWT address location representations. */ @@ -112,7 +112,7 @@ struct DwtMatchTraceEvent { }; /** @brief Returns the program counter carried by a DWT address event, if present. */ -inline std::optional dwtAddressPc(const DwtAddressTraceEvent& event) +inline std::optional dwtAddressPc(const DwtAddressTraceEvent& event) { if (const auto* pc = std::get_if(&event.location)) { return pc->pc; @@ -124,7 +124,7 @@ inline std::optional dwtAddressPc(const DwtAddressTraceEvent& eve } /** @brief Returns the raw address fragment carried by a DWT address event, if present. */ -inline std::optional dwtAddressOffset(const DwtAddressTraceEvent& event) +inline std::optional dwtAddressOffset(const DwtAddressTraceEvent& event) { if (const auto* offset = std::get_if(&event.location)) { return offset->offset; diff --git a/tools/ctrace/src/output/csv/CsvRowMapper.cpp b/tools/ctrace/src/output/csv/CsvRowMapper.cpp index eca02f48a..21a2aba0d 100644 --- a/tools/ctrace/src/output/csv/CsvRowMapper.cpp +++ b/tools/ctrace/src/output/csv/CsvRowMapper.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -106,6 +107,15 @@ static std::string hexValue(std::uint64_t value, std::uint32_t widthBytes) return "0x" + out; } +/** @brief Writes one optional raw DWT address fragment using its original width. */ +static void writeDwtAddressFragment(CsvRow& row, CsvColumn target, + const std::optional& fragment) +{ + if (fragment.has_value()) { + row[column(target)] = hexValue(fragment->value, fragment->size); + } +} + /** @brief Maps a semantic exception action to its CSV value. */ static std::string_view exceptionActionCsvValue(ExceptionAction action) { @@ -156,20 +166,12 @@ static CsvRow eventToCsvRow(const TraceEvent& event) } else if (const auto* data = traceEventPayload(event)) { row[column(CsvColumn::Source)] = std::to_string(data->comparator); row[column(CsvColumn::Value)] = hexValue(data->value, data->size); - if (data->pc.has_value()) { - row[column(CsvColumn::Pc)] = hexValue(*data->pc, 4); - } - if (data->offset.has_value()) { - row[column(CsvColumn::Offset)] = hexValue(data->offset->value, data->offset->size); - } + writeDwtAddressFragment(row, CsvColumn::Pc, data->pc); + writeDwtAddressFragment(row, CsvColumn::Offset, data->offset); } else if (const auto* address = traceEventPayload(event)) { row[column(CsvColumn::Source)] = std::to_string(address->comparator); - if (const auto pc = dwtAddressPc(*address)) { - row[column(CsvColumn::Pc)] = hexValue(*pc, 4); - } - if (const auto offset = dwtAddressOffset(*address)) { - row[column(CsvColumn::Offset)] = hexValue(offset->value, offset->size); - } + writeDwtAddressFragment(row, CsvColumn::Pc, dwtAddressPc(*address)); + writeDwtAddressFragment(row, CsvColumn::Offset, dwtAddressOffset(*address)); } else if (const auto* match = traceEventPayload(event)) { row[column(CsvColumn::Source)] = std::to_string(match->comparator); } else if (const auto* exception = traceEventPayload(event)) { diff --git a/tools/ctrace/src/output/ctf/CtfEncoder.cpp b/tools/ctrace/src/output/ctf/CtfEncoder.cpp index b49428c5e..f34ce732f 100644 --- a/tools/ctrace/src/output/ctf/CtfEncoder.cpp +++ b/tools/ctrace/src/output/ctf/CtfEncoder.cpp @@ -112,25 +112,25 @@ static void writeVariantValue(CtfStreamWriter::Record& record, std::uint32_t dat } } -/** @brief Resolves the CTF representation of an optional raw DWT address offset. */ -static const CtfSchema::DwtOffsetVariant& dwtOffsetVariant(const std::optional& offset) +/** @brief Resolves the CTF representation of an optional raw DWT address fragment. */ +static const CtfSchema::DwtAddressVariant& dwtAddressVariant(const std::optional& fragment) { - if (!offset.has_value()) { - return CtfSchema::DwtOffsetVariants.front(); + if (!fragment.has_value()) { + return CtfSchema::DwtAddressVariants.front(); } - const auto* variant = CtfSchema::dwtOffsetVariantForSize(offset->size); + const auto* variant = CtfSchema::dwtAddressVariantForSize(fragment->size); if (variant == nullptr) { - throw std::runtime_error("CTF DWT address offset has an invalid SWO payload size"); + throw std::runtime_error("CTF DWT address fragment has an invalid SWO payload size"); } return *variant; } -/** @brief Writes an optional raw DWT address offset using its exact SWO width. */ -static void writeDwtOffset(CtfStreamWriter::Record& record, const std::optional& offset, - const CtfSchema::DwtOffsetVariant& variant) +/** @brief Writes an optional raw DWT address fragment using its exact SWO width. */ +static void writeDwtAddress(CtfStreamWriter::Record& record, const std::optional& fragment, + const CtfSchema::DwtAddressVariant& variant) { record.writeU8(CtfSchema::value(variant.tag)); - const auto value = offset.has_value() ? offset->value : 0U; + const auto value = fragment.has_value() ? fragment->value : 0U; if (variant.byteSize == 1U) { record.writeU8(static_cast(value & 0xffU)); } else if (variant.byteSize == 2U) { @@ -326,10 +326,10 @@ void CtfEncoder::writeDwtValueEvent(const TraceEvent& event, const DwtDataTraceE const auto* source = resolvedTraceSource(m_config, "dwt", event.traceBusId, data.comparator); reportDwtSizeMismatch(event, data, source); const auto& variant = dwtValueVariant(source, data.comparator); - const auto hasPc = data.pc.has_value() ? 1U : 0U; - const auto& offsetVariant = dwtOffsetVariant(data.offset); + const auto& pcVariant = dwtAddressVariant(data.pc); + const auto& offsetVariant = dwtAddressVariant(data.offset); const auto payloadSize = - 1U + 1U + 1U + variant.byteSize + 1U + hasPc * 4U + 1U + offsetVariant.byteSize + 1U + 4U; + 1U + 1U + 1U + variant.byteSize + 1U + pcVariant.byteSize + 1U + offsetVariant.byteSize + 1U + 4U; const auto eventTimestamp = allocateEventTimestamp(event.traceBusId); const auto quality = computeSampleQuality(event); m_stream.writeRecord(CtfSchema::value(CtfSchema::EventId::DwtValue), eventTimestamp, event.traceBusId, payloadSize, @@ -340,11 +340,8 @@ void CtfEncoder::writeDwtValueEvent(const TraceEvent& event, const DwtDataTraceE : CtfSchema::DwtAccess::Write)); record.writeU8(CtfSchema::value(variant.tag)); writeVariantValue(record, data.value, data.size, variant); - record.writeU8(static_cast(hasPc)); - if (hasPc != 0U) { - record.writeU32(*data.pc); - } - writeDwtOffset(record, data.offset, offsetVariant); + writeDwtAddress(record, data.pc, pcVariant); + writeDwtAddress(record, data.offset, offsetVariant); record.writeU8(quality.first); record.writeU32(quality.second); }); @@ -379,17 +376,14 @@ void CtfEncoder::writeDwtAddrEvent(const TraceEvent& event, const DwtAddressTrac const auto quality = computeSampleQuality(event); const auto pc = dwtAddressPc(address); const auto offset = dwtAddressOffset(address); - const auto& offsetVariant = dwtOffsetVariant(offset); - const auto hasPc = pc.has_value() ? 1U : 0U; - const auto payloadSize = 1U + 1U + hasPc * 4U + 1U + offsetVariant.byteSize + 1U + 4U; + const auto& pcVariant = dwtAddressVariant(pc); + const auto& offsetVariant = dwtAddressVariant(offset); + const auto payloadSize = 1U + 1U + pcVariant.byteSize + 1U + offsetVariant.byteSize + 1U + 4U; m_stream.writeRecord(CtfSchema::value(CtfSchema::EventId::DwtAddress), eventTimestamp, event.traceBusId, payloadSize, [&](CtfStreamWriter::Record& record) { record.writeU8(static_cast(address.comparator & 0xffU)); - record.writeU8(static_cast(hasPc)); - if (hasPc != 0U) { - record.writeU32(*pc); - } - writeDwtOffset(record, offset, offsetVariant); + writeDwtAddress(record, pc, pcVariant); + writeDwtAddress(record, offset, offsetVariant); record.writeU8(quality.first); record.writeU32(quality.second); }); diff --git a/tools/ctrace/src/output/ctf/CtfMetadataWriter.cpp b/tools/ctrace/src/output/ctf/CtfMetadataWriter.cpp index 3c2d82b4d..84f3c82f4 100644 --- a/tools/ctrace/src/output/ctf/CtfMetadataWriter.cpp +++ b/tools/ctrace/src/output/ctf/CtfMetadataWriter.cpp @@ -170,25 +170,25 @@ static std::string ctfValueFields(const std::string_view& prefix) return out.str(); } -/** @brief Generates the width-tagged TSDL field for a raw DWT address offset. */ -static std::string ctfDwtOffsetFields() +/** @brief Generates a width-tagged TSDL field for a raw DWT address fragment. */ +static std::string ctfDwtAddressFields(const std::string_view& field) { std::ostringstream out; out << " enum : uint8_t { "; - for (std::size_t index = 0; index < CtfSchema::DwtOffsetVariants.size(); ++index) { - const auto& variant = CtfSchema::DwtOffsetVariants[index]; + for (std::size_t index = 0; index < CtfSchema::DwtAddressVariants.size(); ++index) { + const auto& variant = CtfSchema::DwtAddressVariants[index]; if (index > 0U) { out << ", "; } out << variant.name << " = " << static_cast(CtfSchema::value(variant.tag)); } - out << " } cmsis_dwt_offset_type;\n" - << " variant {\n"; - for (const auto& variant : CtfSchema::DwtOffsetVariants) { + out << " } cmsis_dwt_" << field << "_type;\n" + << " variant {\n"; + for (const auto& variant : CtfSchema::DwtAddressVariants) { const auto type = variant.byteSize == 1U ? "uint8_t" : variant.byteSize == 2U ? "uint16_t" : "uint32_t"; out << " " << type << " " << variant.name << ";\n"; } - out << " } cmsis_dwt_offset;\n"; + out << " } cmsis_dwt_" << field << ";\n"; return out.str(); } @@ -426,10 +426,7 @@ event { fields := struct { cmsis_dwt_comparator_t cmsis_dwt_comparator; cmsis_dwt_access_t cmsis_dwt_access; -)" << ctfValueFields("dwt") - << R"( uint8_t cmsis_has_pc; - uint32_t cmsis_pc[cmsis_has_pc]; -)" << ctfDwtOffsetFields() +)" << ctfValueFields("dwt") << ctfDwtAddressFields("pc") << ctfDwtAddressFields("offset") << R"( uint8_t cmsis_sample_flags; uint32_t cmsis_overflow_count; }; @@ -450,9 +447,7 @@ event { << CtfSchema::SwoStreamId << R"(; fields := struct { cmsis_dwt_comparator_t cmsis_dwt_comparator; - uint8_t cmsis_has_pc; - uint32_t cmsis_pc[cmsis_has_pc]; -)" << ctfDwtOffsetFields() +)" << ctfDwtAddressFields("pc") << ctfDwtAddressFields("offset") << R"( uint8_t cmsis_sample_flags; uint32_t cmsis_overflow_count; }; diff --git a/tools/ctrace/src/output/ctf/CtfSchema.h b/tools/ctrace/src/output/ctf/CtfSchema.h index e1d7f43c9..2bd7f5cf9 100644 --- a/tools/ctrace/src/output/ctf/CtfSchema.h +++ b/tools/ctrace/src/output/ctf/CtfSchema.h @@ -79,26 +79,26 @@ enum class ValueTag : std::uint8_t { Float32 = 6U, }; -/** @brief Identifies the width of a raw DWT address offset in CTF. */ -enum class DwtOffsetTag : std::uint8_t { +/** @brief Identifies the width of a raw DWT address fragment in CTF. */ +enum class DwtAddressTag : std::uint8_t { None = 0U, U8 = 1U, U16 = 2U, U32 = 4U, }; -/** @brief Describes one CTF DWT address-offset representation. */ -struct DwtOffsetVariant { - DwtOffsetTag tag; +/** @brief Describes one CTF DWT address-fragment representation. */ +struct DwtAddressVariant { + DwtAddressTag tag; std::string_view name; std::uint8_t byteSize; }; -inline constexpr std::array DwtOffsetVariants{{ - {DwtOffsetTag::None, "none", 1U}, - {DwtOffsetTag::U8, "u8", 1U}, - {DwtOffsetTag::U16, "u16", 2U}, - {DwtOffsetTag::U32, "u32", 4U}, +inline constexpr std::array DwtAddressVariants{{ + {DwtAddressTag::None, "none", 1U}, + {DwtAddressTag::U8, "u8", 1U}, + {DwtAddressTag::U16, "u16", 2U}, + {DwtAddressTag::U32, "u32", 4U}, }}; /** @brief Describes one supported CTF sample value encoding. */ @@ -131,11 +131,11 @@ constexpr const ValueVariant& valueVariant(ValueTag tag) return ValueVariants[static_cast(tag)]; } -/** @brief Resolves a DWT address-offset payload width to its CTF representation. */ -constexpr const DwtOffsetVariant* dwtOffsetVariantForSize(std::uint8_t byteSize) +/** @brief Resolves a DWT address-fragment width to its CTF representation. */ +constexpr const DwtAddressVariant* dwtAddressVariantForSize(std::uint8_t byteSize) { - for (const auto& variant : DwtOffsetVariants) { - if (variant.tag != DwtOffsetTag::None && variant.byteSize == byteSize) { + for (const auto& variant : DwtAddressVariants) { + if (variant.tag != DwtAddressTag::None && variant.byteSize == byteSize) { return &variant; } } @@ -187,8 +187,8 @@ constexpr std::uint8_t value(ValueTag tag) return static_cast(tag); } -/** @brief Returns the integer representation of a DWT offset tag. */ -constexpr std::uint8_t value(DwtOffsetTag tag) +/** @brief Returns the integer representation of a DWT address tag. */ +constexpr std::uint8_t value(DwtAddressTag tag) { return static_cast(tag); } diff --git a/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp b/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp index 90cf3ec06..606316090 100644 --- a/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp +++ b/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp @@ -161,8 +161,8 @@ static std::string dwtMatchHandler() static std::string dwtAddressHandlers() { std::ostringstream handlers; - for (const auto& variant : CtfSchema::DwtOffsetVariants) { - if (variant.tag == CtfSchema::DwtOffsetTag::None) { + for (const auto& variant : CtfSchema::DwtAddressVariants) { + if (variant.tag == CtfSchema::DwtAddressTag::None) { continue; } handlers << R"( diff --git a/tools/ctrace/test/unit/src/decode/DecodePipelineTests.cpp b/tools/ctrace/test/unit/src/decode/DecodePipelineTests.cpp index fce77f150..cbb9841d4 100644 --- a/tools/ctrace/test/unit/src/decode/DecodePipelineTests.cpp +++ b/tools/ctrace/test/unit/src/decode/DecodePipelineTests.cpp @@ -296,7 +296,9 @@ TEST(CtraceUnitTests, testCortexMPostDecoderOverflowFlushesDwtSegments) << "overflow segment first packet should be timestamp"; const auto* address = traceEventPayload(packets[1]); ASSERT_TRUE(address != nullptr) << "overflow should flush pending DWT fragment as an address event"; - ASSERT_TRUE(dwtAddressPc(*address) == std::optional(0x08001234U)) << "flushed DWT PC mismatch"; + ASSERT_TRUE(dwtAddressPc(*address) == + std::optional(DwtAddressFragment{4U, 0x08001234U})) + << "flushed DWT PC mismatch"; ASSERT_TRUE(packets[1].tcyc.has_value() && packets[1].tcyc.value() == 100) << "flushed DWT PC timestamp mismatch"; ASSERT_TRUE(packets[1].quality.has_value() && packets[1].quality->overflow) << "flushed DWT PC should carry overflow status"; @@ -602,6 +604,30 @@ TEST(CtraceUnitTests, testDecodePipelinePreservesPeriodicPcSamples) EXPECT_TRUE(samples[1].sleeping) << "OpenCSD periodic PC sleep indication mismatch"; } +TEST(CtraceUnitTests, testDecodePipelinePreservesCompressedDataTracePcValues) +{ + const std::uint8_t trace[] = { + 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x80U, + 0x45U, 0x58U, + 0x46U, 0x58U, 0x78U, + 0x47U, 0x58U, 0x78U, 0x00U, 0x08U, + }; + const auto decoded = decodeTrace({rawBytes(trace)}); + + std::vector pcs; + for (const auto& event : decoded.events) { + if (const auto* address = traceEventPayload(event)) { + if (const auto pc = dwtAddressPc(*address)) { + pcs.push_back(*pc); + } + } + } + ASSERT_EQ(pcs.size(), 3U); + EXPECT_EQ(pcs[0], (DwtAddressFragment{1U, 0x58U})); + EXPECT_EQ(pcs[1], (DwtAddressFragment{2U, 0x7858U})); + EXPECT_EQ(pcs[2], (DwtAddressFragment{4U, 0x08007858U})); +} + TEST(CtraceUnitTests, testDecodePipelineDoesNotInjectSync) { const std::uint8_t validWithoutAsync[] = {0x01U, static_cast('A')}; diff --git a/tools/ctrace/test/unit/src/decode/DwtPacketDecoderTests.cpp b/tools/ctrace/test/unit/src/decode/DwtPacketDecoderTests.cpp index d96db9364..fc42d8a40 100644 --- a/tools/ctrace/test/unit/src/decode/DwtPacketDecoderTests.cpp +++ b/tools/ctrace/test/unit/src/decode/DwtPacketDecoderTests.cpp @@ -263,13 +263,14 @@ TEST(CtraceUnitTests, testDwtPacketDecoderPreservesRepeatedAddressFragments) << "the second repeated DWT address fragment lost its identity"; if (discriminator == 8U) { - ASSERT_TRUE(firstPc == std::optional(firstValue) && - dwtAddressPc(*secondAddress) == std::optional(secondValue)) + ASSERT_TRUE(firstPc == std::optional(DwtAddressFragment{size, firstValue}) && + dwtAddressPc(*secondAddress) == + std::optional(DwtAddressFragment{size, secondValue})) << "repeated DWT PC fragments were overwritten"; } else { - ASSERT_TRUE(firstOffset == std::optional(DwtAddressOffset{size, firstValue}) && + ASSERT_TRUE(firstOffset == std::optional(DwtAddressFragment{size, firstValue}) && dwtAddressOffset(*secondAddress) == - std::optional(DwtAddressOffset{size, secondValue})) + std::optional(DwtAddressFragment{size, secondValue})) << "repeated DWT offset fragments were overwritten"; } }; @@ -313,7 +314,7 @@ TEST(CtraceUnitTests, testDwtPacketDecoderFlushesPendingComparatorBeforeMatch) ASSERT_EQ(packets.size(), 2U); ASSERT_NE(traceEventPayload(packets[0]), nullptr); EXPECT_EQ(dwtAddressPc(*traceEventPayload(packets[0])), - std::optional(0x08001234U)); + std::optional(DwtAddressFragment{4U, 0x08001234U})); EXPECT_NE(traceEventPayload(packets[1]), nullptr); EXPECT_EQ(packets[0].tcyc, std::optional(100U)); EXPECT_EQ(packets[1].tcyc, std::optional(100U)); @@ -338,8 +339,6 @@ TEST(CtraceUnitTests, testDwtPacketDecoderRejectsUnsupportedAddressWidths) << "unsupported DWT address width diagnostic lost packet identity"; }; - verify(8U, 1U); - verify(8U, 2U); verify(9U, 3U); } @@ -353,7 +352,7 @@ TEST(CtraceUnitTests, testDwtPacketDecoderPreservesRawAddressOffsetWidths) ASSERT_EQ(packets.size(), 1U); const auto* address = traceEventPayload(packets.front()); ASSERT_NE(address, nullptr); - EXPECT_EQ(dwtAddressOffset(*address), std::optional(DwtAddressOffset{size, value})); + EXPECT_EQ(dwtAddressOffset(*address), std::optional(DwtAddressFragment{size, value})); EXPECT_EQ(CsvRowMapper::row(packets.front()), expectedCsv); }; @@ -362,6 +361,25 @@ TEST(CtraceUnitTests, testDwtPacketDecoderPreservesRawAddressOffsetWidths) verify(4U, 0x20007858U, "100,3,dwt,0,,,0x20007858,"); } +TEST(CtraceUnitTests, testDwtPacketDecoderPreservesRawPcWidths) +{ + const auto verify = [](std::uint8_t size, std::uint32_t value, const char* expectedCsv) { + DwtPacketDecoder decoder; + EXPECT_TRUE(decoder.decode(dwtPayload(8U, size, value, 17U, 3U, 99U)).empty()); + + const auto packets = decoder.flush({}, 100U); + ASSERT_EQ(packets.size(), 1U); + const auto* address = traceEventPayload(packets.front()); + ASSERT_NE(address, nullptr); + EXPECT_EQ(dwtAddressPc(*address), std::optional(DwtAddressFragment{size, value})); + EXPECT_EQ(CsvRowMapper::row(packets.front()), expectedCsv); + }; + + verify(1U, 0x58U, "100,3,dwt,0,,0x58,,"); + verify(2U, 0x7858U, "100,3,dwt,0,,0x7858,,"); + verify(4U, 0x08007858U, "100,3,dwt,0,,0x08007858,,"); +} + TEST(CtraceUnitTests, testDwtPacketDecoderMapsAllExceptionActions) { const auto verify = [](std::uint32_t actionCode, ExceptionAction expected) { @@ -426,8 +444,8 @@ TEST(CtraceUnitTests, testDwtPacketDecoderCombinesPcOffsetAndValue) const auto* data = traceEventPayload(packets.front()); ASSERT_NE(data, nullptr); EXPECT_EQ(data->access, AccessType::Read); - EXPECT_EQ(data->pc, std::optional(0x08001234U)); - EXPECT_EQ(data->offset, std::optional(DwtAddressOffset{2U, 0x20U})); + EXPECT_EQ(data->pc, std::optional(DwtAddressFragment{4U, 0x08001234U})); + EXPECT_EQ(data->offset, std::optional(DwtAddressFragment{2U, 0x20U})); DwtPacketDecoder addressDecoder; EXPECT_TRUE(addressDecoder.decode(pc).empty()); @@ -436,6 +454,6 @@ TEST(CtraceUnitTests, testDwtPacketDecoderCombinesPcOffsetAndValue) ASSERT_EQ(addresses.size(), 1U); const auto* address = traceEventPayload(addresses.front()); ASSERT_NE(address, nullptr); - EXPECT_EQ(dwtAddressPc(*address), std::optional(0x08001234U)); - EXPECT_EQ(dwtAddressOffset(*address), std::optional(DwtAddressOffset{2U, 0x20U})); + EXPECT_EQ(dwtAddressPc(*address), std::optional(DwtAddressFragment{4U, 0x08001234U})); + EXPECT_EQ(dwtAddressOffset(*address), std::optional(DwtAddressFragment{2U, 0x20U})); } diff --git a/tools/ctrace/test/unit/src/output/csv/CsvFileOutputTests.cpp b/tools/ctrace/test/unit/src/output/csv/CsvFileOutputTests.cpp index 37ddd6cbf..3e20636ce 100644 --- a/tools/ctrace/test/unit/src/output/csv/CsvFileOutputTests.cpp +++ b/tools/ctrace/test/unit/src/output/csv/CsvFileOutputTests.cpp @@ -128,8 +128,8 @@ TEST(CtraceUnitTests, testCsvFileOutputMatchesSpecification) 4U, 0xfffffdf9U, AccessType::Read, - DwtAddressOffset{2U, 0xfdf9U}, - 0x08001234U, + DwtAddressFragment{2U, 0xfdf9U}, + DwtAddressFragment{4U, 0x08001234U}, }}, 949338400U)); output.writeEvent(atCycle(TraceEvent{ExceptionTraceEvent{11U, ExceptionAction::Entered}}, 950364820U)); diff --git a/tools/ctrace/test/unit/src/output/csv/CsvRowMapperTests.cpp b/tools/ctrace/test/unit/src/output/csv/CsvRowMapperTests.cpp index 04edc0876..bf35d6490 100644 --- a/tools/ctrace/test/unit/src/output/csv/CsvRowMapperTests.cpp +++ b/tools/ctrace/test/unit/src/output/csv/CsvRowMapperTests.cpp @@ -38,7 +38,7 @@ TEST(CtraceUnitTests, testCsvRowMapperAndTraceEventSchema) const std::vector>> semanticTypes{ {TraceEvent{SoftwareTraceEvent{}}, TraceEventType::Itm}, {TraceEvent{DwtDataTraceEvent{}}, TraceEventType::Dwt}, - {TraceEvent{DwtAddressTraceEvent{0U, DwtPcTraceLocation{0U}}}, TraceEventType::Dwt}, + {TraceEvent{DwtAddressTraceEvent{0U, DwtPcTraceLocation{{4U, 0U}}}}, TraceEventType::Dwt}, {TraceEvent{DwtMatchTraceEvent{}}, TraceEventType::Dwt}, {TraceEvent{ExceptionTraceEvent{}}, TraceEventType::Exception}, {TraceEvent{DwtEventTraceEvent{}}, TraceEventType::Event}, @@ -81,8 +81,8 @@ TEST(CtraceUnitTests, testCsvRowMapperCoversAddressAndExceptionVariants) EXPECT_EQ(CsvRowMapper::row(TraceEvent{DwtAddressTraceEvent{2U, DwtOffsetTraceLocation{{2U, 0xabcdU}}}}), ",,dwt,2,,,0xabcd,"); EXPECT_EQ( - CsvRowMapper::row(TraceEvent{DwtAddressTraceEvent{3U, DwtPcAndOffsetTraceLocation{0x1234U, {1U, 0x56U}}}}), - ",,dwt,3,,0x00001234,0x56,"); + CsvRowMapper::row(TraceEvent{DwtAddressTraceEvent{3U, DwtPcAndOffsetTraceLocation{{2U, 0x1234U}, {1U, 0x56U}}}}), + ",,dwt,3,,0x1234,0x56,"); EXPECT_EQ(CsvRowMapper::row(TraceEvent{DwtAddressTraceEvent{1U, DwtOffsetTraceLocation{{4U, 0x20007858U}}}}), ",,dwt,1,,,0x20007858,"); EXPECT_EQ(CsvRowMapper::row(TraceEvent{ExceptionTraceEvent{1U, ExceptionAction::Exited}}), ",,exception,1,0x2,,,"); diff --git a/tools/ctrace/test/unit/src/output/ctf/CtfEncoderTests.cpp b/tools/ctrace/test/unit/src/output/ctf/CtfEncoderTests.cpp index c5c5c598f..cd5b63717 100644 --- a/tools/ctrace/test/unit/src/output/ctf/CtfEncoderTests.cpp +++ b/tools/ctrace/test/unit/src/output/ctf/CtfEncoderTests.cpp @@ -310,37 +310,46 @@ TEST(CtraceUnitTests, testCtfEncoderDwtAddressEncoding) encoder.start(outputDirectory); encoder.writeEvent(atCycle(TraceEvent{DwtAddressTraceEvent{ 3U, - DwtPcAndOffsetTraceLocation{0x12345678U, {2U, 0x0000abcdU}}, + DwtPcAndOffsetTraceLocation{{4U, 0x12345678U}, {2U, 0x0000abcdU}}, }}, 99U)); encoder.writeEvent( atCycle(TraceEvent{DwtAddressTraceEvent{1U, DwtOffsetTraceLocation{{1U, 0x58U}}}}, 100U)); encoder.writeEvent( atCycle(TraceEvent{DwtAddressTraceEvent{2U, DwtOffsetTraceLocation{{4U, 0x20007858U}}}}, 101U)); - encoder.writeEvent(atCycle(TraceEvent{DwtAddressTraceEvent{0U, DwtPcTraceLocation{0x08001234U}}}, 102U)); + encoder.writeEvent(atCycle(TraceEvent{DwtAddressTraceEvent{0U, DwtPcTraceLocation{{4U, 0x08001234U}}}}, 102U)); + encoder.writeEvent(atCycle(TraceEvent{DwtAddressTraceEvent{1U, DwtPcTraceLocation{{1U, 0x58U}}}}, 103U)); + encoder.writeEvent(atCycle(TraceEvent{DwtAddressTraceEvent{2U, DwtPcTraceLocation{{2U, 0x7858U}}}}, 104U)); encoder.stop(); const auto records = readCtfRecords(outputDirectory / "stream_0"); - ASSERT_EQ(records.size(), 4U); + ASSERT_EQ(records.size(), 6U); const auto& record = records.front(); ASSERT_TRUE(record.id == CtfSchema::value(CtfSchema::EventId::DwtAddress)) << "CTF DWT address event ID mismatch"; ASSERT_TRUE(record.timestamp == 99U) << "CTF DWT address timestamp mismatch"; ASSERT_TRUE(record.payload.size() == 14U) << "CTF DWT address event payload size mismatch"; - ASSERT_TRUE(record.payload[0U] == 3U && record.payload[1U] == 1U && - record.payload[6U] == CtfSchema::value(CtfSchema::DwtOffsetTag::U16)) - << "CTF DWT address comparator, PC flag, or offset tag mismatch"; + ASSERT_TRUE(record.payload[0U] == 3U && + record.payload[1U] == CtfSchema::value(CtfSchema::DwtAddressTag::U32) && + record.payload[6U] == CtfSchema::value(CtfSchema::DwtAddressTag::U16)) + << "CTF DWT address comparator, PC tag, or offset tag mismatch"; ASSERT_TRUE(readLe32(record.payload, 2U) == 0x12345678U && readLe16(record.payload, 7U) == 0xabcdU) << "CTF DWT PC/address payload mismatch"; - EXPECT_EQ(records[1].payload.size(), 9U); - EXPECT_EQ(records[1].payload[2U], CtfSchema::value(CtfSchema::DwtOffsetTag::U8)); - EXPECT_EQ(records[1].payload[3U], 0x58U); - EXPECT_EQ(records[2].payload.size(), 12U); - EXPECT_EQ(records[2].payload[2U], CtfSchema::value(CtfSchema::DwtOffsetTag::U32)); - EXPECT_EQ(readLe32(records[2].payload, 3U), 0x20007858U); + EXPECT_EQ(records[1].payload.size(), 10U); + EXPECT_EQ(records[1].payload[3U], CtfSchema::value(CtfSchema::DwtAddressTag::U8)); + EXPECT_EQ(records[1].payload[4U], 0x58U); + EXPECT_EQ(records[2].payload.size(), 13U); + EXPECT_EQ(records[2].payload[3U], CtfSchema::value(CtfSchema::DwtAddressTag::U32)); + EXPECT_EQ(readLe32(records[2].payload, 4U), 0x20007858U); EXPECT_EQ(records[3].payload.size(), 13U); - EXPECT_EQ(records[3].payload[6U], CtfSchema::value(CtfSchema::DwtOffsetTag::None)); + EXPECT_EQ(records[3].payload[6U], CtfSchema::value(CtfSchema::DwtAddressTag::None)); EXPECT_EQ(records[3].payload[7U], 0U); + EXPECT_EQ(records[4].payload.size(), 10U); + EXPECT_EQ(records[4].payload[1U], CtfSchema::value(CtfSchema::DwtAddressTag::U8)); + EXPECT_EQ(records[4].payload[2U], 0x58U); + EXPECT_EQ(records[5].payload.size(), 11U); + EXPECT_EQ(records[5].payload[1U], CtfSchema::value(CtfSchema::DwtAddressTag::U16)); + EXPECT_EQ(readLe16(records[5].payload, 2U), 0x7858U); encoder.abort(); } @@ -387,7 +396,12 @@ TEST(CtraceUnitTests, testCtfEncoderWritesAllDwtValueVariants) auto signed16 = atCycle( onStream(TraceEvent{DwtDataTraceEvent{ - 0U, 2U, 0xff80U, AccessType::Write, DwtAddressOffset{2U, 0x1234U}, 0x08000000U}}, + 0U, + 2U, + 0xff80U, + AccessType::Write, + DwtAddressFragment{2U, 0x1234U}, + DwtAddressFragment{2U, 0x5678U}}}, 1U), 10U); signed16.quality = TraceQuality{false, true, 0U}; @@ -411,10 +425,10 @@ TEST(CtraceUnitTests, testCtfEncoderWritesAllDwtValueVariants) EXPECT_EQ(records[0].payload[1U], CtfSchema::value(CtfSchema::DwtAccess::Write)); EXPECT_EQ(records[0].payload[2U], CtfSchema::value(CtfSchema::ValueTag::Signed16)); EXPECT_EQ(readLe16(records[0].payload, 3U), 0xff80U); - EXPECT_EQ(records[0].payload[5U], 1U); - EXPECT_EQ(readLe32(records[0].payload, 6U), 0x08000000U); - EXPECT_EQ(records[0].payload[10U], CtfSchema::value(CtfSchema::DwtOffsetTag::U16)); - EXPECT_EQ(readLe16(records[0].payload, 11U), 0x1234U); + EXPECT_EQ(records[0].payload[5U], CtfSchema::value(CtfSchema::DwtAddressTag::U16)); + EXPECT_EQ(readLe16(records[0].payload, 6U), 0x5678U); + EXPECT_EQ(records[0].payload[8U], CtfSchema::value(CtfSchema::DwtAddressTag::U16)); + EXPECT_EQ(readLe16(records[0].payload, 9U), 0x1234U); EXPECT_EQ(records[1].timestamp, 11U); EXPECT_EQ(records[1].payload[2U], CtfSchema::value(CtfSchema::ValueTag::Float32)); diff --git a/tools/ctrace/test/unit/src/output/ctf/CtfMetadataWriterTests.cpp b/tools/ctrace/test/unit/src/output/ctf/CtfMetadataWriterTests.cpp index 812fe5a6b..cfba8ec5d 100644 --- a/tools/ctrace/test/unit/src/output/ctf/CtfMetadataWriterTests.cpp +++ b/tools/ctrace/test/unit/src/output/ctf/CtfMetadataWriterTests.cpp @@ -61,6 +61,7 @@ TEST(CtraceUnitTests, testCtfMetadataWriterEscapesAndDeduplicatesSourceLabels) EXPECT_NE(metadata.find("\"Event7\" = 7"), std::string::npos); EXPECT_NE(metadata.find("cmsis_pmu_event_counter_t cmsis_pmu_event_counter;"), std::string::npos); EXPECT_NE(metadata.find("none = 0, u8 = 1, u16 = 2, u32 = 4"), std::string::npos); + EXPECT_NE(metadata.find("variant "), std::string::npos); EXPECT_NE(metadata.find("variant "), std::string::npos); EXPECT_NE(metadata.find("uint32_t u32;"), std::string::npos); } diff --git a/tools/ctrace/test/unit/src/output/ctf/CtfSchemaTests.cpp b/tools/ctrace/test/unit/src/output/ctf/CtfSchemaTests.cpp index 86441344c..d1b8fb648 100644 --- a/tools/ctrace/test/unit/src/output/ctf/CtfSchemaTests.cpp +++ b/tools/ctrace/test/unit/src/output/ctf/CtfSchemaTests.cpp @@ -133,10 +133,10 @@ TEST(CtraceUnitTests, testCtfValueTypes) ASSERT_TRUE(CtfSchema::valueVariantForTraceRunType("unsigned", 8U) == nullptr) << "invalid CTF data size was accepted"; - EXPECT_EQ(CtfSchema::dwtOffsetVariantForSize(1U)->tag, CtfSchema::DwtOffsetTag::U8); - EXPECT_EQ(CtfSchema::dwtOffsetVariantForSize(2U)->tag, CtfSchema::DwtOffsetTag::U16); - EXPECT_EQ(CtfSchema::dwtOffsetVariantForSize(4U)->tag, CtfSchema::DwtOffsetTag::U32); - EXPECT_EQ(CtfSchema::dwtOffsetVariantForSize(3U), nullptr); + EXPECT_EQ(CtfSchema::dwtAddressVariantForSize(1U)->tag, CtfSchema::DwtAddressTag::U8); + EXPECT_EQ(CtfSchema::dwtAddressVariantForSize(2U)->tag, CtfSchema::DwtAddressTag::U16); + EXPECT_EQ(CtfSchema::dwtAddressVariantForSize(4U)->tag, CtfSchema::DwtAddressTag::U32); + EXPECT_EQ(CtfSchema::dwtAddressVariantForSize(3U), nullptr); } TEST(CtraceUnitTests, testCtfExceptionLaneTracker) diff --git a/tools/ctrace/test/unit/support/CtfTestSupport.h b/tools/ctrace/test/unit/support/CtfTestSupport.h index 77748194e..22c8b7378 100644 --- a/tools/ctrace/test/unit/support/CtfTestSupport.h +++ b/tools/ctrace/test/unit/support/CtfTestSupport.h @@ -100,20 +100,20 @@ inline std::size_t ctfValueSize(std::uint8_t tag) return sizes[tag]; } -/** @brief Returns the encoded payload size of a width-tagged DWT offset. */ -inline std::size_t ctfDwtOffsetSize(std::uint8_t tag) +/** @brief Returns the encoded payload size of a width-tagged DWT address fragment. */ +inline std::size_t ctfDwtAddressSize(std::uint8_t tag) { - if (tag == CtfSchema::value(CtfSchema::DwtOffsetTag::None) || - tag == CtfSchema::value(CtfSchema::DwtOffsetTag::U8)) { + if (tag == CtfSchema::value(CtfSchema::DwtAddressTag::None) || + tag == CtfSchema::value(CtfSchema::DwtAddressTag::U8)) { return 1U; } - if (tag == CtfSchema::value(CtfSchema::DwtOffsetTag::U16)) { + if (tag == CtfSchema::value(CtfSchema::DwtAddressTag::U16)) { return 2U; } - if (tag == CtfSchema::value(CtfSchema::DwtOffsetTag::U32)) { + if (tag == CtfSchema::value(CtfSchema::DwtAddressTag::U32)) { return 4U; } - require(false, "CTF test parser encountered an invalid DWT offset tag"); + require(false, "CTF test parser encountered an invalid DWT address tag"); return 0U; } @@ -134,11 +134,9 @@ inline std::size_t ctfPayloadSize(const std::vector& bytes, std:: requirePayload(3U); auto size = 3U + ctfValueSize(bytes[payloadOffset + 2U]); requirePayload(size + 1U); - const auto hasPc = bytes[payloadOffset + size]; - require(hasPc <= 1U, "CTF test parser encountered an invalid DWT PC presence flag"); - size += 1U + (hasPc != 0U ? 4U : 0U); + size += 1U + ctfDwtAddressSize(bytes[payloadOffset + size]); requirePayload(size + 1U); - size += 1U + ctfDwtOffsetSize(bytes[payloadOffset + size]); + size += 1U + ctfDwtAddressSize(bytes[payloadOffset + size]); return size + 5U; } if (eventId == CtfSchema::value(CtfSchema::EventId::TraceStatus)) { @@ -149,11 +147,9 @@ inline std::size_t ctfPayloadSize(const std::vector& bytes, std:: } if (eventId == CtfSchema::value(CtfSchema::EventId::DwtAddress)) { requirePayload(2U); - const auto hasPc = bytes[payloadOffset + 1U]; - require(hasPc <= 1U, "CTF test parser encountered an invalid DWT PC presence flag"); - const auto offsetTagPosition = 2U + (hasPc != 0U ? 4U : 0U); + const auto offsetTagPosition = 2U + ctfDwtAddressSize(bytes[payloadOffset + 1U]); requirePayload(offsetTagPosition + 1U); - return offsetTagPosition + 1U + ctfDwtOffsetSize(bytes[payloadOffset + offsetTagPosition]) + 5U; + return offsetTagPosition + 1U + ctfDwtAddressSize(bytes[payloadOffset + offsetTagPosition]) + 5U; } if (eventId == CtfSchema::value(CtfSchema::EventId::GlobalTimestamp)) { return 9U; From d198c8580f291efa3c96ab280e42dec7eb2d8dbd Mon Sep 17 00:00:00 2001 From: Thorsten de Buhr Date: Mon, 7 Sep 2026 14:16:15 +0200 Subject: [PATCH 3/6] feat(ctrace): rename DWT CSV offset to address --- tools/ctrace/src/output/csv/CsvRowMapper.cpp | 8 ++++---- .../ctrace/src/output/ctf/TraceCompassXmlWriter.cpp | 7 +++---- tools/ctrace/test/data/Blinky+Arm/Blinky+Arm.SWO.csv | 2 +- .../ctrace/test/integration/src/CtraceIntegTests.cpp | 12 ++++++------ .../unit/src/output/TraceOutputLifecycleTests.cpp | 2 +- .../test/unit/src/output/csv/CsvFileOutputTests.cpp | 6 +++--- .../test/unit/src/output/csv/CsvRowMapperTests.cpp | 2 +- 7 files changed, 19 insertions(+), 20 deletions(-) diff --git a/tools/ctrace/src/output/csv/CsvRowMapper.cpp b/tools/ctrace/src/output/csv/CsvRowMapper.cpp index 21a2aba0d..0ca23bad4 100644 --- a/tools/ctrace/src/output/csv/CsvRowMapper.cpp +++ b/tools/ctrace/src/output/csv/CsvRowMapper.cpp @@ -27,7 +27,7 @@ enum class CsvColumn : std::size_t { Source, Value, Pc, - Offset, + Address, Note, Count, }; @@ -39,7 +39,7 @@ constexpr std::array(CsvColumn::Count "source", "value", "pc", - "offset", + "address", "note", }}; @@ -167,11 +167,11 @@ static CsvRow eventToCsvRow(const TraceEvent& event) row[column(CsvColumn::Source)] = std::to_string(data->comparator); row[column(CsvColumn::Value)] = hexValue(data->value, data->size); writeDwtAddressFragment(row, CsvColumn::Pc, data->pc); - writeDwtAddressFragment(row, CsvColumn::Offset, data->offset); + writeDwtAddressFragment(row, CsvColumn::Address, data->offset); } else if (const auto* address = traceEventPayload(event)) { row[column(CsvColumn::Source)] = std::to_string(address->comparator); writeDwtAddressFragment(row, CsvColumn::Pc, dwtAddressPc(*address)); - writeDwtAddressFragment(row, CsvColumn::Offset, dwtAddressOffset(*address)); + writeDwtAddressFragment(row, CsvColumn::Address, dwtAddressOffset(*address)); } else if (const auto* match = traceEventPayload(event)) { row[column(CsvColumn::Source)] = std::to_string(match->comparator); } else if (const auto* exception = traceEventPayload(event)) { diff --git a/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp b/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp index 606316090..50d88fda0 100644 --- a/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp +++ b/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp @@ -161,10 +161,9 @@ static std::string dwtMatchHandler() static std::string dwtAddressHandlers() { std::ostringstream handlers; - for (const auto& variant : CtfSchema::DwtAddressVariants) { - if (variant.tag == CtfSchema::DwtAddressTag::None) { - continue; - } + static_assert(CtfSchema::DwtAddressVariants.front().tag == CtfSchema::DwtAddressTag::None); + for (std::size_t index = 1U; index < CtfSchema::DwtAddressVariants.size(); ++index) { + const auto& variant = CtfSchema::DwtAddressVariants[index]; handlers << R"( diff --git a/tools/ctrace/test/data/Blinky+Arm/Blinky+Arm.SWO.csv b/tools/ctrace/test/data/Blinky+Arm/Blinky+Arm.SWO.csv index f62d11e33..87c5e14da 100644 --- a/tools/ctrace/test/data/Blinky+Arm/Blinky+Arm.SWO.csv +++ b/tools/ctrace/test/data/Blinky+Arm/Blinky+Arm.SWO.csv @@ -1,4 +1,4 @@ -cycles,stream,type,source,value,pc,offset,note +cycles,stream,type,source,value,pc,address,note 74999,,dwt,0,0x000000c2,0x0800b8aa,, 149998,,dwt,0,0x000000c3,0x0800b8aa,, 224997,,dwt,0,0x000000c4,0x0800b8aa,, diff --git a/tools/ctrace/test/integration/src/CtraceIntegTests.cpp b/tools/ctrace/test/integration/src/CtraceIntegTests.cpp index af5b6f215..634ac37ba 100644 --- a/tools/ctrace/test/integration/src/CtraceIntegTests.cpp +++ b/tools/ctrace/test/integration/src/CtraceIntegTests.cpp @@ -145,7 +145,7 @@ TEST_F(CtraceIntegTests, GeneratesAllOutputs) const auto result = run({"ctrace", workDirectory().string(), "--target", "Minimal", "--all"}); EXPECT_EQ(0, result.exitCode) << result.stderrText; - EXPECT_EQ("cycles,stream,type,source,value,pc,offset,note\n" + EXPECT_EQ("cycles,stream,type,source,value,pc,address,note\n" "0,,pcsample,,,0x08001234,,\n" "0,,itm,1,0x41,,,\n", readTextFile(workDirectory() / "Minimal.SWO.csv")); @@ -168,7 +168,7 @@ TEST_F(CtraceIntegTests, ExpandsDwtEventCountersAcrossCsvAndCtf) const auto result = run({"ctrace", workDirectory().string(), "--target", "Events", "--all"}); EXPECT_EQ(0, result.exitCode) << result.stderrText; - EXPECT_EQ("cycles,stream,type,source,value,pc,offset,note\n" + EXPECT_EQ("cycles,stream,type,source,value,pc,address,note\n" "0,,event,0,0x21,,,\n" "0,,itm,1,0x41,,,\n", readTextFile(workDirectory() / "Events.SWO.csv")); @@ -195,7 +195,7 @@ TEST_F(CtraceIntegTests, ConvertsDwtMatchAcrossCsvAndCtf) const auto result = run({"ctrace", workDirectory().string(), "--target", "trace-match", "--all"}); EXPECT_EQ(0, result.exitCode) << result.stderrText; - EXPECT_EQ("cycles,stream,type,source,value,pc,offset,note\n" + EXPECT_EQ("cycles,stream,type,source,value,pc,address,note\n" "1,,dwt,0,,,,\n" "3,,dwt,1,,,,\n" "6,,dwt,2,,,,\n" @@ -290,7 +290,7 @@ TEST_F(CtraceIntegTests, ReportsInvalidDwtEventCounterWithoutPartialDecode) const auto result = run({"ctrace", workDirectory().string(), "--target", "InvalidEvent", "--all"}); EXPECT_EQ(1, result.exitCode); expectContains(result.stderrText, "trace decode error at raw offset 6"); - EXPECT_EQ("cycles,stream,type,source,value,pc,offset,note\n" + EXPECT_EQ("cycles,stream,type,source,value,pc,address,note\n" "0,,error,,,,,\"unsupported DWT event-counter payload: size 1, value 0x41; expected a non-zero 1-byte " "mask using bits 0..5 only\"\n" "0,,itm,1,0x41,,,\n", @@ -311,7 +311,7 @@ TEST_F(CtraceIntegTests, ExpandsPmuEventCountersAcrossCsvAndCtf) 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" + EXPECT_EQ("cycles,stream,type,source,value,pc,address,note\n" "0,,pmu,3,0x81,,,\n" "0,,itm,1,0x41,,,\n", readTextFile(workDirectory() / "Pmu.SWO.csv")); @@ -336,7 +336,7 @@ TEST_F(CtraceIntegTests, ReportsInvalidPmuEventCounterWithoutPartialDecode) const auto result = run({"ctrace", workDirectory().string(), "--target", "InvalidPmu", "--all"}); EXPECT_EQ(1, result.exitCode); expectContains(result.stderrText, "trace decode error at raw offset 6"); - EXPECT_EQ("cycles,stream,type,source,value,pc,offset,note\n" + EXPECT_EQ("cycles,stream,type,source,value,pc,address,note\n" "0,,error,,,,,\"unsupported PMU event-counter payload: size 1, value 0x0; expected a non-zero 1-byte " "mask using bits 0..7\"\n" "0,,itm,1,0x41,,,\n", diff --git a/tools/ctrace/test/unit/src/output/TraceOutputLifecycleTests.cpp b/tools/ctrace/test/unit/src/output/TraceOutputLifecycleTests.cpp index 49dabe0fe..c946e2d02 100644 --- a/tools/ctrace/test/unit/src/output/TraceOutputLifecycleTests.cpp +++ b/tools/ctrace/test/unit/src/output/TraceOutputLifecycleTests.cpp @@ -53,7 +53,7 @@ TEST(CtraceUnitTests, testTraceOutputLifecycleCompletesIndependentOutputs) ASSERT_TRUE(diagnostics.failureCount() == 2U) << "output lifecycle should report start and finalization failures"; const auto contents = readTestTextFile(path); - ASSERT_TRUE(contents.find("cycles,stream,type,source,value,pc,offset,note\n") == 0U && + ASSERT_TRUE(contents.find("cycles,stream,type,source,value,pc,address,note\n") == 0U && contents.find(",,itm,1,0x41,,,\n") != std::string::npos) << "output lifecycle should complete successful outputs despite another output failure"; } diff --git a/tools/ctrace/test/unit/src/output/csv/CsvFileOutputTests.cpp b/tools/ctrace/test/unit/src/output/csv/CsvFileOutputTests.cpp index 3e20636ce..3bb0cd1aa 100644 --- a/tools/ctrace/test/unit/src/output/csv/CsvFileOutputTests.cpp +++ b/tools/ctrace/test/unit/src/output/csv/CsvFileOutputTests.cpp @@ -103,7 +103,7 @@ TEST(CtraceUnitTests, testCsvFileOutputCriteria) output.stop(); ASSERT_TRUE( - (readTestTextFile(outputPath.path()) == "cycles,stream,type,source,value,pc,offset,note\n,2,itm,1,0x41,,,\n")) + (readTestTextFile(outputPath.path()) == "cycles,stream,type,source,value,pc,address,note\n,2,itm,1,0x41,,,\n")) << "CsvFileOutput criteria mismatch"; const TemporaryTestPath errorOutputPath("ctrace-filtered-errors.csv"); @@ -140,7 +140,7 @@ TEST(CtraceUnitTests, testCsvFileOutputMatchesSpecification) const auto lines = readTestLines(csvPath); ASSERT_TRUE(lines.size() == 4U) << "CSV specification row count mismatch"; - ASSERT_TRUE(lines[0] == "cycles,stream,type,source,value,pc,offset,note") << "CSV specification header mismatch"; + ASSERT_TRUE(lines[0] == "cycles,stream,type,source,value,pc,address,note") << "CSV specification header mismatch"; ASSERT_TRUE(lines[1] == "949338400,,dwt,2,0xfffffdf9,0x08001234,0xfdf9,") << "CSV DWT row schema mismatch"; ASSERT_TRUE(lines[2] == "950364820,,exception,11,0x1,,,") << "CSV exception state schema mismatch"; ASSERT_TRUE(lines[3] == "950364900,,pcsample,,,0x08000100,,") << "CSV PC-sample row schema mismatch"; @@ -161,7 +161,7 @@ TEST(CtraceUnitTests, testCsvFileOutputWritesTraceIssues) const auto lines = readTestLines(csvPath); ASSERT_TRUE(lines.size() == 3U) << "CSV issue row count mismatch"; - ASSERT_TRUE(lines[0] == "cycles,stream,type,source,value,pc,offset,note") << "CSV issue header mismatch"; + ASSERT_TRUE(lines[0] == "cycles,stream,type,source,value,pc,address,note") << "CSV issue header mismatch"; ASSERT_TRUE( (lines[1] == "1234,,overflow,,,,,overflow: new timestamp segment; time across boundary may be unreliable")) << "CSV overflow issue row mismatch"; diff --git a/tools/ctrace/test/unit/src/output/csv/CsvRowMapperTests.cpp b/tools/ctrace/test/unit/src/output/csv/CsvRowMapperTests.cpp index bf35d6490..cc5db241b 100644 --- a/tools/ctrace/test/unit/src/output/csv/CsvRowMapperTests.cpp +++ b/tools/ctrace/test/unit/src/output/csv/CsvRowMapperTests.cpp @@ -54,7 +54,7 @@ TEST(CtraceUnitTests, testCsvRowMapperAndTraceEventSchema) ASSERT_TRUE(traceEventType(event) == expectedType) << "semantic TraceEvent type mapping mismatch"; } - ASSERT_TRUE(CsvRowMapper::header() == "cycles,stream,type,source,value,pc,offset,note") + ASSERT_TRUE(CsvRowMapper::header() == "cycles,stream,type,source,value,pc,address,note") << "CSV schema header integration mismatch"; ASSERT_TRUE( (CsvRowMapper::row(TraceEvent{ExceptionTraceEvent{11U, ExceptionAction::Entered}}) == ",,exception,11,0x1,,,")) From 57986e1c249c484af41bcbc3f635e134195b5917 Mon Sep 17 00:00:00 2001 From: Thorsten de Buhr Date: Mon, 7 Sep 2026 14:30:30 +0200 Subject: [PATCH 4/6] refactor(ctrace): use DWT address terminology --- tools/ctrace/src/decode/DwtPacketDecoder.cpp | 20 ++++++------ tools/ctrace/src/decode/DwtPacketDecoder.h | 6 ++-- tools/ctrace/src/model/TraceEvent.h | 31 +++++++++--------- tools/ctrace/src/output/csv/CsvRowMapper.cpp | 4 +-- tools/ctrace/src/output/ctf/CtfEncoder.cpp | 20 ++++++------ .../src/output/ctf/CtfMetadataWriter.cpp | 4 +-- .../src/output/ctf/TraceCompassXmlWriter.cpp | 6 ++-- .../unit/src/decode/DwtPacketDecoderTests.cpp | 32 +++++++++---------- .../unit/src/output/csv/CsvRowMapperTests.cpp | 7 ++-- .../unit/src/output/ctf/CtfEncoderTests.cpp | 11 ++++--- .../src/output/ctf/CtfMetadataWriterTests.cpp | 10 +++--- .../ctrace/test/unit/support/CtfTestSupport.h | 6 ++-- 12 files changed, 80 insertions(+), 77 deletions(-) diff --git a/tools/ctrace/src/decode/DwtPacketDecoder.cpp b/tools/ctrace/src/decode/DwtPacketDecoder.cpp index 940906550..48cbe4928 100644 --- a/tools/ctrace/src/decode/DwtPacketDecoder.cpp +++ b/tools/ctrace/src/decode/DwtPacketDecoder.cpp @@ -271,7 +271,7 @@ void DwtPacketDecoder::decodeDataTrace(const DwtPayloadPacket& payload, std::vec TraceEvent error{TraceIssueEvent{ TraceIssueCode::UnsupportedDwtAddressPayload, TraceIssueSeverity::Error, - "unsupported DWT " + std::string(secondarySubtype ? "address offset" : "PC or match") + + "unsupported DWT " + std::string(secondarySubtype ? "data address" : "PC or match") + " payload size " + std::to_string(payload.size) + "; expected 1, 2, or 4 bytes", std::nullopt, @@ -286,8 +286,8 @@ void DwtPacketDecoder::decodeDataTrace(const DwtPayloadPacket& payload, std::vec } const DwtAddressFragment fragment{payload.size, payload.value}; if (secondarySubtype) { - event.offset = fragment; - event.hasOffset = true; + event.address = fragment; + event.hasAddress = true; } else { event.pc = fragment; event.hasPc = true; @@ -317,18 +317,18 @@ void DwtPacketDecoder::sendDataTraceEvent(std::uint32_t comparator, const Pendin // The individual short-circuit permutations are an implementation detail; // repeated and complementary fragments are covered as complete behaviors. const auto repeatsFragmentKind = (pending->hasPc && event.hasPc) || - (pending->hasOffset && event.hasOffset) || + (pending->hasAddress && event.hasAddress) || (pending->hasValue && event.hasValue); if (!repeatsFragmentKind) { pending->index = event.index; pending->traceBusId = event.traceBusId; pending->pc = event.hasPc ? event.pc : pending->pc; - pending->offset = event.hasOffset ? event.offset : pending->offset; + pending->address = event.hasAddress ? event.address : pending->address; pending->value = event.hasValue ? event.value : pending->value; pending->size = event.hasValue ? event.size : pending->size; pending->isRead = event.hasValue ? event.isRead : pending->isRead; pending->hasPc = pending->hasPc || event.hasPc; - pending->hasOffset = pending->hasOffset || event.hasOffset; + pending->hasAddress = pending->hasAddress || event.hasAddress; pending->hasValue = pending->hasValue || event.hasValue; pending->quality.overflow = pending->quality.overflow || event.quality.overflow; pending->quality.timestampReliable = pending->quality.timestampReliable && event.quality.timestampReliable; @@ -355,14 +355,14 @@ void DwtPacketDecoder::flushPending(std::uint32_t comparator, const TraceQuality pending->size, pending->value, pending->isRead ? AccessType::Read : AccessType::Write, - pending->hasOffset ? std::optional(pending->offset) : std::nullopt, + pending->hasAddress ? std::optional(pending->address) : std::nullopt, pending->hasPc ? std::optional(pending->pc) : std::nullopt, }); } - DwtAddressTraceLocation location = DwtOffsetTraceLocation{pending->offset}; - if (pending->hasPc && pending->hasOffset) { - location = DwtPcAndOffsetTraceLocation{pending->pc, pending->offset}; + DwtAddressTraceLocation location = DwtDataAddressTraceLocation{pending->address}; + if (pending->hasPc && pending->hasAddress) { + location = DwtPcAndDataAddressTraceLocation{pending->pc, pending->address}; } else if (pending->hasPc) { location = DwtPcTraceLocation{pending->pc}; } diff --git a/tools/ctrace/src/decode/DwtPacketDecoder.h b/tools/ctrace/src/decode/DwtPacketDecoder.h index 4c3e08da1..3ddf5bb5b 100644 --- a/tools/ctrace/src/decode/DwtPacketDecoder.h +++ b/tools/ctrace/src/decode/DwtPacketDecoder.h @@ -34,7 +34,7 @@ class DwtPacketDecoder { std::vector decode(const DwtPayloadPacket& payload); /** @brief Flushes incomplete data-trace fragments at a boundary. */ std::vector flush(const TraceQuality& quality, std::uint64_t tcyc); - /** @brief Discards all pending data-trace reconstruction state. */ + /** @brief Discards all pending data-trace assembly state. */ void reset(); private: @@ -45,12 +45,12 @@ class DwtPacketDecoder { std::uint64_t index = 0; std::uint8_t traceBusId = 0U; DwtAddressFragment pc; - DwtAddressFragment offset; + DwtAddressFragment address; std::uint32_t value = 0; std::uint8_t size = 4; bool isRead = false; bool hasPc = false; - bool hasOffset = false; + bool hasAddress = false; bool hasValue = false; TraceQuality quality; }; diff --git a/tools/ctrace/src/model/TraceEvent.h b/tools/ctrace/src/model/TraceEvent.h index f83cb4f1c..a4c86a040 100644 --- a/tools/ctrace/src/model/TraceEvent.h +++ b/tools/ctrace/src/model/TraceEvent.h @@ -71,13 +71,13 @@ constexpr bool operator==(const DwtAddressFragment& left, const DwtAddressFragme return left.size == right.size && left.value == right.value; } -/** @brief Contains a reconstructed DWT data access event. */ +/** @brief Contains an assembled DWT data access event. */ struct DwtDataTraceEvent { std::uint32_t comparator = 0; std::uint8_t size = 0; std::uint32_t value = 0; AccessType access = AccessType::Read; - std::optional offset = std::nullopt; + std::optional address = std::nullopt; std::optional pc = std::nullopt; }; @@ -87,20 +87,21 @@ struct DwtPcTraceLocation { }; /** @brief Identifies a DWT address event by its raw address fragment. */ -struct DwtOffsetTraceLocation { - DwtAddressFragment offset; +struct DwtDataAddressTraceLocation { + DwtAddressFragment address; }; -/** @brief Identifies a DWT address event by program counter and address offset. */ -struct DwtPcAndOffsetTraceLocation { +/** @brief Identifies a DWT address event by program counter and data address. */ +struct DwtPcAndDataAddressTraceLocation { DwtAddressFragment pc; - DwtAddressFragment offset; + DwtAddressFragment address; }; /** @brief Stores one of the supported DWT address location representations. */ -using DwtAddressTraceLocation = std::variant; +using DwtAddressTraceLocation = + std::variant; -/** @brief Contains a reconstructed DWT address event. */ +/** @brief Contains an assembled DWT address event. */ struct DwtAddressTraceEvent { std::uint32_t comparator; DwtAddressTraceLocation location; @@ -117,20 +118,20 @@ inline std::optional dwtAddressPc(const DwtAddressTraceEvent if (const auto* pc = std::get_if(&event.location)) { return pc->pc; } - if (const auto* combined = std::get_if(&event.location)) { + if (const auto* combined = std::get_if(&event.location)) { return combined->pc; } return std::nullopt; } /** @brief Returns the raw address fragment carried by a DWT address event, if present. */ -inline std::optional dwtAddressOffset(const DwtAddressTraceEvent& event) +inline std::optional dwtDataAddress(const DwtAddressTraceEvent& event) { - if (const auto* offset = std::get_if(&event.location)) { - return offset->offset; + if (const auto* address = std::get_if(&event.location)) { + return address->address; } - if (const auto* combined = std::get_if(&event.location)) { - return combined->offset; + if (const auto* combined = std::get_if(&event.location)) { + return combined->address; } return std::nullopt; } diff --git a/tools/ctrace/src/output/csv/CsvRowMapper.cpp b/tools/ctrace/src/output/csv/CsvRowMapper.cpp index 0ca23bad4..8a97fb930 100644 --- a/tools/ctrace/src/output/csv/CsvRowMapper.cpp +++ b/tools/ctrace/src/output/csv/CsvRowMapper.cpp @@ -167,11 +167,11 @@ static CsvRow eventToCsvRow(const TraceEvent& event) row[column(CsvColumn::Source)] = std::to_string(data->comparator); row[column(CsvColumn::Value)] = hexValue(data->value, data->size); writeDwtAddressFragment(row, CsvColumn::Pc, data->pc); - writeDwtAddressFragment(row, CsvColumn::Address, data->offset); + writeDwtAddressFragment(row, CsvColumn::Address, data->address); } else if (const auto* address = traceEventPayload(event)) { row[column(CsvColumn::Source)] = std::to_string(address->comparator); writeDwtAddressFragment(row, CsvColumn::Pc, dwtAddressPc(*address)); - writeDwtAddressFragment(row, CsvColumn::Address, dwtAddressOffset(*address)); + writeDwtAddressFragment(row, CsvColumn::Address, dwtDataAddress(*address)); } else if (const auto* match = traceEventPayload(event)) { row[column(CsvColumn::Source)] = std::to_string(match->comparator); } else if (const auto* exception = traceEventPayload(event)) { diff --git a/tools/ctrace/src/output/ctf/CtfEncoder.cpp b/tools/ctrace/src/output/ctf/CtfEncoder.cpp index f34ce732f..5f95076b1 100644 --- a/tools/ctrace/src/output/ctf/CtfEncoder.cpp +++ b/tools/ctrace/src/output/ctf/CtfEncoder.cpp @@ -327,9 +327,9 @@ void CtfEncoder::writeDwtValueEvent(const TraceEvent& event, const DwtDataTraceE reportDwtSizeMismatch(event, data, source); const auto& variant = dwtValueVariant(source, data.comparator); const auto& pcVariant = dwtAddressVariant(data.pc); - const auto& offsetVariant = dwtAddressVariant(data.offset); + const auto& addressVariant = dwtAddressVariant(data.address); const auto payloadSize = - 1U + 1U + 1U + variant.byteSize + 1U + pcVariant.byteSize + 1U + offsetVariant.byteSize + 1U + 4U; + 1U + 1U + 1U + variant.byteSize + 1U + pcVariant.byteSize + 1U + addressVariant.byteSize + 1U + 4U; const auto eventTimestamp = allocateEventTimestamp(event.traceBusId); const auto quality = computeSampleQuality(event); m_stream.writeRecord(CtfSchema::value(CtfSchema::EventId::DwtValue), eventTimestamp, event.traceBusId, payloadSize, @@ -341,7 +341,7 @@ void CtfEncoder::writeDwtValueEvent(const TraceEvent& event, const DwtDataTraceE record.writeU8(CtfSchema::value(variant.tag)); writeVariantValue(record, data.value, data.size, variant); writeDwtAddress(record, data.pc, pcVariant); - writeDwtAddress(record, data.offset, offsetVariant); + writeDwtAddress(record, data.address, addressVariant); record.writeU8(quality.first); record.writeU32(quality.second); }); @@ -370,20 +370,20 @@ void CtfEncoder::reportDwtSizeMismatch(const TraceEvent& event, const DwtDataTra }); } -void CtfEncoder::writeDwtAddrEvent(const TraceEvent& event, const DwtAddressTraceEvent& address) +void CtfEncoder::writeDwtAddrEvent(const TraceEvent& event, const DwtAddressTraceEvent& data) { const auto eventTimestamp = allocateEventTimestamp(event.traceBusId); const auto quality = computeSampleQuality(event); - const auto pc = dwtAddressPc(address); - const auto offset = dwtAddressOffset(address); + const auto pc = dwtAddressPc(data); + const auto address = dwtDataAddress(data); const auto& pcVariant = dwtAddressVariant(pc); - const auto& offsetVariant = dwtAddressVariant(offset); - const auto payloadSize = 1U + 1U + pcVariant.byteSize + 1U + offsetVariant.byteSize + 1U + 4U; + const auto& addressVariant = dwtAddressVariant(address); + const auto payloadSize = 1U + 1U + pcVariant.byteSize + 1U + addressVariant.byteSize + 1U + 4U; m_stream.writeRecord(CtfSchema::value(CtfSchema::EventId::DwtAddress), eventTimestamp, event.traceBusId, payloadSize, [&](CtfStreamWriter::Record& record) { - record.writeU8(static_cast(address.comparator & 0xffU)); + record.writeU8(static_cast(data.comparator & 0xffU)); writeDwtAddress(record, pc, pcVariant); - writeDwtAddress(record, offset, offsetVariant); + writeDwtAddress(record, address, addressVariant); record.writeU8(quality.first); record.writeU32(quality.second); }); diff --git a/tools/ctrace/src/output/ctf/CtfMetadataWriter.cpp b/tools/ctrace/src/output/ctf/CtfMetadataWriter.cpp index 84f3c82f4..5a22e9069 100644 --- a/tools/ctrace/src/output/ctf/CtfMetadataWriter.cpp +++ b/tools/ctrace/src/output/ctf/CtfMetadataWriter.cpp @@ -426,7 +426,7 @@ event { fields := struct { cmsis_dwt_comparator_t cmsis_dwt_comparator; cmsis_dwt_access_t cmsis_dwt_access; -)" << ctfValueFields("dwt") << ctfDwtAddressFields("pc") << ctfDwtAddressFields("offset") +)" << ctfValueFields("dwt") << ctfDwtAddressFields("pc") << ctfDwtAddressFields("address") << R"( uint8_t cmsis_sample_flags; uint32_t cmsis_overflow_count; }; @@ -447,7 +447,7 @@ event { << CtfSchema::SwoStreamId << R"(; fields := struct { cmsis_dwt_comparator_t cmsis_dwt_comparator; -)" << ctfDwtAddressFields("pc") << ctfDwtAddressFields("offset") +)" << ctfDwtAddressFields("pc") << ctfDwtAddressFields("address") << R"( uint8_t cmsis_sample_flags; uint32_t cmsis_overflow_count; }; diff --git a/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp b/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp index 50d88fda0..16465e765 100644 --- a/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp +++ b/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp @@ -157,7 +157,7 @@ static std::string dwtMatchHandler() return handler.str(); } -/** @brief Generates one DWT address handler for each encoded offset width. */ +/** @brief Generates one DWT address handler for each encoded data-address width. */ static std::string dwtAddressHandlers() { std::ostringstream handlers; @@ -167,7 +167,7 @@ static std::string dwtAddressHandlers() handlers << R"( - + @@ -177,7 +177,7 @@ static std::string dwtAddressHandlers() << CtfSchema::eventName(CtfSchema::EventId::DwtAddress) << R"(" /> - diff --git a/tools/ctrace/test/unit/src/decode/DwtPacketDecoderTests.cpp b/tools/ctrace/test/unit/src/decode/DwtPacketDecoderTests.cpp index fc42d8a40..307c8527f 100644 --- a/tools/ctrace/test/unit/src/decode/DwtPacketDecoderTests.cpp +++ b/tools/ctrace/test/unit/src/decode/DwtPacketDecoderTests.cpp @@ -254,7 +254,7 @@ TEST(CtraceUnitTests, testDwtPacketDecoderPreservesRepeatedAddressFragments) ASSERT_TRUE(firstAddress != nullptr && packets.front().index == 10U && packets.front().traceBusId == 3U) << "the first repeated DWT address fragment lost its identity"; const auto firstPc = dwtAddressPc(*firstAddress); - const auto firstOffset = dwtAddressOffset(*firstAddress); + const auto firstDataAddress = dwtDataAddress(*firstAddress); packets = decoder.flush({}, 300U); ASSERT_TRUE(packets.size() == 1U) << "the second DWT address fragment must remain available"; @@ -268,10 +268,10 @@ TEST(CtraceUnitTests, testDwtPacketDecoderPreservesRepeatedAddressFragments) std::optional(DwtAddressFragment{size, secondValue})) << "repeated DWT PC fragments were overwritten"; } else { - ASSERT_TRUE(firstOffset == std::optional(DwtAddressFragment{size, firstValue}) && - dwtAddressOffset(*secondAddress) == + ASSERT_TRUE(firstDataAddress == std::optional(DwtAddressFragment{size, firstValue}) && + dwtDataAddress(*secondAddress) == std::optional(DwtAddressFragment{size, secondValue})) - << "repeated DWT offset fragments were overwritten"; + << "repeated DWT data-address fragments were overwritten"; } }; @@ -342,7 +342,7 @@ TEST(CtraceUnitTests, testDwtPacketDecoderRejectsUnsupportedAddressWidths) verify(9U, 3U); } -TEST(CtraceUnitTests, testDwtPacketDecoderPreservesRawAddressOffsetWidths) +TEST(CtraceUnitTests, testDwtPacketDecoderPreservesRawDataAddressWidths) { const auto verify = [](std::uint8_t size, std::uint32_t value, const char* expectedCsv) { DwtPacketDecoder decoder; @@ -352,7 +352,7 @@ TEST(CtraceUnitTests, testDwtPacketDecoderPreservesRawAddressOffsetWidths) ASSERT_EQ(packets.size(), 1U); const auto* address = traceEventPayload(packets.front()); ASSERT_NE(address, nullptr); - EXPECT_EQ(dwtAddressOffset(*address), std::optional(DwtAddressFragment{size, value})); + EXPECT_EQ(dwtDataAddress(*address), std::optional(DwtAddressFragment{size, value})); EXPECT_EQ(CsvRowMapper::row(packets.front()), expectedCsv); }; @@ -419,7 +419,7 @@ TEST(CtraceUnitTests, testDwtPacketDecoderFlushesPendingTraceForUnknownSource) EXPECT_TRUE(decoder.decode(unknown).empty()); } -TEST(CtraceUnitTests, testDwtPacketDecoderCombinesPcOffsetAndValue) +TEST(CtraceUnitTests, testDwtPacketDecoderCombinesPcAddressAndValue) { DwtPacketDecoder decoder; @@ -427,12 +427,12 @@ TEST(CtraceUnitTests, testDwtPacketDecoderCombinesPcOffsetAndValue) pc.quality.timestampReliable = true; EXPECT_TRUE(decoder.decode(pc).empty()); - auto offset = pc; - offset.index = 11U; - offset.discriminator = 9U; - offset.size = 2U; - offset.value = 0x20U; - EXPECT_TRUE(decoder.decode(offset).empty()); + auto dataAddress = pc; + dataAddress.index = 11U; + dataAddress.discriminator = 9U; + dataAddress.size = 2U; + dataAddress.value = 0x20U; + EXPECT_TRUE(decoder.decode(dataAddress).empty()); auto value = pc; value.index = 12U; @@ -445,15 +445,15 @@ TEST(CtraceUnitTests, testDwtPacketDecoderCombinesPcOffsetAndValue) ASSERT_NE(data, nullptr); EXPECT_EQ(data->access, AccessType::Read); EXPECT_EQ(data->pc, std::optional(DwtAddressFragment{4U, 0x08001234U})); - EXPECT_EQ(data->offset, std::optional(DwtAddressFragment{2U, 0x20U})); + EXPECT_EQ(data->address, std::optional(DwtAddressFragment{2U, 0x20U})); DwtPacketDecoder addressDecoder; EXPECT_TRUE(addressDecoder.decode(pc).empty()); - EXPECT_TRUE(addressDecoder.decode(offset).empty()); + EXPECT_TRUE(addressDecoder.decode(dataAddress).empty()); const auto addresses = addressDecoder.flush({}, 100U); ASSERT_EQ(addresses.size(), 1U); const auto* address = traceEventPayload(addresses.front()); ASSERT_NE(address, nullptr); EXPECT_EQ(dwtAddressPc(*address), std::optional(DwtAddressFragment{4U, 0x08001234U})); - EXPECT_EQ(dwtAddressOffset(*address), std::optional(DwtAddressFragment{2U, 0x20U})); + EXPECT_EQ(dwtDataAddress(*address), std::optional(DwtAddressFragment{2U, 0x20U})); } diff --git a/tools/ctrace/test/unit/src/output/csv/CsvRowMapperTests.cpp b/tools/ctrace/test/unit/src/output/csv/CsvRowMapperTests.cpp index cc5db241b..70a5f782f 100644 --- a/tools/ctrace/test/unit/src/output/csv/CsvRowMapperTests.cpp +++ b/tools/ctrace/test/unit/src/output/csv/CsvRowMapperTests.cpp @@ -78,12 +78,13 @@ TEST(CtraceUnitTests, testCsvRowMapperAndTraceEventSchema) TEST(CtraceUnitTests, testCsvRowMapperCoversAddressAndExceptionVariants) { - EXPECT_EQ(CsvRowMapper::row(TraceEvent{DwtAddressTraceEvent{2U, DwtOffsetTraceLocation{{2U, 0xabcdU}}}}), + EXPECT_EQ(CsvRowMapper::row(TraceEvent{DwtAddressTraceEvent{2U, DwtDataAddressTraceLocation{{2U, 0xabcdU}}}}), ",,dwt,2,,,0xabcd,"); EXPECT_EQ( - CsvRowMapper::row(TraceEvent{DwtAddressTraceEvent{3U, DwtPcAndOffsetTraceLocation{{2U, 0x1234U}, {1U, 0x56U}}}}), + CsvRowMapper::row( + TraceEvent{DwtAddressTraceEvent{3U, DwtPcAndDataAddressTraceLocation{{2U, 0x1234U}, {1U, 0x56U}}}}), ",,dwt,3,,0x1234,0x56,"); - EXPECT_EQ(CsvRowMapper::row(TraceEvent{DwtAddressTraceEvent{1U, DwtOffsetTraceLocation{{4U, 0x20007858U}}}}), + EXPECT_EQ(CsvRowMapper::row(TraceEvent{DwtAddressTraceEvent{1U, DwtDataAddressTraceLocation{{4U, 0x20007858U}}}}), ",,dwt,1,,,0x20007858,"); EXPECT_EQ(CsvRowMapper::row(TraceEvent{ExceptionTraceEvent{1U, ExceptionAction::Exited}}), ",,exception,1,0x2,,,"); EXPECT_EQ(CsvRowMapper::row(TraceEvent{ExceptionTraceEvent{1U, ExceptionAction::Returned}}), ",,exception,1,0x3,,,"); diff --git a/tools/ctrace/test/unit/src/output/ctf/CtfEncoderTests.cpp b/tools/ctrace/test/unit/src/output/ctf/CtfEncoderTests.cpp index cd5b63717..65d646bfd 100644 --- a/tools/ctrace/test/unit/src/output/ctf/CtfEncoderTests.cpp +++ b/tools/ctrace/test/unit/src/output/ctf/CtfEncoderTests.cpp @@ -310,13 +310,13 @@ TEST(CtraceUnitTests, testCtfEncoderDwtAddressEncoding) encoder.start(outputDirectory); encoder.writeEvent(atCycle(TraceEvent{DwtAddressTraceEvent{ 3U, - DwtPcAndOffsetTraceLocation{{4U, 0x12345678U}, {2U, 0x0000abcdU}}, + DwtPcAndDataAddressTraceLocation{{4U, 0x12345678U}, {2U, 0x0000abcdU}}, }}, 99U)); encoder.writeEvent( - atCycle(TraceEvent{DwtAddressTraceEvent{1U, DwtOffsetTraceLocation{{1U, 0x58U}}}}, 100U)); + atCycle(TraceEvent{DwtAddressTraceEvent{1U, DwtDataAddressTraceLocation{{1U, 0x58U}}}}, 100U)); encoder.writeEvent( - atCycle(TraceEvent{DwtAddressTraceEvent{2U, DwtOffsetTraceLocation{{4U, 0x20007858U}}}}, 101U)); + atCycle(TraceEvent{DwtAddressTraceEvent{2U, DwtDataAddressTraceLocation{{4U, 0x20007858U}}}}, 101U)); encoder.writeEvent(atCycle(TraceEvent{DwtAddressTraceEvent{0U, DwtPcTraceLocation{{4U, 0x08001234U}}}}, 102U)); encoder.writeEvent(atCycle(TraceEvent{DwtAddressTraceEvent{1U, DwtPcTraceLocation{{1U, 0x58U}}}}, 103U)); encoder.writeEvent(atCycle(TraceEvent{DwtAddressTraceEvent{2U, DwtPcTraceLocation{{2U, 0x7858U}}}}, 104U)); @@ -331,7 +331,7 @@ TEST(CtraceUnitTests, testCtfEncoderDwtAddressEncoding) ASSERT_TRUE(record.payload[0U] == 3U && record.payload[1U] == CtfSchema::value(CtfSchema::DwtAddressTag::U32) && record.payload[6U] == CtfSchema::value(CtfSchema::DwtAddressTag::U16)) - << "CTF DWT address comparator, PC tag, or offset tag mismatch"; + << "CTF DWT address comparator, PC tag, or data-address tag mismatch"; ASSERT_TRUE(readLe32(record.payload, 2U) == 0x12345678U && readLe16(record.payload, 7U) == 0xabcdU) << "CTF DWT PC/address payload mismatch"; @@ -375,7 +375,8 @@ TEST(CtraceUnitTests, testCtfEncoderRejectsInvalidClockAndPayloadMetadata) invalidDwt.start(temporaryPath.path()); EXPECT_THROW(invalidDwt.writeEvent(onStream(TraceEvent{DwtDataTraceEvent{0U, 1U, 0U, AccessType::Read}}, 1U)), std::runtime_error); - EXPECT_THROW(invalidDwt.writeEvent(TraceEvent{DwtAddressTraceEvent{0U, DwtOffsetTraceLocation{{3U, 0U}}}}), + EXPECT_THROW( + invalidDwt.writeEvent(TraceEvent{DwtAddressTraceEvent{0U, DwtDataAddressTraceLocation{{3U, 0U}}}}), std::runtime_error); invalidDwt.abort(); } diff --git a/tools/ctrace/test/unit/src/output/ctf/CtfMetadataWriterTests.cpp b/tools/ctrace/test/unit/src/output/ctf/CtfMetadataWriterTests.cpp index cfba8ec5d..5bf5631c9 100644 --- a/tools/ctrace/test/unit/src/output/ctf/CtfMetadataWriterTests.cpp +++ b/tools/ctrace/test/unit/src/output/ctf/CtfMetadataWriterTests.cpp @@ -62,7 +62,7 @@ TEST(CtraceUnitTests, testCtfMetadataWriterEscapesAndDeduplicatesSourceLabels) EXPECT_NE(metadata.find("cmsis_pmu_event_counter_t cmsis_pmu_event_counter;"), std::string::npos); EXPECT_NE(metadata.find("none = 0, u8 = 1, u16 = 2, u32 = 4"), std::string::npos); EXPECT_NE(metadata.find("variant "), std::string::npos); - EXPECT_NE(metadata.find("variant "), std::string::npos); + EXPECT_NE(metadata.find("variant "), std::string::npos); EXPECT_NE(metadata.find("uint32_t u32;"), std::string::npos); } @@ -154,10 +154,10 @@ TEST(CtraceUnitTests, testTraceCompassXmlUsesCurrentCtfEvents) const auto matchPulseEnd = xml.find("value=\"timestamp + 1000\"", matchHandler); ASSERT_NE(matchPulseEnd, std::string::npos); EXPECT_LT(matchPulseEnd, matchHandlerEnd); - EXPECT_NE(xml.find("value=\"cmsis_dwt_offset_type\""), std::string::npos); - EXPECT_NE(xml.find("value=\"cmsis_dwt_offset.u8\" forcedType=\"long\""), std::string::npos); - EXPECT_NE(xml.find("value=\"cmsis_dwt_offset.u16\" forcedType=\"long\""), std::string::npos); - EXPECT_NE(xml.find("value=\"cmsis_dwt_offset.u32\" forcedType=\"long\""), std::string::npos); + EXPECT_NE(xml.find("value=\"cmsis_dwt_address_type\""), std::string::npos); + EXPECT_NE(xml.find("value=\"cmsis_dwt_address.u8\" forcedType=\"long\""), std::string::npos); + EXPECT_NE(xml.find("value=\"cmsis_dwt_address.u16\" forcedType=\"long\""), std::string::npos); + EXPECT_NE(xml.find("value=\"cmsis_dwt_address.u32\" forcedType=\"long\""), std::string::npos); const auto threadModeEntry = xml.find("path=\"EXCEPTION/Thread Mode\""); const auto returnEntry = xml.find("path=\"EXCEPTION_RETURN/*\" displayText=\"true\""); const auto interruptEntries = xml.find("path=\"EXCEPTION/(?!Thread Mode).+\""); diff --git a/tools/ctrace/test/unit/support/CtfTestSupport.h b/tools/ctrace/test/unit/support/CtfTestSupport.h index 22c8b7378..8443b9f5a 100644 --- a/tools/ctrace/test/unit/support/CtfTestSupport.h +++ b/tools/ctrace/test/unit/support/CtfTestSupport.h @@ -147,9 +147,9 @@ inline std::size_t ctfPayloadSize(const std::vector& bytes, std:: } if (eventId == CtfSchema::value(CtfSchema::EventId::DwtAddress)) { requirePayload(2U); - const auto offsetTagPosition = 2U + ctfDwtAddressSize(bytes[payloadOffset + 1U]); - requirePayload(offsetTagPosition + 1U); - return offsetTagPosition + 1U + ctfDwtAddressSize(bytes[payloadOffset + offsetTagPosition]) + 5U; + const auto addressTagPosition = 2U + ctfDwtAddressSize(bytes[payloadOffset + 1U]); + requirePayload(addressTagPosition + 1U); + return addressTagPosition + 1U + ctfDwtAddressSize(bytes[payloadOffset + addressTagPosition]) + 5U; } if (eventId == CtfSchema::value(CtfSchema::EventId::GlobalTimestamp)) { return 9U; From ea599269bbc91f4161f998501dd4661820e6db6c Mon Sep 17 00:00:00 2001 From: Thorsten de Buhr Date: Mon, 7 Sep 2026 14:40:05 +0200 Subject: [PATCH 5/6] test(ctrace): expose DWT XML handler coverage --- tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp b/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp index 16465e765..935cf545f 100644 --- a/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp +++ b/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp @@ -201,8 +201,9 @@ static std::string stateProviderXml() xml << R"( -)" << dwtAddressHandlers() - << R"( +)"; + xml << dwtAddressHandlers(); + xml << R"( )" << valueHandlers(CtfSchema::EventId::Itm, "itm", "cmsis_itm_channel", "value") From a5b52576e4e6eac685699997c9f79982cb614351 Mon Sep 17 00:00:00 2001 From: Thorsten de Buhr Date: Mon, 7 Sep 2026 14:45:29 +0200 Subject: [PATCH 6/6] test(ctrace): expose XML handler coverage consistently --- .../src/output/ctf/TraceCompassXmlWriter.cpp | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp b/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp index 935cf545f..f1111a2d0 100644 --- a/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp +++ b/tools/ctrace/src/output/ctf/TraceCompassXmlWriter.cpp @@ -206,22 +206,26 @@ static std::string stateProviderXml() xml << R"( -)" << valueHandlers(CtfSchema::EventId::Itm, "itm", "cmsis_itm_channel", "value") - << R"( +)"; + xml << valueHandlers(CtfSchema::EventId::Itm, "itm", "cmsis_itm_channel", "value"); + xml << R"( -)" << dwtMatchHandler() - << R"( +)"; + xml << dwtMatchHandler(); + xml << R"( -)" << eventCounterHandlers(CtfSchema::EventId::DwtEvent, "cmsis_dwt_event_counter", kDwtEventCounters, - CtfSchema::dwtEventCounterName) - << R"( +)"; + xml << eventCounterHandlers(CtfSchema::EventId::DwtEvent, "cmsis_dwt_event_counter", kDwtEventCounters, + CtfSchema::dwtEventCounterName); + xml << R"( -)" << eventCounterHandlers(CtfSchema::EventId::PmuEvent, "cmsis_pmu_event_counter", kPmuEventCounters, - CtfSchema::pmuEventCounterName) - << R"( +)"; + xml << eventCounterHandlers(CtfSchema::EventId::PmuEvent, "cmsis_pmu_event_counter", kPmuEventCounters, + CtfSchema::pmuEventCounterName); + xml << R"(