From 92164bb7af192d54189513360851f1afa62c940a Mon Sep 17 00:00:00 2001 From: Anand Kulkarni Date: Thu, 17 Sep 2026 12:55:58 +0530 Subject: [PATCH] fastrpc-test: Replace SoC filters with runtime domain capability validation Remove hardcoded SoC-based domain filtering and per-SoC PD support maps. Use fastrpc-healthcheck as the primary capability source for domain online state and signed/unsigned PD support per domain. An installed healthcheck that fails, times out, or produces malformed output fails the suite; runtime fallback applies only when the tool is absent, using remoteproc/endpoint discovery for domains and a conservative static protocol map for PD support. - Log capability_source, domain_source, and pd_source in [FASTRPC-POLICY] - Automatic mode skips domains missing runtime readiness, endpoint, or library; explicit selection fails with a diagnostic message - Strict healthcheck parser with field validation, duplicate detection, GPDSP0/GPDSP1 canonicalization, and atomic TSV write - Identity-based remoteproc-to-domain mapping; FASTRPC_DOMAIN/NAME force single-domain independently of --domain-mode - Add --healthcheck-timeout (default 15s); enforce finite --timeout (120s) - Validate option arguments, FASTRPC_UNSIGNED_PD, and ALLOW_BIN_FASTRPC - Discover DSP skeletons from actual .so presence, not hardcoded ABI names - Capture kernel evidence once after the run matrix on any failure Signed-off-by: Anand Kulkarni --- .../CDSP/fastrpc_test/fastrpc_test.yaml | 20 +- .../CDSP/fastrpc_test/fastrpc_test_README.md | 207 ++-- .../Multimedia/CDSP/fastrpc_test/run.sh | 506 ++++++--- Runner/utils/lib_fastrpc.sh | 959 ++++++++++++++++-- 4 files changed, 1373 insertions(+), 319 deletions(-) diff --git a/Runner/suites/Multimedia/CDSP/fastrpc_test/fastrpc_test.yaml b/Runner/suites/Multimedia/CDSP/fastrpc_test/fastrpc_test.yaml index 0cae5cf9b..4eae9f807 100644 --- a/Runner/suites/Multimedia/CDSP/fastrpc_test/fastrpc_test.yaml +++ b/Runner/suites/Multimedia/CDSP/fastrpc_test/fastrpc_test.yaml @@ -5,11 +5,10 @@ metadata: The **fastrpc_test** runner validates FastRPC (Fast Remote Procedure Call) on Qualcomm targets. **Enhanced Test Coverage**: - - Now tests ALL supported DSP domains detected at runtime (ADSP, MDSP, SDSP, CDSP, CDSP1, GPDSP0, GPDSP1) - - Tests BOTH signed (system) and unsigned (user) Protection Domains where hardware supports them - - ADSP/MDSP/SDSP: signed PD only; CDSP/CDSP1/GPDSP: both signed and unsigned PDs - - QCS9075, QCS8275, QCS8300, QCS9100: GPDSP0/GPDSP1 domains are skipped (fastrpc_tests binaries not supported temporarily) - - For single-domain testing: set DOMAIN_MODE="single" and specify DOMAIN or DOMAIN_NAME + - Uses fastrpc-healthcheck when available to discover online, FastRPC-capable DSP domains + - Derives signed and unsigned Protection Domain support from the live healthcheck report + - Without healthcheck, discovers domains from remoteproc plus FastRPC endpoints and uses a conservative protocol map for PD support + - DOMAIN or DOMAIN_NAME forces single-domain testing; CLI values take precedence over environment values os: - linux scope: @@ -18,13 +17,14 @@ metadata: params: ARCH: "" # Architecture (only if explicitly provided) BIN_DIR: "/usr/bin" # Directory containing 'fastrpc_test' (default: /usr/bin) - DOMAIN_MODE: "all-supported" # all-supported|single (default: all-supported - tests all detected domains) - DOMAIN: "" # Optional explicit domain id in single-domain mode - DOMAIN_NAME: "" # Optional explicit domain name in single-domain mode + DOMAIN_MODE: "all-supported" # all-supported|single; single requires DOMAIN or DOMAIN_NAME + DOMAIN: "" # Optional explicit domain id; a non-empty value forces single-domain selection + DOMAIN_NAME: "" # Optional explicit domain name; takes precedence over DOMAIN and forces single-domain selection PD_MODE: "both" # both|signed-only|unsigned-only (default: both - tests both PDs where supported) UNSIGNED_PD: "" # If set to non-zero, runs only unsigned PD mode (-U 1) REPEAT: 1 # Number of repetitions (default: 1) - TIMEOUT: "" # Timeout for each run (no timeout if omitted) + TIMEOUT: 120 # Required positive timeout for each fastrpc_test invocation + HEALTHCHECK_TIMEOUT: 15 # Required positive timeout for fastrpc-healthcheck run: steps: @@ -32,5 +32,5 @@ run: - cd Runner/suites/Multimedia/CDSP/fastrpc_test - UNSIGNED_PD_PARAM="" - if [ -n "${UNSIGNED_PD}" ] && [ "${UNSIGNED_PD}" != 0 ]; then UNSIGNED_PD_PARAM="--unsigned-pd"; fi - - ./run.sh --arch "${ARCH}" --bin-dir "${BIN_DIR}" --domain-mode "${DOMAIN_MODE}" --domain "${DOMAIN}" --domain-name "${DOMAIN_NAME}" --pd-mode "${PD_MODE}" --repeat "${REPEAT}" --timeout "${TIMEOUT}" $UNSIGNED_PD_PARAM || true + - ./run.sh --arch "${ARCH}" --bin-dir "${BIN_DIR}" --domain-mode "${DOMAIN_MODE}" --domain "${DOMAIN}" --domain-name "${DOMAIN_NAME}" --pd-mode "${PD_MODE}" --repeat "${REPEAT}" --timeout "${TIMEOUT}" --healthcheck-timeout "${HEALTHCHECK_TIMEOUT}" $UNSIGNED_PD_PARAM || true - $REPO_PATH/Runner/utils/send-to-lava.sh fastrpc_test.res || true diff --git a/Runner/suites/Multimedia/CDSP/fastrpc_test/fastrpc_test_README.md b/Runner/suites/Multimedia/CDSP/fastrpc_test/fastrpc_test_README.md index 85f22b00f..b01571b41 100644 --- a/Runner/suites/Multimedia/CDSP/fastrpc_test/fastrpc_test_README.md +++ b/Runner/suites/Multimedia/CDSP/fastrpc_test/fastrpc_test_README.md @@ -1,33 +1,48 @@ -# FastRPC Test Script for Qualcomm Linux-based Platforms (Yocto) +# FastRPC Test Script for Qualcomm Linux-based Platforms ## Overview -The **fastrpc_test** runner validates FastRPC (Fast Remote Procedure Call) on Qualcomm targets, offloading work to DSP domains (e.g., **CDSP**). -It wraps the public [fastrpc test application](https://github.com/quic/fastrpc) with **robust logging, parameter control, and CI-friendly output**. +The **fastrpc_test** runner validates FastRPC (Fast Remote Procedure Call) on Qualcomm targets, +offloading work to all supported DSP domains (ADSP, MDSP, SDSP, CDSP, CDSP1, GDSP0, GDSP1). +It wraps the public [fastrpc test application](https://github.com/quic/fastrpc) with **robust +logging, parameter control, and CI-friendly output**. Supported capabilities: -- Auto-detect architecture from SoC ID. -- Multiple iterations and optional timeouts. -- Precise control over where the binary and assets live via `--bin-dir` and `--assets-dir`. -- Unbuffered output via `stdbuf` or `script` when available (falls back gracefully). +- Uses `fastrpc-healthcheck` when available to discover online DSPs, FastRPC support, signed and + unsigned PD support, firmware information, DSP library paths, and DMA-BUF heap readiness. +- When healthcheck is unavailable, discovers runnable domains from remoteproc state and FastRPC + character endpoints. The fallback cannot discover PD support directly, so it uses the documented + conservative protocol map. Host, test, and DSP skeleton libraries remain runtime-discovered. +- Multiple iterations with a required finite timeout. +- Precise control over binary location via `--bin-dir`. +- Line-buffered output through `stdbuf` when available. ## Features - **Calculator**, **HAP**, and **Multithreading** examples (as provided by `fastrpc_test`) -- CI-ready logs with timestamps and per-iteration results -- Parameterized control (`--arch`, `--repeat`, `--timeout`, `--bin-dir`, `--assets-dir`, `--verbose`) -- Auto-detection fallback for binary and assets -- Silent directory scan (no noisy `ls` dumps) +- CI-ready logs with timestamps and per-iteration, per-domain/PD results +- Parameterized control (`--arch`, `--repeat`, `--timeout`, `--bin-dir`, `--domain-mode`, + `--domain`, `--domain-name`, `--pd-mode`, `--unsigned-pd`, `--verbose`) +- Auto-discovery of system libraries and DSP skeletons for both Yocto and Debian layouts +- Runtime domain and PD discovery without SoC-name filtering ## Prerequisites Have these on the target (or specify paths with the flags below): - `fastrpc_test` binary (from [github.com/quic/fastrpc](https://github.com/quic/fastrpc)) -- A **parent directory** that contains a `linux/` subfolder with the required libraries (often alongside the binary), and architecture folders such as `v68`, `v73`, `v75`. +- Optional `fastrpc-healthcheck`. When installed, this is the primary distro-independent + capability source. An installed healthcheck that fails, times out, or produces an + unrecognized or malformed report fails the suite. Runtime fallback is used only when the tool + is absent. +- FastRPC system libraries and DSP skeletons auto-discovered from standard locations: + - Yocto: `/usr/local/lib`, `/usr/local/lib/fastrpc_test`, `/usr/local/share/fastrpc_test` + - Debian: `/usr/lib/`, `/usr/lib//fastrpc_test`, `/usr/share/fastrpc_test` + - RPM-based images: `/usr/lib64`, `/usr/lib64/fastrpc_test`, `/usr/share/fastrpc_test` - Optional but recommended: - - `stdbuf` **or** `script` (for unbuffered stdout/stderr) - - `timeout` (GNU coreutils) for wall-clock limiting; the script provides a portable fallback if missing. + - `stdbuf` for line-buffered output. Execution remains bounded without it. +- The suite does not install packages at runtime. Missing optional image assets are reported as + SKIP unless the operator explicitly selected a domain whose required runtime library is absent. ## Directory Structure @@ -38,7 +53,8 @@ Runner/ │ │ ├── CDSP/ │ │ │ ├── fastrpc_test/ │ │ │ │ ├── run.sh -│ │ │ │ ├── fastrpc_test_README.md +│ │ │ │ ├── fastrpc_test_README.md +│ │ │ │ ├── fastrpc_test.yaml ``` ## Usage @@ -49,71 +65,75 @@ Runner/ Usage: run.sh [OPTIONS] Options: - --arch Architecture (only if explicitly provided) - --bin-dir Directory containing 'fastrpc_test' (default: /usr/bin) - --assets-dir Directory that CONTAINS 'linux/' (info only; we run from the binary dir) - --user-pd Use '-U 1' (user/unsigned PD). Default is '-U 0'. - --repeat Number of repetitions (default: 1) - --timeout Timeout for each run (no timeout if omitted) - --verbose Extra logging for CI debugging - --help Show this help + --arch Architecture (only if explicitly provided) + --bin-dir Directory containing 'fastrpc_test' (default: /usr/bin) + --domain <0|1|2|3|4|5|6> DSP domain: 0=ADSP 1=MDSP 2=SDSP 3=CDSP 4=CDSP1 5=GDSP0 6=GDSP1 + --domain-name DSP domain: adsp|mdsp|sdsp|cdsp|cdsp1|gdsp0|gdsp1 + --domain-mode Discover all domains or run only one (default: all-supported) + --pd-mode Select PD mode(s) to run (default: both) + --unsigned-pd Use '-U 1' (user/unsigned PD). Overrides --pd-mode. + --repeat Number of repetitions (default: 1) + --timeout Timeout for each run (default: 120, must be greater than zero) + --healthcheck-timeout Timeout for fastrpc-healthcheck (default: 15) + --verbose Extra logging for CI debugging + --help Show this help Env: - FASTRPC_USER_PD=0|1 Sets PD (-U value). CLI --user-pd overrides to 1. - FASTRPC_EXTRA_FLAGS Extra flags appended to the command. - ALLOW_BIN_FASTRPC=1 Permit using /bin/fastrpc_test (otherwise refused). - -The test executes FROM the assets directory so 'fastrpc_test' can find deps. + FASTRPC_DOMAIN=0|1|2|3|4|5|6 Forces one domain; CLI --domain/--domain-name wins. + FASTRPC_DOMAIN_NAME=adsp|... Forces one named domain; CLI wins. + FASTRPC_UNSIGNED_PD=0|1 Sets PD (-U value). CLI --unsigned-pd overrides to 1. + FASTRPC_EXTRA_FLAGS Extra flags appended (space-separated). + FASTRPC_HEALTHCHECK_BIN Optional path to fastrpc-healthcheck. + ALLOW_BIN_FASTRPC=1 Permit using /bin/fastrpc_test when --bin-dir=/bin. ``` ### Quick start ```bash -# If fastrpc_test is already in PATH and assets are discoverable: +# Default: dynamically discover runnable domains and supported PD modes +./run.sh + +# With repeat and timeout: ./run.sh --repeat 3 --timeout 60 ``` ### Common scenarios ```bash -# Default expects /usr/bin/fastrpc_test and /usr/bin/linux -./run.sh +# 1) Use a custom binary directory +./run.sh --bin-dir /tmp/stage/usr/bin -Common scenarios +# 2) Run only unsigned (user) PD across all domains +./run.sh --pd-mode unsigned-only -# 1) Use a custom binary directory (we will cd there and run ./fastrpc_test) -./run.sh --bin-dir /tmp/stage/usr/bin +# 3) Run only signed PD +./run.sh --pd-mode signed-only -# 2) Opt into user/unsigned PD (-U 1) -./run.sh --user-pd -# or via env -FASTRPC_USER_PD=1 ./run.sh +# 4) Force a specific domain (CDSP) +./run.sh --domain 3 +# or by name: +./run.sh --domain-name cdsp -# 3) Add extra flags (kept intact; -U is appended last as '-U 0/1') -FASTRPC_EXTRA_FLAGS="-d 3" ./run.sh +# 5) Force GDSP0 with unsigned PD +./run.sh --domain-name gdsp0 --pd-mode unsigned-only -# 4) Allow /bin explicitly (generally discouraged unless required) -ALLOW_BIN_FASTRPC=1 ./run.sh --bin-dir /bin +# 6) Run SDSP via environment variable with unsigned PD +FASTRPC_DOMAIN=2 FASTRPC_UNSIGNED_PD=1 ./run.sh -# 5) Run multiple iterations with a timeout and verbose logs +# 7) Run multiple iterations with verbose logs ./run.sh --repeat 3 --timeout 120 --verbose -Force CDSP explicitly: - -# 6) ./run.sh --domain 3 -# or -# 6) ./run.sh --domain-name cdsp - -Use ADSP and user PD: - -# 7) ./run.sh --domain-name adsp --user-pd - -From env (CI): - -FASTRPC_DOMAIN=2 FASTRPC_USER_PD=1 ./run.sh -# => SDSP with -U 1 +# 8) Allow /bin explicitly (generally discouraged) +ALLOW_BIN_FASTRPC=1 ./run.sh --bin-dir /bin ``` +Domain selection uses `CLI > environment > dynamic discovery` precedence. A non-empty +`FASTRPC_DOMAIN_NAME` or `FASTRPC_DOMAIN` selects one domain even when `--domain-mode` remains at +its `all-supported` default. `--domain-mode single` without a CLI or environment domain is invalid. +An explicitly selected domain fails when it is unavailable, lacks its runtime library, or does not +support the requested PD mode. Automatic selection instead excludes only the affected domain or PD +combination and continues with the remaining runnable matrix. + ### LAVA integration example ``` @@ -125,34 +145,65 @@ FASTRPC_DOMAIN=2 FASTRPC_USER_PD=1 ./run.sh ``` [INFO] 2025-09-02 10:44:46 - -------------------Starting fastrpc_test Testcase---------------------------- -[INFO] 2025-09-02 10:44:46 - Using binary: /usr/bin/fastrpc_test -[INFO] 2025-09-02 10:44:46 - PD setting: -U 0 (use --user-pd to set -U 1) -[INFO] 2025-09-02 10:44:46 - Run dir: /usr/bin (launching ./fastrpc_test) -[INFO] 2025-09-02 10:44:46 - Executing: ./fastrpc_test -d 3 -t linux -U 0 ------ iter1 output begin ----- +[INFO] 2025-09-02 10:44:46 - Domain mode: all-supported +[INFO] 2025-09-02 10:44:46 - Domains to test: 0 3 +[INFO] 2025-09-02 10:44:46 - PD mode: both +[INFO] 2025-09-02 10:44:46 - Running ADSP_signed_iter1 | domain=ADSP | pd=signed +[INFO] 2025-09-02 10:44:46 - Executing: ./fastrpc_test -d 0 -t linux -U 0 +----- ADSP_signed_iter1 output begin ----- ... fastrpc_test output ... ------ iter1 output end ----- -[PASS] 2025-09-02 10:44:50 - iter1: success -[PASS] 2025-09-02 10:44:50 - fastrpc_test : Test Passed (1/1) +----- ADSP_signed_iter1 output end ----- +[PASS] 2025-09-02 10:44:50 - ADSP_signed_iter1: success +... +[INFO] ================================================================================ +[INFO] FastRPC Test Summary +[INFO] ================================================================================ +[INFO] Domain | PD Mode | Total | Pass | Fail | Skip | Status +[INFO] -------------------------------------------------------------------------------- +[INFO] ADSP | Signed | 5 | 5 | 0 | 0 | PASS +[INFO] CDSP | Signed | 5 | 5 | 0 | 0 | PASS +[INFO] CDSP | Unsigned | 5 | 5 | 0 | 0 | PASS +[PASS] 2025-09-02 10:44:50 - fastrpc_test : Test Passed (3/3) ``` ## CI debugging aids -- Binary resolved to /bin/fastrpc_test: By default this is blocked to avoid loader/ramdisk mismatches. Set ALLOW_BIN_FASTRPC=1 and/or --bin-dir /bin if you intentionally need it. -- Error resolving path .../linux: Ensure linux/ is next to the binary (e.g., /usr/bin/linux). The script runs from the binary dir specifically to make this work. -- Session create errors with -U 1: If you opt into user/unsigned PD and see 0x80000416, confirm your image includes unsigned shells/policies (or revert to the default -U 0). -- Per-iteration logs: `logs_fastrpc_test_/iterN.out` (+ `iterN.rc`) -- Summary result file: `fastrpc_test.res` (`PASS` / `FAIL`) -- Verbose mode: adds environment, resolutions, and timing details -- Graceful fallbacks when `stdbuf`, `script`, or `timeout` are missing -- Silent scan (no directory spam) during auto-detection + +- **Capability source**: The startup log reports `capability_source`, `domain_source`, and + `pd_source`. Raw healthcheck output is always retained after execution. A normalized table is + retained only when strict parsing succeeds. +- **Offline automatic domain**: Logged and excluded from automatic selection. Other runnable + domains continue. +- **Offline explicitly requested domain**: Fails with the healthcheck or remoteproc state. +- **Missing runtime artifacts**: In automatic mode, a domain missing its required system library is + logged and excluded while other runnable domains continue. The same condition fails an explicitly + selected domain. Missing shared test libraries or DSP skeletons skip the suite before execution. +- **Binary resolved to /bin/fastrpc_test**: Blocked by default. Set `ALLOW_BIN_FASTRPC=1` or + use `--bin-dir` to a non-`/bin` path. +- **Session create errors with -U 1**: If unsigned PD returns `0x80000416`, confirm your image + includes unsigned shells/policies (or use `--pd-mode signed-only`). +- **Domain not discovered**: Check `dmesg` for remoteproc firmware load errors. The test + requires the DSP remoteproc to be registered and its firmware present in DT. +- **Per-iteration logs**: `logs_fastrpc_test_/__iter.out` (+ `.rc`, `.env`, `.cmd`) +- **Kernel evidence on failure**: one shared `logs_fastrpc_test_/kernel/` snapshot is + captured through `scan_dmesg_errors` after the invocation matrix. +- **Summary result file**: `fastrpc_test.res` (`PASS` / `FAIL` / `SKIP`) +- **Verbose mode**: adds environment, library resolution, and timing details ## Notes -- If `--arch` is omitted, the script maps `/sys/devices/soc0/soc_id` to a known arch (defaulting to `v68` when unknown). -- If `fastrpc_test` isn’t in `PATH`, use `--bin-dir` or add it to `PATH`. -- If you see `Error resolving path .../linux: No such file or directory`, point `--assets-dir` to the **parent** directory that actually contains a `linux/` subfolder. -- The script changes working directory to the resolved **assets** dir before invoking `fastrpc_test`, which is required for the binary to locate its shared libs/skeletons. +- Domain and PD support are derived from `fastrpc-healthcheck` when available. The fallback is + selected only when the tool is absent and uses runtime remoteproc and endpoint evidence, with + a conservative protocol mapping for PD support. +- This suite runs the public `fastrpc_test` character-device path and therefore requires either + `/dev/fastrpc-` or `/dev/fastrpc--secure` for every selected domain. +- DSP skeleton directories are discovered dynamically by locating `.so` artifacts instead of + assuming fixed ABI directory names such as `v68` or `v75`. +- Override library discovery with `FASTRPC_LIB_SYS_DIR`, `FASTRPC_LIB_TEST_DIR`, or + `FASTRPC_SKEL_BASE`. CLI domain selection takes precedence over environment selection, which + takes precedence over runtime discovery. +- If `fastrpc_test` is not in the default path, use `--bin-dir` to specify its location. ## License -SPDX-License-Identifier: BSD-3-ClauseCopyright (c) Qualcomm Technologies, Inc. +SPDX-License-Identifier: BSD-3-Clause +Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. diff --git a/Runner/suites/Multimedia/CDSP/fastrpc_test/run.sh b/Runner/suites/Multimedia/CDSP/fastrpc_test/run.sh index d39789957..1ab4fd467 100755 --- a/Runner/suites/Multimedia/CDSP/fastrpc_test/run.sh +++ b/Runner/suites/Multimedia/CDSP/fastrpc_test/run.sh @@ -1,15 +1,14 @@ #!/bin/sh # Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. # SPDX-License-Identifier: BSD-3-Clause -# --------- Robustly source init_env and functestlib.sh ---------- - -TESTNAME="fastrpc_test" -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -RES_FILE="$SCRIPT_DIR/${TESTNAME}.res" -RESULT_FILE="$RES_FILE" - +# ---------- Repo env + helpers ---------- +SCRIPT_DIR="$( + cd "$(dirname "$0")" || exit 1 + pwd +)" INIT_ENV="" SEARCH="$SCRIPT_DIR" + while [ "$SEARCH" != "/" ]; do if [ -f "$SEARCH/init_env" ]; then INIT_ENV="$SEARCH/init_env" @@ -20,39 +19,36 @@ done if [ -z "$INIT_ENV" ]; then echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2 - echo "$TESTNAME FAIL" >"$RES_FILE" 2>/dev/null || true - exit 0 + exit 1 fi - + # Only source once (idempotent) +# NOTE: We intentionally **do not export** any new vars. They stay local to this shell. if [ -z "${__INIT_ENV_LOADED:-}" ]; then # shellcheck disable=SC1090 . "$INIT_ENV" __INIT_ENV_LOADED=1 fi - -# shellcheck disable=SC1090,SC1091 + +# shellcheck disable=SC1090 +. "$INIT_ENV" +# shellcheck disable=SC1091 . "$TOOLS/functestlib.sh" - + # shellcheck disable=SC1090,SC1091 . "$TOOLS/lib_fastrpc.sh" - -# Optional generic package-set recovery. -# This must be a clean no-op when no package-set mapping exists for the active OS/provider. -if [ -f "$TOOLS/lib_pkg_provider.sh" ]; then - # shellcheck disable=SC1091 - . "$TOOLS/lib_pkg_provider.sh" - - if ! pkg_ensure_package_set fastrpc; then - log_skip "$TESTNAME SKIP - required package set is not available: fastrpc" - echo "$TESTNAME SKIP" >"$RES_FILE" - exit 0 - fi -fi + +# shellcheck disable=SC1090,SC1091 +. "$TOOLS/lib_diag.sh" + +TESTNAME="fastrpc_test" +RES_FILE="$SCRIPT_DIR/${TESTNAME}.res" +RESULT_FILE="$RES_FILE" # Defaults REPEAT=1 -TIMEOUT="" +TIMEOUT=120 +HEALTHCHECK_TIMEOUT=15 ARCH="" BIN_DIR="" # directory that CONTAINS fastrpc_test ASSETS_DIR="" # kept for compatibility/logging (not used by new layout) @@ -63,13 +59,17 @@ CLI_DOMAIN_NAME="" DOMAIN_MODE="all-supported" # Default: test all supported domains PD_MODE="both" # Default: test both PDs where supported +# usage +# Print the supported FastRPC test CLI, environment overrides, and selection policy. +# Inputs: none. Output: help text on stdout. +# Returns: 0. Side effects: none. usage() { cat < @@ -77,28 +77,30 @@ Options: --arch Architecture (only if explicitly provided) --bin-dir Directory containing 'fastrpc_test' (default: /usr/bin) --assets-dir (compat) previously used when assets lived under 'linux/' - --domain <0|1|2|3|4|5|6> DSP domain: 0=ADSP, 1=MDSP, 2=SDSP, 3=CDSP, 4=CDSP1, 5=GPDSP0, 6=GPDSP1 - --domain-name DSP domain by name: adsp|mdsp|sdsp|cdsp|cdsp1|gpdsp0|gpdsp1 + --domain <0|1|2|3|4|5|6> DSP domain: 0=ADSP, 1=MDSP, 2=SDSP, 3=CDSP, 4=CDSP1, 5=GDSP0, 6=GDSP1 + --domain-name DSP domain by name: adsp|mdsp|sdsp|cdsp|cdsp1|gdsp0|gdsp1 --domain-mode Discover all supported domains or run only one (default: all-supported) --pd-mode Select PD mode(s) to run (default: both) --unsigned-pd Use '-U 1' (user/unsigned PD). Overrides --pd-mode for compatibility --repeat Number of repetitions (default: 1) - --timeout Timeout for each run (no timeout if omitted) + --timeout Timeout for each run (default: 120, must be greater than zero) + --healthcheck-timeout Timeout for fastrpc-healthcheck (default: 15) --verbose Extra logging for CI debugging --help Show this help Domain Selection Priority: 1. --domain-name (highest priority, forces single domain) 2. --domain (forces single domain) - 3. --domain-mode single + FASTRPC_DOMAIN_NAME env - 4. --domain-mode single + FASTRPC_DOMAIN env - 5. --domain-mode all-supported (default, discovers all) + 3. FASTRPC_DOMAIN_NAME env (forces single domain) + 4. FASTRPC_DOMAIN env (forces single domain) + 5. --domain-mode all-supported with no override (default, discovers all) Env: - FASTRPC_DOMAIN=0|1|2|3|4|5|6 Sets domain; CLI --domain/--domain-name wins. - FASTRPC_DOMAIN_NAME=adsp|... Named domain; CLI wins. + FASTRPC_DOMAIN=0|1|2|3|4|5|6 Forces one domain; CLI --domain/--domain-name wins. + FASTRPC_DOMAIN_NAME=adsp|... Forces one named domain; CLI wins. FASTRPC_UNSIGNED_PD=0|1 Sets PD (-U value). CLI --unsigned-pd overrides to 1. FASTRPC_EXTRA_FLAGS Extra flags appended (space-separated). + FASTRPC_HEALTHCHECK_BIN Optional path to fastrpc-healthcheck. ALLOW_BIN_FASTRPC=1 Permit using /bin/fastrpc_test when --bin-dir=/bin. Notes: @@ -113,33 +115,119 @@ Notes: /usr/lib/ /usr/lib//fastrpc_test /usr/share/fastrpc_test + RPM-based: + /usr/lib64 + /usr/lib64/fastrpc_test + /usr/share/fastrpc_test - Optional overrides: FASTRPC_LIB_SYS_DIR FASTRPC_LIB_TEST_DIR FASTRPC_SKEL_BASE -- Domain mapping: ADSP=0 MDSP=1 SDSP=2 CDSP=3 CDSP1=4 GPDSP0=5 GPDSP1=6 -- PD support: ADSP/MDSP/SDSP support signed only; CDSP/CDSP1/GPDSP support both. +- Domain mapping: ADSP=0 MDSP=1 SDSP=2 CDSP=3 CDSP1=4 GDSP0=5 GDSP1=6 +- A non-empty FASTRPC_DOMAIN_NAME or FASTRPC_DOMAIN forces single-domain selection. +- --domain-mode single without a CLI or environment domain is invalid. +- With healthcheck, domain and PD support come from its live capability report. +- Without healthcheck, domains come from remoteproc plus endpoint discovery, while PD support uses a conservative protocol fallback map. EOF } -# --------------------- Parse arguments ------------------------- -while [ $# -gt 0 ]; do - case "$1" in - --arch) ARCH="$2"; shift 2 ;; - --bin-dir) BIN_DIR="$2"; shift 2 ;; - --assets-dir) ASSETS_DIR="$2"; shift 2 ;; - --domain) CLI_DOMAIN="$2"; shift 2 ;; - --domain-name) CLI_DOMAIN_NAME="$2"; shift 2 ;; - --domain-mode) DOMAIN_MODE="$2"; shift 2 ;; - --pd-mode) PD_MODE="$2"; shift 2 ;; - --unsigned-pd) UNSIGNED_PD_FLAG=1; shift ;; - --repeat) REPEAT="$2"; shift 2 ;; - --timeout) TIMEOUT="$2"; shift 2 ;; - --verbose) VERBOSE=1; shift ;; - --help) usage; exit 0 ;; - *) echo "[ERROR] Unknown argument: $1" >&2; usage; echo "$TESTNAME : FAIL" >"$RESULT_FILE"; exit 0 ;; - esac -done +# require_option_value OPTION ARG... +# Verify that a value-taking option has a following argument before parse_args reads it. +# Inputs: the current option and remaining argument vector. Output: no stdout contract. +# Returns: 0 when a following argument exists, 1 otherwise. Side effects: logs an error and usage on failure. +require_option_value() { + if [ "$#" -lt 2 ]; then + log_error "Missing value for $1" + usage + return 1 + fi + + return 0 +} + +# parse_args ARG... +# Parse command-line policy and compatibility options into suite configuration globals. +# Inputs: the original command-line argument vector. Output: no stdout contract. +# Returns: 0 for valid input, 1 for invalid input, and exits after help. +# Side effects: updates option globals and logs usage for invalid input. +parse_args() { + while [ "$#" -gt 0 ]; do + case "$1" in + --arch) + require_option_value "$@" || return 1 + ARCH="$2" + shift 2 + ;; + --bin-dir) + require_option_value "$@" || return 1 + BIN_DIR="$2" + shift 2 + ;; + --assets-dir) + require_option_value "$@" || return 1 + ASSETS_DIR="$2" + shift 2 + ;; + --domain) + require_option_value "$@" || return 1 + CLI_DOMAIN="$2" + shift 2 + ;; + --domain-name) + require_option_value "$@" || return 1 + CLI_DOMAIN_NAME="$2" + shift 2 + ;; + --domain-mode) + require_option_value "$@" || return 1 + DOMAIN_MODE="$2" + shift 2 + ;; + --pd-mode) + require_option_value "$@" || return 1 + PD_MODE="$2" + shift 2 + ;; + --unsigned-pd) + UNSIGNED_PD_FLAG=1 + shift + ;; + --repeat) + require_option_value "$@" || return 1 + REPEAT="$2" + shift 2 + ;; + --timeout) + require_option_value "$@" || return 1 + TIMEOUT="$2" + shift 2 + ;; + --healthcheck-timeout) + require_option_value "$@" || return 1 + HEALTHCHECK_TIMEOUT="$2" + shift 2 + ;; + --verbose) + VERBOSE=1 + shift + ;; + --help) + usage + exit 0 + ;; + *) + echo "[ERROR] Unknown argument: $1" >&2 + usage + return 1 + ;; + esac + done +} + +if ! parse_args "$@"; then + echo "$TESTNAME : FAIL" >"$RESULT_FILE" + exit 0 +fi # ---- Back-compat: accept --assets-dir but ignore in the new auto-discovered layout. # Export so external tooling (or legacy wrappers) can still read it. @@ -157,13 +245,58 @@ export UNSIGNED_PD_FLAG export VERBOSE # ---------- Validation ---------- -case "$REPEAT" in *[!0-9]*|"") log_error "Invalid --repeat: $REPEAT"; echo "$TESTNAME : FAIL" >"$RESULT_FILE"; exit 0 ;; esac -if [ -n "$TIMEOUT" ]; then - case "$TIMEOUT" in *[!0-9]*|"") log_error "Invalid --timeout: $TIMEOUT"; echo "$TESTNAME : FAIL" >"$RESULT_FILE"; exit 0 ;; esac -fi +case "$REPEAT" in + *[!0-9]*|""|0) + log_error "Invalid --repeat: $REPEAT" + echo "$TESTNAME : FAIL" >"$RESULT_FILE" + exit 0 + ;; +esac +case "$TIMEOUT" in + *[!0-9]*|""|0) + log_error "Invalid --timeout: $TIMEOUT" + echo "$TESTNAME : FAIL" >"$RESULT_FILE" + exit 0 + ;; +esac +case "$HEALTHCHECK_TIMEOUT" in + *[!0-9]*|""|0) + log_error "Invalid --healthcheck-timeout: $HEALTHCHECK_TIMEOUT" + echo "$TESTNAME : FAIL" >"$RESULT_FILE" + exit 0 + ;; +esac +case "${FASTRPC_UNSIGNED_PD:-0}" in + 0|1) + : + ;; + *) + log_error "Invalid FASTRPC_UNSIGNED_PD: $FASTRPC_UNSIGNED_PD" + echo "$TESTNAME : FAIL" >"$RESULT_FILE" + exit 0 + ;; +esac # Validate enhanced options -case "$DOMAIN_MODE" in all-supported|single) : ;; *) log_error "Invalid --domain-mode: $DOMAIN_MODE"; echo "$TESTNAME : FAIL" >"$RESULT_FILE"; exit 0 ;; esac -case "$PD_MODE" in both|signed-only|unsigned-only) : ;; *) log_error "Invalid --pd-mode: $PD_MODE"; echo "$TESTNAME : FAIL" >"$RESULT_FILE"; exit 0 ;; esac +case "$DOMAIN_MODE" in + all-supported|single) + : + ;; + *) + log_error "Invalid --domain-mode: $DOMAIN_MODE" + echo "$TESTNAME : FAIL" >"$RESULT_FILE" + exit 0 + ;; +esac +case "$PD_MODE" in + both|signed-only|unsigned-only) + : + ;; + *) + log_error "Invalid --pd-mode: $PD_MODE" + echo "$TESTNAME : FAIL" >"$RESULT_FILE" + exit 0 + ;; +esac # Ensure we're in the testcase directory (repo convention) test_path="$(find_test_case_by_name "$TESTNAME")" || { @@ -185,7 +318,6 @@ log_info "-------------------Starting $TESTNAME Testcase------------------------ log_info "Kernel: $(uname -a 2>/dev/null || echo N/A)" log_info "Date(UTC): $(date -u 2>/dev/null || echo N/A)" log_soc_info -SOC_MACHINE="$(tr -s ' ' < /sys/devices/soc0/machine 2>/dev/null | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" # -------------------- Binary directory resolution ----------------- if [ -n "$BIN_DIR" ]; then @@ -196,7 +328,7 @@ fi case "$BIN_DIR" in /bin) - if [ "${ALLOW_BIN_FASTRPC:-0}" -ne 1 ]; then + if [ "${ALLOW_BIN_FASTRPC:-0}" != "1" ]; then log_skip "$TESTNAME SKIP - unsupported layout: /bin. Set ALLOW_BIN_FASTRPC=1 or pass --bin-dir." echo "$TESTNAME : SKIP" >"$RESULT_FILE" exit 0 @@ -213,6 +345,60 @@ if [ ! -x "$RUN_BIN" ]; then exit 0 fi +# -------------------- Logging root ----------------------------- +TS="$(date +%Y%m%d-%H%M%S)" +LOG_ROOT="./logs_${TESTNAME}_${TS}" +if ! mkdir -p "$LOG_ROOT"; then + log_error "Cannot create $LOG_ROOT" + echo "$TESTNAME : FAIL" >"$RESULT_FILE" + exit 0 +fi + +log_info "Evidence directory: $test_path/$LOG_ROOT" + +# -------------------- Capability discovery --------------------- +FASTRPC_HEALTHCHECK_LOG="$LOG_ROOT/fastrpc_healthcheck.log" +FASTRPC_HEALTHCHECK_TSV="$LOG_ROOT/fastrpc_healthcheck.tsv" +export FASTRPC_HEALTHCHECK_TSV + +fastrpc_capture_healthcheck \ + "$FASTRPC_HEALTHCHECK_LOG" \ + "$HEALTHCHECK_TIMEOUT" +healthcheck_status=$? + +case "$healthcheck_status" in + 0) + if fastrpc_parse_healthcheck \ + "$FASTRPC_HEALTHCHECK_LOG" \ + "$FASTRPC_HEALTHCHECK_TSV"; then + export FASTRPC_HEALTHCHECK_DSP_LIB_DIR + log_info "[FASTRPC-POLICY] capability_source=fastrpc-healthcheck domain_source=healthcheck pd_source=healthcheck parsed=$FASTRPC_HEALTHCHECK_TSV" + fastrpc_log_healthcheck_records + else + rm -f "$FASTRPC_HEALTHCHECK_TSV" + log_fail "$TESTNAME FAIL - installed fastrpc-healthcheck produced malformed output, reason=$FASTRPC_HEALTHCHECK_PARSE_ERROR artifact=$FASTRPC_HEALTHCHECK_LOG" + echo "$TESTNAME : FAIL" >"$RESULT_FILE" + exit 0 + fi + ;; + 1) + rm -f "$FASTRPC_HEALTHCHECK_TSV" + log_fail "$TESTNAME FAIL - installed fastrpc-healthcheck execution failed, artifact=$FASTRPC_HEALTHCHECK_LOG" + echo "$TESTNAME : FAIL" >"$RESULT_FILE" + exit 0 + ;; + 2) + rm -f "$FASTRPC_HEALTHCHECK_TSV" + log_info "[FASTRPC-POLICY] capability_source=runtime-fallback domain_source=remoteproc-endpoint pd_source=protocol-fallback-map reason=fastrpc-healthcheck-not-installed" + ;; + *) + rm -f "$FASTRPC_HEALTHCHECK_TSV" + log_fail "$TESTNAME FAIL - unexpected fastrpc-healthcheck status=$healthcheck_status, artifact=$FASTRPC_HEALTHCHECK_LOG" + echo "$TESTNAME : FAIL" >"$RESULT_FILE" + exit 0 + ;; +esac + # -------------------- Runtime layout discovery -------------------- fastrpc_setup_runtime_layout @@ -228,59 +414,106 @@ ensure_usr_lib_dsp_symlinks log_dsp_remoteproc_status # -------------------- Domain and PD selection ------------------- -# Resolve domains and PDs to test +if [ -n "$CLI_DOMAIN_NAME" ] || [ -n "$CLI_DOMAIN" ]; then + SELECTION_SOURCE="cli" +elif [ -n "${FASTRPC_DOMAIN_NAME:-}" ] || [ -n "${FASTRPC_DOMAIN:-}" ]; then + SELECTION_SOURCE="environment" +elif [ "$DOMAIN_MODE" = "single" ]; then + SELECTION_SOURCE="single-mode" +else + SELECTION_SOURCE="automatic" +fi + DOMAINS_TO_TEST="$(resolve_domains_to_test)" +if [ -z "$DOMAINS_TO_TEST" ] && [ "$SELECTION_SOURCE" != "automatic" ]; then + log_fail "$TESTNAME FAIL - explicit domain selection is invalid or unavailable, source=$SELECTION_SOURCE" + echo "$TESTNAME : FAIL" >"$RESULT_FILE" + exit 0 +fi + if [ -z "$DOMAINS_TO_TEST" ]; then log_skip "$TESTNAME SKIP - no mapped/supported domains detected" echo "$TESTNAME : SKIP" >"$RESULT_FILE" exit 0 fi -# -------------------- SoC-specific domain blacklist -------------------- -# QRB2210: FastRPC not supported - skip entire test -# QCS9075, QCS8275, QCS8300, QCS9100: GPDSP0 (domain 5) and GPDSP1 (domain 6) not supported currently -# SM8850: libhap_example HAP_mem DMA not supported - treat as known skip per invocation -# -# Do not skip Glymur CRD by SoC name. Newer Glymur/Debian images expose -# ADSP/CDSP remoteproc instances and FastRPC skeletons, so runtime discovery -# should decide whether the test can run. -soc_skip_all=0 -soc_skip_gpdsp=0 - -case "$SOC_MACHINE" in - *QRB2210*|*"Glymur CRD"*) - soc_skip_all=1 - ;; - *QCS9075*|*QCS8275*|*QCS8300*|*QCS9100*) - soc_skip_gpdsp=1 - ;; -esac +# Validate every selected domain before handing it to fastrpc_test. +RUNNABLE_DOMAINS="" +for d in $DOMAINS_TO_TEST; do + fastrpc_domain_ready "$d" + domain_ready_status=$? + + if [ "$domain_ready_status" -ne 0 ]; then + if [ "$SELECTION_SOURCE" != "automatic" ]; then + log_fail "$TESTNAME FAIL - $(domain_to_name "$d") is not ready, selection=$SELECTION_SOURCE" + echo "$TESTNAME : FAIL" >"$RESULT_FILE" + exit 0 + fi + + log_info "[FASTRPC-DISCOVERY] domain=$(domain_to_name "$d") eligible=no reason=runtime-not-ready" + continue + fi + + if ! fastrpc_domain_endpoint_available "$d"; then + label="$(domain_to_endpoint_label "$d")" + if [ "$SELECTION_SOURCE" != "automatic" ]; then + log_fail "$TESTNAME FAIL - $(domain_to_name "$d") has no usable endpoint, expected=/dev/fastrpc-${label} or /dev/fastrpc-${label}-secure selection=$SELECTION_SOURCE" + echo "$TESTNAME : FAIL" >"$RESULT_FILE" + exit 0 + fi -if [ "$soc_skip_all" -eq 1 ]; then - log_skip "$TESTNAME SKIP - SoC $SOC_MACHINE does not support FastRPC" + log_info "[FASTRPC-DISCOVERY] domain=$(domain_to_name "$d") eligible=no reason=endpoint-absent expected=/dev/fastrpc-${label}[-secure]" + continue + fi + + domain_library="$(fastrpc_domain_system_library_path "$d" || true)" + if [ -z "$domain_library" ]; then + required_library="$(fastrpc_domain_field "$d" 7)" + if [ "$SELECTION_SOURCE" != "automatic" ]; then + log_fail "$TESTNAME FAIL - $(domain_to_name "$d") runtime library is missing, expected=$required_library checked=$FASTRPC_LIB_SYS_DIRS_CHECKED selection=$SELECTION_SOURCE" + echo "$TESTNAME : FAIL" >"$RESULT_FILE" + exit 0 + fi + + log_info "[FASTRPC-DISCOVERY] domain=$(domain_to_name "$d") eligible=no reason=runtime-library-missing expected=$required_library checked=$FASTRPC_LIB_SYS_DIRS_CHECKED" + continue + fi + + log_info "[FASTRPC-DISCOVERY] domain=$(domain_to_name "$d") eligible=yes runtime_library=$domain_library" + RUNNABLE_DOMAINS="$(fastrpc_append_word_unique "$RUNNABLE_DOMAINS" "$d")" +done + +DOMAINS_TO_TEST="$RUNNABLE_DOMAINS" + +if [ -z "$DOMAINS_TO_TEST" ]; then + log_skip "$TESTNAME SKIP - no automatically discovered domain has complete runtime readiness, endpoint, and library evidence" echo "$TESTNAME : SKIP" >"$RESULT_FILE" exit 0 fi -if [ "$soc_skip_gpdsp" -eq 1 ]; then - filtered="" - for d in $DOMAINS_TO_TEST; do - case "$d" in - 5|6) log_info "SoC $SOC_MACHINE: skipping $(domain_to_name "$d") (not supported)" ;; - *) filtered="${filtered:+$filtered }$d" ;; - esac +if [ "$SELECTION_SOURCE" != "automatic" ]; then + for selected_domain in $DOMAINS_TO_TEST; do + selected_requested_pds="$(requested_pds)" + selected_supported_pds="$(domain_supported_pds "$selected_domain")" + selected_effective_pds="$(effective_pds_for_domain "$selected_domain")" + + if [ -z "$selected_effective_pds" ]; then + log_fail "$TESTNAME FAIL - explicitly selected $(domain_to_name "$selected_domain") does not support the requested PD mode, requested_mode=$PD_MODE requested_values=$selected_requested_pds supported_values=${selected_supported_pds:-none} selection=$SELECTION_SOURCE" + echo "$TESTNAME : FAIL" >"$RESULT_FILE" + exit 0 + fi done - DOMAINS_TO_TEST="$filtered" fi -if [ -z "$DOMAINS_TO_TEST" ]; then - log_skip "$TESTNAME SKIP - no supported domains remain after SoC filter ($SOC_MACHINE)" +if ! fastrpc_validate_runtime_artifacts; then + log_skip "$TESTNAME SKIP - shared FastRPC test artifacts are incomplete: $FASTRPC_ARTIFACT_ERROR" echo "$TESTNAME : SKIP" >"$RESULT_FILE" exit 0 fi log_info "Domain mode: $DOMAIN_MODE" +log_info "Domain selection source: $SELECTION_SOURCE" log_info "Domains to test: $DOMAINS_TO_TEST" # Build human-readable domain names @@ -298,29 +531,33 @@ done log_info "PD mode: $PD_MODE" # -------------------- Buffering tool availability --------------- -HAVE_STDBUF=0; command -v stdbuf >/dev/null 2>&1 && HAVE_STDBUF=1 -HAVE_SCRIPT=0; command -v script >/dev/null 2>&1 && HAVE_SCRIPT=1 -HAVE_TIMEOUT=0; command -v timeout >/dev/null 2>&1 && HAVE_TIMEOUT=1 +HAVE_STDBUF=0 +if command -v stdbuf >/dev/null 2>&1; then + HAVE_STDBUF=1 +fi buf_label="none" if [ $HAVE_STDBUF -eq 1 ]; then buf_label="stdbuf -oL -eL" -elif [ $HAVE_SCRIPT -eq 1 ]; then - buf_label="script -q" fi -# -------------------- Logging root ----------------------------- -TS="$(date +%Y%m%d-%H%M%S)" -LOG_ROOT="./logs_${TESTNAME}_${TS}" -mkdir -p "$LOG_ROOT" || { log_error "Cannot create $LOG_ROOT"; echo "$TESTNAME : FAIL" >"$RESULT_FILE"; exit 0; } +log_info "Repeats: $REPEAT | Timeout: ${TIMEOUT}s | Buffering: $buf_label" -tmo_label="none"; [ -n "$TIMEOUT" ] && tmo_label="${TIMEOUT}s" -log_info "Repeats: $REPEAT | Timeout: $tmo_label | Buffering: $buf_label" +matrix_combinations=0 +for matrix_domain in $DOMAINS_TO_TEST; do + for matrix_pd in $(effective_pds_for_domain "$matrix_domain"); do + [ -n "$matrix_pd" ] && matrix_combinations=$((matrix_combinations + 1)) + done +done +matrix_invocations=$((matrix_combinations * REPEAT)) +matrix_timeout_budget=$((matrix_invocations * TIMEOUT)) +log_info "[FASTRPC-MATRIX] combinations=$matrix_combinations repeats=$REPEAT invocations=$matrix_invocations maximum_timeout_budget=${matrix_timeout_budget}s" # -------------------- Run loop --------------------------------- # Nested loop over domains and PDs PASS_COUNT=0 TOTAL_COUNT=0 +RUNTIME_FAILURE_SEEN=0 # Track per-domain/per-PD results during execution # Format: "DOMAIN:PD:pass_count:fail_count:subtests_total:subtests_pass:subtests_fail:subtests_skip" @@ -331,7 +568,7 @@ for DOMAIN in $DOMAINS_TO_TEST; do PD_VALUES="$(effective_pds_for_domain "$DOMAIN")" if [ -z "$PD_VALUES" ]; then - log_warn "Skipping $dom_name: requested PD mode unsupported for this domain" + log_info "[FASTRPC-MATRIX] domain=$dom_name eligible=no reason=requested-pd-unsupported requested_mode=$PD_MODE requested_values=$(requested_pds) supported_values=$(domain_supported_pds "$DOMAIN")" continue fi @@ -359,7 +596,6 @@ for DOMAIN in $DOMAINS_TO_TEST; do iter_rc="$LOG_ROOT/${iter_tag}.rc" iter_cmd="$LOG_ROOT/${iter_tag}.cmd" iter_env="$LOG_ROOT/${iter_tag}.env" - iter_dmesg="$LOG_ROOT/${iter_tag}.dmesg" iso_now="$(date -u +%Y-%m-%dT%H:%M:%SZ)" set -- -d "$DOMAIN" -t linux @@ -391,16 +627,13 @@ for DOMAIN in $DOMAINS_TO_TEST; do ( cd "$RUN_DIR" || exit 127 if [ $HAVE_STDBUF -eq 1 ]; then - runWithTimeoutIfSet stdbuf -oL -eL ./fastrpc_test "$@" - elif [ $HAVE_SCRIPT -eq 1 ]; then - cmd_str="./fastrpc_test$(cmd_to_string "$@")" - if [ -n "$TIMEOUT" ] && [ $HAVE_TIMEOUT -eq 1 ]; then - script -q -c "timeout $TIMEOUT $cmd_str" /dev/null - else - script -q -c "$cmd_str" /dev/null - fi + diag_run_with_timeout \ + "$TIMEOUT" \ + stdbuf -oL -eL ./fastrpc_test "$@" else - runWithTimeoutIfSet ./fastrpc_test "$@" + diag_run_with_timeout \ + "$TIMEOUT" \ + ./fastrpc_test "$@" fi ) >"$iter_log" 2>&1 rc=$? @@ -415,8 +648,7 @@ for DOMAIN in $DOMAINS_TO_TEST; do if [ "$rc" -ne 0 ]; then log_fail "$iter_tag: fastrpc_test exited $rc" - dmesg | tail -n 300 > "$iter_dmesg" 2>/dev/null - log_dsp_remoteproc_status + RUNTIME_FAILURE_SEEN=1 fi # Extract and accumulate subtest counts from this iteration's log @@ -433,17 +665,13 @@ for DOMAIN in $DOMAINS_TO_TEST; do fi # Track invocation result immediately - # SM8850: libhap_example HAP_mem DMA handle not supported - treat as known skip if [ "$rc" -eq 0 ] && [ -r "$iter_log" ] && grep -F -q -e "All tests completed successfully" -e "All applicable tests PASSED" "$iter_log"; then PASS_COUNT=$((PASS_COUNT+1)) combo_pass=$((combo_pass+1)) log_pass "$iter_tag: success" - elif case "$SOC_MACHINE" in *SM8850*) true ;; *) false ;; esac && only_hap_example_failed "$iter_log"; then - PASS_COUNT=$((PASS_COUNT+1)) - combo_pass=$((combo_pass+1)) - log_pass "$iter_tag: success (libhap_example.so HAP_mem skipped on $SOC_MACHINE - DMA handle not supported)" else combo_fail=$((combo_fail+1)) + RUNTIME_FAILURE_SEEN=1 log_warn "$iter_tag: success pattern not found" fi @@ -456,6 +684,30 @@ for DOMAIN in $DOMAINS_TO_TEST; do done done +if [ "$RUNTIME_FAILURE_SEEN" -eq 1 ]; then + kernel_evidence_dir="$LOG_ROOT/kernel" + if mkdir -p "$kernel_evidence_dir"; then + if scan_dmesg_errors \ + "$kernel_evidence_dir" \ + "fastrpc|remoteproc|adsprpc|cdsprpc|sdsprpc" \ + ""; then + dmesg_scan_rc=0 + else + dmesg_scan_rc=$? + fi + if [ "$dmesg_scan_rc" -eq 0 ]; then + log_warn "FastRPC-related kernel errors were found, artifact=$kernel_evidence_dir" + elif [ "${DMESG_ACCESS_STATUS:-unknown}" = "unavailable" ]; then + log_warn "FastRPC kernel-log access is unavailable, artifact=$DMESG_ACCESS_LOG" + else + log_info "FastRPC kernel-log scan completed without relevant errors" + fi + else + log_warn "Could not create FastRPC kernel evidence directory: $kernel_evidence_dir" + fi + log_dsp_remoteproc_status +fi + # -------------------- Finalize -------------------------------- # Build detailed summary table from tracked results log_info "==========================================================================" @@ -478,8 +730,8 @@ overall_subtests_pass=0 overall_subtests_fail=0 overall_subtests_skip=0 -# shellcheck disable=SC2034 # _pass_cnt/_fail_cnt consumed from tracker but not used in display -while IFS=: read -r domain pd_val _pass_cnt _fail_cnt combo_total_tests combo_pass_tests combo_fail_tests combo_skip_tests; do +# shellcheck disable=SC2034 # pass_cnt/fail_cnt consumed from tracker but not used in display +while IFS=: read -r domain pd_val pass_cnt fail_cnt combo_total_tests combo_pass_tests combo_fail_tests combo_skip_tests; do [ -z "$domain" ] && continue dom_name="$(domain_to_name "$domain")" diff --git a/Runner/utils/lib_fastrpc.sh b/Runner/utils/lib_fastrpc.sh index dc4700192..ef9b4f9c1 100755 --- a/Runner/utils/lib_fastrpc.sh +++ b/Runner/utils/lib_fastrpc.sh @@ -9,18 +9,22 @@ # Yocto/current: # libs : /usr/local/lib # test libs : /usr/local/lib/fastrpc_test -# skeletons : /usr/local/share/fastrpc_test/v75, v68 +# skeletons : dynamically discovered below /usr/local/share/fastrpc_test # # Debian: # libs : /usr/lib/ # test libs : /usr/lib//fastrpc_test -# skeletons : /usr/share/fastrpc_test/v75, v68 +# skeletons : dynamically discovered below /usr/share/fastrpc_test # # Optional environment overrides: # FASTRPC_LIB_SYS_DIR # FASTRPC_LIB_TEST_DIR # FASTRPC_SKEL_BASE +# fastrpc_append_word_unique CURRENT NEW +# Append NEW to the space-separated CURRENT list when it is non-empty and absent. +# Inputs: two strings. Output: the resulting list on stdout. +# Returns: 0. Side effects: none. fastrpc_append_word_unique() { current="$1" new="$2" @@ -44,6 +48,10 @@ fastrpc_append_word_unique() { fi } +# fastrpc_append_colon_dir CURRENT_PATH NEW_DIR +# Append an existing NEW_DIR to a colon-separated path without duplicates. +# Inputs: a path list and directory. Output: the resulting path on stdout. +# Returns: 0. Side effects: probes NEW_DIR with test -d only. fastrpc_append_colon_dir() { current_path="$1" new_dir="$2" @@ -72,13 +80,21 @@ fastrpc_append_colon_dir() { esac } -fastrpc_first_existing_word_dir() { +# fastrpc_first_dir_with_file CANDIDATE_DIRS FILE_PATTERN +# Find the first space-separated candidate directory containing a matching file. +# Inputs: directory list and find-compatible basename pattern. Output: directory on stdout. +# Returns: 0 when found, 1 otherwise. Side effects: none. +fastrpc_first_dir_with_file() { candidate_dirs="$1" + file_pattern="$2" for candidate_dir in $candidate_dirs; do [ -n "$candidate_dir" ] || continue - if [ -d "$candidate_dir" ]; then + if [ -d "$candidate_dir" ] && + find "$candidate_dir" -maxdepth 1 \( -type f -o -type l \) \ + -name "$file_pattern" -print -quit 2>/dev/null | + grep -q .; then printf '%s\n' "$candidate_dir" return 0 fi @@ -87,6 +103,82 @@ fastrpc_first_existing_word_dir() { return 1 } +# fastrpc_find_file_in_dirs CANDIDATE_DIRS FILE_PATTERN +# Find the first matching regular file or symlink directly below candidate directories. +# Inputs: space-separated directories and basename pattern. Output: file path on stdout. +# Returns: 0 when found, 1 otherwise. Side effects: none. +fastrpc_find_file_in_dirs() { + candidate_dirs="$1" + file_pattern="$2" + + for candidate_dir in $candidate_dirs; do + [ -d "$candidate_dir" ] || continue + + candidate_file="$( + find "$candidate_dir" -maxdepth 1 \( -type f -o -type l \) \ + -name "$file_pattern" -print -quit 2>/dev/null + )" + if [ -n "$candidate_file" ]; then + printf '%s\n' "$candidate_file" + return 0 + fi + done + + return 1 +} + +# fastrpc_find_file_in_trees CANDIDATE_DIRS FILE_PATTERN +# Recursively find the first matching regular file or symlink in candidate trees. +# Inputs: space-separated tree roots and basename pattern. Output: file path on stdout. +# Returns: 0 when found, 1 otherwise. Side effects: none. +fastrpc_find_file_in_trees() { + candidate_dirs="$1" + file_pattern="$2" + + for candidate_dir in $candidate_dirs; do + [ -d "$candidate_dir" ] || continue + + candidate_file="$( + find "$candidate_dir" \( -type f -o -type l \) \ + -name "$file_pattern" -print -quit 2>/dev/null + )" + if [ -n "$candidate_file" ]; then + printf '%s\n' "$candidate_file" + return 0 + fi + done + + return 1 +} + +# fastrpc_find_skeleton_dirs SKELETON_BASES +# Discover unique directories containing any required FastRPC DSP skeleton. +# Input: space-separated tree roots. Output: colon-separated directories on stdout. +# Returns: 0. Side effects: none. +fastrpc_find_skeleton_dirs() { + skeleton_bases="$1" + skeleton_dirs="" + + for skeleton_base in $skeleton_bases; do + [ -d "$skeleton_base" ] || continue + + while IFS= read -r skeleton_dir; do + [ -n "$skeleton_dir" ] || continue + skeleton_dirs="$(fastrpc_append_colon_dir "$skeleton_dirs" "$skeleton_dir")" + done </dev/null | sort -u) +EOF + done + + printf '%s\n' "$skeleton_dirs" +} + +# fastrpc_detect_multiarch_triplet +# Detect the target multiarch triplet from image tools or the running architecture. +# Inputs: none. Output: triplet or an empty line on stdout. +# Returns: 0. Side effects: executes read-only compiler, package, and uname probes. fastrpc_detect_multiarch_triplet() { triplet="" @@ -115,6 +207,11 @@ fastrpc_detect_multiarch_triplet() { printf '%s\n' "$triplet" } +# fastrpc_discover_runtime_layout +# Discover FastRPC system, test, and DSP skeleton library locations. +# Inputs: optional FASTRPC_* path overrides and healthcheck DSP path globals. +# Outputs: FASTRPC_*_CHECKED and FASTRPC_RESOLVED_* shell globals, no stdout contract. +# Returns: 0. Side effects: read-only filesystem traversal. fastrpc_discover_runtime_layout() { FASTRPC_MULTIARCH_TRIPLET="$(fastrpc_detect_multiarch_triplet)" @@ -134,7 +231,9 @@ fastrpc_discover_runtime_layout() { # Yocto/current layout. FASTRPC_LIB_SYS_DIRS_CHECKED="$(fastrpc_append_word_unique "$FASTRPC_LIB_SYS_DIRS_CHECKED" "/usr/local/lib")" + FASTRPC_LIB_SYS_DIRS_CHECKED="$(fastrpc_append_word_unique "$FASTRPC_LIB_SYS_DIRS_CHECKED" "/usr/local/lib64")" FASTRPC_LIB_TEST_DIRS_CHECKED="$(fastrpc_append_word_unique "$FASTRPC_LIB_TEST_DIRS_CHECKED" "/usr/local/lib/fastrpc_test")" + FASTRPC_LIB_TEST_DIRS_CHECKED="$(fastrpc_append_word_unique "$FASTRPC_LIB_TEST_DIRS_CHECKED" "/usr/local/lib64/fastrpc_test")" FASTRPC_SKEL_BASES_CHECKED="$(fastrpc_append_word_unique "$FASTRPC_SKEL_BASES_CHECKED" "/usr/local/share/fastrpc_test")" # Debian/multiarch layout. @@ -145,25 +244,177 @@ fastrpc_discover_runtime_layout() { # Generic fallbacks. FASTRPC_LIB_SYS_DIRS_CHECKED="$(fastrpc_append_word_unique "$FASTRPC_LIB_SYS_DIRS_CHECKED" "/usr/lib")" + FASTRPC_LIB_SYS_DIRS_CHECKED="$(fastrpc_append_word_unique "$FASTRPC_LIB_SYS_DIRS_CHECKED" "/usr/lib64")" FASTRPC_LIB_TEST_DIRS_CHECKED="$(fastrpc_append_word_unique "$FASTRPC_LIB_TEST_DIRS_CHECKED" "/usr/lib/fastrpc_test")" + FASTRPC_LIB_TEST_DIRS_CHECKED="$(fastrpc_append_word_unique "$FASTRPC_LIB_TEST_DIRS_CHECKED" "/usr/lib64/fastrpc_test")" FASTRPC_SKEL_BASES_CHECKED="$(fastrpc_append_word_unique "$FASTRPC_SKEL_BASES_CHECKED" "/usr/share/fastrpc_test")" - FASTRPC_RESOLVED_LIB_SYS_DIR="$(fastrpc_first_existing_word_dir "$FASTRPC_LIB_SYS_DIRS_CHECKED" || true)" - FASTRPC_RESOLVED_LIB_TEST_DIR="$(fastrpc_first_existing_word_dir "$FASTRPC_LIB_TEST_DIRS_CHECKED" || true)" - FASTRPC_RESOLVED_SKEL_BASE="$(fastrpc_first_existing_word_dir "$FASTRPC_SKEL_BASES_CHECKED" || true)" + FASTRPC_RESOLVED_LIB_SYS_DIR="$( + fastrpc_first_dir_with_file "$FASTRPC_LIB_SYS_DIRS_CHECKED" 'lib*dsprpc.so*' || true + )" + FASTRPC_RESOLVED_LIB_SYS_PATH="" + for library_dir in $FASTRPC_LIB_SYS_DIRS_CHECKED; do + if find "$library_dir" -maxdepth 1 \( -type f -o -type l \) \ + -name 'lib*dsprpc.so*' -print -quit 2>/dev/null | + grep -q .; then + FASTRPC_RESOLVED_LIB_SYS_PATH="$( + fastrpc_append_colon_dir "$FASTRPC_RESOLVED_LIB_SYS_PATH" "$library_dir" + )" + fi + done + + FASTRPC_RESOLVED_LIB_TEST_PATH="" + for test_library_dir in $FASTRPC_LIB_TEST_DIRS_CHECKED; do + if find "$test_library_dir" -maxdepth 1 \( -type f -o -type l \) \ + \( -name 'libcalculator.so*' -o -name 'libhap_example.so*' -o -name 'libmultithreading.so*' \) \ + -print -quit 2>/dev/null | grep -q .; then + FASTRPC_RESOLVED_LIB_TEST_PATH="$( + fastrpc_append_colon_dir "$FASTRPC_RESOLVED_LIB_TEST_PATH" "$test_library_dir" + )" + fi + done + if [ -n "${FASTRPC_HEALTHCHECK_DSP_LIB_DIR:-}" ]; then + FASTRPC_SKEL_BASES_CHECKED="$( + fastrpc_append_word_unique \ + "$FASTRPC_SKEL_BASES_CHECKED" \ + "$FASTRPC_HEALTHCHECK_DSP_LIB_DIR" + )" + fi + + FASTRPC_RESOLVED_SKEL_PATH="$(fastrpc_find_skeleton_dirs "$FASTRPC_SKEL_BASES_CHECKED")" +} + +# fastrpc_missing_test_libraries +# Identify required host test libraries absent from the discovered search directories. +# Inputs: FASTRPC_LIB_TEST_DIRS_CHECKED. Output: space-separated names on stdout. +# Returns: 0. Side effects: read-only filesystem traversal. +fastrpc_missing_test_libraries() { + missing_libraries="" + + for test_library in libcalculator.so libhap_example.so libmultithreading.so; do + found_library=0 + + for test_dir in $FASTRPC_LIB_TEST_DIRS_CHECKED; do + if find "$test_dir" -maxdepth 1 \( -type f -o -type l \) \ + -name "${test_library}*" -print -quit 2>/dev/null | + grep -q .; then + found_library=1 + break + fi + done + + if [ "$found_library" -eq 0 ]; then + missing_libraries="$(fastrpc_append_word_unique "$missing_libraries" "$test_library")" + fi + done + + printf '%s\n' "$missing_libraries" +} + +# fastrpc_missing_skeleton_libraries +# Identify required DSP skeleton libraries absent from the discovered search trees. +# Inputs: FASTRPC_SKEL_BASES_CHECKED. Output: space-separated names on stdout. +# Returns: 0. Side effects: read-only filesystem traversal. +fastrpc_missing_skeleton_libraries() { + missing_skeletons="" + + for skeleton_library in \ + libcalculator_skel.so \ + libhap_example_skel.so \ + libmultithreading_skel.so; do + found_skeleton=0 + + for skeleton_base in $FASTRPC_SKEL_BASES_CHECKED; do + if find "$skeleton_base" \( -type f -o -type l \) \ + -name "${skeleton_library}*" -print -quit 2>/dev/null | + grep -q .; then + found_skeleton=1 + break + fi + done + + if [ "$found_skeleton" -eq 0 ]; then + missing_skeletons="$( + fastrpc_append_word_unique "$missing_skeletons" "$skeleton_library" + )" + fi + done + + printf '%s\n' "$missing_skeletons" +} + +# fastrpc_domain_system_library_path DOMAIN_ID +# Resolve the runtime system library required by one FastRPC domain. +# Input: numeric domain ID and FASTRPC_LIB_SYS_DIRS_CHECKED. Output: path on stdout. +# Returns: 0 when found, 1 when absent, 2 for an unmapped domain. Side effects: read-only filesystem traversal. +fastrpc_domain_system_library_path() { + selected_domain="$1" + required_library="$(fastrpc_domain_field "$selected_domain" 7)" + + [ -n "$required_library" ] || return 2 - FASTRPC_RESOLVED_SKEL_PATH="" - if [ -n "$FASTRPC_RESOLVED_SKEL_BASE" ]; then - FASTRPC_RESOLVED_SKEL_PATH="$(fastrpc_append_colon_dir "$FASTRPC_RESOLVED_SKEL_PATH" "$FASTRPC_RESOLVED_SKEL_BASE/v75")" - FASTRPC_RESOLVED_SKEL_PATH="$(fastrpc_append_colon_dir "$FASTRPC_RESOLVED_SKEL_PATH" "$FASTRPC_RESOLVED_SKEL_BASE/v68")" + fastrpc_find_file_in_dirs \ + "$FASTRPC_LIB_SYS_DIRS_CHECKED" \ + "${required_library}*" +} + +# fastrpc_validate_runtime_artifacts +# Validate the FastRPC binary's shared host-side test and skeleton dependencies. +# Inputs: RUN_BIN and discovered FASTRPC_* globals. Output: no stdout contract. +# Returns: 0 when usable, 1 otherwise, and sets FASTRPC_ARTIFACT_ERROR. +# Side effects: runs read-only find and optional ldd probes. +fastrpc_validate_runtime_artifacts() { + FASTRPC_ARTIFACT_ERROR="" + missing_test_libraries="$(fastrpc_missing_test_libraries)" + missing_skeleton_libraries="$(fastrpc_missing_skeleton_libraries)" + + if [ -z "$FASTRPC_RESOLVED_LIB_SYS_DIR" ]; then + FASTRPC_ARTIFACT_ERROR="no FastRPC system library was found" + elif [ -n "$missing_test_libraries" ]; then + FASTRPC_ARTIFACT_ERROR="missing test libraries: $missing_test_libraries" + elif [ -n "$missing_skeleton_libraries" ]; then + FASTRPC_ARTIFACT_ERROR="missing DSP skeleton libraries: $missing_skeleton_libraries" + elif [ -z "$FASTRPC_RESOLVED_SKEL_PATH" ]; then + FASTRPC_ARTIFACT_ERROR="no directory containing required DSP skeleton libraries was found" + elif command -v ldd >/dev/null 2>&1 && + ldd "$RUN_BIN" 2>/dev/null | grep -q 'not found'; then + FASTRPC_ARTIFACT_ERROR="fastrpc_test has unresolved host library dependencies" fi + + if [ -z "$FASTRPC_ARTIFACT_ERROR" ] && command -v ldd >/dev/null 2>&1; then + for test_library in libcalculator.so libhap_example.so libmultithreading.so; do + test_library_path="$( + fastrpc_find_file_in_dirs \ + "$FASTRPC_LIB_TEST_DIRS_CHECKED" \ + "${test_library}*" || true + )" + + if [ -n "$test_library_path" ] && + ldd "$test_library_path" 2>/dev/null | grep -q 'not found'; then + FASTRPC_ARTIFACT_ERROR="$test_library has unresolved host library dependencies" + break + fi + done + fi + + [ -z "$FASTRPC_ARTIFACT_ERROR" ] } +# fastrpc_export_runtime_env +# Build and export host and DSP library search paths from discovered directories. +# Inputs: FASTRPC_RESOLVED_* globals and existing library-path environment values. +# Outputs: exported LD_LIBRARY_PATH and DSP-specific *_LIBRARY_PATH variables. +# Returns: 0. Side effects: mutates the current shell environment. fastrpc_export_runtime_env() { new_ld_library_path="" - new_ld_library_path="$(fastrpc_append_colon_dir "$new_ld_library_path" "$FASTRPC_RESOLVED_LIB_SYS_DIR")" - new_ld_library_path="$(fastrpc_append_colon_dir "$new_ld_library_path" "$FASTRPC_RESOLVED_LIB_TEST_DIR")" + for resolved_dir in $(printf '%s' "$FASTRPC_RESOLVED_LIB_SYS_PATH" | tr ':' ' '); do + new_ld_library_path="$(fastrpc_append_colon_dir "$new_ld_library_path" "$resolved_dir")" + done + + for resolved_dir in $(printf '%s' "$FASTRPC_RESOLVED_LIB_TEST_PATH" | tr ':' ' '); do + new_ld_library_path="$(fastrpc_append_colon_dir "$new_ld_library_path" "$resolved_dir")" + done if [ -n "${LD_LIBRARY_PATH:-}" ]; then if [ -n "$new_ld_library_path" ]; then @@ -188,17 +439,21 @@ fastrpc_export_runtime_env() { fi } +# fastrpc_log_runtime_layout +# Log discovered FastRPC paths and the resolved location of every required artifact. +# Inputs: discovered FASTRPC_* globals. Output: no machine-readable stdout contract. +# Returns: 0. Side effects: emits informational and warning log records. fastrpc_log_runtime_layout() { log_info "FastRPC multiarch triplet: ${FASTRPC_MULTIARCH_TRIPLET:-}" - if [ -n "$FASTRPC_RESOLVED_LIB_SYS_DIR" ]; then - log_info "FastRPC system library dir: $FASTRPC_RESOLVED_LIB_SYS_DIR" + if [ -n "$FASTRPC_RESOLVED_LIB_SYS_PATH" ]; then + log_info "FastRPC system library path: $FASTRPC_RESOLVED_LIB_SYS_PATH" else log_warn "No FastRPC system library dir found. Checked: $FASTRPC_LIB_SYS_DIRS_CHECKED" fi - if [ -n "$FASTRPC_RESOLVED_LIB_TEST_DIR" ]; then - log_info "FastRPC test library dir: $FASTRPC_RESOLVED_LIB_TEST_DIR" + if [ -n "$FASTRPC_RESOLVED_LIB_TEST_PATH" ]; then + log_info "FastRPC test library path: $FASTRPC_RESOLVED_LIB_TEST_PATH" else log_warn "No FastRPC test library dir found. Checked: $FASTRPC_LIB_TEST_DIRS_CHECKED" fi @@ -208,8 +463,45 @@ fastrpc_log_runtime_layout() { else log_warn "No DSP skeleton dirs found. Checked bases: $FASTRPC_SKEL_BASES_CHECKED" fi + + runtime_libraries="$( + fastrpc_domain_table | awk -F '|' '!seen[$7]++ {print $7}' + )" + for runtime_library in $runtime_libraries; do + runtime_library_path="$( + fastrpc_find_file_in_dirs \ + "$FASTRPC_LIB_SYS_DIRS_CHECKED" \ + "${runtime_library}*" || true + )" + log_info "[FASTRPC-ARTIFACT] type=runtime-library name=$runtime_library path=${runtime_library_path:-missing}" + done + + for test_library in libcalculator.so libhap_example.so libmultithreading.so; do + test_library_path="$( + fastrpc_find_file_in_dirs \ + "$FASTRPC_LIB_TEST_DIRS_CHECKED" \ + "${test_library}*" || true + )" + log_info "[FASTRPC-ARTIFACT] type=test-library name=$test_library path=${test_library_path:-missing}" + done + + for skeleton_library in \ + libcalculator_skel.so \ + libhap_example_skel.so \ + libmultithreading_skel.so; do + skeleton_library_path="$( + fastrpc_find_file_in_trees \ + "$FASTRPC_SKEL_BASES_CHECKED" \ + "${skeleton_library}*" || true + )" + log_info "[FASTRPC-ARTIFACT] type=dsp-skeleton name=$skeleton_library path=${skeleton_library_path:-missing}" + done } +# fastrpc_setup_runtime_layout +# Discover, report, and export the complete FastRPC runtime library layout. +# Inputs: optional FASTRPC_* path overrides. Output: discovered/exported globals. +# Returns: 0. Side effects: logs discovery and mutates library-path environment values. fastrpc_setup_runtime_layout() { fastrpc_discover_runtime_layout fastrpc_log_runtime_layout @@ -223,6 +515,10 @@ fastrpc_setup_runtime_layout() { # -------------------- FastRPC test orchestration helpers -------------------- +# log_debug MESSAGE... +# Emit a diagnostic message only when VERBOSE is set to 1. +# Inputs: message arguments and VERBOSE. Output: no stdout contract. +# Returns: 0. Side effects: logs to stderr when enabled. # shellcheck disable=SC2317 log_debug() { if [ "${VERBOSE:-0}" -eq 1 ]; then @@ -230,6 +526,10 @@ log_debug() { fi } +# cmd_to_string ARG... +# Render command arguments as a readable shell-like string for diagnostic logging. +# Inputs: arbitrary command arguments. Output: one escaped string on stdout. +# Returns: 0. Side effects: none. The output is descriptive and must not be evaluated. cmd_to_string() { out="" for a in "$@"; do @@ -246,6 +546,10 @@ cmd_to_string() { printf "%s" "$out" } +# extract_test_summary_counts LOG_FILE +# Parse the final FastRPC test totals from a retained command log. +# Input: readable log path. Output: total:passed:failed:skipped on stdout. +# Returns: 0 and substitutes zero for missing or malformed fields. Side effects: none. extract_test_summary_counts() { log_file="$1" @@ -262,18 +566,12 @@ extract_test_summary_counts() { printf '%s:%s:%s:%s\n' "$total" "$passed" "$failed" "$skipped" } -# Returns true if the only failing subtest is libhap_example.so. -# Used to treat HAP_mem DMA failures as known-skip on affected SoCs. -only_hap_example_failed() { - log_file="$1" - - [ -r "$log_file" ] || return 1 - grep -F -q "[FAIL]" "$log_file" || return 1 - ! grep -F "[FAIL]" "$log_file" | grep -q -v "libhap_example.so" -} - +# log_dsp_remoteproc_status +# Log runtime remoteproc state for every FastRPC firmware alias in the domain table. +# Inputs: runtime DT and remoteproc sysfs through shared helpers. Output: no stdout contract. +# Returns: 0. Side effects: emits informational logs only. log_dsp_remoteproc_status() { - fw_list="adsp mdsp sdsp cdsp cdsp0 cdsp1 gdsp0 gdsp1 gpdsp0 gpdsp1" + fw_list="$(fastrpc_domain_table | awk -F '|' '{print $5}')" any=0 for fw in $fw_list; do @@ -297,32 +595,80 @@ __RPROC__ [ "$any" -eq 0 ] && log_info "rproc: no *dsp remoteproc entries detected via DT" } +# fastrpc_domain_table +# Emit the protocol-defined FastRPC domain metadata used by discovery and policy. +# Fields: ID, name, endpoint, URI suffix, firmware aliases, PD modes, runtime library. +# Inputs: none. Output: pipe-separated domain rows on stdout. +# Returns: 0. Side effects: none. +fastrpc_domain_table() { + cat <<'EOF' +0|ADSP|adsp|adsp|adsp|0|libadsprpc.so +1|MDSP|mdsp|mdsp|mdsp|0|libmdsprpc.so +2|SDSP|sdsp|sdsp|sdsp|0|libsdsprpc.so +3|CDSP|cdsp|cdsp|cdsp cdsp0|0 1|libcdsprpc.so +4|CDSP1|cdsp1|cdsp1|cdsp1|0 1|libcdsprpc.so +5|GDSP0|gdsp0|gdsp0|gdsp0 gpdsp0|0 1|libcdsprpc.so +6|GDSP1|gdsp1|gdsp1|gdsp1 gpdsp1|0 1|libcdsprpc.so +EOF +} + +# fastrpc_domain_field DOMAIN_ID FIELD_NUMBER +# Read one field from the FastRPC domain table for a numeric domain identifier. +# Inputs: domain ID and one-based field number. Output: matching field on stdout. +# Returns: awk status, normally 0 even when no row matches. Side effects: none. +fastrpc_domain_field() { + requested_domain="$1" + requested_field="$2" + + fastrpc_domain_table | awk -F '|' -v domain="$requested_domain" -v field="$requested_field" ' + $1 == domain { + print $field + exit + } + ' +} + +# name_to_domain DOMAIN_NAME +# Map a case-insensitive public domain name or supported alias to its numeric ID. +# Input: one domain name. Output: numeric domain ID on stdout when recognized. +# Returns: awk status, normally 0 even when unmapped. Side effects: none. name_to_domain() { - case "$(printf "%s" "$1" | tr '[:upper:]' '[:lower:]')" in - adsp) echo 0 ;; - mdsp) echo 1 ;; - sdsp) echo 2 ;; - cdsp) echo 3 ;; - cdsp1) echo 4 ;; - gpdsp0|gdsp0) echo 5 ;; - gpdsp1|gdsp1) echo 6 ;; - *) echo "" ;; - esac + requested_name="$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]')" + + fastrpc_domain_table | awk -F '|' -v requested="$requested_name" ' + { + alias_count = split($5, aliases, " ") + for (alias_number = 1; alias_number <= alias_count; alias_number++) { + if (requested == aliases[alias_number]) { + print $1 + exit + } + } + } + ' } +# domain_to_name DOMAIN_ID +# Map a numeric FastRPC domain identifier to its public display name. +# Input: numeric domain ID. Output: domain name or UNKNOWN on stdout. +# Returns: 0. Side effects: none. domain_to_name() { - case "$1" in - 0) echo "ADSP" ;; - 1) echo "MDSP" ;; - 2) echo "SDSP" ;; - 3) echo "CDSP" ;; - 4) echo "CDSP1" ;; - 5) echo "GPDSP0" ;; - 6) echo "GPDSP1" ;; - *) echo "UNKNOWN" ;; - esac + domain_name="$(fastrpc_domain_field "$1" 2)" + printf '%s\n' "${domain_name:-UNKNOWN}" } +# domain_to_endpoint_label DOMAIN_ID +# Return the device-node label associated with a FastRPC domain. +# Input: numeric domain ID. Output: endpoint label or an empty line on stdout. +# Returns: the domain-table lookup status. Side effects: none. +domain_to_endpoint_label() { + fastrpc_domain_field "$1" 3 +} + +# append_unique CURRENT NEW +# Append NEW to a space-separated list when it is not already present. +# Inputs: current list and new word. Output: resulting list on stdout. +# Returns: 0. Side effects: none. append_unique() { current="$1" new="$2" @@ -337,54 +683,411 @@ append_unique() { [ -n "$current" ] && printf "%s %s" "$current" "$new" || printf "%s" "$new" } -canonicalize_domain_name() { - norm="$(printf "%s" "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]')" +# fastrpc_capture_healthcheck LOG_FILE TIMEOUT_SECONDS +# Run the image-provided FastRPC healthcheck under the shared bounded runner. +# Inputs: retained log path, positive timeout, and optional FASTRPC_HEALTHCHECK_BIN. +# Output: no stdout contract. Returns: 0 on success, 1 on execution failure, 2 if absent. +# Side effects: truncates LOG_FILE and emits healthcheck diagnostic logs. +fastrpc_capture_healthcheck() { + healthcheck_log="$1" + healthcheck_timeout="$2" + healthcheck_bin="${FASTRPC_HEALTHCHECK_BIN:-}" + + if [ -z "$healthcheck_bin" ]; then + healthcheck_bin="$(command -v fastrpc-healthcheck 2>/dev/null || true)" + fi + + if [ -z "$healthcheck_bin" ] && [ -x "${BIN_DIR:-/usr/bin}/fastrpc-healthcheck" ]; then + healthcheck_bin="${BIN_DIR:-/usr/bin}/fastrpc-healthcheck" + fi + + [ -n "$healthcheck_bin" ] || return 2 + + : >"$healthcheck_log" + log_info "[FASTRPC-HEALTHCHECK] command=$healthcheck_bin timeout=${healthcheck_timeout}s artifact=$healthcheck_log" + + diag_run_with_timeout "$healthcheck_timeout" "$healthcheck_bin" >"$healthcheck_log" 2>&1 + healthcheck_rc=$? + + if [ "$healthcheck_rc" -eq 0 ]; then + return 0 + fi + + log_warn "[FASTRPC-HEALTHCHECK] command failed rc=$healthcheck_rc artifact=$healthcheck_log" + return 1 +} + +# fastrpc_parse_healthcheck LOG_FILE TSV_FILE +# Parse and validate the healthcheck DSP capability table and advertised DSP library path. +# Inputs: raw healthcheck log and destination TSV path. Output: no stdout contract. +# Returns: 0 for a complete unambiguous table, 1 for malformed or missing domain rows. +# Side effects: atomically replaces TSV_FILE and sets FASTRPC_HEALTHCHECK_DSP_LIB_DIR and FASTRPC_HEALTHCHECK_PARSE_ERROR. +fastrpc_parse_healthcheck() { + healthcheck_log="$1" + healthcheck_tsv="$2" + healthcheck_tsv_temp="${healthcheck_tsv}.tmp.$$" + healthcheck_parse_error="${healthcheck_tsv}.parse-error.$$" + FASTRPC_HEALTHCHECK_PARSE_ERROR="" + + FASTRPC_HEALTHCHECK_DSP_LIB_DIR="$( + sed -n 's/^[[:space:]]*DSP dynamic libs path[[:space:]]*:[[:space:]]*//p' \ + "$healthcheck_log" | tail -n 1 + )" + + if awk -v error_file="$healthcheck_parse_error" ' + function fail_parse(reason) { + print reason > error_file + parse_failed = 1 + exit 1 + } + function canonical_domain(raw, upper) { + upper = toupper(raw) + if (upper == "GPDSP0") { + return "GDSP0" + } + if (upper == "GPDSP1") { + return "GDSP1" + } + if (upper ~ /^(ADSP|MDSP|SDSP|CDSP|CDSP1|GDSP0|GDSP1)$/) { + return upper + } + return "" + } + function yes_or_no(value) { + return value == "yes" || value == "no" + } + BEGIN { + OFS = "\t" + in_table = 0 + parsed_rows = 0 + } + $1 == "DSP" && $2 == "State" && $3 == "SignedPD" { + if (NF < 6 || $4 !~ /^UnsignedPD/ || $5 != "FastRPC" || $6 != "Support") { + fail_parse("DSP capability table header is malformed") + } + in_table = 1 + next + } + in_table && $1 ~ /^-+$/ { + next + } + in_table { + canonical = canonical_domain($1) + if (canonical != "") { + if (NF != 5) { + fail_parse("recognized domain row " canonical " has " NF " fields, expected 5") + } + + state = tolower($2) + signed_pd = tolower($3) + unsigned_pd = tolower($4) + support = tolower($5) + + if (state != "online" && state != "offline") { + fail_parse("domain " canonical " has invalid state " $2) + } + if (!yes_or_no(signed_pd)) { + fail_parse("domain " canonical " has invalid SignedPD value " $3) + } + if (!yes_or_no(unsigned_pd)) { + fail_parse("domain " canonical " has invalid UnsignedPD value " $4) + } + if (!yes_or_no(support)) { + fail_parse("domain " canonical " has invalid FastRPC support value " $5) + } + if (seen[canonical]++) { + fail_parse("domain " canonical " is listed more than once") + } + + print canonical, state, signed_pd, unsigned_pd, support + parsed_rows++ + } + } + END { + if (parse_failed) { + exit 1 + } + if (parsed_rows == 0) { + print "no valid DSP capability rows were found" > error_file + exit 1 + } + } + ' "$healthcheck_log" >"$healthcheck_tsv_temp"; then + if mv -f "$healthcheck_tsv_temp" "$healthcheck_tsv"; then + rm -f "$healthcheck_parse_error" + return 0 + fi + + FASTRPC_HEALTHCHECK_PARSE_ERROR="could not publish normalized healthcheck table" + rm -f "$healthcheck_tsv_temp" "$healthcheck_parse_error" + return 1 + fi + + if [ -r "$healthcheck_parse_error" ]; then + IFS= read -r FASTRPC_HEALTHCHECK_PARSE_ERROR <"$healthcheck_parse_error" + fi + FASTRPC_HEALTHCHECK_PARSE_ERROR="${FASTRPC_HEALTHCHECK_PARSE_ERROR:-healthcheck table parsing failed}" + rm -f "$healthcheck_tsv_temp" "$healthcheck_parse_error" + return 1 +} + +# fastrpc_healthcheck_record DOMAIN_ID +# Select the parsed healthcheck row for a domain, accepting public GDSP aliases. +# Input: numeric domain ID and FASTRPC_HEALTHCHECK_TSV. Output: one TSV row on stdout. +# Returns: awk status, normally 0 even when no row matches. Side effects: none. +fastrpc_healthcheck_record() { + domain_id="$1" + healthcheck_name="$(fastrpc_domain_field "$domain_id" 2)" + + case "$healthcheck_name" in + GDSP0) healthcheck_pattern='GDSP0|GPDSP0' ;; + GDSP1) healthcheck_pattern='GDSP1|GPDSP1' ;; + *) healthcheck_pattern="$healthcheck_name" ;; + esac + + awk -F '\t' -v names="$healthcheck_pattern" ' + BEGIN { + count = split(names, accepted, "|") + } + { + for (item = 1; item <= count; item++) { + if ($1 == accepted[item]) { + print + exit + } + } + } + ' "${FASTRPC_HEALTHCHECK_TSV:-/dev/null}" +} + +# fastrpc_log_healthcheck_records +# Log every parsed FastRPC domain capability record for live CI evidence. +# Input: FASTRPC_HEALTHCHECK_TSV. Output: no machine-readable stdout contract. +# Returns: 0. Side effects: emits informational logs. +fastrpc_log_healthcheck_records() { + while IFS="$(printf '\t')" read -r hc_name hc_state hc_signed hc_unsigned hc_support; do + [ -n "$hc_name" ] || continue + log_info "[FASTRPC-CAPABILITY] domain=$hc_name state=$hc_state signed_pd=$hc_signed unsigned_pd=$hc_unsigned support=$hc_support source=fastrpc-healthcheck" + done <"$FASTRPC_HEALTHCHECK_TSV" +} - case "$norm" in - cdsp0) echo "cdsp" ;; - gdsp0) echo "gpdsp0" ;; - gdsp1) echo "gpdsp1" ;; - *) printf "%s" "$norm" ;; +# fastrpc_healthcheck_supported_domains +# Select online, FastRPC-supported domain IDs from the parsed healthcheck table. +# Input: FASTRPC_HEALTHCHECK_TSV. Output: one numeric domain ID per stdout line. +# Returns: 0. Side effects: logs exclusion and mapping diagnostics to stderr. +fastrpc_healthcheck_supported_domains() { + while IFS="$(printf '\t')" read -r \ + hc_name hc_state hc_signed hc_unsigned hc_support; do + if [ "$hc_state" != "online" ]; then + log_info "[FASTRPC-DISCOVERY] domain=$hc_name eligible=no reason=healthcheck-state-$hc_state" >&2 + continue + fi + + if [ "$hc_support" != "yes" ]; then + log_info "[FASTRPC-DISCOVERY] domain=$hc_name eligible=no reason=healthcheck-support-$hc_support" >&2 + continue + fi + + hc_domain="$(name_to_domain "$hc_name")" + if [ -n "$hc_domain" ]; then + printf '%s\n' "$hc_domain" + else + log_warn "[FASTRPC-DISCOVERY] domain=$hc_name eligible=no reason=unmapped-healthcheck-domain" >&2 + fi + done <"$FASTRPC_HEALTHCHECK_TSV" +} + +# fastrpc_healthcheck_domain_ready DOMAIN_ID +# Enforce that an explicitly selected domain is listed, online, and supported. +# Input: numeric domain ID and FASTRPC_HEALTHCHECK_TSV. Output: no stdout contract. +# Returns: 0 when ready, 1 when unhealthy, 2 when unlisted. +# Side effects: emits failure diagnostics. +fastrpc_healthcheck_domain_ready() { + healthcheck_record="$(fastrpc_healthcheck_record "$1")" + if [ -z "$healthcheck_record" ]; then + log_fail "[FASTRPC-CAPABILITY] domain=$(domain_to_name "$1") expected=healthcheck-record observed=not-listed artifact=$FASTRPC_HEALTHCHECK_TSV" + return 2 + fi + + IFS="$(printf '\t')" read -r hc_name hc_state hc_signed hc_unsigned hc_support </dev/null || true)" + identity_tokens="$( + printf '%s\n%s\n' "$remoteproc_name" "$remoteproc_firmware_name" | + tr '[:upper:]' '[:lower:]' | + tr -cs '[:alnum:]' '\n' + )" + + mapped_domains="" + for identity_token in $identity_tokens; do + identity_domain="$(name_to_domain "$identity_token")" + if [ -n "$identity_domain" ]; then + mapped_domains="$(fastrpc_append_word_unique "$mapped_domains" "$identity_domain")" + fi + done + + case "$mapped_domains" in + "") + return 1 + ;; + *" "*) + return 1 + ;; + *) + printf '%s\n' "$mapped_domains" + return 0 + ;; esac } +# fastrpc_remoteproc_domain_state DOMAIN_ID +# Resolve an exact-domain running remoteproc record, retaining the first exact non-running match for diagnostics. +# Input: numeric domain ID. Output: state|path|firmware|name on stdout. +# Returns: 0 when found, 2 otherwise. Side effects: read-only shared-helper probes. +fastrpc_remoteproc_domain_state() { + domain_id="$1" + domain_aliases="$(fastrpc_domain_field "$domain_id" 5)" + fallback_record="" + + for domain_alias in $domain_aliases; do + remoteproc_entries="$( + get_remoteproc_by_firmware "$domain_alias" "" all 2>/dev/null || true + )" + [ -n "$remoteproc_entries" ] || continue + + while IFS='|' read -r remoteproc_path remoteproc_state remoteproc_firmware remoteproc_name; do + [ -n "$remoteproc_path" ] || continue + remoteproc_domain="$( + fastrpc_remoteproc_identity_domain \ + "$remoteproc_name" \ + "$remoteproc_firmware" || true + )" + + if [ "$remoteproc_domain" != "$domain_id" ]; then + log_debug "remoteproc: requested=$(domain_to_name "$domain_id") rejected_path=$remoteproc_path mapped_domain=${remoteproc_domain:-unmapped} firmware=$remoteproc_firmware name=$remoteproc_name" + continue + fi + + remoteproc_record="${remoteproc_state}|${remoteproc_path}|${remoteproc_firmware}|${remoteproc_name}" + + if [ "$remoteproc_state" = "running" ]; then + printf '%s\n' "$remoteproc_record" + return 0 + fi + + if [ -z "$fallback_record" ]; then + fallback_record="$remoteproc_record" + fi + done <&2 - for fw in adsp mdsp sdsp cdsp cdsp0 cdsp1 gpdsp0 gpdsp1 gdsp0 gdsp1; do + if [ -s "${FASTRPC_HEALTHCHECK_TSV:-}" ]; then + fastrpc_healthcheck_supported_domains + return + fi + + for fw in $(fastrpc_domain_table | awk -F '|' '{print $5}'); do entries="$(get_remoteproc_by_firmware "$fw" "" all 2>/dev/null || true)" if [ -n "$entries" ]; then while IFS='|' read -r rpath rstate rfirm rname; do - nameguess="" - - if [ -n "$rname" ]; then - nameguess="$rname" - elif [ -n "$rfirm" ]; then - nameguess=$(basename "$rfirm" 2>/dev/null | sed 's/\.[^.]*$//') - fi - - [ -n "$nameguess" ] || continue - - canon="$(canonicalize_domain_name "$nameguess")" - d="$(name_to_domain "$canon")" + d="$(fastrpc_remoteproc_identity_domain "$rname" "$rfirm" || true)" if [ -n "$d" ]; then - printf '%s\n' "$d" - log_debug "discover: fw=$fw rname=$rname rfirm=$rfirm canon=$canon domain=$d state=$rstate" + if [ "$rstate" != "running" ]; then + log_info "[FASTRPC-DISCOVERY] domain=$(domain_to_name "$d") eligible=no reason=remoteproc-state-$rstate" >&2 + else + if fastrpc_domain_endpoint_available "$d"; then + printf '%s\n' "$d" + log_debug "discover: fw=$fw rname=$rname rfirm=$rfirm domain=$d state=$rstate" + else + label="$(domain_to_endpoint_label "$d")" + log_info "[FASTRPC-DISCOVERY] domain=$(domain_to_name "$d") eligible=no reason=endpoint-absent expected=/dev/fastrpc-${label}[-secure]" >&2 + fi + fi else - log_debug "discover: fw=$fw rname=$rname rfirm=$rfirm canon=$canon domain=" + log_debug "discover: fw=$fw rname=$rname rfirm=$rfirm domain=" fi done <&2 + else + printf '%s\n' "$d" + log_debug "discover: fw=$fw dt-only domain=$d" + fi fi else log_debug "discover: fw=$fw not present" @@ -392,19 +1095,32 @@ EOF done } +# resolve_domains_to_test +# Apply CLI, environment, and dynamic-discovery precedence to the domain selection. +# Inputs: CLI_DOMAIN*, DOMAIN_MODE, FASTRPC_DOMAIN*, and runtime discovery. +# Output: unique space-separated valid domain IDs on stdout. Returns: 0. +# Side effects: logs invalid entries and verbose diagnostics. resolve_domains_to_test() { resolved="" if [ -n "$CLI_DOMAIN_NAME" ]; then resolved="$(name_to_domain "$CLI_DOMAIN_NAME")" elif [ -n "$CLI_DOMAIN" ]; then - resolved="$CLI_DOMAIN" - elif [ "$DOMAIN_MODE" = "single" ]; then - if [ -n "${FASTRPC_DOMAIN_NAME:-}" ]; then - resolved="$(name_to_domain "$FASTRPC_DOMAIN_NAME")" - elif [ -n "${FASTRPC_DOMAIN:-}" ]; then + if [ -n "$(fastrpc_domain_field "$CLI_DOMAIN" 2)" ]; then + resolved="$CLI_DOMAIN" + else + log_warn "Invalid CLI domain '$CLI_DOMAIN'" >&2 + fi + elif [ -n "${FASTRPC_DOMAIN_NAME:-}" ]; then + resolved="$(name_to_domain "$FASTRPC_DOMAIN_NAME")" + elif [ -n "${FASTRPC_DOMAIN:-}" ]; then + if [ -n "$(fastrpc_domain_field "$FASTRPC_DOMAIN" 2)" ]; then resolved="$FASTRPC_DOMAIN" + else + log_warn "Invalid FASTRPC_DOMAIN '$FASTRPC_DOMAIN'" >&2 fi + elif [ "$DOMAIN_MODE" = "single" ]; then + resolved="" else resolved="$(discover_supported_domains)" fi @@ -413,38 +1129,59 @@ resolve_domains_to_test() { valid="" for d in $resolved; do - case "$d" in - 0|1|2|3|4|5|6) - case " $valid " in - *" $d "*) - : - ;; - *) - if [ -n "$valid" ]; then - valid="${valid} ${d}" - else - valid="$d" - fi - ;; - esac - ;; - *) - log_warn "Ignoring invalid domain '$d'" - ;; - esac + if [ -n "$(fastrpc_domain_field "$d" 2)" ]; then + case " $valid " in + *" $d "*) + : + ;; + *) + if [ -n "$valid" ]; then + valid="${valid} ${d}" + else + valid="$d" + fi + ;; + esac + else + log_warn "Ignoring invalid domain '$d'" >&2 + fi done printf '%s' "$valid" } +# domain_supported_pds DOMAIN_ID +# Return signed and unsigned protection-domain modes supported by one DSP domain. +# Input: numeric domain ID and optional healthcheck table. Output: space-separated 0/1 values. +# Returns: 0. Side effects: none. domain_supported_pds() { - case "$1" in - 0|1|2) printf "%s" "0" ;; - 3|4|5|6) printf "%s" "0 1" ;; - *) printf "%s" "" ;; - esac + domain_id="$1" + + if [ -s "${FASTRPC_HEALTHCHECK_TSV:-}" ]; then + healthcheck_record="$(fastrpc_healthcheck_record "$domain_id")" + + if [ -n "$healthcheck_record" ]; then + IFS="$(printf '\t')" read -r \ + hc_name hc_state hc_signed hc_unsigned hc_support <