diff --git a/tools/ctrace/README.md b/tools/ctrace/README.md index 0d60f6372..b9dda9af8 100644 --- a/tools/ctrace/README.md +++ b/tools/ctrace/README.md @@ -56,6 +56,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. 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; @@ -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 diff --git a/tools/ctrace/docs/architecture.md b/tools/ctrace/docs/architecture.md index 8d8325d8f..7a5f23963 100644 --- a/tools/ctrace/docs/architecture.md +++ b/tools/ctrace/docs/architecture.md @@ -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 diff --git a/tools/ctrace/docs/constraints.md b/tools/ctrace/docs/constraints.md index 7a55b9d67..ee656b017 100644 --- a/tools/ctrace/docs/constraints.md +++ b/tools/ctrace/docs/constraints.md @@ -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. diff --git a/tools/ctrace/docs/ctf-format.md b/tools/ctrace/docs/ctf-format.md index 6d6bf7f5d..e0f2d3254 100644 --- a/tools/ctrace/docs/ctf-format.md +++ b/tools/ctrace/docs/ctf-format.md @@ -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 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/model/MessageCatalog.inc b/tools/ctrace/src/model/MessageCatalog.inc index 164c60242..b138434dc 100644 --- a/tools/ctrace/src/model/MessageCatalog.inc +++ b/tools/ctrace/src/model/MessageCatalog.inc @@ -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) diff --git a/tools/ctrace/src/output/csv/CsvRowMapper.cpp b/tools/ctrace/src/output/csv/CsvRowMapper.cpp index 0e1afa4ed..f362f77b0 100644 --- a/tools/ctrace/src/output/csv/CsvRowMapper.cpp +++ b/tools/ctrace/src/output/csv/CsvRowMapper.cpp @@ -27,7 +27,7 @@ enum class CsvColumn : std::size_t { Cycles, Stream, Type, - Source, + Index, Value, Pc, Address, @@ -39,7 +39,7 @@ constexpr std::array(CsvColumn::Count "cycles", "stream", "type", - "source", + "index", "value", "pc", "address", @@ -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); @@ -136,7 +136,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)); } @@ -144,27 +144,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 4782f1999..ff5691fde 100644 --- a/tools/ctrace/src/tracerun/CtraceRunMeta.cpp +++ b/tools/ctrace/src/tracerun/CtraceRunMeta.cpp @@ -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. */ @@ -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. */ @@ -251,10 +250,10 @@ 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 ? formatMessage(MessageId::ReferenceDuplicateSourceLocated) - : formatMessage(MessageId::ReferenceDuplicateSource)); + reference.line > 0U ? formatMessage(MessageId::ReferenceDuplicateIndexLocated) + : formatMessage(MessageId::ReferenceDuplicateIndex)); } if (problem == ReferenceProblem::InvalidStream) { return configError(config, reference.line, @@ -262,8 +261,8 @@ static std::string referenceProblemMessage(const TraceRunConfig& config, const T : 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); @@ -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()) { @@ -1211,8 +1213,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)); @@ -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)); } } } @@ -1349,8 +1355,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..0ff1ea13e 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; } } } @@ -235,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 6269f078e..b04bf77aa 100644 --- a/tools/ctrace/src/tracerun/YmlTraceRunConfigReader.cpp +++ b/tools/ctrace/src/tracerun/YmlTraceRunConfigReader.cpp @@ -287,32 +287,32 @@ static std::optional parseTraceFormat(const std::string& path, c fail(path, node, formatMessage(MessageId::TraceFormatInvalid)); } -/** @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, configFieldMessage("source", ConfigFieldProblem::UnsignedEntry)); + fail(path, item, configFieldMessage("index", ConfigFieldProblem::UnsignedEntry)); } - 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. */ @@ -404,13 +404,16 @@ static std::optional parseReference(const std::string& path, reference.stream = parseStream(); if (TraceRunSchema::supportsSource(reference.type)) { try { - reference.sources = parseSources(path, element); + reference.indices = parseIndices(path, element); } catch (const std::runtime_error&) { if (diagnostics.error.empty()) { throw; } } } + 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/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/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 b784b2460..43cb2b769 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/README.md b/tools/ctrace/test/data/formatted-synthetic/README.md index 96acc2238..903be5843 100644 --- a/tools/ctrace/test/data/formatted-synthetic/README.md +++ b/tools/ctrace/test/data/formatted-synthetic/README.md @@ -27,7 +27,7 @@ The deterministic generated capture has these properties: - generated raw SHA-256: `e8a62ad20f048385fde894ed1b869bdfb402feabf8a5e4d88283334a92674847`; - trace-run SHA-256: - `a8370d26cd2f75264fc4f48fcdbf5fa2c9e1404c80a61c898dd30de8a44b91c6`. + `7a4980237b850f471e80b5a30ff7076095514ce6f94e7171b4506d8e6d305047`. ## Routes 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 6bd860c04..7f2349e21 100644 --- a/tools/ctrace/test/integration/README.md +++ b/tools/ctrace/test/integration/README.md @@ -42,7 +42,7 @@ while the command returns a failing exit status. `RetainsCsvAndUnfilteredAbortAfterIncompleteFormattedTail` covers a fatal 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 -incomplete-trace indication and compact structured cause when available, with no cycle timestamp, stream, or source. +incomplete-trace indication and compact structured cause when available, with no cycle timestamp, stream, or index. Normal CLI shares that text; `--verbose` adds the processed-byte count and detailed reason. That final row bypasses type and stream filters; the preceding route-local error follows those filters. Incomplete CTF output must be removed diff --git a/tools/ctrace/test/integration/src/CtraceIntegTests.cpp b/tools/ctrace/test/integration/src/CtraceIntegTests.cpp index 650ed0614..95046fcf7 100644 --- a/tools/ctrace/test/integration/src/CtraceIntegTests.cpp +++ b/tools/ctrace/test/integration/src/CtraceIntegTests.cpp @@ -605,7 +605,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) { @@ -631,7 +631,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"}) { @@ -863,7 +863,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")); @@ -887,7 +887,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")); @@ -908,7 +908,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")); } @@ -994,7 +994,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"))); @@ -1046,7 +1046,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,,,,,Invalid ITM packet header\n" "0,1,error,,,,,No ITM resync before EOF; 10 raw bytes affected\n", traceCsvRows(readTestTextFile(workDirectory() / "Invalid.TB.csv"))); @@ -1127,7 +1127,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: no source ID\n", readTestTextFile(workDirectory() / "Unassigned.TB.csv")); EXPECT_FALSE(std::filesystem::exists(workDirectory() / "Unassigned.TB.ctf" / "stream_1")); @@ -1164,7 +1164,7 @@ TEST_F(CtraceIntegTests, DecodesFormattedCaptureAfterUnassignedPrefixAndRetainsI EXPECT_EQ(countOccurrences(csv, " bytes skipped: null source ID"), 1U); EXPECT_EQ(countOccurrences(csv, " bytes skipped: reserved source ID"), 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"); @@ -1259,7 +1259,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")); @@ -1302,7 +1302,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" @@ -1403,7 +1403,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")); } @@ -1436,7 +1436,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: null source ID\n"; @@ -1548,7 +1548,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,,,,,Invalid DWT counter\n" "0,,itm,1,0x41,,,\n", readTestTextFile(workDirectory() / "InvalidEvent.SWO.csv")); @@ -1568,7 +1568,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")); @@ -1594,7 +1594,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,,,,,Invalid PMU counter\n" "0,,itm,1,0x41,,,\n", readTestTextFile(workDirectory() / "InvalidPmu.SWO.csv")); @@ -1641,7 +1641,7 @@ TEST_F(CtraceIntegTests, ReportsDiagnosticsFromConsumedTraceRunReferences) type: itm pname: core stream: 1 - source: 0 + index: 0 info: - configured ITM channel zero - retained secondary setup information @@ -2015,7 +2015,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; @@ -2623,7 +2623,7 @@ TEST_F(CtraceIntegTests, RetainsCsvAndUnfilteredAbortAfterIncompleteFormattedTai const auto lines = readTestLines(csvPath); ASSERT_GE(lines.size(), 2U); EXPECT_EQ(lines.back(), ",,error,,,,," + compactAbortMessage) - << "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; trace incomplete"), 1U); const auto expectedPayload = selection.includesPayload ? "42,1,itm,1,0x41,,,\n" : ""; const auto semanticRows = traceCsvRows(csv); @@ -2634,7 +2634,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,,,,," + compactAbortMessage + "\n"); } expectNotContains(csv, ",pcsample,"); diff --git a/tools/ctrace/test/integration/src/ValidateFixtureIntegrity.cmake b/tools/ctrace/test/integration/src/ValidateFixtureIntegrity.cmake index bcd254e81..335a501c6 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|b03591bdb54d15938e7e3b6f2414b8a71fe21f1be1ca8be897492316ca1560c8|21169" + "Arm-reset/Arm.ctrace-run.yml|56ad6c463f4388338305218474bfee3a69201bf1b94a0391ba8ccc06d79889fb|691" + "Blinky+Arm/Blinky+Arm.SWO.csv|2995f6406ba0c7b6ab27a32a86dc6b1f4bff8d39b66744575c47b7893cef5a06|21168" "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 48094253a..8936ce205 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) @@ -796,8 +796,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: "; const auto compact = "Decode aborted; trace incomplete; " + std::string(compactReason); @@ -1020,7 +1020,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"); const auto* inspection = findDiagnostic(diagnostics, "raw input inspection context"); 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 a7bb43fb5..df7964e38 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 279095b7d..627eb6aa8 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"); @@ -170,7 +170,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"; @@ -195,7 +195,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,,,"); @@ -217,7 +217,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,,,,,Trace overflow; timestamp discontinuity")) << "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 d3e243661..3c934f927 100644 --- a/tools/ctrace/test/unit/src/output/csv/CsvRowMapperTests.cpp +++ b/tools/ctrace/test/unit/src/output/csv/CsvRowMapperTests.cpp @@ -76,7 +76,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"; @@ -99,7 +99,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..38705724f 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,28 @@ 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 + index: [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, TraceRunReaderParsesTraceFormatDeclaration) @@ -253,7 +254,7 @@ TEST(CtraceUnitTests, TraceRunReaderUsesReferencedSetupSizeAsFallback) ctrace-refs: - ref: data#0 type: dwt - source: 0 + index: 0 data-type: signed )yml"); @@ -264,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 - source: 0 + 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); @@ -289,20 +311,30 @@ TEST(CtraceUnitTests, TraceRunReaderIgnoresUnsupportedMetadataNames) 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 - source: 0 - address: [] + index: 0 + 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) @@ -322,7 +354,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 +385,21 @@ 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, ref: [], 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: null, index: 1 }\n", "missing required 'ref' scalar"); - expectReadError(file, "ctrace-run:\n ctrace-refs:\n - { type: itm, ref: null, source: 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 +412,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 +448,7 @@ TEST(CtraceUnitTests, TraceRunReaderIgnoresNullAndNonScalarOptionalReferenceValu ref: data#0 pname: null stream: null - source: null + index: null address: null data-type: null size: null @@ -428,8 +458,8 @@ TEST(CtraceUnitTests, TraceRunReaderIgnoresNullAndNonScalarOptionalReferenceValu error: null - type: itm ref: itm - source: [1, null] - label: null + index: [1, null] + label: [] info: [note, null] warning: [null] error: [null] @@ -440,7 +470,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 +482,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,11 +497,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#, source: 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, source: 0, error: disabled } - - { type: itm, ref: core/itm1, source: 1, error: usable, label: null } - - { type: exception, ref: core/exceptions, pname: core0, stream: 4, error: exception-error } + - { 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, 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"); @@ -488,36 +525,40 @@ 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].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)); + 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"})); 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); @@ -614,20 +655,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", @@ -635,7 +664,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: [{}] @@ -643,6 +672,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"); @@ -681,15 +757,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 +807,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 +824,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 +845,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 +867,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 +883,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 +895,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 +906,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 +930,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; }