Skip to content
Merged
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
13 changes: 8 additions & 5 deletions tools/ctrace/docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@ backend-independent semantic events. Output backends consume these events to cre

The first release profile supports SWO data containing ITM and DWT packets. The command line accepts the stable type
names `itm`, `dwt`, `event`, `pmu`, `exception`, `pcsample`, `global_ts`, `overflow`, and `error`. Output semantics are
currently implemented for `itm`, `dwt`, `exception`, `pcsample`, `global_ts`, `overflow`, and `error`. DWT event-counter
and PMU packets are retained internally but are not mapped to their selectors yet. Periodic PC samples reach CSV and
CTF as semantic events; the CTF event distinguishes a sampled PC from a processor-sleep indication, and Trace Compass
shows processor-sleep intervals as a timeline. Trace Bus input is discovered so that a complete trace directory can be
inspected, but `*.TB.raw` files are reported and skipped until a decoder is implemented.
currently implemented for every listed type. Valid DWT event-counter and PMU trace-on-overflow packets reach CSV as
one row containing the hardware mask. The CTF backend expands each mask into one timestamped record per set bit so
Trace Compass can show exact table rows and labeled one-microsecond visualization pulses. DWT records use their fixed
architectural counter names; PMU records provisionally use `Event0` through `Event7` until trace-run configuration can
resolve the programmable counter assignments. Periodic PC samples reach CSV and CTF as semantic events; the CTF event
distinguishes a sampled PC from a processor-sleep indication, and Trace Compass shows processor-sleep intervals as a
timeline. Trace Bus input is discovered so that a complete trace directory can be inspected, but `*.TB.raw` files are
reported and skipped until a decoder is implemented.

The architecture separates protocol decoding, semantic interpretation, and output generation. This keeps output
formats independent of OpenCSD and allows another raw trace channel to reuse the event model and output backends.
Expand Down
3 changes: 1 addition & 2 deletions tools/ctrace/docs/todo.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@
- [ ] Preserve DWT reference, group, and setup-binding identities when expanding source arrays.
- [ ] Complete Armv7-M linked-comparator, range, and value-match decoding.
- [ ] Add Armv8-M and Armv8.1-M DWT decoding.
- [ ] Add `event` output in its own PR.
- [ ] Add `pmu` output in its own PR.
- [ ] Resolve programmable PMU event-counter names from trace-run configuration.

## Multiple streams

Expand Down
56 changes: 54 additions & 2 deletions tools/ctrace/src/decode/DwtPacketDecoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <cstdint>
#include <iterator>
#include <optional>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
Expand Down Expand Up @@ -55,6 +56,24 @@ constexpr std::uint32_t kPmuOverflowMask = 0xffU;
constexpr std::uint8_t kArmv7MFullPcBytes = 4U;
constexpr std::uint8_t kArmv7MAddressOffsetBytes = 2U;

/** @brief Describes an invalid DWT event-counter payload. */
static std::string invalidEventCounterMessage(const DwtPayloadPacket& payload)
{
std::ostringstream message;
message << "unsupported DWT event-counter payload: size " << static_cast<unsigned>(payload.size) << ", value 0x"
<< std::hex << payload.value << "; expected a non-zero 1-byte mask using bits 0..5 only";
return message.str();
}

/** @brief Describes an invalid PMU trace-on-overflow payload. */
static std::string invalidPmuEventCounterMessage(const DwtPayloadPacket& payload)
{
std::ostringstream message;
message << "unsupported PMU event-counter payload: size " << static_cast<unsigned>(payload.size) << ", value 0x"
<< std::hex << payload.value << "; expected a non-zero 1-byte mask using bits 0..7";
return message.str();
}

