Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions tools/ctrace/src/control/FileDecodeJob.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,21 @@ static ItmTimestampPrescalers timestampPrescalers(const CtraceRunMeta& ctraceRun
return {fallback, ctraceRunMeta.timestampPrescalersByTraceBusId()};
}

/** @brief Converts normalized DWT comparator metadata into decoder configuration. */
static DwtComparatorValuesByTraceBusId dwtComparatorValues(const CtraceRunMeta& ctraceRunMeta)
{
DwtComparatorValuesByTraceBusId result;
for (const auto& [traceBusId, configured] : ctraceRunMeta.dwtComparatorValuesByTraceBusId()) {
auto& values = result[traceBusId];
for (const auto& [comparator, value] : configured) {
if (comparator < values.size()) {
values[comparator] = value;
}
}
}
return result;
}

/** @brief Converts command-line output selection into an output request. */
static TraceOutputRequest outputRequest(const CliOptions& options)
{
Expand Down Expand Up @@ -161,6 +176,7 @@ FileDecodeJob::FileDecodeJob(CliOptions options, std::filesystem::path rawInputP
void FileDecodeJob::run()
{
const auto prescalers = timestampPrescalers(m_ctraceRunMeta);
const auto comparatorValues = dwtComparatorValues(m_ctraceRunMeta);
auto outputPlan = planTraceOutputs(outputRequest(m_options), m_rawInputPath, m_ctraceRunMeta, m_diagnostics);
if (outputPlan.hasRequestedOutputs() && !outputPlan.hasEnabledOutputs()) {
return;
Expand Down Expand Up @@ -198,9 +214,9 @@ void FileDecodeJob::run()
RawFileReader input(m_rawInputPath);
std::unique_ptr<DecodePipeline> pipeline;
if (m_sessionFactory) {
pipeline = std::make_unique<DecodePipeline>(prescalers, consumers, m_sessionFactory);
pipeline = std::make_unique<DecodePipeline>(prescalers, consumers, m_sessionFactory, comparatorValues);
} else {
pipeline = std::make_unique<DecodePipeline>(prescalers, consumers);
pipeline = std::make_unique<DecodePipeline>(prescalers, consumers, comparatorValues);
}
while (true) {
const auto read = input.read();
Expand Down
5 changes: 3 additions & 2 deletions tools/ctrace/src/decode/CortexMPostDecoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@
#include <utility>
#include <vector>

CortexMPostDecoder::CortexMPostDecoder(TraceEventSink& eventSink)
: m_eventSink(eventSink)
CortexMPostDecoder::CortexMPostDecoder(TraceEventSink& eventSink, DwtComparatorValues comparatorValues)
: m_eventSink(eventSink),
m_dwtDecoder(std::move(comparatorValues))
{
}

Expand Down
2 changes: 1 addition & 1 deletion tools/ctrace/src/decode/CortexMPostDecoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
class CortexMPostDecoder final : public OpenCsdTraceElementSink {
public:
/** @brief Creates a post-decoder that emits to the supplied event sink. */
explicit CortexMPostDecoder(TraceEventSink& eventSink);
explicit CortexMPostDecoder(TraceEventSink& eventSink, DwtComparatorValues comparatorValues = {});

/** @brief Appends one OpenCSD trace element. */
void append(OpenCsdTraceElement element) override;
Expand Down
8 changes: 6 additions & 2 deletions tools/ctrace/src/decode/CortexMStreamDecoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@
#include <string>
#include <utility>

CortexMStreamDecoder::CortexMStreamDecoder(ItmTimestampPrescalers prescalers, TraceEventSink& eventSink)
CortexMStreamDecoder::CortexMStreamDecoder(ItmTimestampPrescalers prescalers, TraceEventSink& eventSink,
DwtComparatorValuesByTraceBusId comparatorValues)
: m_prescalers(std::move(prescalers)),
m_comparatorValues(std::move(comparatorValues)),
m_eventSink(eventSink)
{
}
Expand Down Expand Up @@ -73,7 +75,9 @@ CortexMPostDecoder& CortexMStreamDecoder::decoder(std::uint8_t traceBusId)
{
auto& result = m_decoders[traceBusId];
if (!result) {
result = std::make_unique<CortexMPostDecoder>(m_eventSink);
const auto values = m_comparatorValues.find(traceBusId);
result = std::make_unique<CortexMPostDecoder>(
m_eventSink, values != m_comparatorValues.end() ? values->second : DwtComparatorValues{});
}
return *result;
}
10 changes: 8 additions & 2 deletions tools/ctrace/src/decode/CortexMStreamDecoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#define CTRACE_SRC_DECODE_CORTEXMSTREAMDECODER_H

#include "OpenCsdTraceElement.h"
#include "DwtPacketDecoder.h"
#include "TraceEvent.h"

#include <cstdint>
Expand All @@ -24,11 +25,15 @@ struct ItmTimestampPrescalers {
std::map<std::uint8_t, std::uint32_t> byTraceBusId;
};

/** @brief Stores DWT comparator values indexed by CoreSight Trace Bus ID. */
using DwtComparatorValuesByTraceBusId = std::map<std::uint8_t, DwtComparatorValues>;

/** @brief Routes OpenCSD elements to per-stream Cortex-M post-decoders. */
class CortexMStreamDecoder final : public OpenCsdTraceElementSink {
public:
/** @brief Creates a stream router with timestamp scaling configuration. */
CortexMStreamDecoder(ItmTimestampPrescalers prescalers, TraceEventSink& eventSink);
CortexMStreamDecoder(ItmTimestampPrescalers prescalers, TraceEventSink& eventSink,
DwtComparatorValuesByTraceBusId comparatorValues = {});
/** @brief Destroys all per-stream post-decoders. */
~CortexMStreamDecoder();

Expand All @@ -51,8 +56,9 @@ class CortexMStreamDecoder final : public OpenCsdTraceElementSink {
CortexMPostDecoder& decoder(std::uint8_t traceBusId);

ItmTimestampPrescalers m_prescalers;
DwtComparatorValuesByTraceBusId m_comparatorValues;
TraceEventSink& m_eventSink;
std::map<std::uint8_t, std::unique_ptr<CortexMPostDecoder>> m_decoders;
};

#endif // CTRACE_SRC_DECODE_CORTEXMSTREAMDECODER_H
#endif // CTRACE_SRC_DECODE_CORTEXMSTREAMDECODER_H
10 changes: 6 additions & 4 deletions tools/ctrace/src/decode/DecodePipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,17 @@
#include <stdexcept>
#include <utility>

DecodePipeline::DecodePipeline(ItmTimestampPrescalers timestampPrescalers, TraceEventSink& eventSink)
: m_streamDecoder(std::move(timestampPrescalers), eventSink),
DecodePipeline::DecodePipeline(ItmTimestampPrescalers timestampPrescalers, TraceEventSink& eventSink,
DwtComparatorValuesByTraceBusId comparatorValues)
: m_streamDecoder(std::move(timestampPrescalers), eventSink, std::move(comparatorValues)),
m_decoder(m_streamDecoder)
{
}

DecodePipeline::DecodePipeline(ItmTimestampPrescalers timestampPrescalers, TraceEventSink& eventSink,
const OpenCsdItmSessionFactory& sessionFactory)
: m_streamDecoder(std::move(timestampPrescalers), eventSink),
const OpenCsdItmSessionFactory& sessionFactory,
DwtComparatorValuesByTraceBusId comparatorValues)
: m_streamDecoder(std::move(timestampPrescalers), eventSink, std::move(comparatorValues)),
m_decoder(m_streamDecoder, sessionFactory)
{
}
Expand Down
7 changes: 4 additions & 3 deletions tools/ctrace/src/decode/DecodePipeline.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,16 @@ class DecodePipeline final {
* @param timestampPrescalers Default and per-stream timestamp prescalers.
* @param eventSink Sink receiving decoded events synchronously.
*/
DecodePipeline(ItmTimestampPrescalers timestampPrescalers, TraceEventSink& eventSink);
DecodePipeline(ItmTimestampPrescalers timestampPrescalers, TraceEventSink& eventSink,
DwtComparatorValuesByTraceBusId comparatorValues = {});
/**
* @brief Creates a pipeline with an injected OpenCSD session factory.
* @param timestampPrescalers Default and per-stream timestamp prescalers.
* @param eventSink Sink receiving decoded events synchronously.
* @param sessionFactory Factory used to create the OpenCSD session.
*/
DecodePipeline(ItmTimestampPrescalers timestampPrescalers, TraceEventSink& eventSink,
const OpenCsdItmSessionFactory& sessionFactory);
const OpenCsdItmSessionFactory& sessionFactory, DwtComparatorValuesByTraceBusId comparatorValues = {});

/**
* @brief Pushes the next contiguous chunk of raw trace bytes.
Expand All @@ -69,4 +70,4 @@ class DecodePipeline final {
OpenCsdItmDecoder m_decoder;
};

#endif // CTRACE_SRC_DECODE_DECODEPIPELINE_H
#endif // CTRACE_SRC_DECODE_DECODEPIPELINE_H
64 changes: 52 additions & 12 deletions tools/ctrace/src/decode/DwtPacketDecoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,17 @@ constexpr std::uint32_t kExceptionActionMask = 0x3U;
constexpr std::uint32_t kExceptionActionShift = 12U;
constexpr std::uint32_t kPmuOverflowMask = 0xffU;

constexpr std::uint8_t kArmv7MFullPcBytes = 4U;
constexpr std::uint8_t kArmv7MAddressOffsetBytes = 2U;
constexpr std::uint8_t kDwtShortPayloadBytes = 1U;
constexpr std::uint8_t kDwtMediumPayloadBytes = 2U;
constexpr std::uint8_t kDwtLongPayloadBytes = 4U;
constexpr std::uint8_t kArmv8MMatchBytes = 1U;
constexpr std::uint32_t kArmv8MMatchValue = 1U;

DwtPacketDecoder::DwtPacketDecoder(DwtComparatorValues comparatorValues)
: m_comparatorValues(std::move(comparatorValues))
{
}

/** @brief Describes an invalid DWT event-counter payload. */
static std::string invalidEventCounterMessage(const DwtPayloadPacket& payload)
{
Expand Down Expand Up @@ -260,17 +266,14 @@ 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 reject = [&](const std::string& reason) {
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",
reason,
std::nullopt,
std::nullopt,
}};
Expand All @@ -279,13 +282,50 @@ void DwtPacketDecoder::decodeDataTrace(const DwtPayloadPacket& payload, std::vec
error.tcyc = payload.tcyc;
error.quality = payload.quality;
output.push_back(std::move(error));
return;
}
};

if (secondarySubtype) {
event.addressLo16 = payload.value;
if (payload.size == kDwtShortPayloadBytes) {
const auto& comparatorValue = m_comparatorValues[comparator];
if (!comparatorValue.has_value()) {
reject("cannot reconstruct short DWT data-address payload for comparator " + std::to_string(comparator) +
"; ctrace-run does not provide a complete DWT_COMP" + std::to_string(comparator) + " register value");
return;
}
event.addressLo16 = (*comparatorValue & 0xff00U) | (payload.value & 0xffU);
} else if (payload.size == kDwtMediumPayloadBytes || payload.size == kDwtLongPayloadBytes) {
// The existing semantic event carries the architecturally relevant low 16 bits.
// A medium payload supplies them completely; a long payload is truncated here.
event.addressLo16 = payload.value & 0xffffU;
} else {
reject("unsupported DWT data-address payload size " + std::to_string(payload.size) +
"; expected 1, 2, or 4 bytes");
return;
}
event.hasAddressLo16 = true;
} else {
event.pc = payload.value;
if (payload.size != kDwtShortPayloadBytes && payload.size != kDwtMediumPayloadBytes &&
payload.size != kDwtLongPayloadBytes) {
reject("unsupported DWT PC-value payload size " + std::to_string(payload.size) + "; expected 1, 2, or 4 bytes");
return;
}
if ((payload.value & 1U) != 0U) {
reject("invalid DWT PC-value payload for comparator " + std::to_string(comparator) +
": bit 0 is reserved for the match-packet discriminator");
return;
}
if (payload.size == kDwtLongPayloadBytes) {
event.pc = payload.value;
} else {
const auto& comparatorValue = m_comparatorValues[comparator];
if (!comparatorValue.has_value()) {
reject("cannot reconstruct compressed DWT PC-value payload for comparator " + std::to_string(comparator) +
"; ctrace-run does not provide a complete DWT_COMP" + std::to_string(comparator) + " register value");
return;
}
const auto payloadMask = payload.size == kDwtShortPayloadBytes ? 0xffU : 0xffffU;
event.pc = (*comparatorValue & ~payloadMask) | (payload.value & payloadMask);
}
event.hasPc = true;
}
sendDataTraceEvent(comparator, event, payload.quality, payload.tcyc, output);
Expand Down
15 changes: 11 additions & 4 deletions tools/ctrace/src/decode/DwtPacketDecoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,17 @@ struct DwtPayloadPacket {
TraceQuality quality;
};

/** @brief Number of comparators encodable in a DWT data-trace source ID. */
inline constexpr std::size_t kDwtDataTraceComparatorCount = 4U;

/** @brief Stores configured DWT comparator values used for packet decompression. */
using DwtComparatorValues = std::array<std::optional<std::uint32_t>, kDwtDataTraceComparatorCount>;

/** @brief Reconstructs semantic DWT events from hardware payload packets. */
class DwtPacketDecoder {
public:
/** @brief Creates a decoder with the comparator values active for this stream. */
explicit DwtPacketDecoder(DwtComparatorValues comparatorValues = {});
/** @brief Decodes one hardware payload and returns completed semantic events. */
std::vector<TraceEvent> decode(const DwtPayloadPacket& payload);
/** @brief Flushes incomplete data-trace fragments at a boundary. */
Expand All @@ -38,8 +46,6 @@ class DwtPacketDecoder {
void reset();

private:
static constexpr std::size_t kDataTraceComparatorCount = 4U;

/** @brief Accumulates the fragments of one pending DWT data-trace event. */
struct PendingDataTrace {
std::uint64_t index = 0;
Expand Down Expand Up @@ -68,7 +74,8 @@ class DwtPacketDecoder {

/** @brief Maps an encoded DWT exception action to the semantic action. */
static ExceptionAction exceptionAction(std::uint32_t value);
std::array<std::optional<PendingDataTrace>, kDataTraceComparatorCount> m_pendingDataTrace;
DwtComparatorValues m_comparatorValues;
std::array<std::optional<PendingDataTrace>, kDwtDataTraceComparatorCount> m_pendingDataTrace;
};

#endif // CTRACE_SRC_DECODE_DWTPACKETDECODER_H
#endif // CTRACE_SRC_DECODE_DWTPACKETDECODER_H
64 changes: 63 additions & 1 deletion tools/ctrace/src/tracerun/CtraceRunMeta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <map>
#include <optional>
#include <set>
Expand Down Expand Up @@ -303,6 +304,50 @@ static CtraceRunSourceMeta sourceMeta(const TraceRunConfig& config, const TraceR
return meta;
}

/** @brief Resolves a fully specified DWT comparator value from one reference. */
static std::optional<std::uint32_t> dwtComparatorValue(const TraceRunReference& reference, std::uint32_t comparator)
{
const auto registerName = "DWT_COMP" + std::to_string(comparator);
std::uint32_t value = 0U;
std::uint32_t knownMask = 0U;
for (const auto& reg : reference.registers) {
if (reg.name != registerName) {
continue;
}
value = (value & ~reg.mask) | (reg.value & reg.mask);
knownMask |= reg.mask;
}
if (knownMask != std::numeric_limits<std::uint32_t>::max()) {
return std::nullopt;
}
return value;
}

/** @brief Adds comparator values used by compressed DWT data-trace packets. */
static void addDwtComparatorValues(std::map<std::uint8_t, std::map<std::uint32_t, std::uint32_t>>& valuesByTraceBusId,
const TraceRunConfig& config, const TraceRunReference& reference)
{
if (reference.type != "dwt") {
return;
}
const auto traceBusId = static_cast<std::uint8_t>(reference.stream.value_or(0U));
for (const auto comparator : reference.sources) {
const auto value = dwtComparatorValue(reference, comparator);
if (!value.has_value()) {
continue;
}
auto& values = valuesByTraceBusId[traceBusId];
const auto existing = values.find(comparator);
if (existing != values.end() && existing->second != *value) {
throw std::runtime_error(configError(config, reference.line,
"DWT comparator " + std::to_string(comparator) +
" has conflicting values for Trace Bus ID " +
std::to_string(traceBusId)));
}
values[comparator] = *value;
}
}

/** @brief Returns or creates accumulated metadata for one processor. */
static ProcessorMeta& processorMeta(std::vector<ProcessorMeta>& processors, const std::optional<std::string>& name)
{
Expand Down Expand Up @@ -552,7 +597,18 @@ CtraceRunMeta CtraceRunMeta::fromConfig(const TraceRunConfig& config)
if (isUsableStreamBinding(reference) && identity.accepts(reference)) {
(void)processorMeta(processors, identity.canonicalName(reference.processorName));
}
if (!TraceRunSchema::isUsableReference(reference) || !identity.accepts(reference)) {
if (!identity.accepts(reference)) {
continue;
}
// Armv8-M can compress a DWT PC-value packet relative to either
// comparator of an instruction-address range. Such references do not
// describe a data output route, but their DWT_COMP values are still
// required to reconstruct the packet.
if (reference.type == "dwt" && !reference.sources.empty() &&
TraceRunSchema::referenceProblem(reference) == ReferenceProblem::None) {
addDwtComparatorValues(ctraceRunMeta.m_dwtComparatorValuesByTraceBusId, config, reference);
}
if (!TraceRunSchema::isUsableReference(reference)) {
continue;
}
for (const auto source : reference.sources) {
Expand Down Expand Up @@ -610,6 +666,12 @@ const std::map<std::uint8_t, std::uint32_t>& CtraceRunMeta::itmEnableMasksByTrac
return m_itmEnableMasksByTraceBusId;
}

const std::map<std::uint8_t, std::map<std::uint32_t, std::uint32_t>>&
CtraceRunMeta::dwtComparatorValuesByTraceBusId() const
{
return m_dwtComparatorValuesByTraceBusId;
}

const std::vector<std::string>& CtraceRunMeta::timestampClockErrors() const
{
return m_timestampClockErrors;
Expand Down
Loading
Loading