Skip to main content

target-debug

Use when firmware misbehaves on real hardware and its own state must explain it — a wedge, STALL, endless NAK, lost data, a stuck endpoint, HardFault, stuck ISR or spinning core. Evidence over a debug probe: logs, GDB autopsy, RAM trace, PC-sampling, SWO. Also ETM trace and profiling with a SEGGER J-Trace and Ozone (hot functions, coverage, what ran before a hang; "No trace clock present"); SEGGER SystemView per-task and per-ISR load, latency and the scheduling timeline; Espressif chips over the built-in USB-Serial-JTAG (303a:1001) or pad JTAG with openocd-esp32, "could not find or open device", or the debug port becoming the firmware's USB device.

소스 정보

저장소
hathach/agentrc
최근 소스 활동
2026년 10월 30일 10:50
감지된 SKILL.md 언어
영어
스타
2
포크
2

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

파일 탐색기
30 개 파일

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
target-debug
description
Use when firmware misbehaves on real hardware and its own state must explain it — a wedge, STALL, endless NAK, lost data, a stuck endpoint, HardFault, stuck ISR or spinning core. Evidence over a debug probe: logs, GDB autopsy, RAM trace, PC-sampling, SWO. Also ETM trace and profiling with a SEGGER J-Trace and Ozone (hot functions, coverage, what ran before a hang; "No trace clock present"); SEGGER SystemView per-task and per-ISR load, latency and the scheduling timeline; Espressif chips over the built-in USB-Serial-JTAG (303a:1001) or pad JTAG with openocd-esp32, "could not find or open device", or the debug port becoming the firmware's USB device.
# target-debug — target-side capture & debugging over a debug probe The **target** is the MCU running the code under test. The discipline here holds for any firmware on a debug probe, and each technique names the architecture it exists on; the worked cases are USB stacks (device role, host role, or both), where the link peer is not always a Linux PC: an MCU host may face another board or a Linux gadget (e.g. a Raspberry Pi). ## Which skill answers what | Skill | Answers | Needs | | ------------------------- | --- | --- | | **`target-debug`** | **what the target did** (driver state, faults, where the core spins, which instructions ran) | a debug probe on the target; a J-Trace for instruction trace | | `rtt` | the target's log/console stream, and a post-mortem ring dump | a probe, firmware that logs over SEGGER RTT | | `usb-sniffer` | what crossed the wire (PIDs, handshakes, resets) | the hardware tap cabled in; role-agnostic | | SystemView (`sysview.md`) | where CPU time goes: task/ISR schedule, per-context load, switch and ready→run timing, or what ran right before a crash/hang | firmware built with SystemView's recorder; a J-Link/J-Trace for live recording, an OpenOCD probe for RTT streaming, either for a post-mortem dump | | `usb-kernel-debug` | what a Linux host exchanged (usbmon URBs) and why its kernel acted (dynamic debug) | Linux on either end: PC host or gadget peer | **Espressif targets** (ESP32-S2/S3/P4/S31 and the C series): read `espressif.md` first; it names the tools, the JTAG route per chip and the console. ## What the project supplies These skills know probes, not projects. Three things come from the project, and none of them is ever guessed: - **How to build a debug variant** (logging over RTT, trace init, where the ELF lands, the J-Link device name or OpenOCD config of a board): the project's `Build contract:` line in its `CLAUDE.md` names the skill or document that says. No contract: use the ELF and device/config the caller gave, and ask for what is missing. - **How to take a rig board**: the project's `HIL contract:` line names its rig skill, which owns lock and release and says which HIL config json describes the host you are on. No contract: follow the procedure the caller gave; on a shared rig with none, ask before touching a board. - **What its symbols and symptoms mean**: kept here, in `projects/`, for the projects this skill has been used on — see "Project notes" at the end. A HIL config json maps board → probe (`--hil-config <file> --board <name>`). `rtt.py` and `swo_capture.py` turn a board's `flasher` entry into their flags, and a flag given as well must agree: | `flasher` field | Means | Becomes | | --------------- | --- | --- | | `name` | how the rig flashes: `jlink`, `openocd`, `stlink` are probe routes; `esptool`, `lm4flash` are refused: they name no debug probe (Espressif debug transport and selector: `espressif.md`) | the backend (`stlink` → openocd, and then `--cfg` is yours to give) | | `uid` | the probe's serial | `--probe` | | `args` | jlink: exactly `-device <name>`; openocd: its `-f`/`-c` arguments; stlink: refused | `--device`, or `--cfg` whole | | `vid_pid` | the probe model's USB IDs, openocd only | `--vid-pid` | A J-Link entry supplies neither interface nor speed. On the J-Link route `--interface` and `--speed` stay explicit; on the OpenOCD route they live inside `--cfg` and the two flags are refused. `pc_sample.py` and `sysview_record.py` (`jlink` entries only) merge a `jlink` entry the same way; `autopsy.py`, `probe_state.py` and `sysview_dump.py` take `jlink` and `openocd` entries from the entry alone, as `pc_sample.py` takes an `openocd` one. `etm_capture.py` does not read it — a J-Trace is one probe moved between boards, named with `--probe`. ## Delegated sessions When an agent dispatches this work (chief's `hw-validator` and `hw-debugger` units), the prompt is the scope and these rules bind the unit; its role file adds only what it may edit and when it stops. - **Scope.** Stay within the prompt's board, operations and source paths; resolve the build and access through the project's contracts or the ELF, device/config and procedure it supplies. The dispatch supplies hardware task scope; no human grant or verbatim exchange is needed for its hardware operations. Missing technical inputs or scope return `blocked` with what the caller must supply. Use `needs-user` for human-only action or a permission still required for publishing, destructive actions on data or third-party access, instead of waiting for an answer. Establish the physical setup a technique needs before using it. If wiring cannot be established from current evidence or the supplied setup information, name the missing setup and proposed experiment in `next`: for ETM, follow `etm-trace.md`'s per-board wiring rule. - **Identity.** Verify worktree, branch and HEAD, then board, probe serial and host against the prompt, or the probe against the HIL config entry it names, before acting. Resolve the board's family and debug backend from the project's metadata before choosing probe tools, never from the board name or its flashing backend. - **Lock.** Hold the board through its HIL contract's Reservation for the whole hardware phase. A refused hold waits within the lock-wait budget, then returns `blocked` with the holder; a recovery blocker (a cleanup holder, a protected record left behind or a takeover journal) returns `blocked` at once. Never kill a holder, release a hold not yours or bypass a lock unless the prompt carries the scope the shared `hil` page's Board locks names for that board and operation, read by path (`../hil/SKILL.md` from this skill's directory, its symlink resolved first), never through a skill lookup a project's own skill could shadow; a refusal alone never adds it, and a CI runner is never stopped. Reuse a supplied HIL reproduction while its firmware, configuration, reproducer and relevant rig inputs match; otherwise resolve equivalent manual steps through the HIL contract, and if there are none return `blocked` naming the exact HIL reproduction needed, so the caller arranges it through the project's HIL role. A later manual session re-establishes firmware identity and state and never treats a snapshot taken after that handoff as the earlier failure state without evidence. - **Builds.** Through the build contract into a private build directory, leaving firmware staged for HIL untouched. Before the first hardware change, save the restoration artifact apart from later build outputs and pin it: its revision, configuration and hash, recorded as `cleanup.pristine`; cleanup restores that identity, and later commits do not move it. When the project's HIL contract defines a cleanup state instead of a restoration firmware, record that state there. Tracked paths a build may rewrite are in the prompt's temporary scope, or reported before building. - **Instrumentation.** Only the listed paths, from a clean checkout, uncommitted, each diff saved as a patch in the artifact directory. From a previous unit's dirty state (a recovery prompt): inspect the recorded patch and current changes first, restore only what is attributable to it, report ambiguous ownership untouched. - **Observation.** The prompt's observation window is the ceiling on one reproducer attempt; the criterion states the required operations or minimum exposure, and a completed deterministic operation ends the attempt. Every reproducer invocation, including smoke checks, cleanup checks that invoke it and failed or truncated captures, is accounted for in `runs` and counts toward the repetition ceiling; a state read or run-state observation that does not invoke the reproducer costs time, not a repetition. A `runs` row aggregates identical attempts and splits where purpose, firmware, conditions or outcome differ. An early decisive observation may settle the claim; otherwise a shortened capture is partial evidence and cannot establish a pass that needs the full exposure. Anchor every state reading as Warnings, "A register snapshot without a validity anchor lies", says, and record whether attaching halted or reset the target; a post-reset snapshot is never the failure state. - **Host.** Leave the host as found: never clear kernel logs (`dmesg -C`) or other shared history, and restore every setting you change (dynamic debug, module parameters). Anchor kernel-log reads on raw kernel timestamps (`dmesg --time-format raw`) and take the clock mapping from a kernel event you caused at a known wall time: line counts fail once the ring wraps, `-T` and ISO stamps need that mapping checked, and `/proc/uptime` is not it (`-T` and `/proc/uptime` were both ~30 s off on htpc, 2026-10-16). A host with `dmesg_restrict` refuses an unprivileged `dmesg -w` ("Operation not permitted"): read it with sudo. - **Budget.** Stop starting experiments when the remaining time cannot cover the experiment plus cleanup; never cut a restoration short for the deadline, report the overrun. - **Cleanup, before releasing the lock.** Stop owned capture and debug clients before opening the restoration flasher or running the contract's cleanup, dispose of the source as your role says, establish the pinned restoration image on the board (retain the programmed image when verification with the backend's procedure establishes that it matches the pinned artifact and nothing since changed its contents or made them uncertain, citing that verification; otherwise program the pinned artifact and verify its programmed contents, the artifact hash recorded apart from that result) or, when `cleanup.pristine` records the HIL contract's cleanup state, run the contract's cleanup instead, verified only by the run-state observation below; remove owned breakpoints and watchpoints (Espressif: `espressif.md` Attach, "Leave": `delete`, `monitor resume` before `detach`, `shutdown`), restore the firmware's expected run state and record an observation that establishes it (a verified flash alone does not; for a USB device, the expected function, such as an application exchange; a claim that needs re-enumeration records the enumeration events, and a device number alone establishes neither, since the host may reuse it across a reset), close any flasher you opened, restore host settings, release the lock. `restorationFlashes` counts actual programming attempts, a contract cleanup's write included. Run state not established is a failed cleanup. Do only the cleanup your own actions or the explicitly assigned recovery state call for, establishing ownership before restoring a predecessor's state. Blocked before touching hardware with no recovery state assigned, the untouched actions are `n-a`, nothing is flashed and another holder's lock stays. If restoration cannot complete, keep the lock where you can, report the exact remaining source, firmware, process and lock state, and do not call the board ready. - **Return.** The tested firmware identity, decisive observations, artifact locations, cleanup state and budget used; sampling done by hand on a native probe comes back with its command, and the limits name only what was actually unavailable. ## Rig discipline — lock first, always The board lock rules are Delegated sessions' Lock, for a manual session too: held for the WHOLE session (instrument, build, flash, capture, GDB). Rigs and benches run many identical probes: - Select the probe by serial: J-Link `-SelectEmuBySN <uid>`, its GDB server `-select usb=<uid>`, OpenOCD `-c 'adapter serial <uid>'`; a probe without one by `adapter usb location` (`espressif.md`, Attach). - Run on the host that owns the probe. - One probe serves one client: quit JLinkExe before starting JLinkGDBServer on the same probe. ## Pick the least intrusive technique that can answer the question Observation can mask the bug: one can change behavior under logging *and* under the debugger. If the bug disappears when instrumented, that IS a finding (timing-sensitive): move down in intrusiveness, not up. The Core column says where a technique exists; a row that names a core family does not exist elsewhere, and `espressif.md`'s "Technique mapping" gives the Xtensa and RISC-V substitutes. | Technique | Core | Intrusiveness | Reach for it when | | ---------------------------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | | PC-sampling (DWT_PCSR polled) | Cortex-M3 and later; ARMv6-M (M0, M0+, M1) only where PCSR is implemented (`techniques.md`) | no deliberate halt, no firmware change | core wedged/spinning somewhere unknown | | SWO exception trace / hw PC-sample | Cortex-M with the ITM: ARMv7-M, Armv8-M Mainline; SWO pin wired | none | ISR ordering, nesting and duration, a CPU profile, with zero code change | | DWT data trace | Cortex-M with the ITM: ARMv7-M, Armv8-M Mainline; SWO pin wired | none | stream one address's accesses: value + accessor PC | | Vector catch | Cortex-M (ARMv6-M: HardFault and reset only) | none until a fault fires | crash-shaped wedges — autopsy AT the faulting pc | | RAM ring-buffer | any; one ring per writing core | ~tens of cycles per event | ISR ordering/timing bugs | | SystemView | any with an RTT path and a timestamp the firmware supplies (`sysview.md`) | recorder in the firmware, per event | scheduling-shaped bugs: throughput jitter, a task starved or preempted, an ISR too long — not logic/state bugs | | Log lines (RTT) | any with a probe RTT path (J-Link, OpenOCD); none in the measured TinyUSB Espressif builds | µs per line | logic bugs that survive logging | | Log lines (UART) | any | up to ms per line when the write path polls a full FIFO | same, when no debug-probe RTT path | | ETM instruction trace | Cortex-M with an ETM (optional on ARMv7-M, Armv8-M; none on ARMv6-M), wired to a J-Trace | trace setup; halts; reflash+reset unless `--attach` | exact instruction counts, coverage, ISR timing, the history before a fault/hang; confirm J-Trace wiring first | | dprintf / conditional breakpoint | any | halt+resume per hit (~ms) | low-rate probes post-wedge; never ISR-rate events | | GDB halt / breakpoints | any; the hardware budget is per chip (`gdb.md`; `espressif.md`, Attach, "Breakpoint budgets") | stops USB service entirely | post-mortem state autopsy once wedged | Each technique's recipe, read when you reach for it (files beside this one): - PC-sampling: `techniques.md`, "PC-sampling" — `pc_sample.py` over J-Link or OpenOCD, reading the histogram, the explicit halting fallback for a core without a usable PCSR. - Probe-state snapshot (DHCSR anchor, PCSR samples, breakpoint budget, symbol reads, region saves, image read-back for Cleanup's verification): `techniques.md`, "Probe state" — `probe_state.py`. - SWO exception trace, hardware PC-sampling, DWT data trace, ITM stimulus: `swo-dwt.md` — `swo_capture.py`, `swo_decode.py`; per-board notes in `swo-boards.md`. - Vector catch and the fault registers: `fault-autopsy.md`. - RAM ring-buffer: `techniques.md`, "RAM ring-buffer trace" — the ring code, the `nm` check, the GDB dump. - SystemView: `sysview.md` — `sysview_record.py` (live J-Link recording, decoding a raw capture), `sysview_report.py`, `sysview_dump.py` (post-mortem ring); RTT streaming is `rtt.py --channel`. - Log lines (RTT, UART): `techniques.md`, "Log capture" — `console_capture.py` for a UART; the RTT channel and its drain model are the `rtt` skill's. - ETM instruction trace: `etm-trace.md` — `etm_capture.py`, `etm_profile.py`; per-board notes in `etm-boards.md`. - Chip and family constraints (flash reads, reset domains, probe misreads): `families.md`; Espressif chips: `espressif.md`. - dprintf, GDB halt and breakpoints: `gdb.md` — GDB servers, OpenOCD batch sessions, the halted autopsy (`autopsy.py`), the hardware breakpoint budget, watchpoints, FreeRTOS threads, J-Link Commander quirks. - USB enumeration or transfer bugs: capture both sides at once by default — `techniques.md`, "USB: dual-side capture" (`dual_capture.py`). - Manuals (J-Link UM08001, OpenOCD, GDB): `techniques.md`, "Manuals". ## Warnings - **Halting via the probe does NOT disconnect the device**: a halt resets no peripheral, so the USB controller keeps its pull-up, the host's stuck URBs stay stuck and a wedged DUT stays wedged — recover the Linux host side (the rig's own procedure; its HIL contract names it). A probe reset disconnects only when the reset it issues reaches the USB controller or its PHY: check the part (`families.md`: DWC2, STM32F4). - **A bug that vanishes under logging is not fixed**: timing-sensitive until the ring buffer reproduces it, and the added code's own effect on stack, layout and ordering is the alternative to rule out. - **A logging write path that polls a full FIFO blocks** (worst perturbation, including inside the ISR; which driver does what: `techniques.md` "Log capture", `espressif.md` "Console"); RTT is much cheaper but not free; verbose logging multiplies both. - **`pkill -f PATTERN` matches its own caller**: the pattern is in your shell's (or ssh's) command line, so it kills the command running it. Bracket one character (`pkill -f '[J]LinkGDBServer'`) or kill by PID; an openocd-esp32 server is ended with `shutdown`, never killed (`espressif.md`, Attach). - Flash/GDB only with the board lock held (Delegated sessions' Lock). - **Instrumentation is temporary**: before `release`, restore the pinned restoration firmware or cleanup state as Delegated sessions' Cleanup says (the next CI run must not inherit a debug build) and revert the instrumentation diff. Handing the diff over with the diagnosis preserves the evidence; it does not count as cleanup. - **A register snapshot without a validity anchor lies**: debug tool sessions can reset or briefly halt the DUT as a side effect, and a snapshot of a freshly-reset chip (e.g. NVIC ISER = 0) reads like a smoking gun. Cortex-M: read DHCSR (0xE000EDF0: bit 17 S_HALT, bit 25 S_RESET_ST) with every snapshot; Espressif: `espressif.md`, Attach, Anchors, since the USB device number survives a core reset there. Cross-check against something the device demonstrably still does. - **"Flash OK" can lie** (silent no-op — old firmware keeps running). When behavior contradicts the flashed code, read the image back: `probe_state.py --verify <flashed.elf>` (J-Link and OpenOCD boards, `techniques.md` "Probe state"), on RP2040 `rp2040_verify.py` (`families.md`), since `probe_state.py` refuses flash there; on mismatch reflash before
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기