diff --git a/tools/ctrace/README.md b/tools/ctrace/README.md index 25452cbab..2e0a25c0c 100644 --- a/tools/ctrace/README.md +++ b/tools/ctrace/README.md @@ -2,13 +2,7 @@ `ctrace` converts CMSIS Cortex-M trace captures into outputs that can be inspected or processed by other tools. It combines a `.ctrace-run.yml` description with matching raw trace files from one trace directory. - -The current implementation decodes SWO/ITM and DWT data. Trace Bus (`*.TB.raw`) files are discovered and reported, -but deliberately skipped until that decoder is implemented. -ITM payload output supports stimulus ports `1` through `31`. Port `0` remains part of stream decoding but is -deliberately excluded from CSV and CTF event output. Trace Compass observes the same filtered CTF stream. - -![ctrace architecture](docs/architecture.svg) +The [architecture description](docs/architecture.md) documents the supported feature profile and internal design. ## Usage @@ -28,19 +22,17 @@ ctrace [options] Values for `--type` and `--stream` are space-separated, so place the trace directory before these multi-value options. If the directory follows them, terminate option parsing explicitly, for example `ctrace --type itm dwt -- .trace`. With no output option, `ctrace` validates and decodes the capture without writing -output files. +output files. Run `ctrace --help` for the current option details. -The `--type` option accepts the specification-defined selectors `itm`, `dwt`, `event`, `pmu`, `exception`, -`pcsample`, `global_ts`, `overflow`, and `error`. Decoded DWT event counters, PMU packets, and PC samples remain -disabled until their output semantics are implemented, so their selectors currently produce no rows. +## Trace directory -A trace directory uses solution-set-based file names: +Input and output files share a solution-set base name: ```text .trace/ Board.ctrace-run.yml Board.SWO.raw - Board.TB.raw # discovered, currently not decoded + Board.TB.raw # optional Trace Bus input ``` For `ctrace .trace --target Board --all`, the supported input produces: @@ -54,30 +46,6 @@ For `ctrace .trace --target Board --all`, the supported input produces: Board.SWO.traceanalysis.xml ``` -The YAML reader validates fields consumed by `ctrace` and ignores unrelated or unknown fields. A malformed consumed -field is an error; an unneeded extension to the trace-run format does not break the tool. - -## Project structure - -| Path | Responsibility | -|---|---| -| `src/CtraceMain.h` | Platform-independent entry point used by the executable trampoline | -| `src/cli` | Command-line parsing and validation | -| `src/control` | Trace-directory orchestration, raw-file access, and per-file decode jobs | -| `src/tracerun` | Trace-run discovery, YAML parsing, and normalized metadata | -| `src/decode` | OpenCSD adapter, recovery, and Cortex-M semantic decoding | -| `src/model` | Backend-independent trace events and selections | -| `src/output/csv` | CSV schema and writer | -| `src/output/ctf` | CTF bundle and Trace Compass XML writers | -| `src/diagnostics` | Structured diagnostics and trace-issue reporting | -| `test/unit` | GoogleTest cases, arranged like the production modules | -| `test/integration` | GoogleTest integration suite for the application entry point and stable fixtures | -| `test/data` | Stable fixtures and expected output | - -The module boundaries, dependency direction, runtime flow, and extension points are described in the -[architecture documentation](docs/architecture.md). See also the [verified constraints](docs/constraints.md) and the -compact [remaining TODO list](docs/todo.md). - ## Build and test Initialize all dependencies and configure the repository from its root: @@ -97,27 +65,13 @@ ctest --test-dir build -C Debug -R '^(CtraceUnitTests|CtraceIntegTests|ctrace-)' Editors using `clangd` should open the devtools repository root and configure into `build`. The tool-local `.clangd` file points clangd at that compilation database. -## Dependencies and releases - -`ctrace` uses the devtools copies of `cxxopts`, `yaml-cpp`, GoogleTest, and the `external/OpenCSD` submodule. It does -not carry private copies below `tools/ctrace`. -Known defects in the pinned OpenCSD revision that can affect ctrace are recorded -in the [OpenCSD issue notes](docs/opencsd-issues.md). - -Publishing a GitHub Release for a tag named `tools/ctrace/`, for example `tools/ctrace/0.0.1`, runs the ctrace -workflow. It builds Windows AMD64/Arm64, Linux AMD64/Arm64, and macOS Arm64 variants and attaches `ctrace.zip` to the -release. Unit and integration tests run on Windows AMD64 and Linux AMD64; ARM64 targets are compiled but not executed, -matching the other devtools workflows. Coverage runs on Linux AMD64. The executable version is derived from the same -tool-specific tag. - -The project is licensed under Apache-2.0. Product dependencies and their licenses are recorded in the repository's -top-level `LICENSE.md`. The release archive contains the project license, third-party notices, OpenCSD copyright -notices, the application-dependency license texts, and checksums for all included files. Compiler and operating-system -runtime contents must still be inspected for each production build; the application dependency list does not claim to -cover them. Release artifacts are currently unsigned; signing, archive checksums, macOS notarization, and a formal -SBOM remain explicit release-hardening decisions. - -The code, build, test, and packaging structure is ready for release-candidate validation. The Blinky SWO and TB -captures are approved redistributable test assets, as documented in the [fixture README](test/data/README.md). -Publishing the current statically linked Linux artifacts additionally requires the runtime-license and relinking work -tracked in the [release TODO](docs/todo.md). +## Further documentation + +- [Architecture](docs/architecture.md): supported features, runtime flow, module boundaries, dependencies, tests, + and CI. +- [Constraints](docs/constraints.md): contracts that implementation changes must preserve. +- [TODO](docs/todo.md): planned work and pull-request boundaries. +- [OpenCSD issues](docs/opencsd-issues.md): known issues in the pinned decoder revision. +- [Third-party notices](docs/THIRD_PARTY_NOTICES.md): dependency versions, licenses, and build configuration. +- [Test data](test/data/README.md) and [integration tests](test/integration/README.md): fixture provenance and test + scope. diff --git a/tools/ctrace/docs/architecture.md b/tools/ctrace/docs/architecture.md index 5725f0657..3609119c0 100644 --- a/tools/ctrace/docs/architecture.md +++ b/tools/ctrace/docs/architecture.md @@ -1,9 +1,8 @@ # ctrace Architecture This document describes the internal structure of `ctrace`, the runtime data flow, and the intended extension points. -For command-line usage, build instructions, and release information, see the [project README](../README.md). The -[verified constraints](constraints.md) record preserved contracts; the compact [TODO list](todo.md) tracks remaining -work. +For command-line usage and build instructions, see the [project README](../README.md). The [verified +constraints](constraints.md) record preserved contracts; the compact [TODO list](todo.md) tracks remaining work. ![ctrace architecture](architecture.svg) @@ -19,18 +18,13 @@ packets are retained internally but are not mapped to their selectors yet; perio Bus input is discovered so that a complete trace directory can be inspected, but `*.TB.raw` files are reported and skipped until a decoder is implemented. -ITM stream decoding covers the architectural stimulus-port domain `0` through `31`. Public ITM payload output is -restricted to ports `1` through `31`; port `0` is decoded for stream integrity but excluded from CSV and CTF event -output. Trace Compass observes the same filtered CTF stream. - The architecture separates protocol decoding, semantic interpretation, and output generation. This keeps output formats independent of OpenCSD and allows another raw trace channel to reuse the event model and output backends. ## How it works at a glance -`ctrace` processes one solution set at a time. The common base name joins configuration and trace data; for example, -`Board.ctrace-run.yml` describes the sources and timing metadata required to decode `Board.SWO.raw`. A matching -`Board.TB.raw` is discovered as part of the same solution set but is skipped by the first release profile. +`ctrace` processes one solution set at a time. The [README](../README.md#trace-directory) describes how configuration, +raw input, and generated output files are grouped by their common base name. The main in-memory path is: @@ -68,32 +62,6 @@ The `TraceEvent` boundary is the central design point. Before it, code handles b recovery, and Cortex-M state. After it, code sees backend-independent events in decode order and does not depend on OpenCSD types. -| Stage | Owner | Transformation | -| --- | --- | --- | -| Discover | `TraceDirectoryJob` | Trace directory and target selection to solution-set configuration and raw inputs | -| Prepare | `CtraceRunMeta`, `FileDecodeJob` | YAML representation to normalized runtime metadata and an output plan | -| Decode protocol | `DecodePipeline`, `OpenCsdItmDecoder` | Raw byte chunks to recoverable OpenCSD trace elements | -| Interpret | `CortexMStreamDecoder`, `CortexMPostDecoder` | Protocol elements to timestamped semantic events | -| Consume | `DecodeConsumers` | One ordered event stream to diagnostics and every enabled output backend | -| Complete | `TraceOutputLifecycle` | Complete active artifacts, or remove them after output or decoder failure | - -## Runtime flow - -1. `CtraceMain` parses and validates the command line. -2. `TraceRunDiscovery` finds one or all `.ctrace-run.yml` files in the selected trace directory. -3. `YmlTraceRunConfigReader` reads fields used by `ctrace`; unrelated and unknown YAML fields are ignored. -4. `CtraceRunMeta` normalizes processor, timestamp, route, ITM, and DWT source metadata. -5. `TraceDirectoryJob` associates the configuration with matching raw trace channels. -6. `FileDecodeJob` creates the requested output plan and reads supported raw files in 64 KiB blocks. -7. `DecodePipeline` receives non-owning `RawByteView` values while preserving decoder state across every file-read - boundary. -8. The OpenCSD adapter decodes ITM protocol elements and preserves decoder warnings, errors, and recovery boundaries. -9. The Cortex-M post-decoder converts protocol elements into semantic `TraceEvent` values. -10. `DecodeConsumers` forwards every event to diagnostics and the selected output backends. -11. Output lifecycle handling completes valid artifacts or removes incomplete artifacts after a failure. - -Without `--csv`, `--ctf`, or `--all`, the same pipeline runs in validation-only mode without creating output files. - ## Processing state and ownership One `DecodePipeline` is created for each supported raw file. It owns the OpenCSD adapter and Cortex-M stream decoder, @@ -105,12 +73,7 @@ buffer; each `RawByteView` borrows that buffer only for the synchronous `DecodeP into the same `TraceEventSink`, preserving input order while keeping stream-specific timestamp and DWT state apart. There is no application-wide event queue. `DecodeConsumers` forwards each event synchronously to the output -lifecycle and issue reporter. Output backends own their files and are isolated from one another: failure of one -backend aborts its incomplete artifact but does not directly stop another active backend. A non-recoverable decoder -error aborts every still-active output for that raw file. - -The diagnostic sink lives for the complete command invocation. It therefore aggregates failures across solution sets -and determines the final process status after processing has continued wherever possible. +lifecycle and issue reporter. ## Recovery after damaged trace @@ -124,8 +87,8 @@ the Cortex-M post-decoder flushes pending events, resets incomplete DWT correlat until the stream supplies enough timing information again. The issue remains part of the ordered `TraceEvent` stream, so diagnostics and enabled output backends observe the same recovery boundary. -Failure to reset OpenCSD, repeated lack of decoder progress, or an unsuccessful wait/flush operation aborts only the -current raw-file job. Other solution sets continue to be processed where possible. +Failure to reset OpenCSD, repeated lack of decoder progress, or an unsuccessful wait/flush operation aborts the +current raw-file job. ## Suggested code-reading path @@ -165,9 +128,7 @@ must not depend on control jobs or command-line details. | --- | --- | | `src/tracerun` | File discovery, YAML parsing, schema subset validation, and normalized metadata | -The YAML reader intentionally consumes only data required by `ctrace`. A malformed consumed field is an error, while -an unknown field is ignored. This permits compatible trace-run format extensions without weakening validation of the -data used for decoding or output generation. +The YAML reader's validation and metadata rules are recorded in the [constraints](constraints.md#boundaries). `CtraceRunMeta` is the boundary between the YAML representation and runtime processing. Decode and output modules use normalized metadata instead of navigating YAML nodes. @@ -211,27 +172,22 @@ Decoder issue packets remain part of the event stream. `DecodeConsumers` reports of output filters and forwards all events to the backends. The backends apply stream and type selection internally; selected issues become CSV error rows or CTF trace-status events. Repeated issues are not silently collapsed. -Processing continues with other solution sets where possible. Errors are rendered as `error` even when their impact -causes a non-zero exit status. Unhandled internal ctrace failures also terminate the command after an error diagnostic. +An invocation-wide diagnostic sink aggregates failures while other solution sets continue where possible, then +determines the final process status. Errors are rendered as `error` even when their impact causes a non-zero exit +status. Unhandled internal ctrace failures also terminate the command after an error diagnostic. ## External dependencies -| Dependency | Use | -| --- | --- | -| `cxxopts` | Command-line parsing | -| `yaml-cpp` | Trace-run YAML parsing | -| `OpenCSD` 1.8.3 | ITM protocol decoding; pinned as a repository submodule | -| GoogleTest | Unit-test framework; not linked into the product executable | +`cxxopts` provides command-line parsing, `yaml-cpp` is confined to the trace-run reader, OpenCSD is isolated behind +the decode adapters, and GoogleTest is used only by test targets. Exact revisions, licenses, and dependency build +configuration are documented in the [third-party notices](THIRD_PARTY_NOTICES.md). -Dependencies are provided by the devtools repository. `tools/ctrace` does not maintain private library copies. The ITM adapter currently uses OpenCSD `common/` and `interfaces/` headers because the public OpenCSD 1.8.3 boundary does not provide equivalent access: its installed headers omit the ITM configuration and packet types required by the decoder callbacks, and its C API exposes only the last structured error rather than all errors from one data-path operation. Moving the adapter to the public API therefore also requires resolving these two gaps. -The pinned OpenCSD source is built without a downstream source patch. A known unsafe empty-buffer access in an -upstream diagnostic path, its reachability, and a proposed upstream fix are recorded in the -[OpenCSD issue notes](opencsd-issues.md). +A known decoder defect and its proposed upstream fix are recorded in the [OpenCSD issue notes](opencsd-issues.md). ## Extension points @@ -256,27 +212,18 @@ Do not introduce backend-specific state into the decode pipeline or event model. Unit tests under `test/unit/src` mirror the production modules. Shared file, event, and diagnostic helpers live under `test/unit/support`. One CTest entry runs the complete GoogleTest executable and writes its XML report. -The `CtraceIntegTests` GoogleTest target calls `CtraceMain` through the same `ctracelib` application object used by the -executable. It verifies command-line behavior, diagnostics, output cleanup, and fixture conversion. A small set of -`ctrace-` CTest smoke tests retains coverage of the platform executable and Windows manifest. Test data and expected -artifacts live under `test/data`; generated files are written only below the CMake build directory. - -CI runs the unit and integration suites on Windows AMD64 and Linux AMD64. ARM64 targets are compiled but not executed, -matching the other devtools workflows. +Executable-level coverage and fixture ownership are documented next to the +[integration tests](../test/integration/README.md) and [test data](../test/data/README.md). -## Build and release structure +## Build and CI structure The source tree has seven static library targets: `model`, `cli`, `trace-run`, `diagnostics`, `decode`, `output`, and `control`. The shared `ctracelib` object contains `CtraceMain`; the executable adds only the platform trampoline and manifest where required. Dependencies form a directed, cycle-free graph with `control` as the composition root. The tool-specific GitHub workflow is selected by a `tools/ctrace/` release tag. It builds Windows AMD64 and -Arm64, Linux AMD64 and Arm64, and macOS Arm64 binaries. -The release archive contains the Apache-2.0 project license, application-dependency notices and license texts, -retained OpenCSD copyright notices, and per-file SHA-256 checksums. The version compiled into the executable is derived -from the same tag. The actual compiler and operating-system runtime content still requires inspection for each -production release. - -The checked-in SWO and TB captures are approved ctrace test assets and may be redistributed with devtools. Together -with the tool-specific build, test, packaging, versioning, and license integration, this forms the technical basis for -the first open-source release. +Arm64, Linux AMD64 and Arm64, and macOS Arm64 binaries. Unit and integration tests run on Windows AMD64 and Linux +AMD64; the remaining targets are compile-only. +The version compiled into the executable is derived from the same tag. Archive contents and license material are +described in the [third-party notices](THIRD_PARTY_NOTICES.md); unfinished release work remains in the +[TODO list](todo.md). diff --git a/tools/ctrace/docs/constraints.md b/tools/ctrace/docs/constraints.md index 08440425d..d5690d3ea 100644 --- a/tools/ctrace/docs/constraints.md +++ b/tools/ctrace/docs/constraints.md @@ -1,10 +1,10 @@ # ctrace Constraints -This document records contracts that implementation changes must preserve. It intentionally does not describe the -runtime flow, module inventory, supported feature profile, build, or release process; those belong in the -[architecture description](architecture.md), [README](../README.md), and [TODO list](todo.md). The CMSIS-Toolbox -[trace specification](https://github.com/Open-CMSIS-Pack/cmsis-toolbox/blob/main/docs/Experimental-Features.md#file-structure-of-ctrace-runyml) -remains authoritative for the external `*.ctrace-run.yml` format. +This document records contracts that implementation changes must preserve. Runtime design and the supported feature +profile belong in the [architecture description](architecture.md), working instructions in the [README](../README.md), +and unfinished work in the [TODO list](todo.md). The CMSIS-Toolbox +[trace specification](https://open-cmsis-pack.github.io/cmsis-toolbox/Experimental-Features/) remains authoritative +for the external `*.ctrace-run.yml` format. ## Boundaries @@ -16,6 +16,9 @@ remains authoritative for the external `*.ctrace-run.yml` format. - YAML types remain inside the trace-run reader. The rest of ctrace consumes normalized configuration and metadata. - The YAML reader validates fields consumed by ctrace; unrelated fields are outside its validation scope. Malformed consumed fields remain errors. An ITM reference without `source` values is valid and contributes no source events. +- 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. - Backend-specific requirements and failures remain independent; requesting CTF must not disable otherwise valid CSV output, or vice versa. diff --git a/tools/ctrace/docs/opencsd-issues.md b/tools/ctrace/docs/opencsd-issues.md index 4c56ad9bf..ab9fc53a9 100644 --- a/tools/ctrace/docs/opencsd-issues.md +++ b/tools/ctrace/docs/opencsd-issues.md @@ -21,8 +21,8 @@ may show no failure, but checked standard-library implementations, sanitizers, or compiler optimizations can expose the undefined behavior as an assertion, crash, or other incorrect behavior. -devtools does not apply a downstream source patch. A possible upstream fix is -to use the vector data pointer, which is valid to pass with a zero size: +The pinned revision still contains this issue. A possible upstream fix is to +use the vector data pointer, which is valid to pass with a zero size: ```cpp outputRawPacketToMonitor( diff --git a/tools/ctrace/docs/todo.md b/tools/ctrace/docs/todo.md index c75589673..d736ac26c 100644 --- a/tools/ctrace/docs/todo.md +++ b/tools/ctrace/docs/todo.md @@ -1,11 +1,18 @@ # ctrace TODO +## Pull-request cleanup + +- [ ] Split commit `88c3f4dc`; it mixes PC Sampling and Exception-Return handling. +- [ ] Submit Exception-Return preservation as an independent bug-fix PR. + ## DWT - [ ] Preserve DWT reference, group, and setup-binding identities when expanding source arrays. - [ ] Complete Armv7-M linked-comparator, range, and value-match decoding. - [ ] Add Armv8-M and Armv8.1-M DWT decoding. -- [ ] Add `event`, `pmu`, and `pcsample` events to CSV, CTF, and Trace Compass output. +- [ ] Add `event` output in its own PR. +- [ ] Add `pmu` output in its own PR. +- [ ] Add PC Sampling in its own PR after agreeing the sleep/`PC_SAMPLE` CTF contract. ## Multiple streams @@ -14,9 +21,10 @@ ## Additional decoders -- [ ] Decode formatted `*.TB.raw` input by Trace Bus ID. -- [ ] Add ETM instruction trace decoding and output. -- [ ] Add MTB instruction trace decoding and output. +- [ ] Add named trace-buffer discovery (`TB_`) in its own PR. +- [ ] Decode formatted `*.TB.raw` input by Trace Bus ID in its own PR. +- [ ] Add ETM instruction trace decoding and output in its own PR. +- [ ] Add MTB instruction trace decoding and output in its own PR. ## Dependencies and release diff --git a/tools/ctrace/src/output/OutputRequirements.cpp b/tools/ctrace/src/output/OutputRequirements.cpp index 0379e3ef1..a77a4fae7 100644 --- a/tools/ctrace/src/output/OutputRequirements.cpp +++ b/tools/ctrace/src/output/OutputRequirements.cpp @@ -103,10 +103,11 @@ static bool validateCtfRouteIdentity(const CtraceRunMeta& ctraceRunMeta, const T continue; } const auto& first = *found->second; - const auto sameMetadata = first.label == source.label && first.symbolAddress == source.symbolAddress && - first.valueType == source.valueType && first.valueSize == source.valueSize && - first.symbolTypeError == source.symbolTypeError && - first.symbolSizeError == source.symbolSizeError; + const auto sameMetadata = first.label == source.label && first.address == source.address && + first.dataType == source.dataType && first.dataSize == source.dataSize && + first.addressError == source.addressError && + first.dataTypeError == source.dataTypeError && + first.dataSizeError == source.dataSizeError; const auto indistinguishableProcessors = first.traceBusId == source.traceBusId && first.processorName != source.processorName; if ((sameMetadata && !indistinguishableProcessors) || !reported.insert(key).second) { @@ -259,7 +260,7 @@ static std::optional resolveCtfClock(const CtraceRunMeta& ctraceR return resolveDefaultCtfClock(ctraceRunMeta, selection, diagnostics); } -/** @brief Validates value type and size metadata for selected DWT routes. */ +/** @brief Validates address, data type, and size metadata for selected DWT routes. */ static bool validateCtfDwtMetadata(const CtraceRunMeta& ctraceRunMeta, const TraceSelection& selection, DiagnosticSink& diagnostics) { @@ -269,44 +270,52 @@ static bool validateCtfDwtMetadata(const CtraceRunMeta& ctraceRunMeta, const Tra continue; } bool sourceValid = true; - if (source.symbolTypeError.has_value()) { + if (source.addressError.has_value()) { valid = false; sourceValid = false; auto context = routeContext("ctf", ctraceRunMeta, source); - context.emplace_back("error", *source.symbolTypeError); - reportRequirementError(diagnostics, - "CTF output cannot use the configured ctrace-run data.symbol-type", std::move(context)); + context.emplace_back("error", *source.addressError); + reportRequirementError(diagnostics, "CTF output cannot use the configured ctrace-run address", + std::move(context)); } - if (source.symbolSizeError.has_value()) { + if (source.dataTypeError.has_value()) { valid = false; sourceValid = false; auto context = routeContext("ctf", ctraceRunMeta, source); - context.emplace_back("error", *source.symbolSizeError); - reportRequirementError(diagnostics, - "CTF output cannot use the configured ctrace-run data.symbol-size", std::move(context)); + context.emplace_back("error", *source.dataTypeError); + reportRequirementError(diagnostics, "CTF output cannot use the configured ctrace-run data-type", + std::move(context)); + } + if (source.dataSizeError.has_value()) { + valid = false; + sourceValid = false; + auto context = routeContext("ctf", ctraceRunMeta, source); + context.emplace_back("error", *source.dataSizeError); + reportRequirementError(diagnostics, "CTF output cannot use the configured ctrace-run size", + std::move(context)); } if (!sourceValid) { continue; } - const auto validType = TraceRunSchema::isDwtDataType(source.valueType); - const auto* valueVariant = CtfSchema::valueVariantForTraceRunType(source.valueType, source.valueSize); + const auto validType = TraceRunSchema::isDwtDataType(source.dataType); + const auto* valueVariant = CtfSchema::valueVariantForTraceRunType(source.dataType, source.dataSize); if (!validType) { valid = false; auto context = routeContext("ctf", ctraceRunMeta, source); - context.emplace_back("dataType", source.valueType); + context.emplace_back("dataType", source.dataType); reportRequirementError(diagnostics, - "CTF output cannot use ctrace-run data.symbol-type '" + source.valueType + "'; " + + "CTF output cannot use ctrace-run data-type '" + source.dataType + "'; " + std::string(CtfSchema::ValueTypeRequirements), std::move(context)); } - if (!TraceRunSchema::isDwtDataSize(source.valueSize) || (validType && valueVariant == nullptr)) { + if (!TraceRunSchema::isDwtDataSize(source.dataSize) || (validType && valueVariant == nullptr)) { valid = false; auto context = routeContext("ctf", ctraceRunMeta, source); - context.emplace_back("dataType", source.valueType); - context.emplace_back("dataSize", std::to_string(source.valueSize)); + context.emplace_back("dataType", source.dataType); + context.emplace_back("dataSize", std::to_string(source.dataSize)); reportRequirementError(diagnostics, - "CTF output cannot use ctrace-run data.symbol-size " + std::to_string(source.valueSize) + - " with data.symbol-type '" + source.valueType + "'; " + + "CTF output cannot use ctrace-run size " + std::to_string(source.dataSize) + + " with data-type '" + source.dataType + "'; " + std::string(CtfSchema::ValueTypeRequirements), std::move(context)); } @@ -332,9 +341,9 @@ static std::vector resolveCtfSources(const CtraceRunMeta& c route.source, route.traceBusId, route.label, - route.symbolAddress, - route.valueType, - static_cast(route.valueSize), + route.address, + route.dataType, + static_cast(route.dataSize), }); } return sources; diff --git a/tools/ctrace/src/output/TraceOutputConfig.h b/tools/ctrace/src/output/TraceOutputConfig.h index 09fb8a450..da0c9e60c 100644 --- a/tools/ctrace/src/output/TraceOutputConfig.h +++ b/tools/ctrace/src/output/TraceOutputConfig.h @@ -30,9 +30,9 @@ struct ResolvedTraceSource { std::uint32_t source = 0; std::uint8_t traceBusId = 0U; std::optional label; - std::optional symbolAddress; - std::string valueType = "unsigned int"; - std::uint8_t valueSize = 4U; + std::optional address; + std::string dataType = "unsigned"; + std::uint8_t dataSize = 4U; }; /** @brief Configures one CSV output artifact. */ diff --git a/tools/ctrace/src/output/ctf/CtfEncoder.cpp b/tools/ctrace/src/output/ctf/CtfEncoder.cpp index 6d570e96f..cd6427f58 100644 --- a/tools/ctrace/src/output/ctf/CtfEncoder.cpp +++ b/tools/ctrace/src/output/ctf/CtfEncoder.cpp @@ -36,10 +36,10 @@ static const CtfSchema::ValueVariant& dwtValueVariant(const ResolvedTraceSource* { static const ResolvedTraceSource defaults; const auto& resolved = source != nullptr ? *source : defaults; - const auto* variant = CtfSchema::valueVariantForTraceRunType(resolved.valueType, resolved.valueSize); + const auto* variant = CtfSchema::valueVariantForTraceRunType(resolved.dataType, resolved.dataSize); if (variant == nullptr) { throw std::runtime_error("CTF DWT value for comparator " + std::to_string(comparator) + - " has invalid ctrace-run data.symbol-type/data.symbol-size metadata"); + " has invalid ctrace-run data-type/size metadata"); } return *variant; } @@ -49,8 +49,7 @@ static bool equivalentSourceMetadata(const ResolvedTraceSource& left, const Reso { // Trace Bus ID identifies the route, not the metadata attached to it. return left.type == right.type && left.source == right.source && left.label == right.label && - left.symbolAddress == right.symbolAddress && left.valueType == right.valueType && - left.valueSize == right.valueSize; + left.address == right.address && left.dataType == right.dataType && left.dataSize == right.dataSize; } /** @brief Finds an unambiguous configured source for one event route. */ @@ -241,7 +240,7 @@ std::uint64_t CtfEncoder::allocateEventTimestamp(std::uint8_t traceBusId) void CtfEncoder::writeSoftwareEvent(const TraceEvent& event, const SoftwareTraceEvent& software) { - const auto* variant = CtfSchema::valueVariantForTraceRunType("unsigned int", software.size); + const auto* variant = CtfSchema::valueVariantForTraceRunType("unsigned", software.size); if (variant == nullptr) { throw std::runtime_error("CTF ITM value has an invalid SWO payload size"); } @@ -292,7 +291,7 @@ void CtfEncoder::writeDwtValueEvent(const TraceEvent& event, const DwtDataTraceE void CtfEncoder::reportDwtSizeMismatch(const TraceEvent& event, const DwtDataTraceEvent& data, const ResolvedTraceSource* source) { - const auto configuredSize = source != nullptr ? source->valueSize : ResolvedTraceSource{}.valueSize; + const auto configuredSize = source != nullptr ? source->dataSize : ResolvedTraceSource{}.dataSize; if (configuredSize == data.size || m_config.diagnostics == nullptr || !m_reportedDwtSizeMismatches.insert({event.traceBusId, data.comparator}).second) { return; @@ -307,7 +306,7 @@ void CtfEncoder::reportDwtSizeMismatch(const TraceEvent& event, const DwtDataTra context.emplace_back("stream", std::to_string(event.traceBusId)); m_config.diagnostics->report({ DiagnosticSink::Severity::Warning, - "configured ctrace-run data.symbol-size does not match the decoded SWO payload size", + "configured ctrace-run size does not match the decoded SWO payload size", std::move(context), }); } diff --git a/tools/ctrace/src/output/ctf/CtfMetadataWriter.cpp b/tools/ctrace/src/output/ctf/CtfMetadataWriter.cpp index a869ccc29..9632103e1 100644 --- a/tools/ctrace/src/output/ctf/CtfMetadataWriter.cpp +++ b/tools/ctrace/src/output/ctf/CtfMetadataWriter.cpp @@ -194,15 +194,15 @@ static MetadataSymbols collectMetadataSymbols(const std::vector(source.valueSize - 1U); - if (*source.symbolAddress <= std::numeric_limits::max() - extent) { - symbols.dwtAddressEnds[id] = *source.symbolAddress + extent; + if (source.address.has_value()) { + symbols.dwtAddressStarts[id] = *source.address; + const auto extent = static_cast(source.dataSize - 1U); + if (*source.address <= std::numeric_limits::max() - extent) { + symbols.dwtAddressEnds[id] = *source.address + extent; } } } diff --git a/tools/ctrace/src/output/ctf/CtfSchema.h b/tools/ctrace/src/output/ctf/CtfSchema.h index d5d436fa9..dee4dd6f9 100644 --- a/tools/ctrace/src/output/ctf/CtfSchema.h +++ b/tools/ctrace/src/output/ctf/CtfSchema.h @@ -81,8 +81,8 @@ inline constexpr std::array ValueVariants{{ }}; inline constexpr std::string_view ValueTypeRequirements = - "supported data.symbol-type values are 'unsigned int', 'signed int', and 'float'; " - "data.symbol-size must be 1, 2, or 4, and float requires size 4"; + "supported data-type values are 'unsigned', 'signed', and 'float'; " + "size must be 1, 2, or 4, and float requires size 4"; /** @brief Returns the schema descriptor for a value tag. */ constexpr const ValueVariant& valueVariant(ValueTag tag) @@ -97,8 +97,8 @@ constexpr const ValueVariant* valueVariantForTraceRunType(const std::string_view return byteSize == 4U ? &valueVariant(ValueTag::Float32) : nullptr; } - const auto signedInteger = typeName == "signed int"; - if (!signedInteger && typeName != "unsigned int") { + const auto signedInteger = typeName == "signed"; + if (!signedInteger && typeName != "unsigned") { return nullptr; } if (byteSize == 1U) { diff --git a/tools/ctrace/src/tracerun/CtraceRunMeta.cpp b/tools/ctrace/src/tracerun/CtraceRunMeta.cpp index bd729c456..78ed47f6b 100644 --- a/tools/ctrace/src/tracerun/CtraceRunMeta.cpp +++ b/tools/ctrace/src/tracerun/CtraceRunMeta.cpp @@ -277,20 +277,30 @@ static CtraceRunSourceMeta sourceMeta(const TraceRunConfig& config, const TraceR std::uint32_t source, const ProcessorIdentity& processorIdentity) { const auto* dataSetup = reference.type == "dwt" ? referencedDataSetup(config, reference) : nullptr; - return { - reference.type, - processorIdentity.canonicalName(reference.processorName), - static_cast(reference.stream.value_or(0U)), - source, - reference.label, - reference.type == "dwt" ? reference.symbolAddress : std::nullopt, - dataSetup != nullptr ? dataSetup->symbolType.value_or(std::string(TraceRunSchema::kDefaultDwtDataType)) - : std::string(TraceRunSchema::kDefaultDwtDataType), - dataSetup != nullptr ? dataSetup->symbolSize.value_or(TraceRunSchema::kDefaultDwtDataSize) - : TraceRunSchema::kDefaultDwtDataSize, - dataSetup != nullptr ? dataSetup->symbolTypeError : std::nullopt, - dataSetup != nullptr ? dataSetup->symbolSizeError : std::nullopt, - }; + CtraceRunSourceMeta meta; + meta.type = reference.type; + meta.processorName = processorIdentity.canonicalName(reference.processorName); + meta.traceBusId = static_cast(reference.stream.value_or(0U)); + meta.source = source; + meta.label = reference.label; + if (reference.type != "dwt") { + return meta; + } + + meta.address = reference.address; + meta.addressError = reference.addressError; + if (reference.dataType.has_value() || reference.dataTypeError.has_value()) { + meta.dataType = reference.dataType.value_or(std::string(TraceRunSchema::kDefaultDwtDataType)); + meta.dataTypeError = reference.dataTypeError; + } + if (reference.dataSize.has_value() || reference.dataSizeError.has_value()) { + meta.dataSize = reference.dataSize.value_or(TraceRunSchema::kDefaultDwtDataSize); + meta.dataSizeError = reference.dataSizeError; + } else if (dataSetup != nullptr) { + meta.dataSize = dataSetup->size.value_or(TraceRunSchema::kDefaultDwtDataSize); + meta.dataSizeError = dataSetup->sizeError; + } + return meta; } /** @brief Returns or creates accumulated metadata for one processor. */ diff --git a/tools/ctrace/src/tracerun/CtraceRunMeta.h b/tools/ctrace/src/tracerun/CtraceRunMeta.h index 2bfb93dec..d6e1992d2 100644 --- a/tools/ctrace/src/tracerun/CtraceRunMeta.h +++ b/tools/ctrace/src/tracerun/CtraceRunMeta.h @@ -25,11 +25,12 @@ struct CtraceRunSourceMeta { std::uint8_t traceBusId = 0U; std::uint32_t source = 0; std::optional label; - std::optional symbolAddress; - std::string valueType = "unsigned int"; - std::uint64_t valueSize = 4U; - std::optional symbolTypeError; - std::optional symbolSizeError; + std::optional address; + std::string dataType = "unsigned"; + std::uint64_t dataSize = 4U; + std::optional addressError; + std::optional dataTypeError; + std::optional dataSizeError; }; /** @brief Stores normalized timestamp metadata for one processor stream. */ diff --git a/tools/ctrace/src/tracerun/TraceRunConfig.h b/tools/ctrace/src/tracerun/TraceRunConfig.h index ef977aaa6..6117ffeb5 100644 --- a/tools/ctrace/src/tracerun/TraceRunConfig.h +++ b/tools/ctrace/src/tracerun/TraceRunConfig.h @@ -27,7 +27,7 @@ inline constexpr std::array kTimestampPrescalers{{ 64U, }}; inline constexpr std::uint32_t kDefaultTimestampPrescaler = 1U; -inline constexpr std::string_view kDefaultDwtDataType = "unsigned int"; +inline constexpr std::string_view kDefaultDwtDataType = "unsigned"; inline constexpr std::uint8_t kDefaultDwtDataSize = 4U; /** @brief Tests whether a fixed array contains a value. */ @@ -51,7 +51,7 @@ constexpr bool isTimestampPrescaler(std::uint32_t prescaler) /** @brief Tests whether a DWT data type is supported. */ constexpr bool isDwtDataType(const std::string_view& type) { - return type == "unsigned int" || type == "signed int" || type == "float"; + return type == "unsigned" || type == "signed" || type == "float"; } /** @brief Tests whether a DWT data size is supported. */ @@ -105,7 +105,12 @@ struct TraceRunReference { std::optional info; std::optional warning; std::optional error; - std::optional symbolAddress; + std::optional address; + std::optional dataType; + std::optional dataSize; + std::optional addressError; + std::optional dataTypeError; + std::optional dataSizeError; std::optional label; // The reader retains the complete YAML value. CtraceRunMeta validates and // narrows it to the CoreSight ATB trace-ID domain. @@ -134,6 +139,12 @@ inline bool isItmChannelZero(const TraceRunReference& reference) reference.sources.front() == CoreSight::kExcludedItmStimulusPort; } +/** @brief Tests whether a DWT reference describes a generated data route. */ +inline bool isDwtDataReference(const TraceRunReference& reference) +{ + return reference.type == "dwt" && reference.dataSetupIndex.has_value(); +} + /** @brief Tests whether a reference has the fields needed for a decoded route. */ inline bool hasConsumedRouteShape(const TraceRunReference& reference) { @@ -143,7 +154,7 @@ inline bool hasConsumedRouteShape(const TraceRunReference& reference) if (reference.type == "itm") { return !isItmChannelZero(reference); } - return true; + return isDwtDataReference(reference); } /** @brief Tests whether a reference represents timestamp configuration. */ @@ -212,12 +223,10 @@ struct TraceRunTimestampSetup { std::size_t line = 0U; }; -/** @brief Stores data-type fields copied from one DWT data setup. */ +/** @brief Stores size metadata copied from one DWT data setup. */ struct TraceRunDataSetup { - std::optional symbolType = std::nullopt; - std::optional symbolSize = std::nullopt; - std::optional symbolTypeError = std::nullopt; - std::optional symbolSizeError = std::nullopt; + std::optional size = std::nullopt; + std::optional sizeError = std::nullopt; }; /** @brief Stores ITM stimulus-port configuration copied from one trace setup. */ diff --git a/tools/ctrace/src/tracerun/YmlTraceRunConfigReader.cpp b/tools/ctrace/src/tracerun/YmlTraceRunConfigReader.cpp index 4e4712168..7debf1826 100644 --- a/tools/ctrace/src/tracerun/YmlTraceRunConfigReader.cpp +++ b/tools/ctrace/src/tracerun/YmlTraceRunConfigReader.cpp @@ -157,15 +157,40 @@ static std::optional deferredUnsignedAttribute(const std::string& } } -/** @brief Parses an optional unsigned value only when its setup is consumed. */ -static std::optional bestEffortUnsignedAttribute(const std::string& path, const Node& element, - const std::string_view& name, std::uint64_t maximum) +/** @brief Parses an optional unsigned reference field and defers validation errors. */ +static std::optional deferredReferenceUnsignedAttribute( + const std::string& path, const Node& element, const std::string_view& name, std::uint64_t maximum, + std::optional& error) { + const auto node = childNode(element, name); + if (!node) { + return std::nullopt; + } + if (!node.IsScalar()) { + error = "'" + std::string(name) + "' must be a scalar unsigned integer"; + return std::nullopt; + } try { - return optionalUnsignedAttribute(path, element, name, maximum); - } catch (const std::runtime_error&) { + return unsignedValue(path, node, name, node.Scalar(), maximum); + } catch (const std::runtime_error& ex) { + error = ex.what(); + return std::nullopt; + } +} + +/** @brief Parses an optional scalar reference field and defers shape errors. */ +static std::optional deferredReferenceStringAttribute(const Node& element, const std::string_view& name, + std::optional& error) +{ + const auto node = childNode(element, name); + if (!node) { return std::nullopt; } + if (!node.IsScalar()) { + error = "'" + std::string(name) + "' must be a scalar string"; + return std::nullopt; + } + return node.Scalar(); } // ctrace-ref identifies the originating ctrace.yml node. The data index links @@ -332,8 +357,13 @@ static std::optional parseReference(const std::string& path, reference.stream = parseStream(); reference.sources = parseSources(path, element); if (reference.type == "dwt") { - reference.symbolAddress = - bestEffortUnsignedAttribute(path, element, "symbol-address", std::numeric_limits::max()); + reference.address = deferredReferenceUnsignedAttribute(path, element, "address", + std::numeric_limits::max(), + reference.addressError); + reference.dataType = deferredReferenceStringAttribute(element, "data-type", reference.dataTypeError); + reference.dataSize = deferredReferenceUnsignedAttribute(path, element, "size", + std::numeric_limits::max(), + reference.dataSizeError); } reference.label = optionalAttribute(element, "label"); }; @@ -467,18 +497,12 @@ static std::vector parseReferencedDataSetups(const std::strin dataSetups.push_back(std::move(data)); continue; } - const auto type = childNode(item, "symbol-type"); - if (type && !type.IsScalar() && !type.IsNull()) { - data.symbolTypeError = "'data.symbol-type' must be a scalar string"; - } else if (type && !type.IsNull()) { - data.symbolType = optionalAttribute(item, "symbol-type"); - } - const auto size = childNode(item, "symbol-size"); + const auto size = childNode(item, "size"); if (size && !size.IsScalar() && !size.IsNull()) { - data.symbolSizeError = "'data.symbol-size' must be a scalar unsigned integer"; + data.sizeError = "'data.size' must be a scalar unsigned integer"; } else if (size && !size.IsNull()) { - data.symbolSize = deferredUnsignedAttribute(path, item, "symbol-size", std::numeric_limits::max(), - data.symbolSizeError); + data.size = deferredUnsignedAttribute(path, item, "size", std::numeric_limits::max(), + data.sizeError); } dataSetups.push_back(std::move(data)); } 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 efeea0951..5b844ab90 100644 --- a/tools/ctrace/test/data/Arm-reset/Arm.ctrace-run.yml +++ b/tools/ctrace/test/data/Arm-reset/Arm.ctrace-run.yml @@ -9,20 +9,28 @@ ctrace-run: - ctrace-ref: data#0 type: dwt source: 0 - symbol-address: 0x2404AEA4 + address: 0x2404AEA4 + size: 4 + data-type: unsigned label: Sine - ctrace-ref: data#1 type: dwt source: 1 - symbol-address: 0x2404AEA0 + address: 0x2404AEA0 + size: 4 + data-type: unsigned label: Rectangle - ctrace-ref: data#2 type: dwt source: 2 - symbol-address: 0x2404AE9C + address: 0x2404AE9C + size: 4 + data-type: unsigned label: Random - ctrace-ref: data#3 type: dwt source: 3 - symbol-address: 0x24000E88 + address: 0x24000E88 + size: 4 + data-type: unsigned label: Current 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 e54084523..38a82c5b7 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 @@ -7,11 +7,7 @@ ctrace-run: - location: Blinky_cm7|osRtxInfo.kernel.tick access: W output: PC+value - symbol-file: Blinky_cm7.axf - symbol: osRtxInfo.kernel.tick - address: 0x24000024 size: 4 - type: unsigned int synchronization: DWT: 16M timestamps: @@ -26,7 +22,7 @@ ctrace-run: period: 16384 ctrace-refs: - ctrace-ref: CM7/timestamps - type: dwt + type: itm pname: CM7 regs: - name: ITM_TCR @@ -36,7 +32,10 @@ ctrace-run: type: dwt pname: CM7 symbol-file: Blinky_cm7.axf - symbol-address: 0x24000024 + address: 0x24000024 + size: 4 + data-type: unsigned + source: 0 regs: - name: DWT_COMP0 value: 0x24000024 diff --git a/tools/ctrace/test/data/README.md b/tools/ctrace/test/data/README.md index 5271d3657..16b6247c0 100644 --- a/tools/ctrace/test/data/README.md +++ b/tools/ctrace/test/data/README.md @@ -27,7 +27,7 @@ The approved Blinky fixture set is identified by these SHA-256 values: - SWO capture: `f2de14241242697fa0948f1878850cce81575c404233c5c135aa68fc582dc72c` - TB capture: `b0fccabe1a326ffe9fadf12d5c3a205d87628985e5e75a99da23c97d7f33d13b` - Derived CSV: `6138cc60deee8bc16a8a889a6d9156ed76f389c4831afafc5125e4a0d00074cc` -- Trace-run YAML: `deef176a7a924a9c24a126ea460994e86afee3d216e6839da515296758797966` +- Trace-run YAML: `c9816183dde98ded93e57afd44312fb3026e3efdd1681f745bc03f7426713563` The `Arm-reset` fixture is an approved excerpt of an Arm target capture. It starts at the hardware ITM sync immediately before an MCU-reset discontinuity @@ -38,7 +38,7 @@ portable across CI platforms. The trace-run YAML retains only metadata needed by the test. - SWO capture: `8c7ba2b90e42188517c7b793e8b7dd4030fa5455b7a38a2de15d8ca2b47995c9` -- Trace-run YAML: `455c28a490c771d8960b1c5f44785ff2deb05cd6e3a640251c0c7b78f09c5ed1` +- Trace-run YAML: `372e3bf3986fd6860dee5046920cbe129db6fd298c3e22468b3e374c09b8cf52` `trace-run` contains only the small current-schema inputs needed by executable tests. Reader unit tests cover only the fields consumed by ctrace. A C++ diff --git a/tools/ctrace/test/integration/README.md b/tools/ctrace/test/integration/README.md index a9cb07e7a..a890fc895 100644 --- a/tools/ctrace/test/integration/README.md +++ b/tools/ctrace/test/integration/README.md @@ -11,7 +11,5 @@ workflows with fixtures from `test/data` and writes generated output only under the CMake build directory. Small CTest smoke tests separately cover the platform executable and Windows manifest. -The complete Blinky fixture covers exact CSV output and discovery of -unsupported Trace Bus input. The versioned Arm target capture covers recovery -across a real MCU-reset trace discontinuity and verifies that DWT decoding -continues after the next hardware ITM sync. +Fixture provenance and the scenarios covered by each capture are documented in +the [test-data README](../data/README.md). diff --git a/tools/ctrace/test/unit/src/model/TraceSelectionTests.cpp b/tools/ctrace/test/unit/src/model/TraceSelectionTests.cpp index f9461d311..9d95870da 100644 --- a/tools/ctrace/test/unit/src/model/TraceSelectionTests.cpp +++ b/tools/ctrace/test/unit/src/model/TraceSelectionTests.cpp @@ -22,8 +22,8 @@ TEST(CtraceUnitTests, testTraceSelection) EXPECT_TRUE(CoreSight::isItmStimulusPort(0U)); EXPECT_TRUE(CoreSight::isItmStimulusPort(31U)); EXPECT_FALSE(CoreSight::isItmStimulusPort(32U)); - EXPECT_TRUE(TraceRunSchema::isDwtDataType("unsigned int")); - EXPECT_TRUE(TraceRunSchema::isDwtDataType("signed int")); + EXPECT_TRUE(TraceRunSchema::isDwtDataType("unsigned")); + EXPECT_TRUE(TraceRunSchema::isDwtDataType("signed")); EXPECT_TRUE(TraceRunSchema::isDwtDataType("float")); TraceRunReference timestampReference; timestampReference.ctraceRef = "timestamps"; diff --git a/tools/ctrace/test/unit/src/output/OutputRequirementsTests.cpp b/tools/ctrace/test/unit/src/output/OutputRequirementsTests.cpp index 9434254e1..9c851a62c 100644 --- a/tools/ctrace/test/unit/src/output/OutputRequirementsTests.cpp +++ b/tools/ctrace/test/unit/src/output/OutputRequirementsTests.cpp @@ -46,10 +46,11 @@ static TraceRunConfig backendRequirementsConfig() TraceRunConfig config; config.path = "BackendRequirements.ctrace-run.yml"; auto setup = TraceRunTestSupport::makeTimestampSetup(std::nullopt, 400000000U, 1U); - setup.data.push_back(TraceRunDataSetup{"double", 4U}); + setup.data.push_back(TraceRunDataSetup{4U}); config.setups.push_back(setup); auto reference = TraceRunTestSupport::makeReference("dwt", std::nullopt, std::nullopt, {0U}, "opaque/dwt"); reference.dataSetupIndex = 0U; + reference.dataType = "double"; config.references.push_back(reference); return config; } @@ -115,14 +116,17 @@ TEST(CtraceUnitTests, testDwtDataMetadataDefaultsAndValidation) auto setup = TraceRunTestSupport::makeTimestampSetup(std::nullopt, 100000000U, 1U); setup.data = { TraceRunDataSetup{}, - TraceRunDataSetup{"unsigned int", 1U}, - TraceRunDataSetup{"signed int", 2U}, + TraceRunDataSetup{1U}, + TraceRunDataSetup{2U}, }; config.setups.push_back(std::move(setup)); for (std::uint32_t comparator = 0U; comparator < 3U; ++comparator) { auto reference = TraceRunTestSupport::makeReference("dwt", std::nullopt, std::nullopt, {comparator}, "opaque/dwt-" + std::to_string(comparator)); reference.dataSetupIndex = comparator; + if (comparator == 2U) { + reference.dataType = "signed"; + } config.references.push_back(std::move(reference)); } const auto meta = CtraceRunMeta::fromConfig(config); @@ -134,17 +138,17 @@ TEST(CtraceUnitTests, testDwtDataMetadataDefaultsAndValidation) << "valid or missing DWT metadata must not disable CSV or CTF"; ASSERT_TRUE(configurationDiagnostics.events().empty()) << "valid or missing DWT metadata must not produce diagnostics"; - ASSERT_TRUE(outputPlan.ctf->sources.size() == 3U && outputPlan.ctf->sources[0].valueType == "unsigned int" && - outputPlan.ctf->sources[0].valueSize == 4U && outputPlan.ctf->sources[1].valueType == "unsigned int" && - outputPlan.ctf->sources[1].valueSize == 1U && outputPlan.ctf->sources[2].valueType == "signed int" && - outputPlan.ctf->sources[2].valueSize == 2U) - << "CTF data.symbol-type/data.symbol-size defaults or explicit values mismatch"; + ASSERT_TRUE(outputPlan.ctf->sources.size() == 3U && outputPlan.ctf->sources[0].dataType == "unsigned" && + outputPlan.ctf->sources[0].dataSize == 4U && outputPlan.ctf->sources[1].dataType == "unsigned" && + outputPlan.ctf->sources[1].dataSize == 1U && outputPlan.ctf->sources[2].dataType == "signed" && + outputPlan.ctf->sources[2].dataSize == 2U) + << "CTF data-type/size defaults or explicit values mismatch"; - config.setups[0].data[1].symbolSize = 0U; + config.setups[0].data[1].size = 0U; CollectingDiagnosticSink invalidSizeDiagnostics; const auto invalidSizePlan = planOutputs(allRequest, "DwtSize.SWO.raw", config, invalidSizeDiagnostics); ASSERT_TRUE(invalidSizePlan.csv.has_value() && !invalidSizePlan.ctf.has_value()) - << "invalid data.symbol-size must disable only CTF"; + << "invalid ctrace-setup data.size must disable only CTF"; invalidSizeDiagnostics.singleEvent(); } @@ -156,45 +160,66 @@ TEST(CtraceUnitTests, testOutputRequirementsAreBackendSpecific) CollectingDiagnosticSink invalidTypeCsvDiagnostics; const auto invalidTypeCsv = planOutputs(csvRequest, "BackendRequirements.SWO.raw", config, invalidTypeCsvDiagnostics); ASSERT_TRUE(invalidTypeCsv.csv.has_value() && !invalidTypeCsv.ctf.has_value()) - << "an invalid explicit data.symbol-type must not disable CSV"; - ASSERT_TRUE(invalidTypeCsvDiagnostics.events().empty()) << "CSV must not inspect CTF-only data.symbol-type metadata"; + << "an invalid explicit data-type must not disable CSV"; + ASSERT_TRUE(invalidTypeCsvDiagnostics.events().empty()) << "CSV must not inspect CTF-only data-type metadata"; const auto allRequest = outputRequest(true, true); CollectingDiagnosticSink invalidTypeAllDiagnostics; const auto invalidTypeAll = planOutputs(allRequest, "BackendRequirements.SWO.raw", config, invalidTypeAllDiagnostics); ASSERT_TRUE(invalidTypeAll.csv.has_value() && !invalidTypeAll.ctf.has_value()) - << "an invalid explicit data.symbol-type must disable only CTF for --all"; - invalidTypeAllDiagnostics.singleEvent(); + << "an invalid explicit data-type must disable only CTF for --all"; + EXPECT_EQ(invalidTypeAllDiagnostics.singleEvent().message, + "CTF output cannot use ctrace-run data-type 'double'; supported data-type values are 'unsigned', " + "'signed', and 'float'; size must be 1, 2, or 4, and float requires size 4"); + EXPECT_TRUE(invalidTypeAllDiagnostics.containsContext("backend", "ctf")); - config.setups[0].data[0].symbolType.reset(); - config.setups[0].data[0].symbolSize.reset(); + config.references[0].dataType.reset(); CollectingDiagnosticSink missingTypeDiagnostics; const auto missingType = planOutputs(allRequest, "BackendRequirements.SWO.raw", config, missingTypeDiagnostics); ASSERT_TRUE(missingType.csv.has_value() && missingType.ctf.has_value()) - << "missing data.symbol-type/data.symbol-size must use CTF defaults and leave CSV enabled"; + << "missing data-type must use the CTF default and leave CSV enabled"; ASSERT_TRUE( (missingType.csv->outputPath == std::filesystem::path("BackendRequirements.SWO.csv") && missingType.ctf->outputDirectory == std::filesystem::path("BackendRequirements.ctf") && missingType.ctf->traceCompassXmlPath == std::filesystem::path("BackendRequirements.SWO.traceanalysis.xml") && missingType.ctf->coreClockHz == 400000000U && missingType.ctf->sources.size() == 1U && - missingType.ctf->sources[0].valueType == "unsigned int" && missingType.ctf->sources[0].valueSize == 4U)) + missingType.ctf->sources[0].dataType == "unsigned" && missingType.ctf->sources[0].dataSize == 4U)) << "output preflight must resolve artifact paths, clock, routes, and defaults"; ASSERT_TRUE(missingTypeDiagnostics.events().empty()) - << "missing optional data.symbol-type/data.symbol-size must not produce diagnostics"; + << "missing optional data-type must not produce diagnostics"; - config.setups[0].data[0].symbolTypeError = "data.symbol-type must be scalar"; + config.references[0].dataTypeError = "data-type must be scalar"; CollectingDiagnosticSink malformedTypeDiagnostics; const auto malformedType = planOutputs(allRequest, "BackendRequirements.SWO.raw", config, malformedTypeDiagnostics); ASSERT_TRUE(malformedType.csv.has_value() && !malformedType.ctf.has_value()) - << "malformed data.symbol-type must disable only CTF"; + << "malformed data-type must disable only CTF"; - config.setups[0].data[0].symbolTypeError.reset(); - config.setups[0].data[0].symbolSizeError = "data.symbol-size must be unsigned"; + config.references[0].dataTypeError.reset(); + config.references[0].dataSizeError = "size must be unsigned"; CollectingDiagnosticSink malformedSizeDiagnostics; const auto malformedSize = planOutputs(allRequest, "BackendRequirements.SWO.raw", config, malformedSizeDiagnostics); ASSERT_TRUE(malformedSize.csv.has_value() && !malformedSize.ctf.has_value()) - << "malformed data.symbol-size must disable only CTF"; + << "malformed size must disable only CTF"; malformedSizeDiagnostics.singleEvent(); + + config.references[0].dataSizeError.reset(); + config.references[0].dataType = "signed"; + config.references[0].dataSize = 1U; + CollectingDiagnosticSink currentMetadataDiagnostics; + const auto currentMetadata = planOutputs(allRequest, "BackendRequirements.SWO.raw", config, + currentMetadataDiagnostics); + ASSERT_TRUE(currentMetadata.ctf.has_value() && currentMetadata.ctf->sources[0].dataType == "signed" && + currentMetadata.ctf->sources[0].dataSize == 1U) + << "reference data-type/size must be retained for CTF"; + ASSERT_TRUE(currentMetadataDiagnostics.events().empty()); + + config.references[0].addressError = "address must be unsigned"; + CollectingDiagnosticSink malformedAddressDiagnostics; + const auto malformedAddress = planOutputs(allRequest, "BackendRequirements.SWO.raw", config, + malformedAddressDiagnostics); + ASSERT_TRUE(malformedAddress.csv.has_value() && !malformedAddress.ctf.has_value()) + << "malformed address must disable only CTF"; + malformedAddressDiagnostics.singleEvent(); } TEST(CtraceUnitTests, testCtfOutputRequiresAValidClock) @@ -244,7 +269,7 @@ TEST(CtraceUnitTests, testOutputPreflightRejectsAmbiguousRoutesForCtfOnly) TraceRunConfig config; config.path = "AmbiguousRoutes.ctrace-run.yml"; auto setup = TraceRunTestSupport::makeTimestampSetup(std::nullopt, 400000000U, 1U); - setup.data.push_back(TraceRunDataSetup{"unsigned int", 4U}); + setup.data.push_back(TraceRunDataSetup{4U}); config.setups.push_back(setup); auto first = TraceRunTestSupport::makeReference("dwt", std::nullopt, 1U, {0U}, "opaque/dwt-a"); @@ -297,15 +322,15 @@ TEST(CtraceUnitTests, testOutputPreflightRejectsAmbiguousRoutesForCtfOnly) ASSERT_TRUE(selectedDiagnostics.events().empty()) << "an unambiguous selected route must not produce preflight diagnostics"; - config.setups[0].data.push_back(TraceRunDataSetup{"unsigned int", 2U}); + config.setups[0].data.push_back(TraceRunDataSetup{2U}); config.references[1].stream = 1U; config.references[1].ctraceRef = "opaque/dwt-b"; config.references[1].dataSetupIndex = 1U; CollectingDiagnosticSink sizeDiagnostics; const auto csvPlan = planOutputs(outputRequest(true, false), "captures/AmbiguousRoutes.SWO.raw", config, sizeDiagnostics); - ASSERT_TRUE(csvPlan.csv.has_value()) << "CTF-only data.symbol-size metadata must not disable CSV"; - ASSERT_TRUE(sizeDiagnostics.events().empty()) << "CSV must not inspect CTF-only data.symbol-size metadata"; + ASSERT_TRUE(csvPlan.csv.has_value()) << "CTF-only data.size metadata must not disable CSV"; + ASSERT_TRUE(sizeDiagnostics.events().empty()) << "CSV must not inspect CTF-only data.size metadata"; } TEST(CtraceUnitTests, testOutputRequirementsValidateDefaultClockWithoutRoutes) diff --git a/tools/ctrace/test/unit/src/output/ctf/CtfBundleOutputTests.cpp b/tools/ctrace/test/unit/src/output/ctf/CtfBundleOutputTests.cpp index 1413abcb7..2bb8692e5 100644 --- a/tools/ctrace/test/unit/src/output/ctf/CtfBundleOutputTests.cpp +++ b/tools/ctrace/test/unit/src/output/ctf/CtfBundleOutputTests.cpp @@ -94,9 +94,9 @@ static ResolvedTraceSource resolvedSource(const CtraceRunSourceMeta& source) source.source, source.traceBusId, source.label, - source.symbolAddress, - source.valueType, - static_cast(source.valueSize), + source.address, + source.dataType, + static_cast(source.dataSize), }; } @@ -180,19 +180,18 @@ TEST(CtraceUnitTests, testCtfBundleOutputUsesCtraceRunMeta) auto signedByteReference = TraceRunTestSupport::makeReference("dwt", std::nullopt, 7U, {0U}, "opaque/signed-byte"); signedByteReference.dataSetupIndex = 0U; signedByteReference.label = "Sine"; + signedByteReference.dataType = "signed"; + signedByteReference.dataSize = 1U; traceRun.references.push_back(signedByteReference); auto reference = TraceRunTestSupport::makeReference("dwt", std::nullopt, std::nullopt, {2U}, "opaque/current"); reference.dataSetupIndex = 2U; reference.label = "Current\n\t\"\\\x01"; - reference.symbolAddress = 0x24000e88U; + reference.address = 0x24000e88U; + reference.dataType = "signed"; + reference.dataSize = 4U; traceRun.references.push_back(reference); TraceRunSetup setup; - setup.data.resize(3U); - setup.data[0].symbolType = "signed int"; - setup.data[0].symbolSize = 1U; - setup.data[2].symbolType = "signed int"; - setup.data[2].symbolSize = 4U; setup.timestamps = TraceRunTimestampSetup{280000000U, 1U}; traceRun.setups.push_back(std::move(setup)); @@ -203,7 +202,7 @@ TEST(CtraceUnitTests, testCtfBundleOutputUsesCtraceRunMeta) auto options = std::move(*outputPlan.ctf); ASSERT_TRUE(!options.sources.empty()) << "resolved CTF source missing"; ASSERT_TRUE(options.sources.front().traceBusId == 7U) << "resolved CTF source must retain its Trace Bus ID"; - ASSERT_TRUE(options.sources.front().valueType == "signed int") << "resolved CTF source must retain its value type"; + ASSERT_TRUE(options.sources.front().dataType == "signed") << "resolved CTF source must retain its data type"; CtfBundleOutput output(std::move(options)); output.start(); output.writeEvent(atCycle(onStream(TraceEvent{DwtDataTraceEvent{0U, 1U, 0xffU, AccessType::Write}}, 7U), 100U)); @@ -213,9 +212,9 @@ TEST(CtraceUnitTests, testCtfBundleOutputUsesCtraceRunMeta) const auto stream = readTestBinaryFile(outputDir / "stream_0"); ASSERT_TRUE(metadata.find("freq = 280000000") != std::string::npos) << "CTF trace-run clock mismatch"; - ASSERT_TRUE(metadata.find("cmsis_dwt0_value_type = \"signed int\"") != std::string::npos) + ASSERT_TRUE(metadata.find("cmsis_dwt0_value_type = \"signed\"") != std::string::npos) << "CTF signed-byte source type mismatch"; - ASSERT_TRUE(metadata.find("cmsis_dwt2_value_type = \"signed int\"") != std::string::npos) + ASSERT_TRUE(metadata.find("cmsis_dwt2_value_type = \"signed\"") != std::string::npos) << "CTF trace-run type mismatch"; ASSERT_TRUE(metadata.find("cmsis_dwt2_address_start = \"0x24000E88\"") != std::string::npos) << "CTF trace-run start address mismatch"; @@ -245,9 +244,9 @@ TEST(CtraceUnitTests, testCtfBundleOutputDefaultsDwtValueType) defaultReference.dataSetupIndex = 0U; defaultTraceRun.references.push_back(defaultReference); const auto defaultMeta = CtraceRunMeta::fromConfig(defaultTraceRun); - ASSERT_TRUE(defaultMeta.sources().size() == 1U && defaultMeta.sources().front().valueType == "unsigned int" && - defaultMeta.sources().front().valueSize == 4U) - << "missing DWT data.symbol-type/data.symbol-size must default to unsigned int/4"; + ASSERT_TRUE(defaultMeta.sources().size() == 1U && defaultMeta.sources().front().dataType == "unsigned" && + defaultMeta.sources().front().dataSize == 4U) + << "missing DWT data-type/size must default to unsigned/4"; const auto defaultOutputDir = root / "default"; auto defaultOptions = makeCtfBundleConfig(defaultOutputDir, 1000000U); @@ -265,10 +264,10 @@ TEST(CtraceUnitTests, testCtfBundleOutputDefaultsDwtValueType) << "DWT size mismatch must be reported once per channel"; TraceRunConfig signedTraceRun; - TraceRunSetup signedSetup; - signedSetup.data.push_back(TraceRunDataSetup{"signed int", 1U}); - signedTraceRun.setups.push_back(std::move(signedSetup)); - signedTraceRun.references.push_back(defaultReference); + auto signedReference = defaultReference; + signedReference.dataType = "signed"; + signedReference.dataSize = 1U; + signedTraceRun.references.push_back(signedReference); const auto signedMeta = CtraceRunMeta::fromConfig(signedTraceRun); ASSERT_TRUE(signedMeta.sources().size() == 1U) << "signed DWT source missing"; const auto signedOutputDir = root / "signed"; @@ -280,17 +279,16 @@ TEST(CtraceUnitTests, testCtfBundleOutputDefaultsDwtValueType) signedOutput.writeEvent(TraceEvent{DwtDataTraceEvent{0U, 1U, 0xffU, AccessType::Write}}); signedOutput.stop(); ASSERT_TRUE(readFirstCtfDwtValueTag(signedOutputDir / "stream_0") == 0U) - << "explicit signed int/1 metadata must select the signed 8-bit CTF variant"; + << "explicit signed/1 metadata must select the signed 8-bit CTF variant"; ASSERT_TRUE(signedDiagnostics.events().empty()) << "matching DWT sizes must not produce a warning"; TraceRunConfig traceRun; traceRun.path = "ambiguous-streams.ctrace-run.yml"; - TraceRunSetup setup; - setup.data.push_back(TraceRunDataSetup{"signed int", 4U}); - traceRun.setups.push_back(std::move(setup)); auto first = TraceRunTestSupport::makeReference("dwt", std::nullopt, 1U, {0U}, "opaque/dwt-route"); first.dataSetupIndex = 0U; first.label = "core-one"; + first.dataType = "signed"; + first.dataSize = 4U; TraceRunReference second = first; second.stream = 2U; second.label = "core-two"; diff --git a/tools/ctrace/test/unit/src/output/ctf/CtfEncoderTests.cpp b/tools/ctrace/test/unit/src/output/ctf/CtfEncoderTests.cpp index 99a7ae444..2334208c5 100644 --- a/tools/ctrace/test/unit/src/output/ctf/CtfEncoderTests.cpp +++ b/tools/ctrace/test/unit/src/output/ctf/CtfEncoderTests.cpp @@ -63,8 +63,8 @@ static ResolvedTraceSource resolvedDwtSource(std::uint32_t comparator, std::uint source.type = "dwt"; source.source = comparator; source.traceBusId = traceBusId; - source.valueType = std::move(type); - source.valueSize = size; + source.dataType = std::move(type); + source.dataSize = size; return source; } @@ -208,11 +208,11 @@ TEST(CtraceUnitTests, testCtfEncoderWritesAllDwtValueVariants) const TemporaryTestPath temporaryPath("ctrace-ctf-value-variants-test"); temporaryPath.createDirectory(); std::vector sources{ - resolvedDwtSource(0U, 1U, "signed int", 2U), resolvedDwtSource(1U, 1U, "float", 4U), - resolvedDwtSource(2U, 1U, "signed int", 4U), resolvedDwtSource(3U, 7U, "unsigned int", 1U), - resolvedDwtSource(4U, 1U, "signed int", 1U), resolvedDwtSource(4U, 2U, "signed int", 1U), + resolvedDwtSource(0U, 1U, "signed", 2U), resolvedDwtSource(1U, 1U, "float", 4U), + resolvedDwtSource(2U, 1U, "signed", 4U), resolvedDwtSource(3U, 7U, "unsigned", 1U), + resolvedDwtSource(4U, 1U, "signed", 1U), resolvedDwtSource(4U, 2U, "signed", 1U), }; - sources.push_back(resolvedDwtSource(99U, 7U, "unsigned int", 1U)); + sources.push_back(resolvedDwtSource(99U, 7U, "unsigned", 1U)); CtfEncoder encoder(CtfEncoderConfig{1000000U, TraceSelection{{"dwt"}, {}}, sources}); encoder.start(temporaryPath.path()); @@ -269,7 +269,7 @@ TEST(CtraceUnitTests, testCtfEncoderRejectsConflictingUnformattedDwtRoutes) const TemporaryTestPath temporaryPath("ctrace-ctf-conflicting-dwt-routes-test"); temporaryPath.createDirectory(); const std::vector sources{ - resolvedDwtSource(0U, 1U, "signed int", 1U), + resolvedDwtSource(0U, 1U, "signed", 1U), resolvedDwtSource(0U, 2U, "float", 4U), }; CtfEncoder encoder(CtfEncoderConfig{1000000U, TraceSelection{{"dwt"}, {}}, sources}); diff --git a/tools/ctrace/test/unit/src/output/ctf/CtfMetadataWriterTests.cpp b/tools/ctrace/test/unit/src/output/ctf/CtfMetadataWriterTests.cpp index 43962bdd8..8f1aec669 100644 --- a/tools/ctrace/test/unit/src/output/ctf/CtfMetadataWriterTests.cpp +++ b/tools/ctrace/test/unit/src/output/ctf/CtfMetadataWriterTests.cpp @@ -31,14 +31,14 @@ TEST(CtraceUnitTests, testCtfMetadataWriterEscapesAndDeduplicatesSourceLabels) const TemporaryTestPath path("ctrace-metadata-writer"); path.createDirectory(); const std::vector sources{ - {"itm", 1U, 1U, std::string("ITM3"), std::nullopt, "unsigned int", 4U}, - {"itm", 2U, 1U, std::string("ITM3_1"), std::nullopt, "unsigned int", 4U}, - {"itm", 3U, 1U, std::string("ITM3"), std::nullopt, "unsigned int", 4U}, - {"itm", 4U, 1U, std::string("line\rbreak"), std::nullopt, "unsigned int", 4U}, - {"itm", 5U, 1U, std::nullopt, std::nullopt, "unsigned int", 4U}, - {"itm", 6U, 1U, std::string("ITM3"), std::nullopt, "unsigned int", 4U}, - {"future", 7U, 1U, std::string("ignored"), std::nullopt, "unsigned int", 4U}, - {"dwt", 0U, 1U, std::nullopt, std::numeric_limits::max(), "unsigned int", 4U}, + {"itm", 1U, 1U, std::string("ITM3"), std::nullopt, "unsigned", 4U}, + {"itm", 2U, 1U, std::string("ITM3_1"), std::nullopt, "unsigned", 4U}, + {"itm", 3U, 1U, std::string("ITM3"), std::nullopt, "unsigned", 4U}, + {"itm", 4U, 1U, std::string("line\rbreak"), std::nullopt, "unsigned", 4U}, + {"itm", 5U, 1U, std::nullopt, std::nullopt, "unsigned", 4U}, + {"itm", 6U, 1U, std::string("ITM3"), std::nullopt, "unsigned", 4U}, + {"future", 7U, 1U, std::string("ignored"), std::nullopt, "unsigned", 4U}, + {"dwt", 0U, 1U, std::nullopt, std::numeric_limits::max(), "unsigned", 4U}, }; CtfMetadataWriter::write(path.path(), "00000000-0000-4000-8000-000000000000", 1000000U, sources, diff --git a/tools/ctrace/test/unit/src/output/ctf/CtfSchemaTests.cpp b/tools/ctrace/test/unit/src/output/ctf/CtfSchemaTests.cpp index 45cd49180..73cc451cc 100644 --- a/tools/ctrace/test/unit/src/output/ctf/CtfSchemaTests.cpp +++ b/tools/ctrace/test/unit/src/output/ctf/CtfSchemaTests.cpp @@ -68,9 +68,9 @@ TEST(CtraceUnitTests, testCtfValueTypes) std::uint8_t size; }; const std::vector supported{ - {"unsigned int", CtfSchema::ValueTag::Unsigned8, 1U}, {"unsigned int", CtfSchema::ValueTag::Unsigned16, 2U}, - {"unsigned int", CtfSchema::ValueTag::Unsigned32, 4U}, {"signed int", CtfSchema::ValueTag::Signed8, 1U}, - {"signed int", CtfSchema::ValueTag::Signed16, 2U}, {"signed int", CtfSchema::ValueTag::Signed32, 4U}, + {"unsigned", CtfSchema::ValueTag::Unsigned8, 1U}, {"unsigned", CtfSchema::ValueTag::Unsigned16, 2U}, + {"unsigned", CtfSchema::ValueTag::Unsigned32, 4U}, {"signed", CtfSchema::ValueTag::Signed8, 1U}, + {"signed", CtfSchema::ValueTag::Signed16, 2U}, {"signed", CtfSchema::ValueTag::Signed32, 4U}, {"float", CtfSchema::ValueTag::Float32, 4U}, }; for (const auto& item : supported) { @@ -89,7 +89,7 @@ TEST(CtraceUnitTests, testCtfValueTypes) ASSERT_TRUE(CtfSchema::valueVariantForTraceRunType("double", 4U) == nullptr) << "unsupported CTF type was accepted"; ASSERT_TRUE(CtfSchema::valueVariantForTraceRunType("float", 2U) == nullptr) << "incompatible CTF float width was accepted"; - ASSERT_TRUE(CtfSchema::valueVariantForTraceRunType("unsigned int", 8U) == nullptr) + ASSERT_TRUE(CtfSchema::valueVariantForTraceRunType("unsigned", 8U) == nullptr) << "invalid CTF data size was accepted"; } diff --git a/tools/ctrace/test/unit/src/tracerun/CtraceRunMetaTests.cpp b/tools/ctrace/test/unit/src/tracerun/CtraceRunMetaTests.cpp index 59e3f1161..437db2ada 100644 --- a/tools/ctrace/test/unit/src/tracerun/CtraceRunMetaTests.cpp +++ b/tools/ctrace/test/unit/src/tracerun/CtraceRunMetaTests.cpp @@ -57,7 +57,9 @@ TEST(CtraceUnitTests, testCtraceRunMetaRejectsInvalidReferences) const char* message; }; std::vector cases; - cases.push_back({makeReference("dwt", std::nullopt, 1U, {1U, 1U}), "duplicate value in source array"}); + auto duplicateDwtSource = makeReference("dwt", std::nullopt, 1U, {1U, 1U}); + duplicateDwtSource.dataSetupIndex = 0U; + cases.push_back({duplicateDwtSource, "duplicate value in source 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({makeReference("itm", std::nullopt, 1U, {32U}), "ITM source must be between 0 and 31"}); @@ -92,7 +94,9 @@ TEST(CtraceUnitTests, testCtraceRunMetaExpandsItmAndDwtSourceArrays) { TraceRunConfig config; config.references.push_back(makeReference("itm", std::nullopt, 1U, {1U, 2U})); - config.references.push_back(makeReference("dwt", std::nullopt, 1U, {3U, 4U})); + auto dwtReference = makeReference("dwt", std::nullopt, 1U, {3U, 4U}); + dwtReference.dataSetupIndex = 0U; + config.references.push_back(dwtReference); const auto meta = CtraceRunMeta::fromConfig(config); ASSERT_EQ(meta.sources().size(), 4U); @@ -184,7 +188,9 @@ TEST(CtraceUnitTests, testCtraceRunMetaWarnsForSingleSetupIdentityConflicts) TraceRunConfig referencesOnly; referencesOnly.path = "trace.yml"; referencesOnly.references = unnamedSetup.references; - referencesOnly.references.push_back(makeReference("dwt", std::nullopt, 3U, {0U})); + auto unnamedDwtReference = makeReference("dwt", std::nullopt, 3U, {0U}); + unnamedDwtReference.dataSetupIndex = 0U; + referencesOnly.references.push_back(unnamedDwtReference); EXPECT_TRUE(metaRejects(referencesOnly, "pname is required for every ctrace-ref")); referencesOnly.references.pop_back(); @@ -249,30 +255,30 @@ TEST(CtraceUnitTests, testCtraceRunMetaResolvesDwtDataAndDefaults) config.path = "trace.yml"; auto setup = makeTimestampSetup("core"); setup.data.resize(2U); - setup.data[0].symbolType = "signed int"; - setup.data[0].symbolSize = 2U; - setup.data[0].symbolTypeError = "type warning"; - setup.data[0].symbolSizeError = "size warning"; + setup.data[0].size = 2U; + setup.data[0].sizeError = "size warning"; config.setups.push_back(setup); auto configured = makeReference("dwt", "core", 1U, {0U}, "core/data#0"); configured.dataSetupIndex = 0U; - configured.symbolAddress = 0x20000000U; + configured.address = 0x20000000U; + configured.dataType = "float"; + configured.dataSize = 4U; auto missingIndex = makeReference("dwt", "core", 1U, {1U}, "core/data"); auto outOfRange = makeReference("dwt", "core", 1U, {2U}, "core/data#9"); outOfRange.dataSetupIndex = 9U; config.references = {configured, missingIndex, outOfRange}; const auto meta = CtraceRunMeta::fromConfig(config); - ASSERT_EQ(meta.sources().size(), 3U); + ASSERT_EQ(meta.sources().size(), 2U); EXPECT_EQ(meta.configPath(), "trace.yml"); - EXPECT_EQ(meta.sources()[0].valueType, "signed int"); - EXPECT_EQ(meta.sources()[0].valueSize, 2U); - EXPECT_EQ(meta.sources()[0].symbolAddress, std::optional(0x20000000U)); - EXPECT_EQ(meta.sources()[0].symbolTypeError, std::optional("type warning")); - EXPECT_EQ(meta.sources()[0].symbolSizeError, std::optional("size warning")); - EXPECT_EQ(meta.sources()[1].valueType, std::string(TraceRunSchema::kDefaultDwtDataType)); - EXPECT_EQ(meta.sources()[2].valueSize, TraceRunSchema::kDefaultDwtDataSize); + EXPECT_EQ(meta.sources()[0].dataType, "float"); + EXPECT_EQ(meta.sources()[0].dataSize, 4U); + EXPECT_EQ(meta.sources()[0].address, std::optional(0x20000000U)); + EXPECT_FALSE(meta.sources()[0].dataTypeError.has_value()); + EXPECT_FALSE(meta.sources()[0].dataSizeError.has_value()); + EXPECT_EQ(meta.sources()[1].dataType, std::string(TraceRunSchema::kDefaultDwtDataType)); + EXPECT_EQ(meta.sources()[1].dataSize, TraceRunSchema::kDefaultDwtDataSize); } TEST(CtraceUnitTests, testCtraceRunMetaIgnoresInactiveSetups) @@ -287,5 +293,19 @@ TEST(CtraceUnitTests, testCtraceRunMetaIgnoresInactiveSetups) const auto meta = CtraceRunMeta::fromConfig(config); EXPECT_EQ(meta.processorCount(), 1U); - EXPECT_EQ(meta.sources().front().valueType, std::string(TraceRunSchema::kDefaultDwtDataType)); + EXPECT_EQ(meta.sources().front().dataType, std::string(TraceRunSchema::kDefaultDwtDataType)); +} + +TEST(CtraceUnitTests, testCtraceRunMetaDoesNotExposeDwtControlReferencesAsDataSources) +{ + TraceRunConfig config; + auto data = makeReference("dwt", "core", 1U, {0U}, "core/data#0"); + data.dataSetupIndex = 0U; + auto start = makeReference("dwt", "core", 1U, {1U}, "core/instructions/start"); + config.references = {data, start}; + + const auto meta = CtraceRunMeta::fromConfig(config); + ASSERT_EQ(meta.sources().size(), 1U); + EXPECT_EQ(meta.sources().front().source, 0U); + EXPECT_EQ(meta.processorCount(), 1U); } diff --git a/tools/ctrace/test/unit/src/tracerun/TraceRunConfigReaderTests.cpp b/tools/ctrace/test/unit/src/tracerun/TraceRunConfigReaderTests.cpp index 5a86a9084..af983de88 100644 --- a/tools/ctrace/test/unit/src/tracerun/TraceRunConfigReaderTests.cpp +++ b/tools/ctrace/test/unit/src/tracerun/TraceRunConfigReaderTests.cpp @@ -85,8 +85,7 @@ TEST(CtraceUnitTests, TraceRunReaderParsesConsumedFields) itm: enable: 0x00000006 data: - - symbol-type: signed int - symbol-size: 1 + - size: 4 ctrace-refs: - ctrace-ref: core0/itm pname: core0 @@ -99,7 +98,9 @@ TEST(CtraceUnitTests, TraceRunReaderParsesConsumedFields) type: dwt stream: 2 source: 0 - symbol-address: 0x20000100 + address: 0x20000100 + data-type: signed + size: 1 label: Current )yml"); ASSERT_EQ(config.references.size(), 2U); @@ -122,9 +123,9 @@ TEST(CtraceUnitTests, TraceRunReaderParsesConsumedFields) EXPECT_EQ(dwt.type, "dwt"); EXPECT_EQ(dwt.traceBusId, 2U); EXPECT_EQ(dwt.source, 0U); - EXPECT_EQ(dwt.valueType, "signed int"); - EXPECT_EQ(dwt.valueSize, 1U); - EXPECT_EQ(dwt.symbolAddress, std::optional(0x20000100U)); + EXPECT_EQ(dwt.dataType, "signed"); + EXPECT_EQ(dwt.dataSize, 1U); + EXPECT_EQ(dwt.address, std::optional(0x20000100U)); EXPECT_EQ(dwt.label, std::optional("Current")); } @@ -151,6 +152,68 @@ TEST(CtraceUnitTests, TraceRunReaderAcceptsScalarAndArraySourceNotation) EXPECT_TRUE(config.references[2].sources == std::vector({4U, 5U})); } +TEST(CtraceUnitTests, TraceRunReaderUsesReferencedSetupSizeAsFallback) +{ + TraceRunFixture file("ctrace-run-reader-setup-size-test"); + const auto config = file.read(R"yml(ctrace-run: + ctrace-setup: + - data: + - size: 2 + ctrace-refs: + - ctrace-ref: data#0 + type: dwt + source: 0 + data-type: signed +)yml"); + + const auto meta = CtraceRunMeta::fromConfig(config); + ASSERT_EQ(meta.sources().size(), 1U); + EXPECT_FALSE(meta.sources()[0].address.has_value()); + EXPECT_EQ(meta.sources()[0].dataType, "signed"); + EXPECT_EQ(meta.sources()[0].dataSize, 2U); +} + +TEST(CtraceUnitTests, TraceRunReaderIgnoresUnsupportedMetadataNames) +{ + TraceRunFixture file("ctrace-run-reader-unsupported-data-metadata-test"); + const auto config = file.read(R"yml(ctrace-run: + ctrace-setup: + - data: + - symbol-type: signed int + symbol-size: 1 + ctrace-refs: + - ctrace-ref: data#0 + type: dwt + source: 0 + symbol-address: 0x20000100 +)yml"); + + const auto meta = CtraceRunMeta::fromConfig(config); + ASSERT_EQ(meta.sources().size(), 1U); + EXPECT_FALSE(meta.sources()[0].address.has_value()); + EXPECT_EQ(meta.sources()[0].dataType, "unsigned"); + EXPECT_EQ(meta.sources()[0].dataSize, 4U); +} + +TEST(CtraceUnitTests, TraceRunReaderDefersMalformedDwtMetadataToOutputPlanning) +{ + TraceRunFixture file("ctrace-run-reader-malformed-data-metadata-test"); + const auto config = file.read(R"yml(ctrace-run: + ctrace-refs: + - ctrace-ref: data#0 + type: dwt + source: 0 + address: [] + 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()); +} + TEST(CtraceUnitTests, TraceRunReaderAcceptsProcessorItmReferenceWithoutEnabledChannels) { TraceRunFixture file("ctrace-run-reader-empty-itm-reference-test"); @@ -251,7 +314,7 @@ TEST(CtraceUnitTests, TraceRunReaderPreservesDiagnosticReferences) - { type: event, ctrace-ref: core/event, pname: core0, info: note } - { type: pmu, ctrace-ref: core/pmu, pname: core1, warning: warning } - { type: pcsample, ctrace-ref: core/pc, pname: null, error: unavailable } - - { type: dwt, ctrace-ref: core/data#, source: 0, symbol-address: invalid, error: diagnostic } + - { type: dwt, ctrace-ref: core/data#, source: 0, address: invalid, error: diagnostic } - { type: dwt, ctrace-ref: core/data#x, stream: [], source: 0, error: malformed } - { type: dwt, ctrace-ref: core/notdata#2, error: unrouted } - { type: itm, ctrace-ref: core/itm0, source: 0, error: disabled } @@ -261,7 +324,7 @@ TEST(CtraceUnitTests, TraceRunReaderPreservesDiagnosticReferences) EXPECT_EQ(config.references[0].processorName, std::optional("core0")); EXPECT_EQ(config.references[1].processorName, std::optional("core1")); EXPECT_FALSE(config.references[2].processorName.has_value()); - EXPECT_FALSE(config.references[3].symbolAddress.has_value()); + EXPECT_FALSE(config.references[3].address.has_value()); EXPECT_FALSE(config.references[3].dataSetupIndex.has_value()); EXPECT_FALSE(config.references[4].stream.has_value()); EXPECT_FALSE(config.references[4].dataSetupIndex.has_value()); @@ -357,36 +420,33 @@ TEST(CtraceUnitTests, TraceRunReaderParsesReferencedDataVariants) data: - {} - not-a-map - - { symbol-type: [] } - - { symbol-size: [] } - - { symbol-type: null, symbol-size: null } - - { symbol-size: invalid } + - { size: [] } + - { size: null } + - { size: invalid } + - { size: 2 } - {} - pname: core1 data: not-an-array - pname: core2 data: [{}] - data: - - { symbol-type: null, symbol-size: null } + - { size: null } - data: [{}] )yml"); ASSERT_EQ(config.setups.size(), 4U); ASSERT_EQ(config.setups[0].data.size(), 7U); - EXPECT_FALSE(config.setups[0].data[1].symbolType.has_value()); - EXPECT_EQ(config.setups[0].data[2].symbolTypeError, - std::optional("'data.symbol-type' must be a scalar string")); - EXPECT_EQ(config.setups[0].data[3].symbolSizeError, - std::optional("'data.symbol-size' must be a scalar unsigned integer")); - EXPECT_FALSE(config.setups[0].data[4].symbolType.has_value()); - EXPECT_FALSE(config.setups[0].data[4].symbolSize.has_value()); - EXPECT_TRUE(config.setups[0].data[5].symbolSizeError.has_value()); - EXPECT_TRUE(config.setups[2].data[0].symbolType == std::nullopt); - EXPECT_TRUE(config.setups[2].data[0].symbolSize == std::nullopt); + EXPECT_FALSE(config.setups[0].data[1].size.has_value()); + EXPECT_EQ(config.setups[0].data[2].sizeError, + std::optional("'data.size' must be a scalar unsigned integer")); + EXPECT_FALSE(config.setups[0].data[3].size.has_value()); + EXPECT_TRUE(config.setups[0].data[4].sizeError.has_value()); + EXPECT_EQ(config.setups[0].data[5].size, std::optional(2U)); + EXPECT_TRUE(config.setups[2].data[0].size == std::nullopt); expectReadError(file, R"yml(ctrace-run: ctrace-refs: [{ type: dwt, ctrace-ref: data#0, source: 0 }] ctrace-setup: - - data: [{ symbol-type: one, symbol-type: two }] + - data: [{ size: 1, size: 2 }] )yml", "map keys must be unique"); }