From 3b4399e3a94a1ee2e37a38c944aa6776c0c6ae87 Mon Sep 17 00:00:00 2001 From: Thorsten de Buhr Date: Thu, 1 Oct 2026 15:22:31 +0200 Subject: [PATCH 1/5] fix(ctrace): rename source to index in trace metadata and CSV --- tools/ctrace/README.md | 3 + tools/ctrace/docs/architecture.md | 2 +- tools/ctrace/docs/constraints.md | 4 +- tools/ctrace/docs/ctf-format.md | 2 +- tools/ctrace/docs/todo.md | 2 +- tools/ctrace/src/output/csv/CsvRowMapper.cpp | 18 +-- tools/ctrace/src/tracerun/CtraceRunMeta.cpp | 18 +-- tools/ctrace/src/tracerun/TraceRunConfig.h | 33 ++-- .../src/tracerun/YmlTraceRunConfigReader.cpp | 34 ++-- .../test/data/Arm-reset/Arm.ctrace-run.yml | 8 +- .../test/data/Blinky+Arm/Blinky+Arm.SWO.csv | 2 +- .../data/Blinky+Arm/Blinky+Arm.ctrace-run.yml | 2 +- .../data/TB-Trace/Blinky+Arm.ctrace-run.yml | 2 +- .../Synthetic.ctrace-run.yml | 20 +-- .../trace-match/trace-match.ctrace-run.yml | 8 +- .../test/data/trace-run/Board.ctrace-run.yml | 4 +- tools/ctrace/test/integration/README.md | 2 +- .../test/integration/src/CtraceIntegTests.cpp | 40 ++--- .../src/ValidateFixtureIntegrity.cmake | 14 +- .../src/control/TraceDirectoryJobTests.cpp | 10 +- .../src/output/OutputRequirementsTests.cpp | 4 +- .../src/output/TraceOutputLifecycleTests.cpp | 2 +- .../src/output/csv/CsvFileOutputTests.cpp | 8 +- .../unit/src/output/csv/CsvRowMapperTests.cpp | 4 +- .../unit/src/tracerun/CtraceRunMetaTests.cpp | 12 +- .../tracerun/TraceRunConfigReaderTests.cpp | 148 ++++++++++-------- .../test/unit/support/TraceRunTestSupport.h | 4 +- 27 files changed, 218 insertions(+), 192 deletions(-) diff --git a/tools/ctrace/README.md b/tools/ctrace/README.md index a0c729339..2ef5f0d48 100644 --- a/tools/ctrace/README.md +++ b/tools/ctrace/README.md @@ -51,6 +51,9 @@ CSV and CTF bundle, using `.` 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. Replace legacy +`source:` keys with `index:` in `ctrace-refs`. 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; diff --git a/tools/ctrace/docs/architecture.md b/tools/ctrace/docs/architecture.md index 955ad8b2e..bd6506d25 100644 --- a/tools/ctrace/docs/architecture.md +++ b/tools/ctrace/docs/architecture.md @@ -348,7 +348,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`, a descriptive note, -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. 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 source warning remains separate from byte accounting. A route-bound missing-sync Error follows ordinary output diff --git a/tools/ctrace/docs/constraints.md b/tools/ctrace/docs/constraints.md index 994469e45..2d53c4548 100644 --- a/tools/ctrace/docs/constraints.md +++ b/tools/ctrace/docs/constraints.md @@ -27,7 +27,9 @@ 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. Legacy `source` fields on ITM/DWT references are rejected with an + explicit migration error. - 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. diff --git a/tools/ctrace/docs/ctf-format.md b/tools/ctrace/docs/ctf-format.md index f6bb4281e..bc921ff4b 100644 --- a/tools/ctrace/docs/ctf-format.md +++ b/tools/ctrace/docs/ctf-format.md @@ -92,7 +92,7 @@ and multi-clock CTF bundles remain valid. Historical per-channel XML files are n A fatal OpenCSD decode abort removes the incomplete CTF bundle, including data already written for healthy routes, and excludes it from target XML. If CSV output remains healthy, ctrace retains its selected rows and appends one input-wide `error` -row with the processed-byte count and abort reason. That final row has no cycle timestamp, stream, or source and +row with the processed-byte count and abort reason. That final row has no cycle timestamp, stream, or index and bypasses type and stream filters. A recoverable route-local error instead 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 diff --git a/tools/ctrace/docs/todo.md b/tools/ctrace/docs/todo.md index ace515993..2f2330d5b 100644 --- a/tools/ctrace/docs/todo.md +++ b/tools/ctrace/docs/todo.md @@ -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. diff --git a/tools/ctrace/src/output/csv/CsvRowMapper.cpp b/tools/ctrace/src/output/csv/CsvRowMapper.cpp index da87a41b1..e93e33369 100644 --- a/tools/ctrace/src/output/csv/CsvRowMapper.cpp +++ b/tools/ctrace/src/output/csv/CsvRowMapper.cpp @@ -25,7 +25,7 @@ enum class CsvColumn : std::size_t { Cycles, Stream, Type, - Source, + Index, Value, Pc, Address, @@ -37,7 +37,7 @@ constexpr std::array(CsvColumn::Count "cycles", "stream", "type", - "source", + "index", "value", "pc", "address", @@ -136,14 +136,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); @@ -152,7 +152,7 @@ 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)); } @@ -160,27 +160,27 @@ static void writePayloadColumns(CsvRow& row, const DwtAddressTraceEvent& 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); } diff --git a/tools/ctrace/src/tracerun/CtraceRunMeta.cpp b/tools/ctrace/src/tracerun/CtraceRunMeta.cpp index 3e49d2728..721779059 100644 --- a/tools/ctrace/src/tracerun/CtraceRunMeta.cpp +++ b/tools/ctrace/src/tracerun/CtraceRunMeta.cpp @@ -171,7 +171,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. */ @@ -254,9 +254,9 @@ static void addRootInconsistency(std::vector& 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 ? "duplicate value in 'source' array" : "duplicate value in source array"); + reference.line > 0U ? "duplicate value in 'index' array" : "duplicate value in index array"); } if (problem == ReferenceProblem::InvalidStream) { return configError(config, reference.line, @@ -264,8 +264,8 @@ static std::string referenceProblemMessage(const TraceRunConfig& config, const T : "stream must be a CoreSight ATB trace ID between 1 and 111"); } return configError(config, reference.line, - reference.line > 0U ? "ITM 'source' must be between 0 and 31" - : "ITM source must be between 0 and 31"); + reference.line > 0U ? "ITM 'index' must be between 0 and 31" + : "ITM index must be between 0 and 31"); } static bool setupContainsReference(const TraceRunSetup& setup, const TraceRunReference& reference); @@ -1217,8 +1217,8 @@ static std::vector 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)); @@ -1354,8 +1354,8 @@ static std::vector 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; diff --git a/tools/ctrace/src/tracerun/TraceRunConfig.h b/tools/ctrace/src/tracerun/TraceRunConfig.h index 1d213455f..e6e12b078 100644 --- a/tools/ctrace/src/tracerun/TraceRunConfig.h +++ b/tools/ctrace/src/tracerun/TraceRunConfig.h @@ -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. */ @@ -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 stream; - std::vector sources; + // ITM channel or DWT comparator indices from the scalar/list index field. + std::vector indices; // Index of the referenced ctrace-setup.data entry, derived from the // specified ref path form [/]data#. std::optional dataSetupIndex; @@ -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. */ @@ -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") { @@ -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)); } @@ -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; } } } diff --git a/tools/ctrace/src/tracerun/YmlTraceRunConfigReader.cpp b/tools/ctrace/src/tracerun/YmlTraceRunConfigReader.cpp index fd984fa12..b86a84098 100644 --- a/tools/ctrace/src/tracerun/YmlTraceRunConfigReader.cpp +++ b/tools/ctrace/src/tracerun/YmlTraceRunConfigReader.cpp @@ -293,32 +293,32 @@ static std::optional parseTraceFormat(const std::string& path, c fail(path, node, "'trace-format' must be 'unformatted' or 'formatted'"); } -/** @brief Parses scalar or sequence source identifiers from one reference. */ -static std::vector parseSources(const std::string& path, const Node& reference) +/** @brief Parses scalar or sequence ITM channel or DWT comparator indices from one reference. */ +static std::vector parseIndices(const std::string& path, const Node& reference) { - const auto sourceNode = childNode(reference, "source"); - if (!sourceNode || sourceNode.IsNull()) { + const auto indexNode = childNode(reference, "index"); + if (!indexNode || indexNode.IsNull()) { return {}; } - if (sourceNode.IsScalar()) { + if (indexNode.IsScalar()) { return {static_cast( - unsignedValue(path, sourceNode, "source", sourceNode.Scalar(), std::numeric_limits::max()))}; + unsignedValue(path, indexNode, "index", indexNode.Scalar(), std::numeric_limits::max()))}; } - requireSequence(path, sourceNode, "source"); - std::vector sources; - for (const auto& item : sourceNode) { + requireSequence(path, indexNode, "index"); + std::vector indices; + for (const auto& item : indexNode) { if (item.IsNull()) { continue; } if (!item.IsScalar() || item.Scalar().empty()) { - fail(path, item, "each 'source' entry must be an unsigned integer"); + fail(path, item, "each 'index' entry must be an unsigned integer"); } - const auto source = static_cast( - unsignedValue(path, item, "source", item.Scalar(), std::numeric_limits::max())); - sources.push_back(source); + const auto index = static_cast( + unsignedValue(path, item, "index", item.Scalar(), std::numeric_limits::max())); + indices.push_back(index); } - return sources; + return indices; } /** @brief Stores diagnostics copied from one parsed trace reference. */ @@ -409,8 +409,12 @@ static std::optional parseReference(const std::string& path, reference.processorName = processorNameAttribute(path, element); reference.stream = parseStream(); if (TraceRunSchema::supportsSource(reference.type)) { + const auto legacySource = childNode(element, "source"); + if (legacySource) { + fail(path, legacySource, "'source' is no longer supported; use 'index' in 'ctrace-refs' entries"); + } try { - reference.sources = parseSources(path, element); + reference.indices = parseIndices(path, element); } catch (const std::runtime_error&) { if (diagnostics.error.empty()) { throw; diff --git a/tools/ctrace/test/data/Arm-reset/Arm.ctrace-run.yml b/tools/ctrace/test/data/Arm-reset/Arm.ctrace-run.yml index b29d401da..924c5111a 100644 --- a/tools/ctrace/test/data/Arm-reset/Arm.ctrace-run.yml +++ b/tools/ctrace/test/data/Arm-reset/Arm.ctrace-run.yml @@ -8,28 +8,28 @@ ctrace-run: type: itm - ref: data#0 type: dwt - source: 0 + index: 0 address: 0x2404AEA4 size: 4 data-type: unsigned label: Sine - ref: data#1 type: dwt - source: 1 + index: 1 address: 0x2404AEA0 size: 4 data-type: unsigned label: Rectangle - ref: data#2 type: dwt - source: 2 + index: 2 address: 0x2404AE9C size: 4 data-type: unsigned label: Random - ref: data#3 type: dwt - source: 3 + index: 3 address: 0x24000E88 size: 4 data-type: unsigned 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 969c5f234..0897d5e4c 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,address,note +cycles,stream,type,index,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/data/Blinky+Arm/Blinky+Arm.ctrace-run.yml b/tools/ctrace/test/data/Blinky+Arm/Blinky+Arm.ctrace-run.yml index bddbbf2ee..d2ed6d204 100644 --- a/tools/ctrace/test/data/Blinky+Arm/Blinky+Arm.ctrace-run.yml +++ b/tools/ctrace/test/data/Blinky+Arm/Blinky+Arm.ctrace-run.yml @@ -35,7 +35,7 @@ ctrace-run: address: 0x24000024 size: 4 data-type: unsigned - source: 0 + index: 0 regs: - name: DWT_COMP0 value: 0x24000024 diff --git a/tools/ctrace/test/data/TB-Trace/Blinky+Arm.ctrace-run.yml b/tools/ctrace/test/data/TB-Trace/Blinky+Arm.ctrace-run.yml index 370b582f4..667f904c8 100644 --- a/tools/ctrace/test/data/TB-Trace/Blinky+Arm.ctrace-run.yml +++ b/tools/ctrace/test/data/TB-Trace/Blinky+Arm.ctrace-run.yml @@ -90,7 +90,7 @@ ctrace-run: address: 0x24000024 size: 4 data-type: unsigned - source: 0 + index: 0 stream: 2 regs: - name: DWT_COMP0 diff --git a/tools/ctrace/test/data/formatted-synthetic/Synthetic.ctrace-run.yml b/tools/ctrace/test/data/formatted-synthetic/Synthetic.ctrace-run.yml index 61952ebe6..60f841246 100644 --- a/tools/ctrace/test/data/formatted-synthetic/Synthetic.ctrace-run.yml +++ b/tools/ctrace/test/data/formatted-synthetic/Synthetic.ctrace-run.yml @@ -26,33 +26,33 @@ ctrace-run: type: itm pname: anchored stream: 1 - source: [1, 2, 3] + index: [1, 2, 3] - ref: anchored/data#0 type: dwt pname: anchored stream: 1 - source: 0 + index: 0 size: 1 data-type: unsigned - ref: anchored/data#1 type: dwt pname: anchored stream: 1 - source: 1 + index: 1 size: 2 data-type: unsigned - ref: anchored/data#2 type: dwt pname: anchored stream: 1 - source: 2 + index: 2 size: 4 data-type: unsigned - ref: anchored/data#3 type: dwt pname: anchored stream: 1 - source: 3 + index: 3 size: 4 data-type: unsigned @@ -63,32 +63,32 @@ ctrace-run: type: dwt pname: fallback stream: 2 - source: 0 + index: 0 size: 1 data-type: unsigned - ref: fallback/data#1 type: dwt pname: fallback stream: 2 - source: 1 + index: 1 size: 2 data-type: unsigned - ref: fallback/data#2 type: dwt pname: fallback stream: 2 - source: 2 + index: 2 size: 4 data-type: unsigned - ref: fallback/data#3 type: dwt pname: fallback stream: 2 - source: 3 + index: 3 size: 4 data-type: unsigned - ref: fallback/messages type: itm pname: fallback stream: 2 - source: [1, 2, 3] + index: [1, 2, 3] diff --git a/tools/ctrace/test/data/trace-match/trace-match.ctrace-run.yml b/tools/ctrace/test/data/trace-match/trace-match.ctrace-run.yml index e77b2c50b..e2a3766de 100644 --- a/tools/ctrace/test/data/trace-match/trace-match.ctrace-run.yml +++ b/tools/ctrace/test/data/trace-match/trace-match.ctrace-run.yml @@ -23,25 +23,25 @@ ctrace-run: address: 0x20000000 size: 4 data-type: unsigned - source: 0 + index: 0 - ref: data#1 type: dwt label: Match Comparator 1 address: 0x20000004 size: 4 data-type: unsigned - source: 1 + index: 1 - ref: data#2 type: dwt label: Match Comparator 2 address: 0x20000008 size: 4 data-type: unsigned - source: 2 + index: 2 - ref: data#3 type: dwt label: Match Comparator 3 address: 0x2000000C size: 4 data-type: unsigned - source: 3 + index: 3 diff --git a/tools/ctrace/test/data/trace-run/Board.ctrace-run.yml b/tools/ctrace/test/data/trace-run/Board.ctrace-run.yml index 1599dc901..4dbecbd19 100644 --- a/tools/ctrace/test/data/trace-run/Board.ctrace-run.yml +++ b/tools/ctrace/test/data/trace-run/Board.ctrace-run.yml @@ -10,11 +10,11 @@ ctrace-run: type: itm info: configured ITM channel stream: 1 - source: 0 + index: 0 - ref: data#0 type: dwt info: configured DWT data trace warning: aligned DWT range label: sample counter stream: 1 - source: [0, 1] + index: [0, 1] diff --git a/tools/ctrace/test/integration/README.md b/tools/ctrace/test/integration/README.md index ed3bea565..0888c960c 100644 --- a/tools/ctrace/test/integration/README.md +++ b/tools/ctrace/test/integration/README.md @@ -42,7 +42,7 @@ the command returns a failing exit status. end-of-input decode failure after a valid payload. It requires selected CSV rows to remain, followed by exactly one input-wide `error` row containing the processed-byte count and abort reason, with no cycle timestamp, stream, or -source. That final row bypasses type and stream filters; the preceding +index. That final row bypasses type and stream filters; the preceding route-local error follows those filters. Incomplete CTF output must be removed and excluded from target XML. The cases cover unfiltered output, `--type itm`, and `--stream 2` when the input uses route 1. diff --git a/tools/ctrace/test/integration/src/CtraceIntegTests.cpp b/tools/ctrace/test/integration/src/CtraceIntegTests.cpp index 6a84c1f0a..36581a50b 100644 --- a/tools/ctrace/test/integration/src/CtraceIntegTests.cpp +++ b/tools/ctrace/test/integration/src/CtraceIntegTests.cpp @@ -500,7 +500,7 @@ void expectChannelArtifacts(const std::filesystem::path& directory, std::string_ std::ostringstream value; value << std::hex << static_cast(fixture.value); EXPECT_EQ(traceCsvRows(readTestTextFile(csvPath)), - "cycles,stream,type,source,value,pc,address,note\n" + prefix + "itm,1,0x" + value.str() + ",,,\n" + + "cycles,stream,type,index,value,pc,address,note\n" + prefix + "itm,1,0x" + value.str() + ",,,\n" + prefix + "pcsample,,,,,CPU Sleeping\n"); } if (ctf) { @@ -526,7 +526,7 @@ void writeSwoAndTbTopicFixture(const std::filesystem::path& directory, std::stri - size: 1 ctrace-refs: - { ref: core/itm, type: itm, pname: core, stream: 1 } - - { ref: core/data#0, type: dwt, pname: core, stream: 1, source: 0, size: 1, data-type: unsigned } + - { ref: core/data#0, type: dwt, pname: core, stream: 1, index: 0, size: 1, data-type: unsigned } )yml"; writeTestFile(directory / (std::string(target) + ".ctrace-run.yml"), traceRun); for (const auto channel : {"SWO", "TB"}) { @@ -758,7 +758,7 @@ TEST_F(CtraceIntegTests, GeneratesCsvAndCtfWithoutEmptyGraphicalConfiguration) 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,address,note\n" + EXPECT_EQ("cycles,stream,type,index,value,pc,address,note\n" "0,,pcsample,,,0x08001234,,\n" "0,,itm,1,0x41,,,\n", readTestTextFile(workDirectory() / "Minimal.SWO.csv")); @@ -782,7 +782,7 @@ TEST_F(CtraceIntegTests, DecodesExplicitUnformattedNamedTraceBuffer) const auto result = run({"ctrace", workDirectory().string(), "--target", "Named", "--all"}); EXPECT_EQ(0, result.exitCode) << result.stderrText; - EXPECT_EQ("cycles,stream,type,source,value,pc,address,note\n" + EXPECT_EQ("cycles,stream,type,index,value,pc,address,note\n" "0,,pcsample,,,0x08001234,,\n" "0,,itm,1,0x41,,,\n", readTestTextFile(workDirectory() / "Named.TB_MTB.csv")); @@ -803,7 +803,7 @@ TEST_F(CtraceIntegTests, DefaultsNullTraceFormatToUnformattedForSwo) const auto result = run({"ctrace", workDirectory().string(), "--target", "Minimal", "--csv"}); EXPECT_EQ(0, result.exitCode) << result.stderrText; - EXPECT_EQ("cycles,stream,type,source,value,pc,address,note\n" + EXPECT_EQ("cycles,stream,type,index,value,pc,address,note\n" "0,,itm,1,0x41,,,\n", readTestTextFile(workDirectory() / "Minimal.SWO.csv")); } @@ -889,7 +889,7 @@ TEST_F(CtraceIntegTests, SkipsUnsupportedFormattedSourceOnceAndKeepsConfiguredRo EXPECT_EQ(0, result.exitCode) << result.stderrText; EXPECT_EQ(countOccurrences(result.stderrText, "skipping unsupported formatted CoreSight trace source"), 1U); expectContains(result.stderrText, "stream=42"); - EXPECT_EQ("cycles,stream,type,source,value,pc,address,note\n" + EXPECT_EQ("cycles,stream,type,index,value,pc,address,note\n" "0,1,itm,1,0x41,,,\n" "0,1,itm,2,0x42,,,\n", traceCsvRows(readTestTextFile(workDirectory() / "Mixed.TB.csv"))); @@ -941,7 +941,7 @@ TEST_F(CtraceIntegTests, PublishesOutputsWithUnresolvedFormattedRouteRecovery) expectContains(result.stderrText, "invalid ITM packet header at raw offset 6"); expectContains(result.stderrText, "ITM decoding did not resume before end of input at raw offset 16"); expectContains(result.stderrText, "no later hardware SYNC; affected raw interval [6, 16) spans 10 bytes"); - EXPECT_EQ("cycles,stream,type,source,value,pc,address,note\n" + EXPECT_EQ("cycles,stream,type,index,value,pc,address,note\n" "0,1,error,,,,,\"OpenCSD detected an invalid ITM packet header at raw offset 6. " "0x0014 (OCSD_ERR_INVALID_PCKT_HDR) [Invalid packet header]; packet=RESERVED, size=1 byte, bytes=[04]\"\n" "0,1,error,,,,,\"ITM decoding did not resume before end of input at raw offset 16; " @@ -1024,7 +1024,7 @@ TEST_F(CtraceIntegTests, ReportsUnassignedFormatterOnlyInputWithoutInventingRout "[info] 15 bytes skipped due to missing source ID; first formatter group at raw offset 0"); expectNotContains(result.stderrText, "no hardware ITM SYNC"); expectNotContains(result.stderrText, "[error]"); - EXPECT_EQ("cycles,stream,type,source,value,pc,address,note\n" + EXPECT_EQ("cycles,stream,type,index,value,pc,address,note\n" ",,info,,,,,15 bytes skipped due to missing source ID; " "first formatter group at raw offset 0\n", readTestTextFile(workDirectory() / "Unassigned.TB.csv")); @@ -1061,7 +1061,7 @@ TEST_F(CtraceIntegTests, DecodesFormattedCaptureAfterUnassignedPrefixAndRetainsI EXPECT_EQ(countOccurrences(csv, " bytes skipped for null source ID 0;"), 1U); EXPECT_EQ(countOccurrences(csv, " bytes skipped for reserved source ID 127;"), prefixByte == 0xffU ? 1U : 0U); if (filtered) { - EXPECT_EQ(traceCsvRows(csv), "cycles,stream,type,source,value,pc,address,note\n"); + EXPECT_EQ(traceCsvRows(csv), "cycles,stream,type,index,value,pc,address,note\n"); EXPECT_EQ(countOccurrences(csv, ",info,"), prefixByte == 0xffU ? 3U : 2U); } else { expectCompleteSyntheticCsv(directory / "Synthetic.TB.csv"); @@ -1157,7 +1157,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,address,note\n" + EXPECT_EQ("cycles,stream,type,index,value,pc,address,note\n" "0,,event,0,0x21,,,\n" "0,,itm,1,0x41,,,\n", readTestTextFile(workDirectory() / "Events.SWO.csv")); @@ -1200,7 +1200,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,address,note\n" + EXPECT_EQ("cycles,stream,type,index,value,pc,address,note\n" "1,,dwt,0,,,,\n" "3,,dwt,1,,,,\n" "6,,dwt,2,,,,\n" @@ -1301,7 +1301,7 @@ TEST_F(CtraceIntegTests, DoesNotSelectPcSamplingMarkersAsErrors) EXPECT_EQ(result.exitCode, 0) << result.stderrText; expectNotContains(result.stderrText, "[error]"); EXPECT_EQ(readTestTextFile(workDirectory() / "trace-pc-sample.SWO.csv"), - "cycles,stream,type,source,value,pc,address,note\n"); + "cycles,stream,type,index,value,pc,address,note\n"); EXPECT_TRUE(CtfTestSupport::readCtfRecords(workDirectory() / "trace-pc-sample.SWO.ctf" / "stream_0").empty()); EXPECT_FALSE(std::filesystem::exists(workDirectory() / "trace-pc-sample.traceanalysis.xml")); } @@ -1334,7 +1334,7 @@ TEST_F(CtraceIntegTests, RetainsProhibitedOnlyTraceDataWithoutGeneratingEmptyXml "--type", "pcsample"}); EXPECT_EQ(result.exitCode, 0) << result.stderrText; expectNotContains(result.stderrText, "[error]"); - auto expectedCsv = std::string("cycles,stream,type,source,value,pc,address,note\n6,") + + auto expectedCsv = std::string("cycles,stream,type,index,value,pc,address,note\n6,") + (formatted ? "1" : "") + ",pcsample,,,,,Trace prohibited\n"; if (formatted) { expectedCsv += ",0,info,,,,,4 bytes skipped for null source ID 0; first formatter group at raw offset 10\n"; @@ -1424,7 +1424,7 @@ TEST_F(CtraceIntegTests, ReportsInvalidDwtEventCounterWithoutPartialDecode) EXPECT_EQ(1, result.exitCode); expectContains(result.stderrText, "unsupported DWT event-counter payload: size 1, value 0x41"); expectContains(result.stderrText, "raw_offset=6"); - EXPECT_EQ("cycles,stream,type,source,value,pc,address,note\n" + EXPECT_EQ("cycles,stream,type,index,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", @@ -1445,7 +1445,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,address,note\n" + EXPECT_EQ("cycles,stream,type,index,value,pc,address,note\n" "0,,pmu,3,0x81,,,\n" "0,,itm,1,0x41,,,\n", readTestTextFile(workDirectory() / "Pmu.SWO.csv")); @@ -1471,7 +1471,7 @@ TEST_F(CtraceIntegTests, ReportsInvalidPmuEventCounterWithoutPartialDecode) EXPECT_EQ(1, result.exitCode); expectContains(result.stderrText, "unsupported PMU event-counter payload: size 1, value 0x0"); expectContains(result.stderrText, "raw_offset=6"); - EXPECT_EQ("cycles,stream,type,source,value,pc,address,note\n" + EXPECT_EQ("cycles,stream,type,index,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", @@ -1519,7 +1519,7 @@ TEST_F(CtraceIntegTests, ReportsDiagnosticsFromConsumedTraceRunReferences) type: itm pname: core stream: 1 - source: 0 + index: 0 info: - configured ITM channel zero - retained secondary setup information @@ -1893,7 +1893,7 @@ TEST_F(CtraceIntegTests, AppliesTypeAndStreamFiltersIndependentlyToEveryChannel) EXPECT_FALSE(std::filesystem::exists(workDirectory() / (base + ".traceanalysis.xml"))); const auto ctfDirectory = workDirectory() / (base + ".ctf"); if (fixture.channel == "SWO") { - EXPECT_EQ(csv, "cycles,stream,type,source,value,pc,address,note\n"); + EXPECT_EQ(csv, "cycles,stream,type,index,value,pc,address,note\n"); EXPECT_FALSE(std::filesystem::exists(ctfDirectory / "stream_0")); expectNotContains(readTestTextFile(ctfDirectory / "metadata"), "\nstream {"); continue; @@ -2388,7 +2388,7 @@ TEST_F(CtraceIntegTests, RetainsCsvAndUnfilteredAbortAfterIncompleteFormattedTai const auto lines = readTestLines(csvPath); ASSERT_GE(lines.size(), 2U); EXPECT_EQ(lines.back(), ",,error,,,,," + abortMessage) - << "the final abort is input-wide, has no cycles/source, and bypasses selection"; + << "the final abort is input-wide, has no cycles/index, and bypasses selection"; EXPECT_EQ(countOccurrences(csv, "decode aborted after processing "), 1U); const auto expectedPayload = selection.includesPayload ? "42,1,itm,1,0x41,,,\n" : ""; const auto semanticRows = traceCsvRows(csv); @@ -2399,7 +2399,7 @@ TEST_F(CtraceIntegTests, RetainsCsvAndUnfilteredAbortAfterIncompleteFormattedTai } EXPECT_EQ(countOccurrences(csv, ",1,error,"), selection.includesRouteError ? 1U : 0U); if (!selection.includesRouteError) { - EXPECT_EQ(semanticRows, "cycles,stream,type,source,value,pc,address,note\n" + + EXPECT_EQ(semanticRows, "cycles,stream,type,index,value,pc,address,note\n" + std::string(expectedPayload) + ",,error,,,,," + abortMessage + "\n"); } expectNotContains(csv, ",pcsample,"); diff --git a/tools/ctrace/test/integration/src/ValidateFixtureIntegrity.cmake b/tools/ctrace/test/integration/src/ValidateFixtureIntegrity.cmake index ac09b72ce..e51ebdc4a 100644 --- a/tools/ctrace/test/integration/src/ValidateFixtureIntegrity.cmake +++ b/tools/ctrace/test/integration/src/ValidateFixtureIntegrity.cmake @@ -30,26 +30,26 @@ endfunction() # Markdown documentation does not affect fixture identity and is excluded. set(fixture_entries "Arm-reset/Arm.SWO.raw|8c7ba2b90e42188517c7b793e8b7dd4030fa5455b7a38a2de15d8ca2b47995c9|131071" - "Arm-reset/Arm.ctrace-run.yml|da591d82cd7ac6d0d71bb5117de182647a647dad5aa19cdde7cb438249a690a3|695" - "Blinky+Arm/Blinky+Arm.SWO.csv|239af51acb46f18f858c99287e085b1273cd7dae3534ecd8a0401af1a86e0661|21534" + "Arm-reset/Arm.ctrace-run.yml|56ad6c463f4388338305218474bfee3a69201bf1b94a0391ba8ccc06d79889fb|691" + "Blinky+Arm/Blinky+Arm.SWO.csv|9951a4d21680b51640c32f0ca82abf0d97b09072060883e1db569a2513647a49|21533" "Blinky+Arm/Blinky+Arm.SWO.raw|f2de14241242697fa0948f1878850cce81575c404233c5c135aa68fc582dc72c|12288" "Blinky+Arm/Blinky+Arm.TB.raw|b0fccabe1a326ffe9fadf12d5c3a205d87628985e5e75a99da23c97d7f33d13b|4096" - "Blinky+Arm/Blinky+Arm.ctrace-run.yml|705afb070de7ab9b3f80e7b03a34d7a39f0d1468450ca52c488f48c2e8d4087c|1150" + "Blinky+Arm/Blinky+Arm.ctrace-run.yml|f15a4a11c382c66ebc371dd8cd826187daed1995ff41a7430247e065d0790bb2|1149" "Blinky+Arm/expected/Blinky+Arm.traceanalysis.xml|905fc1f458f87fe988aff698936d4b39b063770e4e2570ce5643f89996fa8655|6802" "Blinky+Arm/expected/Blinky+Arm.ctf/metadata|2769285fcf6cc9731cebc317ace247c29bf32403c7593d877a4c5956ded2cd69|8098" "Blinky+Arm/expected/Blinky+Arm.ctf/stream_0|2054d43163cf1ff8e921be92b397469e2eb75fb55f4f81c08c20382f38918ef6|65536" "TB-Trace/Blinky+Arm.TB.raw|aab49e56a07783b984fa7c6faeea101a51141423e66ba043dbd8d30702012639|4096" - "TB-Trace/Blinky+Arm.ctrace-run.yml|a9349fc5e8736d516326063904f384ccdb0acd1e26c054064bb04d0c054e9bb7|3010" + "TB-Trace/Blinky+Arm.ctrace-run.yml|74d7177f2d62a36a6079c2576506c3754ce961d1b860fe1f6c675f04a8464111|3009" "TB-Trace/regenerate_tb_trace.py|8ce6ca54cedc216c04a03587b8388003a8ab0563e6c79c39ebf436d9bfcd0050|10687" "TB-Trace/split_tb_trace.py|0ce65b99a2c51b2978cf0b790c653f5172715a1fa86521da8088fbd851ef9347|5361" - "formatted-synthetic/Synthetic.ctrace-run.yml|2952b0b4fa454dd7e958e155ed8edfe8a9af641b517137a3cedabcb1f2369ff2|2039" + "formatted-synthetic/Synthetic.ctrace-run.yml|7a4980237b850f471e80b5a30ff7076095514ce6f94e7171b4506d8e6d305047|2029" "trace-event/trace-event.ctrace-run.yml|a7b924d89854ac85e2751d1297ec78783fa12cb3fa54f5638691dd48d546a34e|89" "trace-event/trace-event.raw|97807dad2f69b1274df8960d3459426d1da4a6892d05e7623f3e16f06c5d85c8|19999" - "trace-match/trace-match.ctrace-run.yml|67461dd89a35d3f56decea32d90101161d75d90a5c35150826d93566e036681e|1104" + "trace-match/trace-match.ctrace-run.yml|6d3dcb3a46fe96a7202e61ab6c8f62895e7a26d42431b63c64dbbaf7adbe6571|1100" "trace-match/trace-match.raw|5cffb5803675dc02ecd5ed4939a42c660ad7cabd3542b8ca1506230e20d14a50|18" "trace-pc-sample/trace-pc-sample.ctrace-run.yml|e8875a9c5ec034ad4fae9ac823a3d442bbb4246af1ddd8d62093496bb8c52e1b|173" "trace-pc-sample/trace-pc-sample.raw|4e75b75e27be0dab3bdfdecb6331d7bfc1d51337d2fcad8ea2adfe5026992ad4|24" - "trace-run/Board.ctrace-run.yml|c93a98daef814d560a1afade236da6b212f30fc5f7d78fe03c140f46fddbf803|433" + "trace-run/Board.ctrace-run.yml|bc7d073e12c313dbb259d293ca597d0e0b6e4c41f9ada59eb5263726693bfa00|431" "trace-run/Minimal.ctrace-run.yml|12af0fdf3d19abfda197253fc60897f212e13f8c649d1d93ca9798019333b696|89" ) diff --git a/tools/ctrace/test/unit/src/control/TraceDirectoryJobTests.cpp b/tools/ctrace/test/unit/src/control/TraceDirectoryJobTests.cpp index fad9a2bd8..cda209e71 100644 --- a/tools/ctrace/test/unit/src/control/TraceDirectoryJobTests.cpp +++ b/tools/ctrace/test/unit/src/control/TraceDirectoryJobTests.cpp @@ -438,7 +438,7 @@ TEST(CtraceUnitTests, testTraceDirectoryDecodesAllExplicitInputsWithoutReplacing EXPECT_EQ(readTestTextFile(traceDir / "Multiple.ctf" / "sentinel"), "legacy bundle"); for (const auto& channel : channels) { const auto capture = "Multiple." + channel; - EXPECT_EQ(readTestTextFile(traceDir / (capture + ".csv")), "cycles,stream,type,source,value,pc,address,note\n"); + EXPECT_EQ(readTestTextFile(traceDir / (capture + ".csv")), "cycles,stream,type,index,value,pc,address,note\n"); EXPECT_TRUE(std::filesystem::is_regular_file(traceDir / (capture + ".ctf") / "metadata")); EXPECT_FALSE(std::filesystem::exists(traceDir / (capture + ".ctf") / "sentinel")); EXPECT_EQ(readTestTextFile(traceDir / (capture + ".traceanalysis.xml")), "xml sentinel"); @@ -747,7 +747,7 @@ TEST(CtraceUnitTests, testFileDecodeJobUsesInjectedSessionForFormattedInput) EXPECT_TRUE(sessionCreated); EXPECT_FALSE(diagnostics.containsMessage("formatted trace input is not enabled yet")); EXPECT_EQ(readTestTextFile(temporaryPath.path() / "formatted.TB.csv"), - "cycles,stream,type,source,value,pc,address,note\n"); + "cycles,stream,type,index,value,pc,address,note\n"); } TEST(CtraceUnitTests, testInputSelectionAndPreflightNeverConstructDecoder) @@ -795,8 +795,8 @@ static void expectGlobalDecodeAbort(const std::filesystem::path& csvPath, const { const auto lines = readTestLines(csvPath); ASSERT_GE(lines.size(), 2U); - EXPECT_EQ(lines.front(), "cycles,stream,type,source,value,pc,address,note"); - EXPECT_EQ(lines.back().find(",,error,,,,,"), 0U) << "abort must have no timestamp, stream, or source"; + EXPECT_EQ(lines.front(), "cycles,stream,type,index,value,pc,address,note"); + EXPECT_EQ(lines.back().find(",,error,,,,,"), 0U) << "abort must have no timestamp, stream, or index"; const auto prefix = "decode aborted after processing " + std::to_string(processed) + " input bytes; trace is incomplete: "; EXPECT_NE(lines.back().find(prefix), std::string::npos); @@ -981,7 +981,7 @@ TEST(CtraceUnitTests, testFileDecodeJobConsumesPreflightedHandleAfterPathReplace EXPECT_NO_THROW(job.run()); EXPECT_EQ(diagnostics.failureCount(), 0U); EXPECT_EQ(readTestTextFile(temporaryPath.path() / "retained.SWO.csv"), - "cycles,stream,type,source,value,pc,address,note\n" + "cycles,stream,type,index,value,pc,address,note\n" "0,,pcsample,,,0x08001234,,\n" "0,,itm,1,0x41,,,\n"); } diff --git a/tools/ctrace/test/unit/src/output/OutputRequirementsTests.cpp b/tools/ctrace/test/unit/src/output/OutputRequirementsTests.cpp index d46de9136..fac2422ff 100644 --- a/tools/ctrace/test/unit/src/output/OutputRequirementsTests.cpp +++ b/tools/ctrace/test/unit/src/output/OutputRequirementsTests.cpp @@ -273,7 +273,7 @@ TEST(CtraceUnitTests, testOutputRequirementsValidateDwtAddressRangeForCtfOnly) TEST(CtraceUnitTests, testOutputRequirementsRejectDwtComparatorOutsideCtfDomainOnly) { auto config = backendRequirementsConfig(); - config.references[0].sources = {4U}; + config.references[0].indices = {4U}; config.references[0].dataType = "unsigned"; CollectingDiagnosticSink diagnostics; @@ -541,7 +541,7 @@ TEST(CtraceUnitTests, testOutputPreflightRejectsAmbiguousRoutesForCtfOnly) const auto processorPlan = planOutputs(outputRequest(true, false), "captures/AmbiguousProcessors.SWO.raw", processorConfig, processorDiagnostics); ASSERT_TRUE(processorPlan.csv.has_value() && processorDiagnostics.events().empty()) - << "CSV must preserve raw stream/source values without consuming processor metadata"; + << "CSV must preserve raw stream/index values without consuming processor metadata"; allRequest.selection.streams = {1U}; CollectingDiagnosticSink selectedDiagnostics; diff --git a/tools/ctrace/test/unit/src/output/TraceOutputLifecycleTests.cpp b/tools/ctrace/test/unit/src/output/TraceOutputLifecycleTests.cpp index 9dfb48b05..75a49844e 100644 --- a/tools/ctrace/test/unit/src/output/TraceOutputLifecycleTests.cpp +++ b/tools/ctrace/test/unit/src/output/TraceOutputLifecycleTests.cpp @@ -125,7 +125,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,address,note\n") == 0U && + ASSERT_TRUE(contents.find("cycles,stream,type,index,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 7dd719f5e..72b0c40db 100644 --- a/tools/ctrace/test/unit/src/output/csv/CsvFileOutputTests.cpp +++ b/tools/ctrace/test/unit/src/output/csv/CsvFileOutputTests.cpp @@ -104,7 +104,7 @@ TEST(CtraceUnitTests, testCsvFileOutputCriteria) output.stop(); ASSERT_TRUE( - (readTestTextFile(outputPath.path()) == "cycles,stream,type,source,value,pc,address,note\n,2,itm,1,0x41,,,\n")) + (readTestTextFile(outputPath.path()) == "cycles,stream,type,index,value,pc,address,note\n,2,itm,1,0x41,,,\n")) << "CsvFileOutput criteria mismatch"; const TemporaryTestPath errorOutputPath("ctrace-filtered-errors.csv"); @@ -172,7 +172,7 @@ TEST(CtraceUnitTests, testCsvFileOutputMatchesSpecification) const auto lines = readTestLines(csvPath); ASSERT_TRUE(lines.size() == 6U) << "CSV specification row count mismatch"; - ASSERT_TRUE(lines[0] == "cycles,stream,type,source,value,pc,address,note") << "CSV specification header mismatch"; + ASSERT_TRUE(lines[0] == "cycles,stream,type,index,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"; @@ -197,7 +197,7 @@ TEST(CtraceUnitTests, testCsvFileOutputPreservesInterleavedRouteOrderAndOnlyWrit const auto lines = readTestLines(temporaryPath.path()); ASSERT_EQ(lines.size(), 5U); - EXPECT_EQ(lines[0], "cycles,stream,type,source,value,pc,address,note"); + EXPECT_EQ(lines[0], "cycles,stream,type,index,value,pc,address,note"); EXPECT_EQ(lines[1], ",7,itm,1,0x41,,,"); EXPECT_EQ(lines[2], ",2,itm,2,0x42,,,"); EXPECT_EQ(lines[3], ",7,itm,3,0x43,,,"); @@ -219,7 +219,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,address,note") << "CSV issue header mismatch"; + ASSERT_TRUE(lines[0] == "cycles,stream,type,index,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 599eec935..79156635d 100644 --- a/tools/ctrace/test/unit/src/output/csv/CsvRowMapperTests.cpp +++ b/tools/ctrace/test/unit/src/output/csv/CsvRowMapperTests.cpp @@ -56,7 +56,7 @@ TEST(CtraceUnitTests, testCsvRowMapperAndTraceEventSchema) ASSERT_TRUE(traceEventType(event) == expectedType) << "semantic TraceEvent type mapping mismatch"; } - ASSERT_TRUE(CsvRowMapper::header() == "cycles,stream,type,source,value,pc,address,note") + ASSERT_TRUE(CsvRowMapper::header() == "cycles,stream,type,index,value,pc,address,note") << "CSV schema header integration mismatch"; EXPECT_EQ(CsvRowMapper::row(TraceEvent{LocalTimestampTraceEvent{}}), ",,,,,,,") << "local timestamp control packets must not populate payload-specific CSV columns"; @@ -79,7 +79,7 @@ TEST(CtraceUnitTests, testCsvRowMapperAndTraceEventSchema) EXPECT_EQ(CsvRowMapper::row(TraceEvent{DwtDataTraceEvent{0U, 2U, 0U, AccessType::Write}}), ",,dwt,0,0x0000,,,"); EXPECT_EQ(CsvRowMapper::row(TraceEvent{DwtDataTraceEvent{0U, 4U, 0U, AccessType::Write}}), ",,dwt,0,0x00000000,,,"); ASSERT_TRUE(CsvRowMapper::row(TraceEvent{DwtMatchTraceEvent{2U}}) == ",,dwt,2,,,,") - << "CSV must expose a match only through its DWT comparator source"; + << "CSV must expose a match only through its DWT comparator index"; ASSERT_TRUE(CsvRowMapper::row(atCycle(TraceEvent{PcSampleTraceEvent{0x08001234U}}, 949339000U)) == "949339000,,pcsample,,,0x08001234,,") << "CSV PC-sample row mismatch"; diff --git a/tools/ctrace/test/unit/src/tracerun/CtraceRunMetaTests.cpp b/tools/ctrace/test/unit/src/tracerun/CtraceRunMetaTests.cpp index fdbb5ce23..e797bd2d5 100644 --- a/tools/ctrace/test/unit/src/tracerun/CtraceRunMetaTests.cpp +++ b/tools/ctrace/test/unit/src/tracerun/CtraceRunMetaTests.cpp @@ -94,14 +94,14 @@ TEST(CtraceUnitTests, testCtraceRunMetaRejectsInvalidReferences) const char* message; }; std::vector cases; - auto duplicateDwtSource = makeReference("dwt", std::nullopt, 1U, {1U, 1U}); - duplicateDwtSource.dataSetupIndex = 0U; - cases.push_back({duplicateDwtSource, "duplicate value in source array"}); + auto duplicateDwtIndex = makeReference("dwt", std::nullopt, 1U, {1U, 1U}); + duplicateDwtIndex.dataSetupIndex = 0U; + cases.push_back({duplicateDwtIndex, "duplicate value in index array"}); cases.push_back( {makeReference("itm", std::nullopt, 0U, {1U}), "stream must be a CoreSight ATB trace ID between 1 and 111"}); cases.push_back( {routeReference("event", "events#0", "core", 0U), "stream must be a CoreSight ATB trace ID between 1 and 111"}); - cases.push_back({makeReference("itm", std::nullopt, 1U, {32U}), "ITM source must be between 0 and 31"}); + cases.push_back({makeReference("itm", std::nullopt, 1U, {32U}), "ITM index must be between 0 and 31"}); for (const auto& testCase : cases) { TraceRunConfig config; @@ -119,7 +119,7 @@ TEST(CtraceUnitTests, testCtraceRunMetaRejectsInvalidReferences) EXPECT_TRUE(metaRejects(diagnosed, "stream must be a CoreSight ATB trace ID between 1 and 111")); diagnosed.references.front() = makeReference("itm", std::nullopt, 0U, {1U, 1U}); - diagnosed.references.front().error = {"producer rejected duplicate sources"}; + diagnosed.references.front().error = {"producer rejected duplicate indices"}; EXPECT_TRUE(metaRejects(diagnosed, "stream must be a CoreSight ATB trace ID between 1 and 111")); TraceRunConfig diagnosedBinding; @@ -699,7 +699,7 @@ TEST(CtraceUnitTests, testCtraceRunMetaValidatesFormattedRouteBindingsAndIds) "anchor must use reference type 'itm'")); EXPECT_TRUE(metaRejects(formattedConfig({routeReference("itm", "core/itm", "core", 1U, {32U})}), - "ITM source must be between 0 and 31")); + "ITM index must be between 0 and 31")); const auto adoptedProcessor = CtraceRunMeta::fromConfig(formattedConfig({ routeReference("itm", "itm", std::nullopt, 1U), diff --git a/tools/ctrace/test/unit/src/tracerun/TraceRunConfigReaderTests.cpp b/tools/ctrace/test/unit/src/tracerun/TraceRunConfigReaderTests.cpp index 65a2c61ea..9dfee48b5 100644 --- a/tools/ctrace/test/unit/src/tracerun/TraceRunConfigReaderTests.cpp +++ b/tools/ctrace/test/unit/src/tracerun/TraceRunConfigReaderTests.cpp @@ -116,13 +116,13 @@ TEST(CtraceUnitTests, TraceRunReaderParsesConsumedFields) pname: core0 type: itm stream: 2 - source: 1 + index: 1 label: Console - ref: core0/data#0 pname: core0 type: dwt stream: 2 - source: 0 + index: 0 address: 0x20000100 data-type: signed size: 1 @@ -156,27 +156,43 @@ TEST(CtraceUnitTests, TraceRunReaderParsesConsumedFields) EXPECT_EQ(dwt.label, std::optional("Current")); } -TEST(CtraceUnitTests, TraceRunReaderAcceptsScalarAndArraySourceNotation) +TEST(CtraceUnitTests, TraceRunReaderAcceptsScalarAndArrayIndexNotation) { TraceRunFixture file("ctrace-run-reader-consumed-fields-test"); const auto config = file.read(R"yml(ctrace-run: ctrace-refs: - ref: relevant/itm type: itm - source: [1, 2] + index: [1, 2] - ref: relevant/dwt-scalar type: dwt - source: 3 + index: 3 - ref: relevant/dwt-array type: dwt - source: [4, 5] + index: [4, 5] - ref: ignored/unsupported type: unsupported + source: [invalid] )yml"); ASSERT_EQ(config.references.size(), 3U); - EXPECT_TRUE(config.references[0].sources == std::vector({1U, 2U})); - EXPECT_TRUE(config.references[1].sources == std::vector({3U})); - EXPECT_TRUE(config.references[2].sources == std::vector({4U, 5U})); + EXPECT_TRUE(config.references[0].indices == std::vector({1U, 2U})); + EXPECT_TRUE(config.references[1].indices == std::vector({3U})); + EXPECT_TRUE(config.references[2].indices == std::vector({4U, 5U})); +} + +TEST(CtraceUnitTests, TraceRunReaderRejectsLegacySourceKeyEvenWithProducerErrors) +{ + TraceRunFixture file("ctrace-run-reader-legacy-source-test"); + for (const auto* type : {"itm", "dwt"}) { + for (const auto* fields : {"source: 1", "source: [1, 2]", "source: null", "source: 1, index: 2", + "index: 2, source: 1", "index: 2, source: null"}) { + for (const auto* diagnostic : {"", ", error: producer-error"}) { + const auto yaml = std::string("ctrace-run:\n ctrace-refs:\n - { type: ") + type + + ", ref: core/data#0, " + fields + diagnostic + " }\n"; + expectReadError(file, yaml, "'source' is no longer supported; use 'index' in 'ctrace-refs' entries"); + } + } + } } TEST(CtraceUnitTests, TraceRunReaderParsesTraceFormatDeclaration) @@ -253,7 +269,7 @@ TEST(CtraceUnitTests, TraceRunReaderUsesReferencedSetupSizeAsFallback) ctrace-refs: - ref: data#0 type: dwt - source: 0 + index: 0 data-type: signed )yml"); @@ -275,7 +291,7 @@ TEST(CtraceUnitTests, TraceRunReaderIgnoresUnsupportedMetadataNames) ctrace-refs: - ref: data#0 type: dwt - source: 0 + index: 0 symbol-address: 0x20000100 )yml"); @@ -293,7 +309,7 @@ TEST(CtraceUnitTests, TraceRunReaderDefersMalformedDwtMetadataToOutputPlanning) ctrace-refs: - ref: data#0 type: dwt - source: 0 + index: 0 address: [] data-type: [] size: [] @@ -322,7 +338,7 @@ TEST(CtraceUnitTests, TraceRunReaderAcceptsProcessorItmReferenceWithoutEnabledCh )yml"); ASSERT_EQ(config.references.size(), 1U); - EXPECT_TRUE(config.references.front().sources.empty()); + EXPECT_TRUE(config.references.front().indices.empty()); const auto meta = CtraceRunMeta::fromConfig(config); EXPECT_TRUE(sourceCount(meta) == 0U); @@ -353,23 +369,23 @@ TEST(CtraceUnitTests, TraceRunReaderRejectsMalformedReferenceContainers) expectReadError(file, "ctrace-run:\n ctrace-refs: {}\n", "'ctrace-refs' must be an array"); expectReadError(file, "ctrace-run:\n ctrace-refs: []\n ctrace-refs: []\n", "map keys must be unique"); expectReadError(file, "ctrace-run:\n ctrace-refs: [invalid]\n", "each 'ctrace-refs' entry must be a map"); - expectReadError(file, "ctrace-run:\n ctrace-refs:\n - { ref: core/itm, source: 1 }\n", + expectReadError(file, "ctrace-run:\n ctrace-refs:\n - { ref: core/itm, index: 1 }\n", "missing required 'type' scalar"); - expectReadError(file, "ctrace-run:\n ctrace-refs:\n - { type: [], ref: core/itm, source: 1 }\n", + expectReadError(file, "ctrace-run:\n ctrace-refs:\n - { type: [], ref: core/itm, index: 1 }\n", "missing required 'type' scalar"); - expectReadError(file, "ctrace-run:\n ctrace-refs:\n - { type: null, ref: core/itm, source: 1 }\n", + expectReadError(file, "ctrace-run:\n ctrace-refs:\n - { type: null, ref: core/itm, index: 1 }\n", "missing required 'type' scalar"); - expectReadError(file, "ctrace-run:\n ctrace-refs:\n - { type: '', ref: core/itm, source: 1 }\n", + expectReadError(file, "ctrace-run:\n ctrace-refs:\n - { type: '', ref: core/itm, index: 1 }\n", "missing required 'type' scalar"); - expectReadError(file, "ctrace-run:\n ctrace-refs:\n - type: itm\n source: 1\n", + expectReadError(file, "ctrace-run:\n ctrace-refs:\n - type: itm\n index: 1\n", "missing required 'ref' scalar"); - expectReadError(file, "ctrace-run:\n ctrace-refs:\n - { type: itm, ctrace-ref: core/itm, source: 1 }\n", + expectReadError(file, "ctrace-run:\n ctrace-refs:\n - { type: itm, ctrace-ref: core/itm, index: 1 }\n", "missing required 'ref' scalar"); - expectReadError(file, "ctrace-run:\n ctrace-refs:\n - { type: itm, ref: [], source: 1 }\n", + expectReadError(file, "ctrace-run:\n ctrace-refs:\n - { type: itm, ref: [], index: 1 }\n", "missing required 'ref' scalar"); - expectReadError(file, "ctrace-run:\n ctrace-refs:\n - { type: itm, ref: null, source: 1 }\n", + expectReadError(file, "ctrace-run:\n ctrace-refs:\n - { type: itm, ref: null, index: 1 }\n", "missing required 'ref' scalar"); - expectReadError(file, "ctrace-run:\n ctrace-refs:\n - { type: itm, ref: '', source: 1 }\n", + expectReadError(file, "ctrace-run:\n ctrace-refs:\n - { type: itm, ref: '', index: 1 }\n", "missing required 'ref' scalar"); } @@ -382,18 +398,18 @@ TEST(CtraceUnitTests, TraceRunReaderRejectsMalformedReferenceRoutes) const char* error; }; constexpr Case cases[] = { - {"pname: []\n source: 1", "'pname' must be a scalar string"}, - {"stream: []\n source: 1", "'stream' must be a scalar unsigned integer"}, - {"source: {}", "'source' must be an array"}, - {"source: 0x", "'source' must be an unsigned integer"}, - {"source: -1", "'source' must be an unsigned integer in range"}, - {"source: 4294967296", "'source' must be an unsigned integer in range"}, - {"source: [1, {}]", "each 'source' entry must be an unsigned integer"}, - {"source: [1, '']", "each 'source' entry must be an unsigned integer"}, - {"source: 1\n source: 2", "map keys must be unique"}, - {"source: 1\n info: {}", "'info' must be a string or list of strings"}, - {"source: 1\n warning: [valid, {}]", "each 'warning' entry must be a string"}, - {"source: 1\n error: [valid, []]", "each 'error' entry must be a string"}, + {"pname: []\n index: 1", "'pname' must be a scalar string"}, + {"stream: []\n index: 1", "'stream' must be a scalar unsigned integer"}, + {"index: {}", "'index' must be an array"}, + {"index: 0x", "'index' must be an unsigned integer"}, + {"index: -1", "'index' must be an unsigned integer in range"}, + {"index: 4294967296", "'index' must be an unsigned integer in range"}, + {"index: [1, {}]", "each 'index' entry must be an unsigned integer"}, + {"index: [1, '']", "each 'index' entry must be an unsigned integer"}, + {"index: 1\n index: 2", "map keys must be unique"}, + {"index: 1\n info: {}", "'info' must be a string or list of strings"}, + {"index: 1\n warning: [valid, {}]", "each 'warning' entry must be a string"}, + {"index: 1\n error: [valid, []]", "each 'error' entry must be a string"}, }; for (const auto& testCase : cases) { const auto yaml = std::string("ctrace-run:\n ctrace-refs:\n - type: itm\n ref: core/itm\n ") + @@ -418,7 +434,7 @@ TEST(CtraceUnitTests, TraceRunReaderIgnoresNullAndNonScalarOptionalReferenceValu ref: data#0 pname: null stream: null - source: null + index: null address: null data-type: null size: null @@ -428,7 +444,7 @@ TEST(CtraceUnitTests, TraceRunReaderIgnoresNullAndNonScalarOptionalReferenceValu error: null - type: itm ref: itm - source: [1, null] + index: [1, null] label: null info: [note, null] warning: [null] @@ -440,7 +456,7 @@ TEST(CtraceUnitTests, TraceRunReaderIgnoresNullAndNonScalarOptionalReferenceValu const auto& emptyRoute = config.references[0]; EXPECT_FALSE(emptyRoute.processorName.has_value()); EXPECT_FALSE(emptyRoute.stream.has_value()); - EXPECT_TRUE(emptyRoute.sources.empty()); + EXPECT_TRUE(emptyRoute.indices.empty()); EXPECT_FALSE(emptyRoute.address.has_value()); EXPECT_FALSE(emptyRoute.addressError.has_value()); EXPECT_FALSE(emptyRoute.dataType.has_value()); @@ -452,7 +468,7 @@ TEST(CtraceUnitTests, TraceRunReaderIgnoresNullAndNonScalarOptionalReferenceValu EXPECT_TRUE(emptyRoute.warning.empty()); EXPECT_TRUE(emptyRoute.error.empty()); - EXPECT_EQ(config.references[1].sources, (std::vector{1U})); + EXPECT_EQ(config.references[1].indices, (std::vector{1U})); EXPECT_FALSE(config.references[1].label.has_value()); EXPECT_EQ(config.references[1].info, (std::vector{"note"})); EXPECT_TRUE(config.references[1].warning.empty()); @@ -467,10 +483,10 @@ TEST(CtraceUnitTests, TraceRunReaderPreservesDiagnosticReferences) - { type: event, ref: core/events#0, pname: core0, stream: 1, info: [note, detail], warning: [] } - { type: pmu, ref: core/events#1, pname: core1, stream: 2, warning: [warning] } - { type: pcsample, ref: core/pcsampling, pname: null, stream: 3, error: unavailable } - - { type: dwt, ref: core/data#, source: 0, address: invalid, error: [diagnostic, detail] } + - { type: dwt, ref: core/data#, index: 0, address: invalid, error: [diagnostic, detail] } - { type: dwt, ref: core/notdata#2, error: null } - - { type: itm, ref: core/itm0, source: 0, error: disabled } - - { type: itm, ref: core/itm1, source: 1, error: usable, label: null } + - { type: itm, ref: core/itm0, index: 0, error: disabled } + - { type: itm, ref: core/itm1, index: 1, error: usable, label: null } - { type: exception, ref: core/exceptions, pname: core0, stream: 4, error: exception-error } - { type: global_ts, ref: core/timesync, pname: core0, stream: 5, warning: global-warning } - { type: overflow, ref: core/overflow, pname: core0, stream: 6, info: overflow-info } @@ -491,7 +507,7 @@ TEST(CtraceUnitTests, TraceRunReaderPreservesDiagnosticReferences) EXPECT_TRUE(config.references[3].addressError.has_value()); EXPECT_FALSE(config.references[3].dataSetupIndex.has_value()); EXPECT_TRUE(config.references[4].error.empty()); - EXPECT_TRUE(config.references[5].sources == std::vector{0U}); + EXPECT_TRUE(config.references[5].indices == std::vector{0U}); EXPECT_FALSE(config.references[6].label.has_value()); EXPECT_EQ(config.references[7].error, (std::vector{"exception-error"})); EXPECT_EQ(config.references[7].stream, std::optional(4U)); @@ -501,23 +517,23 @@ TEST(CtraceUnitTests, TraceRunReaderPreservesDiagnosticReferences) EXPECT_EQ(config.references[9].stream, std::optional(6U)); } -TEST(CtraceUnitTests, TraceRunReaderRetainsSourceLessBindingsWithProducerErrors) +TEST(CtraceUnitTests, TraceRunReaderRetainsBindingsWithoutIndicesWithProducerErrors) { - TraceRunFixture file("ctrace-run-reader-source-less-bindings-test"); + TraceRunFixture file("ctrace-run-reader-bindings-without-indices-test"); const auto config = file.read(R"yml(ctrace-run: trace-format: formatted ctrace-refs: - { type: itm, ref: core/itm, pname: core, stream: 7, error: itm-error } - { type: itm, ref: core/timestamps, pname: core, stream: 7, error: normative-error } - { type: dwt, ref: core/timestamps, pname: core, stream: 7, error: transitional-error } - - { type: itm, ref: core/itm, pname: core, stream: 7, source: [invalid], error: source-error } + - { type: itm, ref: core/itm, pname: core, stream: 7, index: [invalid], error: index-error } )yml"); ASSERT_EQ(config.references.size(), 4U); for (const auto& reference : config.references) { EXPECT_EQ(reference.processorName, std::optional("core")); EXPECT_EQ(reference.stream, std::optional(7U)); - EXPECT_TRUE(reference.sources.empty()); + EXPECT_TRUE(reference.indices.empty()); ASSERT_EQ(reference.error.size(), 1U); } const auto meta = CtraceRunMeta::fromConfig(config); @@ -635,7 +651,7 @@ TEST(CtraceUnitTests, TraceRunReaderRejectsMalformedConsumedSetups) expectReadError(file, R"yml(ctrace-run: ctrace-refs: - - { type: dwt, ref: core/data#0, source: 0 } + - { type: dwt, ref: core/data#0, index: 0 } ctrace-setup: - pname: [] data: [{}] @@ -681,15 +697,15 @@ TEST(CtraceUnitTests, TraceRunReaderParsesReferencedDataVariants) TraceRunFixture file("ctrace-run-reader-data-variants-test"); const auto config = file.read(R"yml(ctrace-run: ctrace-refs: - - { type: dwt, ref: core0/data#1, pname: core0, source: 0 } - - { type: dwt, ref: core0/data#2, pname: core0, source: 1 } - - { type: dwt, ref: core0/data#3, pname: core0, source: 2 } - - { type: dwt, ref: core0/data#4, pname: core0, source: 3 } - - { type: dwt, ref: core0/data#5, pname: core0, source: 4 } - - { type: dwt, ref: core1/data#0, pname: core1, source: 0 } - - { type: dwt, ref: core2/data#9, pname: core2, source: 0 } - - { type: dwt, ref: data#0, source: 6 } - - { type: dwt, ref: data#9, source: 7 } + - { type: dwt, ref: core0/data#1, pname: core0, index: 0 } + - { type: dwt, ref: core0/data#2, pname: core0, index: 1 } + - { type: dwt, ref: core0/data#3, pname: core0, index: 2 } + - { type: dwt, ref: core0/data#4, pname: core0, index: 3 } + - { type: dwt, ref: core0/data#5, pname: core0, index: 4 } + - { type: dwt, ref: core1/data#0, pname: core1, index: 0 } + - { type: dwt, ref: core2/data#9, pname: core2, index: 0 } + - { type: dwt, ref: data#0, index: 6 } + - { type: dwt, ref: data#9, index: 7 } ctrace-setup: - pname: core0 data: @@ -731,7 +747,7 @@ TEST(CtraceUnitTests, TraceRunReaderParsesReferencedDataVariants) EXPECT_TRUE(config.setups[4].data[0].present); expectReadError(file, R"yml(ctrace-run: - ctrace-refs: [{ type: dwt, ref: data#0, source: 0 }] + ctrace-refs: [{ type: dwt, ref: data#0, index: 0 }] ctrace-setup: - data: [{ size: 1, size: 2 }] )yml", @@ -748,7 +764,7 @@ TEST(CtraceUnitTests, TraceRunReaderDefersMalformedDataContainerWithoutIndexSize data: invalid ctrace-refs: - { type: dwt, ref: core/data#)yml") + - index + ", pname: core, source: 0 }\n"); + index + ", pname: core, index: 0 }\n"); ASSERT_EQ(config.setups.size(), 1U); EXPECT_TRUE(config.setups.front().data.empty()); @@ -769,7 +785,7 @@ TEST(CtraceUnitTests, TraceRunReaderPropagatesMalformedDataEntryToSourceMetadata data: [invalid] ctrace-refs: - { type: itm, ref: core/itm, pname: core, stream: 1 } - - { type: dwt, ref: core/data#0, pname: core, stream: 1, source: 0 } + - { type: dwt, ref: core/data#0, pname: core, stream: 1, index: 0 } )yml"); ASSERT_EQ(config.setups.size(), 1U); @@ -791,7 +807,7 @@ TEST(CtraceUnitTests, TraceRunReaderDoesNotCreateSetupMetadataFromNullDataEntrie - pname: core data: [null] ctrace-refs: - - { type: dwt, ref: core/data#0, pname: core, stream: 1, source: 0 } + - { type: dwt, ref: core/data#0, pname: core, stream: 1, index: 0 } )yml"); EXPECT_TRUE(config.setups.empty()); @@ -807,7 +823,7 @@ TEST(CtraceUnitTests, TraceRunReaderDoesNotCreateSetupMetadataFromNullDataEntrie - pname: other timestamps: null ctrace-refs: - - { type: dwt, ref: core/data#0, pname: core, source: 0 } + - { type: dwt, ref: core/data#0, pname: core, index: 0 } )yml"); ASSERT_EQ(otherProcessor.setups.size(), 1U); EXPECT_EQ(otherProcessor.setups.front().processorName, std::optional("other")); @@ -819,7 +835,7 @@ TEST(CtraceUnitTests, TraceRunReaderDoesNotCreateSetupMetadataFromNullDataEntrie ctrace-setup: - data: [{ size: 2 }] ctrace-refs: - - { type: dwt, ref: data#invalid, source: 0 } + - { type: dwt, ref: data#invalid, index: 0 } )yml"); ASSERT_EQ(malformedIndex.references.size(), 1U); EXPECT_FALSE(malformedIndex.references.front().dataSetupIndex.has_value()); @@ -830,7 +846,7 @@ TEST(CtraceUnitTests, TraceRunReaderDoesNotCreateSetupMetadataFromNullDataEntrie - pname: core data: invalid-but-irrelevant ctrace-refs: - - { type: dwt, ref: other/data#0, source: 0 } + - { type: dwt, ref: other/data#0, index: 0 } )yml"); ASSERT_EQ(foreignProcessor.setups.size(), 1U); EXPECT_FALSE(foreignProcessor.setups.front().dataError.has_value()); @@ -854,9 +870,9 @@ TEST(CtraceUnitTests, TraceRunReaderSkipsEmptyActiveSetupsAndNullDataEntries) - pname: core data: [null, {}, null, { size: 2 }] ctrace-refs: - - { type: dwt, ref: core/data#0, pname: core, source: 2 } - - { type: dwt, ref: core/data#1, pname: core, source: 0 } - - { type: dwt, ref: core/data#3, pname: core, source: 1 } + - { type: dwt, ref: core/data#0, pname: core, index: 2 } + - { type: dwt, ref: core/data#1, pname: core, index: 0 } + - { type: dwt, ref: core/data#3, pname: core, index: 1 } )yml"); ASSERT_EQ(config.setups.size(), 1U); diff --git a/tools/ctrace/test/unit/support/TraceRunTestSupport.h b/tools/ctrace/test/unit/support/TraceRunTestSupport.h index a405e9fcc..b85aadef1 100644 --- a/tools/ctrace/test/unit/support/TraceRunTestSupport.h +++ b/tools/ctrace/test/unit/support/TraceRunTestSupport.h @@ -20,14 +20,14 @@ namespace TraceRunTestSupport { /** @brief Creates a trace-run reference with common test defaults. */ inline TraceRunReference makeReference(std::string type, std::optional processorName, - std::optional stream, std::vector sources, + std::optional stream, std::vector indices, std::string ref = "route") { TraceRunReference reference; reference.type = std::move(type); reference.processorName = std::move(processorName); reference.stream = stream; - reference.sources = std::move(sources); + reference.indices = std::move(indices); reference.ref = std::move(ref); return reference; } From 9aa0bf423d08ec73f81772beaa0c95a4da921ab1 Mon Sep 17 00:00:00 2001 From: Thorsten de Buhr Date: Thu, 1 Oct 2026 16:33:34 +0200 Subject: [PATCH 2/5] test(ctrace): remove legacy source rejection test --- .../src/tracerun/TraceRunConfigReaderTests.cpp | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/tools/ctrace/test/unit/src/tracerun/TraceRunConfigReaderTests.cpp b/tools/ctrace/test/unit/src/tracerun/TraceRunConfigReaderTests.cpp index 9dfee48b5..5d9df43f4 100644 --- a/tools/ctrace/test/unit/src/tracerun/TraceRunConfigReaderTests.cpp +++ b/tools/ctrace/test/unit/src/tracerun/TraceRunConfigReaderTests.cpp @@ -180,21 +180,6 @@ TEST(CtraceUnitTests, TraceRunReaderAcceptsScalarAndArrayIndexNotation) EXPECT_TRUE(config.references[2].indices == std::vector({4U, 5U})); } -TEST(CtraceUnitTests, TraceRunReaderRejectsLegacySourceKeyEvenWithProducerErrors) -{ - TraceRunFixture file("ctrace-run-reader-legacy-source-test"); - for (const auto* type : {"itm", "dwt"}) { - for (const auto* fields : {"source: 1", "source: [1, 2]", "source: null", "source: 1, index: 2", - "index: 2, source: 1", "index: 2, source: null"}) { - for (const auto* diagnostic : {"", ", error: producer-error"}) { - const auto yaml = std::string("ctrace-run:\n ctrace-refs:\n - { type: ") + type + - ", ref: core/data#0, " + fields + diagnostic + " }\n"; - expectReadError(file, yaml, "'source' is no longer supported; use 'index' in 'ctrace-refs' entries"); - } - } - } -} - TEST(CtraceUnitTests, TraceRunReaderParsesTraceFormatDeclaration) { TraceRunFixture file("ctrace-run-reader-trace-format-test"); From d8842cf63decd389468679b0e953d24f3cf9c6ec Mon Sep 17 00:00:00 2001 From: Thorsten de Buhr Date: Thu, 1 Oct 2026 16:38:43 +0200 Subject: [PATCH 3/5] fix(ctrace): ignore unconsumed YAML fields --- tools/ctrace/README.md | 4 +-- tools/ctrace/docs/constraints.md | 3 +- tools/ctrace/src/model/MessageCatalog.inc | 1 - .../src/tracerun/YmlTraceRunConfigReader.cpp | 4 --- .../src/tracerun/YmlTraceRunConfigReader.h | 1 + .../tracerun/TraceRunConfigReaderTests.cpp | 31 ++++++++++++++++--- 6 files changed, 30 insertions(+), 14 deletions(-) diff --git a/tools/ctrace/README.md b/tools/ctrace/README.md index 78e182274..b9dda9af8 100644 --- a/tools/ctrace/README.md +++ b/tools/ctrace/README.md @@ -56,8 +56,8 @@ CSV and CTF bundle, using `.` 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. Replace legacy -`source:` keys with `index:` in `ctrace-refs`. The fourth CSV column is also named `index`; its values are unchanged. +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 diff --git a/tools/ctrace/docs/constraints.md b/tools/ctrace/docs/constraints.md index 9b06779d8..ee656b017 100644 --- a/tools/ctrace/docs/constraints.md +++ b/tools/ctrace/docs/constraints.md @@ -28,8 +28,7 @@ configuration with trace communication, separate from trace-source setup. `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 `index` values is valid and contributes no source events. The `index` field accepts a scalar or - array of ITM channel or DWT comparator numbers. Legacy `source` fields on ITM/DWT references are rejected with an - explicit migration error. + 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. diff --git a/tools/ctrace/src/model/MessageCatalog.inc b/tools/ctrace/src/model/MessageCatalog.inc index 4d35a4d8a..b138434dc 100644 --- a/tools/ctrace/src/model/MessageCatalog.inc +++ b/tools/ctrace/src/model/MessageCatalog.inc @@ -518,7 +518,6 @@ CTRACE_MESSAGE(YamlRootMissing, "missing top-level 'ctrace-run' node", nullptr) CTRACE_MESSAGE(YamlRootMapInvalid, "top-level 'ctrace-run' node must be a map", nullptr) CTRACE_MESSAGE(TraceFormatInvalid, "'trace-format' must be 'unformatted' or 'formatted'", nullptr) CTRACE_MESSAGE(ReferenceArrayMissing, "missing required 'ctrace-refs' array", nullptr) -CTRACE_MESSAGE(ReferenceLegacySourceUnsupported, "'source' is no longer supported; use 'index' in 'ctrace-refs' entries", nullptr) 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) diff --git a/tools/ctrace/src/tracerun/YmlTraceRunConfigReader.cpp b/tools/ctrace/src/tracerun/YmlTraceRunConfigReader.cpp index 059446cfb..2c3cb9728 100644 --- a/tools/ctrace/src/tracerun/YmlTraceRunConfigReader.cpp +++ b/tools/ctrace/src/tracerun/YmlTraceRunConfigReader.cpp @@ -403,10 +403,6 @@ static std::optional parseReference(const std::string& path, reference.processorName = processorNameAttribute(path, element); reference.stream = parseStream(); if (TraceRunSchema::supportsSource(reference.type)) { - const auto legacySource = childNode(element, "source"); - if (legacySource) { - fail(path, legacySource, formatMessage(MessageId::ReferenceLegacySourceUnsupported)); - } try { reference.indices = parseIndices(path, element); } catch (const std::runtime_error&) { diff --git a/tools/ctrace/src/tracerun/YmlTraceRunConfigReader.h b/tools/ctrace/src/tracerun/YmlTraceRunConfigReader.h index bffa321cf..4514fec42 100644 --- a/tools/ctrace/src/tracerun/YmlTraceRunConfigReader.h +++ b/tools/ctrace/src/tracerun/YmlTraceRunConfigReader.h @@ -18,6 +18,7 @@ class YmlTraceRunConfigReader final : public TraceRunConfigReader { public: /** * @brief Reads and validates the ctrace-relevant YAML fields. + * Fields not consumed by ctrace are ignored. * @param path YAML trace-run configuration path. * @return Parsed processors, trace sources, and setup metadata. * @throws std::runtime_error If YAML syntax or required fields are invalid. diff --git a/tools/ctrace/test/unit/src/tracerun/TraceRunConfigReaderTests.cpp b/tools/ctrace/test/unit/src/tracerun/TraceRunConfigReaderTests.cpp index 5d9df43f4..94181eb57 100644 --- a/tools/ctrace/test/unit/src/tracerun/TraceRunConfigReaderTests.cpp +++ b/tools/ctrace/test/unit/src/tracerun/TraceRunConfigReaderTests.cpp @@ -172,7 +172,7 @@ TEST(CtraceUnitTests, TraceRunReaderAcceptsScalarAndArrayIndexNotation) index: [4, 5] - ref: ignored/unsupported type: unsupported - source: [invalid] + index: [invalid] )yml"); ASSERT_EQ(config.references.size(), 3U); EXPECT_TRUE(config.references[0].indices == std::vector({1U, 2U})); @@ -265,23 +265,44 @@ TEST(CtraceUnitTests, TraceRunReaderUsesReferencedSetupSizeAsFallback) EXPECT_EQ(sourceAt(meta, 0).dataSize, 2U); } -TEST(CtraceUnitTests, TraceRunReaderIgnoresUnsupportedMetadataNames) +TEST(CtraceUnitTests, TraceRunReaderIgnoresUnconsumedFields) { - TraceRunFixture file("ctrace-run-reader-unsupported-data-metadata-test"); - const auto config = file.read(R"yml(ctrace-run: + TraceRunFixture file("ctrace-run-reader-unconsumed-fields-test"); + const auto config = file.read(R"yml(unconsumed-document-data: + ctrace-run: invalid +ctrace-run: + unconsumed: { ctrace-refs: invalid } ctrace-setup: - - data: + - unconsumed: { pname: [] } + timestamps: + clock: 1000000 + unconsumed: { clock: invalid } + itm: + enable: 3 + unconsumed: [invalid] + data: - symbol-type: signed int symbol-size: 1 + unconsumed: { size: [] } ctrace-refs: - ref: data#0 type: dwt index: 0 symbol-address: 0x20000100 + unconsumed: { index: [], stream: [] } + - type: unsupported + ref: [] + index: invalid )yml"); + ASSERT_EQ(config.references.size(), 1U); + ASSERT_EQ(config.setups.size(), 1U); const auto meta = CtraceRunMeta::fromConfig(config); + ASSERT_EQ(meta.routes().size(), 1U); + EXPECT_EQ(meta.routes().front().timestampClockHz, std::optional(1000000U)); + EXPECT_EQ(meta.routes().front().itmEnableMask, 3U); ASSERT_EQ(sourceCount(meta), 1U); + EXPECT_EQ(sourceAt(meta, 0).source, 0U); EXPECT_FALSE(sourceAt(meta, 0).address.has_value()); EXPECT_EQ(sourceAt(meta, 0).dataType, "unsigned"); EXPECT_EQ(sourceAt(meta, 0).dataSize, 4U); From 97c2fd4288cc7761a904649b20ee432aae4b81d3 Mon Sep 17 00:00:00 2001 From: Thorsten de Buhr Date: Thu, 1 Oct 2026 16:53:43 +0200 Subject: [PATCH 4/5] fix(ctrace): validate YAML metadata only when consumed --- tools/ctrace/src/tracerun/CtraceRunMeta.cpp | 16 ++-- tools/ctrace/src/tracerun/TraceRunConfig.h | 1 + .../src/tracerun/YmlTraceRunConfigReader.cpp | 19 +++-- .../tracerun/TraceRunConfigReaderTests.cpp | 84 ++++++++++++++----- 4 files changed, 89 insertions(+), 31 deletions(-) diff --git a/tools/ctrace/src/tracerun/CtraceRunMeta.cpp b/tools/ctrace/src/tracerun/CtraceRunMeta.cpp index ff9315200..ff5691fde 100644 --- a/tools/ctrace/src/tracerun/CtraceRunMeta.cpp +++ b/tools/ctrace/src/tracerun/CtraceRunMeta.cpp @@ -182,9 +182,8 @@ 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() && @@ -192,7 +191,7 @@ static std::string itmEnableError(const TraceRunConfig& config, const TraceRunSe 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. */ @@ -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)) { @@ -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()) { @@ -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( @@ -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)); } } } diff --git a/tools/ctrace/src/tracerun/TraceRunConfig.h b/tools/ctrace/src/tracerun/TraceRunConfig.h index e6e12b078..0ff1ea13e 100644 --- a/tools/ctrace/src/tracerun/TraceRunConfig.h +++ b/tools/ctrace/src/tracerun/TraceRunConfig.h @@ -236,6 +236,7 @@ struct TraceRunTimestampSetup { std::optional timestampPrescaler = std::nullopt; std::optional clockError = std::nullopt; std::size_t line = 0U; + std::optional prescalerError = std::nullopt; }; /** @brief Stores size metadata copied from one DWT data setup. */ diff --git a/tools/ctrace/src/tracerun/YmlTraceRunConfigReader.cpp b/tools/ctrace/src/tracerun/YmlTraceRunConfigReader.cpp index 2c3cb9728..b04bf77aa 100644 --- a/tools/ctrace/src/tracerun/YmlTraceRunConfigReader.cpp +++ b/tools/ctrace/src/tracerun/YmlTraceRunConfigReader.cpp @@ -411,6 +411,9 @@ static std::optional parseReference(const std::string& path, } } } + if (!TraceRunSchema::isUsableReference(reference)) { + return reference; + } if (reference.type == "dwt") { reference.address = deferredReferenceUnsignedAttribute( path, element, "address", std::numeric_limits::max(), reference.addressError); @@ -473,12 +476,16 @@ static std::optional parseTimestampSetup(const std::stri std::numeric_limits::max(), timestamps.clockError); } if (childContainer(timestampsNode, "itm-prescaler")) { - fail(path, timestampsNode, configFieldMessage("timestamps.itm-prescaler", ConfigFieldProblem::ScalarUnsigned)); - } - const auto prescaler = - optionalUnsignedAttribute(path, timestampsNode, "itm-prescaler", std::numeric_limits::max()); - if (prescaler.has_value()) { - timestamps.timestampPrescaler = static_cast(*prescaler); + timestamps.prescalerError = + errorMessage(path, timestampsNode, + configFieldMessage("timestamps.itm-prescaler", ConfigFieldProblem::ScalarUnsigned)); + } else { + const auto prescaler = deferredUnsignedAttribute(path, timestampsNode, "itm-prescaler", + std::numeric_limits::max(), + timestamps.prescalerError); + if (prescaler.has_value()) { + timestamps.timestampPrescaler = static_cast(*prescaler); + } } return timestamps; } diff --git a/tools/ctrace/test/unit/src/tracerun/TraceRunConfigReaderTests.cpp b/tools/ctrace/test/unit/src/tracerun/TraceRunConfigReaderTests.cpp index 94181eb57..118e2e970 100644 --- a/tools/ctrace/test/unit/src/tracerun/TraceRunConfigReaderTests.cpp +++ b/tools/ctrace/test/unit/src/tracerun/TraceRunConfigReaderTests.cpp @@ -385,8 +385,6 @@ TEST(CtraceUnitTests, TraceRunReaderRejectsMalformedReferenceContainers) "missing required 'type' scalar"); expectReadError(file, "ctrace-run:\n ctrace-refs:\n - type: itm\n index: 1\n", "missing required 'ref' scalar"); - expectReadError(file, "ctrace-run:\n ctrace-refs:\n - { type: itm, ctrace-ref: core/itm, index: 1 }\n", - "missing required 'ref' scalar"); expectReadError(file, "ctrace-run:\n ctrace-refs:\n - { type: itm, ref: [], index: 1 }\n", "missing required 'ref' scalar"); expectReadError(file, "ctrace-run:\n ctrace-refs:\n - { type: itm, ref: null, index: 1 }\n", @@ -451,7 +449,7 @@ TEST(CtraceUnitTests, TraceRunReaderIgnoresNullAndNonScalarOptionalReferenceValu - type: itm ref: itm index: [1, null] - label: null + label: [] info: [note, null] warning: [null] error: [null] @@ -489,11 +487,18 @@ TEST(CtraceUnitTests, TraceRunReaderPreservesDiagnosticReferences) - { type: event, ref: core/events#0, pname: core0, stream: 1, info: [note, detail], warning: [] } - { type: pmu, ref: core/events#1, pname: core1, stream: 2, warning: [warning] } - { type: pcsample, ref: core/pcsampling, pname: null, stream: 3, error: unavailable } - - { type: dwt, ref: core/data#, index: 0, address: invalid, error: [diagnostic, detail] } + - type: dwt + ref: core/data# + index: 0 + address: invalid + size: [] + data-type: [] + label: unused + error: [diagnostic, detail] - { type: dwt, ref: core/notdata#2, error: null } - { type: itm, ref: core/itm0, index: 0, error: disabled } - { type: itm, ref: core/itm1, index: 1, error: usable, label: null } - - { type: exception, ref: core/exceptions, pname: core0, stream: 4, error: exception-error } + - { type: exception, ref: core/exceptions, pname: core0, stream: 4, error: exception-error, label: unused } - { type: global_ts, ref: core/timesync, pname: core0, stream: 5, warning: global-warning } - { type: overflow, ref: core/overflow, pname: core0, stream: 6, info: overflow-info } )yml"); @@ -510,13 +515,17 @@ TEST(CtraceUnitTests, TraceRunReaderPreservesDiagnosticReferences) EXPECT_EQ(config.references[2].error, (std::vector{"unavailable"})); EXPECT_EQ(config.references[3].error, (std::vector{"diagnostic", "detail"})); EXPECT_FALSE(config.references[3].address.has_value()); - EXPECT_TRUE(config.references[3].addressError.has_value()); + EXPECT_FALSE(config.references[3].addressError.has_value()); + EXPECT_FALSE(config.references[3].dataSizeError.has_value()); + EXPECT_FALSE(config.references[3].dataTypeError.has_value()); + EXPECT_FALSE(config.references[3].label.has_value()); EXPECT_FALSE(config.references[3].dataSetupIndex.has_value()); EXPECT_TRUE(config.references[4].error.empty()); EXPECT_TRUE(config.references[5].indices == std::vector{0U}); EXPECT_FALSE(config.references[6].label.has_value()); EXPECT_EQ(config.references[7].error, (std::vector{"exception-error"})); EXPECT_EQ(config.references[7].stream, std::optional(4U)); + EXPECT_FALSE(config.references[7].label.has_value()); EXPECT_EQ(config.references[8].warning, (std::vector{"global-warning"})); EXPECT_EQ(config.references[8].stream, std::optional(5U)); EXPECT_EQ(config.references[9].info, (std::vector{"overflow-info"})); @@ -636,20 +645,8 @@ TEST(CtraceUnitTests, TraceRunReaderTreatsNullOptionalSetupValuesAsAbsent) TEST(CtraceUnitTests, TraceRunReaderRejectsMalformedConsumedSetups) { TraceRunFixture file("ctrace-run-reader-setup-errors-test"); - constexpr std::string_view prefix = "ctrace-run:\n ctrace-refs: []\n ctrace-setup:\n - "; - /** @brief Describes malformed setup fields and their expected errors. */ - struct Case { - const char* setup; - const char* error; - }; - constexpr Case cases[] = { - {"timestamps: { itm-prescaler: [] }", "'timestamps.itm-prescaler' must be a scalar unsigned integer"}, - {"timestamps: { itm-prescaler: invalid }", "'itm-prescaler' must be an unsigned integer in range"}, - {"itm: { enable: 1, enable: 2 }", "map keys must be unique"}, - }; - for (const auto& testCase : cases) { - expectReadError(file, std::string(prefix) + testCase.setup + "\n", testCase.error); - } + expectReadError(file, "ctrace-run:\n ctrace-refs: []\n ctrace-setup:\n - itm: { enable: 1, enable: 2 }\n", + "map keys must be unique"); expectReadError(file, "ctrace-run:\n ctrace-refs: []\n ctrace-setup: {}\n", "'ctrace-setup' must be an array"); expectReadError(file, "ctrace-run:\n ctrace-refs: []\n ctrace-setup: [invalid]\n", @@ -665,6 +662,53 @@ TEST(CtraceUnitTests, TraceRunReaderRejectsMalformedConsumedSetups) "'pname' must be a scalar string"); } +TEST(CtraceUnitTests, TraceRunReaderValidatesPrescalerOnlyForConsumedSetups) +{ + TraceRunFixture file("ctrace-run-reader-selected-prescaler-test"); + struct Case { + const char* value; + const char* error; + }; + constexpr Case cases[] = { + {"[]", "'timestamps.itm-prescaler' must be a scalar unsigned integer"}, + {"{}", "'timestamps.itm-prescaler' must be a scalar unsigned integer"}, + {"invalid", "'itm-prescaler' must be an unsigned integer in range"}, + {"4294967296", "'itm-prescaler' must be an unsigned integer in range"}, + {"0x", "'itm-prescaler' must be an unsigned integer"}, + {"2", "'timestamps.itm-prescaler' must be one of 1, 4, 16, or 64"}, + }; + for (const auto* format : {"unformatted", "formatted"}) { + for (const auto& testCase : cases) { + SCOPED_TRACE(format); + SCOPED_TRACE(testCase.value); + auto config = file.read(std::string("ctrace-run:\n trace-format: ") + format + R"yml( + ctrace-setup: + - pname: selected + timestamps: { clock: 1000000, itm-prescaler: 4 } + - pname: unused + timestamps: { itm-prescaler: )yml" + testCase.value + R"yml( } + ctrace-refs: + - { type: itm, ref: selected/itm, pname: selected, stream: 1, index: 1 } +)yml"); + + const auto meta = CtraceRunMeta::fromConfig(config); + ASSERT_EQ(meta.routes().size(), 1U); + EXPECT_EQ(meta.routes().front().processorName, std::optional("selected")); + EXPECT_EQ(meta.routes().front().timestampClockHz, std::optional(1000000U)); + EXPECT_EQ(meta.routes().front().timestampPrescaler, 4U); + EXPECT_TRUE(meta.warnings().empty()); + + config.references.front().processorName = "unused"; + config.references.front().ref = "unused/itm"; + const auto error = captureExceptionMessage([&] { (void)CtraceRunMeta::fromConfig(config); }); + ASSERT_TRUE(error.has_value()); + EXPECT_NE(error->find(testCase.error), std::string::npos) << *error; + EXPECT_EQ(error->find(config.path + "(7): "), 0U) << *error; + EXPECT_EQ(error->find(config.path, config.path.size()), std::string::npos) << *error; + } + } +} + TEST(CtraceUnitTests, TraceRunReaderDefersMalformedItmSetupMetadata) { TraceRunFixture file("ctrace-run-reader-deferred-itm-errors-test"); From 291d9c97029e442c6ec8e2c78f2d90357a01846e Mon Sep 17 00:00:00 2001 From: Thorsten de Buhr Date: Thu, 1 Oct 2026 18:31:50 +0200 Subject: [PATCH 5/5] test(ctrace): cover deferred numeric metadata errors on consumed routes --- .../tracerun/TraceRunConfigReaderTests.cpp | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/tools/ctrace/test/unit/src/tracerun/TraceRunConfigReaderTests.cpp b/tools/ctrace/test/unit/src/tracerun/TraceRunConfigReaderTests.cpp index 118e2e970..38705724f 100644 --- a/tools/ctrace/test/unit/src/tracerun/TraceRunConfigReaderTests.cpp +++ b/tools/ctrace/test/unit/src/tracerun/TraceRunConfigReaderTests.cpp @@ -311,20 +311,30 @@ ctrace-run: TEST(CtraceUnitTests, TraceRunReaderDefersMalformedDwtMetadataToOutputPlanning) { TraceRunFixture file("ctrace-run-reader-malformed-data-metadata-test"); - const auto config = file.read(R"yml(ctrace-run: + for (const auto* value : {"[]", "invalid"}) { + SCOPED_TRACE(value); + const auto config = file.read(std::string(R"yml(ctrace-run: ctrace-refs: - ref: data#0 type: dwt index: 0 - address: [] + address: )yml") + value + R"yml( data-type: [] - size: [] -)yml"); - - ASSERT_EQ(config.references.size(), 1U); - EXPECT_TRUE(config.references[0].addressError.has_value()); - EXPECT_TRUE(config.references[0].dataTypeError.has_value()); - EXPECT_TRUE(config.references[0].dataSizeError.has_value()); + size: )yml" + value + "\n"); + + ASSERT_EQ(config.references.size(), 1U); + const auto& reference = config.references.front(); + EXPECT_TRUE(reference.addressError.has_value()); + EXPECT_TRUE(reference.dataTypeError.has_value()); + EXPECT_TRUE(reference.dataSizeError.has_value()); + + const auto meta = CtraceRunMeta::fromConfig(config); + ASSERT_EQ(sourceCount(meta), 1U); + const auto& source = sourceAt(meta, 0U); + EXPECT_EQ(source.addressError, reference.addressError); + EXPECT_EQ(source.dataTypeError, reference.dataTypeError); + EXPECT_EQ(source.dataSizeError, reference.dataSizeError); + } } TEST(CtraceUnitTests, TraceRunReaderAcceptsProcessorItmReferenceWithoutEnabledChannels)