summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHa Thach <[email protected]>2026-06-11 23:19:17 +0700
committerGitHub <[email protected]>2026-06-11 23:19:17 +0700
commit5014146fef1aac07362ee35c0c361b8475f7636d (patch)
tree880ae25885c649108a044d185a3b2166d9284079
parent848216a6a278f98ba5b977219adac5eabb07e0c4 (diff)
parentdd31ba4530f1904b0d2d83fa6b008502354fb400 (diff)
Merge pull request #3695 from hathach/claude/adoring-pasteur-kbaFa
Add pvs skill to run PVS-Studio analysis for a board
-rw-r--r--.claude/skills/pvs/SKILL.md36
-rwxr-xr-x.claude/skills/pvs/run_pvs.sh85
-rw-r--r--.gitignore1
-rw-r--r--AGENTS.md19
4 files changed, 135 insertions, 6 deletions
diff --git a/.claude/skills/pvs/SKILL.md b/.claude/skills/pvs/SKILL.md
new file mode 100644
index 000000000..bc4bf01bd
--- /dev/null
+++ b/.claude/skills/pvs/SKILL.md
@@ -0,0 +1,36 @@
+---
+name: pvs
+description: Use when running PVS-Studio static analysis (SAST + MISRA C:2023 / C++:2008) on TinyUSB for a given board. Builds the examples with an exported compile_commands.json, runs pvs-studio-analyzer against .PVS-Studio/.pvsconfig, and emits readable + SARIF output.
+---
+
+# PVS-Studio Static Analysis
+
+`run_pvs.sh <BOARD>` builds all examples for the board (with
+`compile_commands.json` exported), runs `pvs-studio-analyzer` against
+`.PVS-Studio/.pvsconfig`, then writes `pvs-<board>.log` and `pvs-<board>.sarif`
+plus printed errorfile findings. Needs a license file or `$PVS_STUDIO_CREDENTIALS`.
+
+```bash
+.claude/skills/pvs/run_pvs.sh raspberry_pi_pico # whole project for a board
+
+# Scope to specific files — -S takes a plaintext list (one path per line),
+# NOT a source file directly. Extra args pass through to the analyzer.
+printf 'src/tusb.c\nsrc/class/cdc/cdc_device.c\n' > /tmp/files.txt
+.claude/skills/pvs/run_pvs.sh stm32f407disco -S /tmp/files.txt
+```
+
+## Notes
+
+- **Board:** `raspberry_pi_pico` mirrors CI (needs Pico SDK deps);
+ `stm32f407disco` is fastest (no external SDK). Any board works once its deps
+ are fetched (`python3 tools/get_deps.py -b <board>`).
+- `.pvsconfig` already excludes vendored code and suppresses accepted MISRA
+ deviations — don't re-add those on the command line. Surviving `src/` findings
+ are genuinely new.
+- Timing is dominated by the build (deps may push it past a minute); the analysis
+ pass is ~10-30 s. Use a timeout ≥ 10 min when deps must be fetched first.
+- `--dump-files` is intentionally omitted — it scatters `.PVS-Studio.i/.cfg`
+ dumps across the tree (FP-debugging only); add it back via the passthrough args.
+
+After a run, summarize findings by rule/severity from the errorfile output and
+point the user at `pvs-<board>.sarif`.
diff --git a/.claude/skills/pvs/run_pvs.sh b/.claude/skills/pvs/run_pvs.sh
new file mode 100755
index 000000000..7406a3b4f
--- /dev/null
+++ b/.claude/skills/pvs/run_pvs.sh
@@ -0,0 +1,85 @@
+#!/usr/bin/env bash
+# Run PVS-Studio static analysis on TinyUSB for a given BOARD.
+#
+# Mirrors the "Static Analysis (PVS-Studio)" section in AGENTS.md / CLAUDE.md:
+# - build all examples for BOARD with compile_commands.json exported
+# - run pvs-studio-analyzer against that compile DB using .PVS-Studio/.pvsconfig
+# - convert the log to human-readable (errorfile) and SARIF output
+#
+# Usage:
+# .claude/skills/pvs/run_pvs.sh <BOARD> [extra pvs-studio-analyzer args...]
+#
+# Examples:
+# .claude/skills/pvs/run_pvs.sh raspberry_pi_pico
+# # Scope to specific files via a plaintext list (one path per line):
+# printf 'src/tusb.c\nsrc/class/cdc/cdc_device.c\n' > /tmp/files.txt
+# .claude/skills/pvs/run_pvs.sh stm32f407disco -S /tmp/files.txt
+set -euo pipefail
+
+BOARD="${1:-}"
+if [ -z "$BOARD" ]; then
+ echo "Usage: $0 <BOARD> [extra pvs-studio-analyzer args...]" >&2
+ echo "Example: $0 raspberry_pi_pico" >&2
+ exit 2
+fi
+shift
+
+REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
+cd "$REPO_ROOT"
+
+BUILD_DIR="examples/cmake-build-${BOARD}"
+COMPILE_DB="${BUILD_DIR}/compile_commands.json"
+REPORT_LOG="pvs-${BOARD}.log"
+SARIF_OUT="pvs-${BOARD}.sarif"
+JOBS="$(nproc 2>/dev/null || echo 4)"
+
+# --- License ---------------------------------------------------------------
+# Use an existing license file, else materialize one from PVS_STUDIO_CREDENTIALS
+# (format: "<name> <key>", as supplied by the CI secret of the same name).
+if ! pvs-studio-analyzer lic-info >/dev/null 2>&1; then
+ if [ -n "${PVS_STUDIO_CREDENTIALS:-}" ]; then
+ echo ">>> Registering PVS-Studio license from PVS_STUDIO_CREDENTIALS"
+ # Split "<name> <key>" into exactly two fields, quoted, so a key containing
+ # glob chars or extra spaces can't be word-split/expanded.
+ read -r _pvs_name _pvs_key <<< "$PVS_STUDIO_CREDENTIALS"
+ pvs-studio-analyzer credentials "$_pvs_name" "$_pvs_key"
+ else
+ echo "ERROR: no PVS-Studio license found and PVS_STUDIO_CREDENTIALS is unset." >&2
+ exit 1
+ fi
+fi
+
+# --- Build (exports compile_commands.json) ---------------------------------
+echo ">>> Building all examples for ${BOARD} (this also fetches the compile DB)"
+cmake examples -B "${BUILD_DIR}" -G Ninja \
+ -DBOARD="${BOARD}" \
+ -DCMAKE_BUILD_TYPE=MinSizeRel \
+ -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
+cmake --build "${BUILD_DIR}"
+
+if [ ! -f "${COMPILE_DB}" ]; then
+ echo "ERROR: ${COMPILE_DB} was not produced by the build." >&2
+ exit 1
+fi
+
+# --- Analyze ---------------------------------------------------------------
+echo ">>> Running PVS-Studio analyzer (-j${JOBS})"
+# Note: AGENTS.md shows --dump-files, but that scatters .PVS-Studio.i/.cfg dump
+# files across the source tree (only useful for debugging false positives). It is
+# omitted here to keep the working tree clean; add it back via "$@" if needed.
+pvs-studio-analyzer analyze \
+ -f "${COMPILE_DB}" \
+ -R .PVS-Studio/.pvsconfig \
+ -o "${REPORT_LOG}" -j"${JOBS}" \
+ --security-related-issues \
+ --misra-c-version 2023 --misra-cpp-version 2008 --use-old-parser \
+ "$@"
+
+# --- Report ----------------------------------------------------------------
+echo ">>> General-analysis + MISRA findings (errorfile):"
+plog-converter -a GA:1,2 -t errorfile "${REPORT_LOG}" || true
+plog-converter -t sarif -o "${SARIF_OUT}" "${REPORT_LOG}" >/dev/null
+
+echo ">>> Done."
+echo " Raw log : ${REPORT_LOG}"
+echo " SARIF : ${SARIF_OUT}"
diff --git a/.gitignore b/.gitignore
index 0f3c9ce49..8fe09e65f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,7 @@ hil_report.json
*.env
*.ind
*.log
+*.sarif
*.map
*.obj
*.jlink
diff --git a/AGENTS.md b/AGENTS.md
index 93faa6332..25dcdd618 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -153,28 +153,35 @@ Reports land in `cmake-metrics/<board>/metrics_compare.md` (per-board) and `cmak
## Static Analysis (PVS-Studio)
-Requires `compile_commands.json` (CMake `-DCMAKE_EXPORT_COMPILE_COMMANDS=ON`).
+Requires `compile_commands.json`, which the examples build exports by default
+(`hw/bsp/family_support.cmake` sets `CMAKE_EXPORT_COMPILE_COMMANDS ON`). The
+`pvs` skill (`.claude/skills/pvs/SKILL.md`) wraps the build + analyze flow for a
+board; the commands below are the underlying steps.
```bash
# Whole project:
pvs-studio-analyzer analyze \
-f examples/cmake-build-raspberry_pi_pico/compile_commands.json \
-R .PVS-Studio/.pvsconfig \
- -o pvs-report.log -j12 --dump-files \
+ -o pvs-report.log -j12 \
+ --security-related-issues \
--misra-c-version 2023 --misra-cpp-version 2008 --use-old-parser
-# Specific files (add one or more `-S <file>`):
+# Specific files: -S takes a plaintext list (one path per line), not paths directly:
+printf 'src/foo.c\nsrc/bar.c\n' > files.txt
pvs-studio-analyzer analyze \
-f examples/cmake-build-raspberry_pi_pico/compile_commands.json \
-R .PVS-Studio/.pvsconfig \
- -S src/foo.c -S src/bar.c \
- -o pvs-report.log -j12 --dump-files \
+ -S files.txt \
+ -o pvs-report.log -j12 \
+ --security-related-issues \
--misra-c-version 2023 --misra-cpp-version 2008 --use-old-parser
plog-converter -a GA:1,2 -t errorfile pvs-report.log # view results
```
-Takes ~10-30 s.
+Takes ~10-30 s. (`--dump-files` adds preprocessed `.PVS-Studio.i/.cfg` dumps next
+to every source for false-positive debugging — omit it for normal runs.)
## Validation After Changes