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
5 changes: 4 additions & 1 deletion tools/ctrace/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ CSV and CTF bundle, using `<solution-set>.<channel>` as their common base name.
this channel-qualified name even for a single input; older `Board.ctf` bundles are neither reused nor removed.
The [CTF profile](docs/ctf-format.md#files-and-common-structure) records the required specification alignment.

Trace references use `index:` for ITM channel and DWT comparator numbers, as a scalar or array. The YAML reader
ignores fields it does not consume. The fourth CSV column is also named `index`; its values are unchanged.

One optional `Board.traceanalysis.xml` collects graphical views from all eligible CTF bundles completed for that
target in the current invocation. Each contributing bundle must retain exactly one clock domain. A multi-clock bundle
remains valid CTF but is omitted from the XML with a warning. Independent single-clock bundles can contribute together;
Expand Down Expand Up @@ -124,7 +127,7 @@ CSV notes and normal CLI messages state what happened, retaining the cause and m
positions, intervals, native error/response codes and packet data. For example, the captured SWO errors produce:

```csv
cycles,stream,type,source,value,pc,address,note
cycles,stream,type,index,value,pc,address,note
29249610,,error,,,,,Invalid ITM packet sequence
29249610,,error,,,,,2179 raw bytes without usable ITM packets
39449474,,error,,,,,Incomplete ITM packet at EOF
Expand Down
2 changes: 1 addition & 1 deletion tools/ctrace/docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@ a ctrace output contract beyond the published specification, which does not defi
global abort records. It does not turn ordinary route-bound diagnostics into unfiltered CSV rows.

Byte-skip annotations are non-failing Info, not synchronization events. CSV uses `type=info`, `N bytes skipped: reason`,
the observed formatter ID in `stream` when known, and empty `cycles`, `source`, `value`, `pc`, and `address` fields.
the observed formatter ID in `stream` when known, and empty `cycles`, `index`, `value`, `pc`, and `address` fields.
The first formatter output group's raw offset is included only in the verbose CLI message.
These rows bypass both type and stream filters; `info` is not a new selectable event type. CTF ignores them instead
of creating routes or clocks, and CLI Info remains visible in CTF-only mode. The existing once-per-ID unsupported
Expand Down
3 changes: 2 additions & 1 deletion tools/ctrace/docs/constraints.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ configuration with trace communication, separate from trace-source setup.
other operation-specific requirements are likewise evaluated after reading. Missing or null
`ctrace-setup.itm.enable` is absent; a malformed enable value bound to an active route is an Error. Conflicting valid
masks on one route produce one Warning and disable that route's optional received-on-disabled-channel check. An ITM
reference without `source` values is valid and contributes no source events.
reference without `index` values is valid and contributes no source events. The `index` field accepts a scalar or
array of ITM channel or DWT comparator numbers. Unconsumed fields are ignored without validation or diagnostics.
- DWT data metadata comes from reference-level `address`, `size`, and `data-type`. When reference `size` is absent,
the referenced `ctrace-setup.data.size` supplies it. DWT instruction-control references may bind a processor stream
but do not create decoded data-source routes.
Expand Down
2 changes: 1 addition & 1 deletion tools/ctrace/docs/ctf-format.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ A fatal OpenCSD decode abort removes the incomplete CTF bundle, including data a
and excludes it from target XML. If CSV output remains healthy, ctrace retains its selected rows and appends one
input-wide `error` row stating `Decode aborted; trace incomplete`, with a compact structured cause when available.
Normal CLI uses the same text; `--verbose` adds the processed-byte count and detailed abort reason. That final row has
no cycle timestamp, stream, or source and bypasses type and stream filters. A recoverable route-local error allows
no cycle timestamp, stream, or index and bypasses type and stream filters. A recoverable route-local error allows
normal output completion, as described
under [`TRACE_STATUS`](#trace_status-event-id-3). Both kinds of Error produce a failing command exit status. Remaining
raw inputs are still processed, and their completed bundles and XML contributions are unaffected by another input's
Expand Down
2 changes: 1 addition & 1 deletion tools/ctrace/docs/todo.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## DWT

- [ ] Preserve logical DWT reference and setup identities when expanding comparator source arrays.
- [ ] Preserve logical DWT reference and setup identities when expanding comparator index arrays.
- [ ] Complete Armv7-M linked-comparator, range, and value-match decoding.
- [ ] Resolve programmable PMU event-counter names from trace-run configuration.

Expand Down
8 changes: 4 additions & 4 deletions tools/ctrace/src/model/MessageCatalog.inc
Original file line number Diff line number Diff line change
Expand Up @@ -521,12 +521,12 @@ CTRACE_MESSAGE(ReferenceArrayMissing, "missing required 'ctrace-refs' array", nu
CTRACE_MESSAGE(YamlDocumentCount, "expected exactly one YAML document", nullptr)
CTRACE_MESSAGE(ItmEnableMapRequired, "'itm' must be a map containing 'enable'", nullptr)
CTRACE_MESSAGE(TimestampsMapRequired, "'timestamps' must be empty or a map", nullptr)
CTRACE_MESSAGE(ReferenceDuplicateSourceLocated, "duplicate value in 'source' array", nullptr)
CTRACE_MESSAGE(ReferenceDuplicateSource, "duplicate value in source array", nullptr)
CTRACE_MESSAGE(ReferenceDuplicateIndexLocated, "duplicate value in 'index' array", nullptr)
CTRACE_MESSAGE(ReferenceDuplicateIndex, "duplicate value in index array", nullptr)
CTRACE_MESSAGE(ReferenceStreamRangeLocated, "'stream' must be a CoreSight ATB trace ID between 1 and 111", nullptr)
CTRACE_MESSAGE(ReferenceStreamRange, "stream must be a CoreSight ATB trace ID between 1 and 111", nullptr)
CTRACE_MESSAGE(ReferenceItmSourceRangeLocated, "ITM 'source' must be between 0 and 31", nullptr)
CTRACE_MESSAGE(ReferenceItmSourceRange, "ITM source must be between 0 and 31", nullptr)
CTRACE_MESSAGE(ReferenceItmIndexRangeLocated, "ITM 'index' must be between 0 and 31", nullptr)
CTRACE_MESSAGE(ReferenceItmIndexRange, "ITM index must be between 0 and 31", nullptr)
CTRACE_MESSAGE(SetupProcessorRequired, "pname is required for every ctrace-setup in a multi-processor configuration", nullptr)
CTRACE_MESSAGE(SingleProcessorBindingRequired, "unformatted SINGLE trace requires one unambiguous processor metadata binding", nullptr)
CTRACE_MESSAGE(ReferenceProcessorRequired, "pname is required for every ref in a multi-processor configuration", nullptr)
Expand Down
18 changes: 9 additions & 9 deletions tools/ctrace/src/output/csv/CsvRowMapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ enum class CsvColumn : std::size_t {
Cycles,
Stream,
Type,
Source,
Index,
Value,
Pc,
Address,
Expand All @@ -39,7 +39,7 @@ constexpr std::array<std::string_view, static_cast<std::size_t>(CsvColumn::Count
"cycles",
"stream",
"type",
"source",
"index",
"value",
"pc",
"address",
Expand Down Expand Up @@ -120,14 +120,14 @@ static std::string_view exceptionActionCsvValue(ExceptionAction action)
/** @brief Writes one ITM software packet to the CSV event columns. */
static void writePayloadColumns(CsvRow& row, const SoftwareTraceEvent& event)
{
row[column(CsvColumn::Source)] = std::to_string(event.channel);
row[column(CsvColumn::Index)] = std::to_string(event.channel);
row[column(CsvColumn::Value)] = hexValue(event.value, event.size);
}

/** @brief Writes one DWT data packet to the CSV event columns. */
static void writePayloadColumns(CsvRow& row, const DwtDataTraceEvent& event)
{
row[column(CsvColumn::Source)] = std::to_string(event.comparator);
row[column(CsvColumn::Index)] = std::to_string(event.comparator);
row[column(CsvColumn::Value)] = hexValue(event.value, event.size);
writeDwtAddressFragment(row, CsvColumn::Pc, event.pc);
writeDwtAddressFragment(row, CsvColumn::Address, event.address);
Expand All @@ -136,35 +136,35 @@ static void writePayloadColumns(CsvRow& row, const DwtDataTraceEvent& event)
/** @brief Writes one DWT address packet to the CSV event columns. */
static void writePayloadColumns(CsvRow& row, const DwtAddressTraceEvent& event)
{
row[column(CsvColumn::Source)] = std::to_string(event.comparator);
row[column(CsvColumn::Index)] = std::to_string(event.comparator);
writeDwtAddressFragment(row, CsvColumn::Pc, dwtAddressPc(event));
writeDwtAddressFragment(row, CsvColumn::Address, dwtDataAddress(event));
}

/** @brief Writes one comparator-only DWT match to the CSV event columns. */
static void writePayloadColumns(CsvRow& row, const DwtMatchTraceEvent& event)
{
row[column(CsvColumn::Source)] = std::to_string(event.comparator);
row[column(CsvColumn::Index)] = std::to_string(event.comparator);
}

/** @brief Writes one exception transition to the CSV event columns. */
static void writePayloadColumns(CsvRow& row, const ExceptionTraceEvent& event)
{
row[column(CsvColumn::Source)] = std::to_string(event.number);
row[column(CsvColumn::Index)] = std::to_string(event.number);
row[column(CsvColumn::Value)] = exceptionActionCsvValue(event.action);
}

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

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

Expand Down
36 changes: 21 additions & 15 deletions tools/ctrace/src/tracerun/CtraceRunMeta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ using ReferenceProblem = TraceRunSchema::ReferenceProblem;
static bool isDiscardableSourceProblem(const TraceRunReference& reference, ReferenceProblem problem)
{
return (!reference.stream.has_value() || CoreSight::isAtbTraceId(*reference.stream)) && !reference.error.empty() &&
(problem == ReferenceProblem::DuplicateSource || problem == ReferenceProblem::InvalidItmSource);
(problem == ReferenceProblem::DuplicateIndex || problem == ReferenceProblem::InvalidItmIndex);
}

/** @brief Formats a trace-run validation error with source location. */
Expand All @@ -182,17 +182,16 @@ static std::string configError(const TraceRunConfig& config, std::size_t line, c
}

/** @brief Preserves reader locations while locating programmatically supplied setup errors. */
static std::string itmEnableError(const TraceRunConfig& config, const TraceRunSetup& setup)
static std::string setupFieldError(const TraceRunConfig& config, std::size_t line, const std::string& error)
{
const auto& error = *setup.itm->enableError;
const auto pathLocation = config.path + ':';
const auto lineLocation = config.path + '(';
if (!config.path.empty() &&
(error.compare(0U, pathLocation.size(), pathLocation) == 0 ||
error.compare(0U, lineLocation.size(), lineLocation) == 0)) {
return error;
}
return configError(config, setup.line, error);
return configError(config, line, error);
}

/** @brief Merges one optional clock fragment without treating an absent scalar as a conflict. */
Expand Down Expand Up @@ -251,19 +250,19 @@ static void addRootInconsistency(std::vector<CtraceRunWarning>& warnings, std::s
static std::string referenceProblemMessage(const TraceRunConfig& config, const TraceRunReference& reference,
ReferenceProblem problem)
{
if (problem == ReferenceProblem::DuplicateSource) {
if (problem == ReferenceProblem::DuplicateIndex) {
return configError(config, reference.line,
reference.line > 0U ? formatMessage(MessageId::ReferenceDuplicateSourceLocated)
: formatMessage(MessageId::ReferenceDuplicateSource));
reference.line > 0U ? formatMessage(MessageId::ReferenceDuplicateIndexLocated)
: formatMessage(MessageId::ReferenceDuplicateIndex));
}
if (problem == ReferenceProblem::InvalidStream) {
return configError(config, reference.line,
reference.line > 0U ? formatMessage(MessageId::ReferenceStreamRangeLocated)
: formatMessage(MessageId::ReferenceStreamRange));
}
return configError(config, reference.line,
reference.line > 0U ? formatMessage(MessageId::ReferenceItmSourceRangeLocated)
: formatMessage(MessageId::ReferenceItmSourceRange));
reference.line > 0U ? formatMessage(MessageId::ReferenceItmIndexRangeLocated)
: formatMessage(MessageId::ReferenceItmIndexRange));
}

static bool setupContainsReference(const TraceRunSetup& setup, const TraceRunReference& reference);
Expand Down Expand Up @@ -942,6 +941,9 @@ class FormattedRouteMetadata final {
for (const auto* setup : setupFragments(route)) {
if (setup->timestamps.has_value()) {
const auto& timestamps = *setup->timestamps;
if (timestamps.prescalerError.has_value()) {
throw std::runtime_error(setupFieldError(m_config, timestamps.line, *timestamps.prescalerError));
}
const auto candidatePrescaler =
timestamps.timestampPrescaler.value_or(TraceRunSchema::kDefaultTimestampPrescaler);
if (!TraceRunSchema::isTimestampPrescaler(candidatePrescaler)) {
Expand All @@ -962,7 +964,7 @@ class FormattedRouteMetadata final {
}
if (setup->itm.has_value()) {
if (setup->itm->enableError.has_value()) {
throw std::runtime_error(itmEnableError(m_config, *setup));
throw std::runtime_error(setupFieldError(m_config, setup->line, *setup->itm->enableError));
}
const auto candidateMask = setup->itm->enableMask;
if (!candidateMask.has_value()) {
Expand Down Expand Up @@ -1211,8 +1213,8 @@ static std::vector<CtraceRunRoute> materializeFormattedRoutes(const TraceRunConf
if (!routeId.has_value() || *routeId != traceBusId || !TraceRunSchema::isUsableReference(reference)) {
continue;
}
for (const auto source : reference.sources) {
route.sources.push_back(routeMetadata.source(reference, source, route));
for (const auto sourceIndex : reference.indices) {
route.sources.push_back(routeMetadata.source(reference, sourceIndex, route));
}
}
routes.push_back(std::move(route));
Expand Down Expand Up @@ -1268,6 +1270,10 @@ static void validateUnformattedSetups(const TraceRunConfig& config, const Proces
if (!isSelectedUnformattedSetup(config, identity, setup)) {
continue;
}
if (setup.timestamps.has_value() && setup.timestamps->prescalerError.has_value()) {
throw std::runtime_error(
setupFieldError(config, setup.timestamps->line, *setup.timestamps->prescalerError));
}
if (setup.timestamps.has_value() && setup.timestamps->timestampPrescaler.has_value() &&
!TraceRunSchema::isTimestampPrescaler(*setup.timestamps->timestampPrescaler)) {
throw std::runtime_error(
Expand All @@ -1277,7 +1283,7 @@ static void validateUnformattedSetups(const TraceRunConfig& config, const Proces
: formatMessage(MessageId::TimestampPrescalerRange)));
}
if (setup.itm.has_value() && setup.itm->enableError.has_value()) {
throw std::runtime_error(itmEnableError(config, setup));
throw std::runtime_error(setupFieldError(config, setup.line, *setup.itm->enableError));
}
}
}
Expand Down Expand Up @@ -1349,8 +1355,8 @@ static std::vector<CtraceRunSourceMeta> unformattedSources(const TraceRunConfig&
if (!TraceRunSchema::isUsableReference(reference) || !identity.accepts(reference)) {
continue;
}
for (const auto source : reference.sources) {
sources.push_back(sourceMeta(config, reference, source, identity));
for (const auto sourceIndex : reference.indices) {
sources.push_back(sourceMeta(config, reference, sourceIndex, identity));
}
}
return sources;
Expand Down
34 changes: 18 additions & 16 deletions tools/ctrace/src/tracerun/TraceRunConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,9 @@ constexpr bool consumesReferenceMetadata(const std::string_view& type)
}

/** @brief Tests whether an ITM stimulus port number is valid. */
constexpr bool isItmSource(std::uint32_t source)
constexpr bool isItmIndex(std::uint32_t index)
{
return CoreSight::isItmStimulusPort(source);
return CoreSight::isItmStimulusPort(index);
}

/** @brief Converts empty processor names to an absent value. */
Expand Down Expand Up @@ -128,7 +128,8 @@ struct TraceRunReference {
// The reader retains the complete YAML value. CtraceRunMeta validates and
// narrows it to the CoreSight ATB trace-ID domain.
std::optional<std::uint32_t> stream;
std::vector<std::uint32_t> sources;
// ITM channel or DWT comparator indices from the scalar/list index field.
std::vector<std::uint32_t> indices;
// Index of the referenced ctrace-setup.data entry, derived from the
// specified ref path form [<pname>/]data#<index>.
std::optional<std::size_t> dataSetupIndex;
Expand All @@ -140,16 +141,16 @@ namespace TraceRunSchema {
/** @brief Classifies structural problems in a parsed trace reference. */
enum class ReferenceProblem {
None,
DuplicateSource,
DuplicateIndex,
InvalidStream,
InvalidItmSource,
InvalidItmIndex,
};

/** @brief Tests whether a reference selects ITM stimulus port zero, which is excluded from output. */
inline bool isItmChannelZero(const TraceRunReference& reference)
{
return reference.type == "itm" && reference.sources.size() == 1U &&
reference.sources.front() == CoreSight::kExcludedItmStimulusPort;
return reference.type == "itm" && reference.indices.size() == 1U &&
reference.indices.front() == CoreSight::kExcludedItmStimulusPort;
}

/** @brief Tests whether a DWT reference describes a generated data route. */
Expand All @@ -161,7 +162,7 @@ inline bool isDwtDataReference(const TraceRunReference& reference)
/** @brief Tests whether a reference has the fields needed for a decoded route. */
inline bool hasConsumedRouteShape(const TraceRunReference& reference)
{
if (!supportsSource(reference.type) || reference.sources.empty()) {
if (!supportsSource(reference.type) || reference.indices.empty()) {
return false;
}
if (reference.type == "itm") {
Expand Down Expand Up @@ -194,7 +195,7 @@ inline bool isProcessorItmReference(const TraceRunReference& reference)
inline bool contributesStreamBinding(const TraceRunReference& reference)
{
return (reference.type == "dwt" || reference.type == "itm") &&
(!reference.sources.empty() || isDwtDataReference(reference) || isTimestampReference(reference) ||
(!reference.indices.empty() || isDwtDataReference(reference) || isTimestampReference(reference) ||
isProcessorItmReference(reference));
}

Expand All @@ -204,17 +205,17 @@ inline ReferenceProblem referenceProblem(const TraceRunReference& reference)
if (reference.stream.has_value() && !CoreSight::isAtbTraceId(*reference.stream)) {
return ReferenceProblem::InvalidStream;
}
for (std::size_t left = 0U; left < reference.sources.size(); ++left) {
for (std::size_t right = left + 1U; right < reference.sources.size(); ++right) {
if (reference.sources[left] == reference.sources[right]) {
return ReferenceProblem::DuplicateSource;
for (std::size_t left = 0U; left < reference.indices.size(); ++left) {
for (std::size_t right = left + 1U; right < reference.indices.size(); ++right) {
if (reference.indices[left] == reference.indices[right]) {
return ReferenceProblem::DuplicateIndex;
}
}
}
if (reference.type == "itm") {
for (const auto source : reference.sources) {
if (!isItmSource(source)) {
return ReferenceProblem::InvalidItmSource;
for (const auto index : reference.indices) {
if (!isItmIndex(index)) {
return ReferenceProblem::InvalidItmIndex;
}
}
}
Expand All @@ -235,6 +236,7 @@ struct TraceRunTimestampSetup {
std::optional<std::uint32_t> timestampPrescaler = std::nullopt;
std::optional<std::string> clockError = std::nullopt;
std::size_t line = 0U;
std::optional<std::string> prescalerError = std::nullopt;
};

/** @brief Stores size metadata copied from one DWT data setup. */
Expand Down
Loading
Loading