From b1becd8f5fcaf4a8a07aaa76fa68d3e9f7bb18a3 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 24 Jul 2026 14:55:59 +0700 Subject: [PATCH] docs(target-debug): manuals, breakpoint/watchpoint arsenal, RTT via OpenOCD - Link J-Link UM08001, OpenOCD and GDB (Tenth Ed.) manuals - bp/wp depth with halt-per-hit cost model. Verified on stm32f407disco (J-Link) + raspberry_pi_pico (OpenOCD): FPB/DWT budget reads (M4 6 bp/4 wp, M0+ 4/2 exact), 'Hardware watchpoint' confirmation rule (software fallback single-steps = USB death), OpenOCD data-VALUE watchpoints, dprintf + breakpoint command lists exercised on hardware; JLinkGDBServer -singlerun lifecycle gotcha - RTT is not J-Link-only: OpenOCD rtt setup/start/server verified on pico (control block found at the nm address, LOG=2 boot banner captured over nc) --- .claude/skills/target-debug/SKILL.md | 78 +++++++++++++++++++- .idea/codeStyles/Project.xml | 10 +++ .idea/codeStyles/codeStyleConfig.xml | 5 ++ .idea/improve-debug-skill-agent.iml | 2 + .idea/inspectionProfiles/Project_Default.xml | 17 +++++ .idea/modules.xml | 8 ++ 6 files changed, 117 insertions(+), 3 deletions(-) create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/codeStyles/codeStyleConfig.xml create mode 100644 .idea/improve-debug-skill-agent.iml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/modules.xml diff --git a/.claude/skills/target-debug/SKILL.md b/.claude/skills/target-debug/SKILL.md index c82237ea3..21dea38db 100644 --- a/.claude/skills/target-debug/SKILL.md +++ b/.claude/skills/target-debug/SKILL.md @@ -60,7 +60,9 @@ that IS a finding (timing-sensitive): move down in intrusiveness, not up. ## TU_LOG capture Build with `LOG=2` (`LOG=3` adds per-transfer noise and much more timing skew). -`LOGGER=rtt` routes it over the debug probe (J-Link only) — no UART wiring: +`LOGGER=rtt` routes it over the debug probe — no UART wiring. SEGGER's host +tools need a J-Link, but OpenOCD serves the same RTT buffer on ST-Link / +CMSIS-DAP / WCH-Link boards: ```bash # RTT: JLinkGDBServer from CLAUDE.md "GDB Debugging" + -RTTTelnetPort, then: @@ -69,6 +71,18 @@ timeout 20s JLinkRTTClient > /tmp/rtt.log # non-interactive capture stty -F /dev/ttyACM 115200 raw && timeout 20s cat /dev/ttyACM | tee /tmp/uart.log ``` +```bash +# OpenOCD RTT (any probe OpenOCD drives) — in telnet :4444 (or -c equivalents): +rtt setup 0x20000000 0x8000 "SEGGER RTT" # search range = RAM ORIGIN + LENGTH (from the .ld / map file) +rtt start # after firmware booted; rerun after each reflash +rtt server start 19021 0 +# then: timeout 20s nc localhost 19021 > /tmp/rtt.log +``` + +OpenOCD polls the buffer: bursty logs can drop lines a J-Link would keep — +prefer J-Link where both exist; the drain-model warning below applies +unchanged. + An RTT-built firmware that has since wedged still holds a log tail in RAM — but ONLY what fits the drain model: the default SEGGER mode (NO_BLOCK_SKIP) **drops** writes once the ring fills with no reader, so an undrained target @@ -85,8 +99,11 @@ reads don't halt the target. ## GDB — state autopsy and watchpoints Connect/load recipes per probe family (J-Link, OpenOCD for ST-Link / -CMSIS-DAP / WCH-Link) are in CLAUDE.md "GDB Debugging". Release builds keep -DWARF (`MinSizeRel`), so `p`/struct access works on HIL firmware. +CMSIS-DAP / WCH-Link) are in CLAUDE.md "GDB Debugging". For scripted/batch +sessions add `-singlerun` to JLinkGDBServer — the server exits with the +connection; back-to-back server relaunches race the probe handle and hang at +startup. Release builds keep DWARF (`MinSizeRel`), so `p`/struct access works +on HIL firmware. **Autopsy of a wedged board: attach and halt ONLY** — skip CLAUDE.md's `monitor reset halt` + `load` (those are for fresh starts; a reset destroys @@ -104,6 +121,50 @@ watch xfer_status[2][1].total_len # HW watchpoint (Cortex-M: ~4); dwc2 names break dcd_int_handler # works, but see warning below ``` +**Hardware budget — read it off the chip, not from memory** (verified: F407/M4 += 6 bp + 4 wp, rp2040/M0+ = 4 + 2; M7 typically 8/4): + +```gdb +p ((*(unsigned*)0xE0002000)>>4) & 0xF # FPB NUM_CODE = hw breakpoints (M7 adds bits[14:12]) +p (*(unsigned*)0xE0001000)>>28 # DWT_CTRL NUMCOMP = watchpoint comparators +``` + +- `hbreak`/`thbreak` force a hardware breakpoint (code in flash can't take a + software break unless the probe does flash breakpoints — J-Link does, + OpenOCD needs `bp 2 hw`); `tbreak` = one-shot. +- `watch -l ` watches the *address* the expression evaluates to once — + cheap and what you almost always want; `rwatch`/`awatch` trap reads/any + access (hardware-only — they error rather than fall back). OpenOCD (telnet + :4444) adds a data-VALUE match GDB cannot express: `wp 4 w + [mask]` — fires only when the written value matches (e.g. catch who writes + 0 into a busy flag, ignoring writes of 1). +- **Demand the word "Hardware" in the confirmation.** `watch` silently falls + back to a SOFTWARE watchpoint when no DWT comparator fits (expression too + wide/complex, budget exhausted): GDB then single-steps the whole program — + hundreds of times slower, certain USB death. `Watchpoint 2:` without + "Hardware" = delete it; narrowing the expression (`watch -l`, cast to a + 4-byte int) is the real fix. +- Conditional breaks/watches (`break dcd_edpt_xfer if ep_addr==0x81`) are + evaluated by GDB on the HOST with our stubs — neither JLinkGDBServer nor + OpenOCD supports target-side agent expressions on Cortex-M — so every hit + is a halt+resume (~ms) whether the condition matches or not: fine + post-wedge or on cold paths, wrong under live USB traffic. +- `commands ... end` auto-runs GDB commands at each hit (start with + `silent`, end with `continue` for hands-free evidence collection) — same + halt-per-hit cost. +- `dprintf ,"fmt",args` = printf without recompiling. Stay on the + default `dprintf-style gdb` (host prints): the `call` style runs the + target's own printf mid-halt and `agent` needs stub support — neither is + viable on these probes. Same cost model as conditional breaks; for + ISR-rate events use the RAM ring buffer instead. +- Stepping while the USB ISR fires between every step is chaos: OpenOCD + `cortex_m maskisr steponly` masks interrupts during single-steps only. + The bus keeps running either way — the host may still reset a device that + stops responding mid-step. +- While halted you can poke state to test a hypothesis (`set var + _usbd_dev.ep_status[2][1].busy = 0`) — but that invalidates the snapshot + as post-mortem evidence; dump first, poke after. + While halted the device answers **nothing**: host control transfers time out in ~5 s and the OS may reset/re-enumerate — after `continue`, the bus traffic shows recovery, not the original bug. Prefer one halt for a post-mortem dump @@ -176,6 +237,17 @@ number on the target gives a shared clock when you need finer alignment. When host and target evidence disagree, or the host sees nothing at all, add the wire itself: `usb-sniffer` skill (hardware tap, PID-level). +## Manuals + +- J-Link / J-Trace User Guide (UM08001): — flash breakpoints, RTT, SWO, monitor mode, Commander commands. +- OpenOCD User's Guide: — `rtt`, `bp`/`wp`, `cortex_m vector_catch` / `maskisr`, `itm`/`tpiu`. +- "Debugging with GDB" (the official manual; §5.1 covers break/watch/dprintf): + calibre library first (`read-doc` skill) — use the **Tenth Edition (GDB 18)** + copy, not the 2002 Ninth-Edition txt also present; fallback + `curl -sL -o /tmp/gdb.pdf https://sourceware.org/gdb/current/onlinedocs/gdb.pdf` + (the HTML mirror blocks fetchers; the PDF works). The installed + `arm-none-eabi-gdb`'s `help ` is authoritative for what this rig runs. + ## Warnings - **Halting/resetting via the probe does NOT disconnect the device**: a DWC2 diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 000000000..35c56fc87 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 000000000..79ee123c2 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/improve-debug-skill-agent.iml b/.idea/improve-debug-skill-agent.iml new file mode 100644 index 000000000..4c9423543 --- /dev/null +++ b/.idea/improve-debug-skill-agent.iml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 000000000..6b55c5c28 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,17 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 000000000..e97c64966 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file