std::vector<TraceEvent> DwtPacketDecoder::decode(const DwtPayloadPacket& payload)
{
std::vector<TraceEvent> output;
Expand All @@ -63,7 +82,24 @@ std::vector<TraceEvent> DwtPacketDecoder::decode(const DwtPayloadPacket& payload
const auto source = static_cast<DwtPacketSource>(discriminator);
if (source == DwtPacketSource::EventCounter) {
output = flush(payload.quality, payload.tcyc);
TraceEvent packet{DwtEventTraceEvent{discriminator, payload.size, payload.value}};
const auto validPayload = payload.size == 1U && payload.value != 0U &&
(payload.value & ~static_cast<std::uint32_t>(kDwtEventCounterValidMask)) == 0U;
if (!validPayload) {
TraceEvent error{TraceIssueEvent{
TraceIssueCode::UnsupportedDwtEventCounterPayload,
TraceIssueSeverity::Error,
invalidEventCounterMessage(payload),
std::nullopt,
std::nullopt,
}};
error.index = payload.index;
error.traceBusId = payload.traceBusId;
error.tcyc = payload.tcyc;
error.quality = payload.quality;
output.push_back(std::move(error));
return output;
}
TraceEvent packet{DwtEventTraceEvent{static_cast<std::uint8_t>(payload.value)}};
packet.index = payload.index;
packet.traceBusId = payload.traceBusId;
packet.tcyc = payload.tcyc;
Expand All @@ -74,7 +110,23 @@ std::vector<TraceEvent> DwtPacketDecoder::decode(const DwtPayloadPacket& payload

if (source == DwtPacketSource::PmuTraceOnOverflow) {
output = flush(payload.quality, payload.tcyc);
TraceEvent packet{PmuTraceEvent{static_cast<std::uint8_t>(payload.value & kPmuOverflowMask)}};
const auto validPayload = payload.size == 1U && payload.value != 0U && (payload.value & ~kPmuOverflowMask) == 0U;
Comment thread
thorstendb-ARM marked this conversation as resolved.
if (!validPayload) {
TraceEvent error{TraceIssueEvent{
TraceIssueCode::UnsupportedPmuEventCounterPayload,
TraceIssueSeverity::Error,
invalidPmuEventCounterMessage(payload),
std::nullopt,
std::nullopt,
}};
error.index = payload.index;
error.traceBusId = payload.traceBusId;
error.tcyc = payload.tcyc;
error.quality = payload.quality;
output.push_back(std::move(error));
return output;
}
TraceEvent packet{PmuTraceEvent{static_cast<std::uint8_t>(payload.value)}};
packet.index = payload.index;
packet.traceBusId = payload.traceBusId;
packet.tcyc = payload.tcyc;
Expand Down
2 changes: 2 additions & 0 deletions tools/ctrace/src/diagnostics/TraceIssueReporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ static std::string displayErrorMessage(const TraceEvent& event, const TraceIssue
return "OpenCSD initialization failed";
case TraceIssueCode::DecodeError:
case TraceIssueCode::InvalidExceptionAction:
case TraceIssueCode::UnsupportedDwtEventCounterPayload:
case TraceIssueCode::UnsupportedPmuEventCounterPayload:
case TraceIssueCode::UnsupportedDwtAddressPayload:
case TraceIssueCode::UnsupportedDwtPcSamplePayload:
case TraceIssueCode::OpenCsdDecodeError:
Expand Down
68 changes: 64 additions & 4 deletions tools/ctrace/src/model/TraceEvent.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#ifndef CTRACE_SRC_MODEL_TRACEEVENT_H
#define CTRACE_SRC_MODEL_TRACEEVENT_H

#include <array>
#include <cstdint>
#include <optional>
#include <string>
Expand Down Expand Up @@ -39,6 +40,8 @@ enum class TraceIssueCode {
DecodeError,
DataLoss,
InvalidExceptionAction,
UnsupportedDwtEventCounterPayload,
UnsupportedPmuEventCounterPayload,
UnsupportedDwtAddressPayload,
UnsupportedDwtPcSamplePayload,
OpenCsdDecodeError,
Expand Down Expand Up @@ -125,13 +128,70 @@ struct ExceptionTraceEvent {
ExceptionAction action = ExceptionAction::Unknown;
};

/** @brief Contains a decoded DWT event-counter packet. */
/** @brief Identifies one counter reported by a DWT event-counter packet. */
enum class DwtEventCounter : std::uint8_t {
Cpi = 0U,
Exception = 1U,
Sleep = 2U,
LoadStore = 3U,
Fold = 4U,
Cycle = 5U,
};

inline constexpr std::array<DwtEventCounter, 6U> kDwtEventCounters{{
DwtEventCounter::Cpi,
DwtEventCounter::Exception,
DwtEventCounter::Sleep,
DwtEventCounter::LoadStore,
DwtEventCounter::Fold,
DwtEventCounter::Cycle,
}};

/** @brief Returns the wire bit represented by one DWT event counter. */
constexpr std::uint8_t dwtEventCounterBit(DwtEventCounter counter)
{
return static_cast<std::uint8_t>(1U << static_cast<std::uint8_t>(counter));
}

inline constexpr std::uint8_t kDwtEventCounterValidMask =
dwtEventCounterBit(DwtEventCounter::Cpi) | dwtEventCounterBit(DwtEventCounter::Exception) |
dwtEventCounterBit(DwtEventCounter::Sleep) | dwtEventCounterBit(DwtEventCounter::LoadStore) |
dwtEventCounterBit(DwtEventCounter::Fold) | dwtEventCounterBit(DwtEventCounter::Cycle);

/** @brief Contains the counter-overflow mask from one decoded DWT event packet. */
struct DwtEventTraceEvent {
std::uint32_t discriminator = 0;
std::uint8_t size = 0;
std::uint32_t value = 0;
std::uint8_t counterMask = 0;
};

/** @brief Identifies one programmable counter reported by a PMU trace packet. */
enum class PmuEventCounter : std::uint8_t {
Event0 = 0U,
Event1 = 1U,
Event2 = 2U,
Event3 = 3U,
Event4 = 4U,
Event5 = 5U,
Event6 = 6U,
Event7 = 7U,
};

inline constexpr std::array<PmuEventCounter, 8U> kPmuEventCounters{{
PmuEventCounter::Event0,
PmuEventCounter::Event1,
PmuEventCounter::Event2,
PmuEventCounter::Event3,
PmuEventCounter::Event4,
PmuEventCounter::Event5,
PmuEventCounter::Event6,
PmuEventCounter::Event7,
}};

/** @brief Returns the wire bit represented by one programmable PMU event counter. */
constexpr std::uint8_t pmuEventCounterBit(PmuEventCounter counter)
{
return static_cast<std::uint8_t>(1U << static_cast<std::uint8_t>(counter));
}

/** @brief Contains the OVn counter mask from a decoded PMU trace-on-overflow packet. */
struct PmuTraceEvent {
std::uint8_t overflowMask = 0;
Expand Down
8 changes: 4 additions & 4 deletions tools/ctrace/src/model/TraceSelection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,16 @@ static std::optional<TraceEventType> typeFor(const ExceptionTraceEvent&)
return TraceEventType::Exception;
}

/** @brief Keeps DWT counter payloads disabled until their output semantics are implemented. */
/** @brief Exposes DWT event-counter payloads through their public output selector. */
static std::optional<TraceEventType> typeFor(const DwtEventTraceEvent&)
{
return std::nullopt;
return TraceEventType::Event;
}

/** @brief Keeps PMU payloads disabled until their output semantics are implemented. */
/** @brief Exposes PMU trace-on-overflow payloads through their public output selector. */
static std::optional<TraceEventType> typeFor(const PmuTraceEvent&)
{
return std::nullopt;
return TraceEventType::Pmu;
}

/** @brief Exposes periodic DWT PC samples through their public output selector. */
Expand Down
18 changes: 18 additions & 0 deletions tools/ctrace/src/output/csv/CsvRowMapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,20 @@ static std::string_view exceptionActionCsvValue(ExceptionAction action)
return "0x0";
}

/** @brief Writes one DWT event-counter packet to the CSV event columns. */
static void writeDwtEvent(CsvRow& row, const DwtEventTraceEvent& event)
{
row[column(CsvColumn::Source)] = "0";
row[column(CsvColumn::Value)] = hexValue(event.counterMask, 1U);
}

/** @brief Writes one PMU trace-on-overflow packet to the CSV event columns. */
static void writePmuEvent(CsvRow& row, const PmuTraceEvent& event)
{
row[column(CsvColumn::Source)] = "3";
row[column(CsvColumn::Value)] = hexValue(event.overflowMask, 1U);
}

/** @brief Maps one semantic trace event to all CSV columns. */
static CsvRow eventToCsvRow(const TraceEvent& event)
{
Expand Down Expand Up @@ -159,6 +173,10 @@ static CsvRow eventToCsvRow(const TraceEvent& event)
} else if (const auto* exception = traceEventPayload<ExceptionTraceEvent>(event)) {
row[column(CsvColumn::Source)] = std::to_string(exception->number);
row[column(CsvColumn::Value)] = exceptionActionCsvValue(exception->action);
} else if (const auto* counter = traceEventPayload<DwtEventTraceEvent>(event)) {
Comment thread
thorstendb-ARM marked this conversation as resolved.
writeDwtEvent(row, *counter);
} else if (const auto* counter = traceEventPayload<PmuTraceEvent>(event)) {
writePmuEvent(row, *counter);
} else if (const auto* sample = traceEventPayload<PcSampleTraceEvent>(event)) {
if (!sample->sleeping) {
row[column(CsvColumn::Pc)] = hexValue(sample->pc, 4);
Expand Down
45 changes: 45 additions & 0 deletions tools/ctrace/src/output/ctf/CtfEncoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,14 @@ void CtfEncoder::writeEvent(const TraceEvent& event)
if (selected) {
writeDwtAddrEvent(event, *address);
}
} else if (const auto* counters = traceEventPayload<DwtEventTraceEvent>(event)) {
Comment thread
thorstendb-ARM marked this conversation as resolved.
if (selected) {
writeDwtEvent(event, *counters);
}
} else if (const auto* counters = traceEventPayload<PmuTraceEvent>(event)) {
if (selected) {
writePmuEvent(event, *counters);
}
} else if (const auto* sample = traceEventPayload<PcSampleTraceEvent>(event)) {
if (selected) {
writePcSampleEvent(event, *sample);
Expand Down Expand Up @@ -355,6 +363,43 @@ void CtfEncoder::writeDwtAddrEvent(const TraceEvent& event, const DwtAddressTrac
});
}

void CtfEncoder::writeDwtEvent(const TraceEvent& event, const DwtEventTraceEvent& counters)
{
constexpr auto payloadSize = 1U + 1U + 4U;
const auto eventTimestamp = allocateEventTimestamp(event.traceBusId);
const auto quality = computeSampleQuality(event);
for (const auto counter : kDwtEventCounters) {
const auto counterBit = dwtEventCounterBit(counter);
if ((counters.counterMask & counterBit) == 0U) {
continue;
}
m_stream.writeRecord(CtfSchema::value(CtfSchema::EventId::DwtEvent), eventTimestamp, event.traceBusId, payloadSize,
[&](CtfStreamWriter::Record& record) {
record.writeU8(CtfSchema::value(counter));
record.writeU8(quality.first);
record.writeU32(quality.second);
});
}
}

void CtfEncoder::writePmuEvent(const TraceEvent& event, const PmuTraceEvent& counters)
{
constexpr auto payloadSize = 1U + 1U + 4U;
const auto eventTimestamp = allocateEventTimestamp(event.traceBusId);
const auto quality = computeSampleQuality(event);
for (const auto counter : kPmuEventCounters) {
if ((counters.overflowMask & pmuEventCounterBit(counter)) == 0U) {
continue;
}
m_stream.writeRecord(CtfSchema::value(CtfSchema::EventId::PmuEvent), eventTimestamp, event.traceBusId, payloadSize,
[&](CtfStreamWriter::Record& record) {
record.writeU8(CtfSchema::value(counter));
record.writeU8(quality.first);
record.writeU32(quality.second);
});
}
}

void CtfEncoder::writeGlobalTimestampEvent(const TraceEvent& event, const GlobalTimestampTraceEvent& timestamp)
{
constexpr auto payloadSize = 8U + 1U;
Expand Down
4 changes: 4 additions & 0 deletions tools/ctrace/src/output/ctf/CtfEncoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ class CtfEncoder final {
void reportDwtSizeMismatch(const TraceEvent& event, const DwtDataTraceEvent& data, const ResolvedTraceSource* source);
/** @brief Encodes one DWT address event. */
void writeDwtAddrEvent(const TraceEvent& event, const DwtAddressTraceEvent& address);
/** @brief Expands one DWT event-counter mask into individual CTF records. */
void writeDwtEvent(const TraceEvent& event, const DwtEventTraceEvent& counters);
/** @brief Expands one PMU trace-on-overflow mask into individual CTF records. */
void writePmuEvent(const TraceEvent& event, const PmuTraceEvent& counters);
/** @brief Encodes one periodic PC-sample or processor-sleep event. */
void writePcSampleEvent(const TraceEvent& event, const PcSampleTraceEvent& sample);
/** @brief Encodes one reconstructed global timestamp event. */
Expand Down
Loading
Loading