Skip to main content

etm-trace

Use when logs, GDB or PC-sampling cannot answer and you need instruction-level execution data from a SEGGER J-Trace and Ozone — hot functions by instruction count, ISR timing, on-target code coverage, what ran right before a fault or hang. ETM trace; "No trace clock present".

소스 정보

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

설치 방법

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

소스 파일 검토

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

파일 탐색기
4 개 파일

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
etm-trace
description
Use when logs, GDB or PC-sampling cannot answer and you need instruction-level execution data from a SEGGER J-Trace and Ozone — hot functions by instruction count, ISR timing, on-target code coverage, what ran right before a fault or hang. ETM trace; "No trace clock present".
# etm-trace — unattended ETM instruction trace via J-Trace + Ozone Streams full instruction (ETM) trace from a board wired to a SEGGER J-Trace, headlessly: no GUI, scripted end to end. Produces hot-function profile, code coverage, and optionally the raw instruction history. Which debug skill answers what: `target-debug`'s table. Use `target-debug`'s DWT PC-sampling for a quick statistical profile; use this skill for exact counts, coverage, or instruction-by-instruction history. ## Requirements - J-Trace on USB (`lsusb -d 1366:1020`) wired to the board's trace header; select it by J-Link USB **nickname** (e.g. `jtrace`) — never commit serials. - **Physical setup is per-board and exclusive** (one J-Trace, moved between boards; some rigs are fly-wired): unless the user just asked for trace on this board or your task states it is wired, **confirm with the user** that the J-Trace is connected to the target before flashing or capturing. - Linux, with Ozone found as `ozone` or `Ozone` on PATH or named by `ETM_OZONE` (≥ V3.38 for the automation socket) and `xvfb-run`; the capture script refuses to run without either. - Firmware whose init enables the trace pins and trace clock. How to build that variant is the project's build contract (`target-debug`, "What the project supplies"); what the init is called there, its notes (`target-debug`'s `projects/`). - A board with a reference Ozone project captures with `--jdebug <file>` and inherits its device, TIF speed, trace timing/width, core clock, hooks and J-Link script (where a project keeps them: its notes); any other target uses `--device <J-Link name>` plus `--target-if`, `--tif-speed` and `--trace-width`: nothing about the target is assumed. A target whose reference defines no `AfterTargetReset`/`AfterTargetDownload` (or has no reference) is refused until you pass `--cortex-m-default-hooks` (SP/PC from the ELF's vector table) — right for a plain Cortex-M image, wrong for one a ROM bootloader must start; `--attach` runs neither hook and asks for none. Verified boards: `boards.md` in this skill directory. ## Rig discipline - One probe, one client: quit interactive Ozone/JLinkExe/GDB on the probe first. Kill only processes you started — if it's held by someone else's session (check `fuser /dev/bus/usb/<bus>/<dev>`), surface it and ask. The capture script uses automation port **19201**, never an interactive Ozone's 19200. - Shared rig boards: hold the project's board lock first (its `HIL contract:` names the skill that owns it). - Reference projects are interactive projects — automation never opens them (Ozone rewrites project files); the script generates a throwaway project. - The default capture reflashes and resets the target (`--attach` doesn't). - A target whose own USB port is on this host stops answering it while halted; the host then retries enumeration for ~30 s and a JLinkExe started meanwhile can hang ("JLinkExe hung") — wait it out, and never start two back to back. ## Capture and analyze ```bash # 1. Build with the trace init enabled (the project's build contract says how) # 2. Capture (all options + defaults: etm_capture.py --help): python3 <skill dir>/scripts/etm_capture.py \ --jdebug <reference.jdebug> --elf <elf> --probe jtrace --duration-ms 10000 --out <dir> # no reference project: --device <J-Link name> --target-if SWD --tif-speed '4 MHz' --trace-width 4 # --cortex-m-default-hooks --elf <elf> --probe jtrace [--jlink-script <file>] # 3. Analyze (hot functions, coverage, hottest lines): python3 <skill dir>/scripts/etm_profile.py <dir> --elf <elf> ``` Every capture — project firmware or vendor demo — goes through `etm_capture.py`; extend it when a board needs something new, never hand-roll Ozone drivers. Choosing capture flags (semantics in `--help`): - fresh-boot profile/coverage: defaults (flash + reset + trace from startup) - a LIVE state established first (for example flash, wait for enumeration, then capture): `--attach` — no reflash/reset (flashed firmware must match `--elf` and have trace init built in); the default would erase that state - raw history: `--trace-csv` (~80 MB/1M instructions) — when sequence/timing matters, e.g. feeding `--isr` - stream dies (overflow/unknown-packet): `--no-timestamps`, then reduce the core clock (`boards.md`); marginal wiring: sweep `--trace-timing`, isolate lines with `--trace-width`. "capture OK" requires nonzero profile totals — silence (no trace at all) fails with its own error - deeper data: `--profile-lines-csv` (hottest lines), `--profile-insts-csv` (branch bias), `--sample "expr,.."` (data sampling), `--power` (probe-powered targets only), `--os-plugin` (RTOS timeline), `--trace-only` (experimental, see Warnings) - targets without a reference project: `--device` with its interface, speed and width, plus `--jlink-script` when the firmware doesn't init the trace pins First trace on a board — or after any rewiring — is a bring-up, not a plain capture: follow `boards.md`, "Adding a new board" (vendor example first), and read it before touching hardware. Analyzer: `--isr ENTRY[,BODY..] --tick-symbol SYM --tick-hz HZ` gives ISR min/median/avg/worst from a `--trace-csv` capture with timestamps, calibrated against a periodic handler the firmware runs at a rate you know (`nm` the ELF for its name: `SysTick_Handler`, pico-sdk `isr_systick`, ...; a wrong rate scales every duration); without the pair, timing is reported unavailable. Disassemble both entries first (`objdump -d`): an `--isr` entry that is also a loop head splits one invocation into several episodes, and the tick handler must be short and return to other code between beats (fast-enumerating boards need a short no-eviction run). Outputs in `<dir>`: `code_profile.txt` (run/fetch counts + coverage); on request `itrace.csv`, `profile_lines.csv`, `profile_insts.csv`, `samples.csv`, `power.csv`; `session.log` / `ozone_console.log` / `jlink.log` as evidence. ## Reading results - **Load %** = share of instruction **fetches** — Ozone has no per-function time; time comes only from itrace timestamps (`--isr`, time-share table). - ISR timing: sub-µs values are approximate (interpolated timestamps — hence the SysTick calibration); instruction counts are exact. Time-share ≫ instruction-share = stalled/waiting (e.g. slave-mode FIFO at wire pace). - "Fully covered" needs both branch directions — 100% is not expected from an idle run. - itrace timestamps scale by `VAR_TRACE_CORE_CLOCK` (from the board reference; `--core-clock` overrides): ordering is exact, absolute times approximate. - A ms+ "largest gap" or `Trace overflow detected` beyond the startup burst = lost packets — reduce the core clock or trace a quieter phase. - One `Invalid trace timestamp` line at `Debug.Halt` is a normal decoder artifact. - `itrace.csv is truncated` = the run executed code Ozone has no image of, and its export only reaches back to the last such address. Code the ELF says startup copies to RAM is read into Ozone's instruction cache for you; boot-ROM calls or code built at run time are not — trace a phase without them. A profile-only capture survives with a warning: those fetches are missing. - `Unknown trace data packet … Trace collection stopped!` = stream dead from that point (the script exits non-zero): retry with `--no-timestamps`, then reduce the core clock. ## Timing - Capture ≈ `--duration-ms` + 15 s overhead; add ~5 s per 1M instructions with `--trace-csv`. Bash timeout: duration + 120000 ms. - Analyzer: < 5 s for a 2M-row itrace.csv. ## Warnings - **Trace starts at the firmware's trace init**, not at reset: earlier board-init code shows as never-executed and Ozone logs `No trace clock present` — both expected. Trace-from-reset needs a SEGGER J-Link script (`.pex`) instead of firmware init. - Never commit capture output (`itrace.csv` can exceed 100 MB) — keep `--out` in scratchpad/`/tmp`; `*.jdebug.user` files stay untracked. - The automation socket can't evaluate symbolic constants (`EXPORT_AS_CSV`): the scripts send numeric/plain commands only — keep it that way when extending them (UM08025 §6.7). - Ozone has no `--help`/`--version` — any such probe opens the GUI; check with `which ozone` only. - **`--trace-only` is experimental**: ETM start/stop comparators are scarce and erratic — low-rate handler windows may silently not record, adjacent instructions leak in, timestamps are invalid across gaps, and the profile becomes share-of-traced-stream. Use only for instruction-exact inventories of high-rate symbols; for ISR timing use full trace + `--isr`. ## Per-board notes Every validated board has a row (config: core clock, TRACECLK, width, timing, physical setup, TODO) and a caveat entry in `boards.md` (same directory) — **read a board's row and caveat before capturing on it**; new validations add both. Timing semantics and clock columns are explained at the top of that file. ## References - Ozone manual (UM08025, automation socket §6.7, project commands §7): <https://www.segger.com/downloads/jlink/UM08025_Ozone.pdf>; the installed Ozone ships its own revision of it under its `Doc/` directory (offline fallback — section numbers can differ between revisions). - J-Link / J-Trace manual (UM08001, trace ch. 10, timing troubleshooting): <https://kb.segger.com/UM08001_J-Link_/_J-Trace_User_Guide>
GitHub에서 보기