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;