ppsspp_breakpoint
Control PPSSPP debugging: set, remove, update, list CPU breakpoints and memory watchpoints; block until a hit or trace the hit with registers and backtrace then resume.
Instructions
PURPOSE: Manage breakpoints AND consume their hits — set/remove/update/list CPU execution breakpoints and memory watchpoints, strict-wait for a hit, or one-call arm-hit-capture-resume tracing.
USAGE: session_id optional when exactly one session is active; management actions as below; action='wait' blocks until any breakpoint is hit (set/mem_set first; lock-free; breakpoint stays armed); action='trace' arms a temporary MEMORY breakpoint at address, waits, captures pc/registers/backtrace, always removes it and resumes (defaults to read access; narrow with read/write/size). For EXECUTION breakpoints use action='set' + 'wait'.
ROUTING: persistent breakpoint management -> here; one-shot strict-wait -> action='wait'; armed hit-capture -> action='trace'. BEHAVIOR: MUTATING. trace arms/removes and set/mem_* manage state; Reliable hits need CPUCore=2 (IR Interpreter). mem_remove resolves the watchpoint's real size via mem_list first (address+size matching); mem_update merges existing read/write/change unconditionally (PPSSPP zero-omits omitted bools). CPU set/remove return no data — the tool follows with a list for verification. wait/trace are lock-free during the wait itself (concurrent reads keep working); do NOT submit step/pause/resume during a wait. CONDITION SEMANTICS: PPSSPP's IR mode ignores register conditions, so any condition is enforced MCP-side — the breakpoint is armed unconditionally and each hit's expression is evaluated with cpu.evaluate; a falsy hit is auto-resumed (not surfaced) and counted in filtered_hits.
RETURNS: stats → {mode:"stats", window_s, total_hits, by_pc: [{pc, count, first_seen, last_seen}]} (fixed ~30s sampling window — no shorter-window option, probe_changes?: [{probe, old, new, ts}], note}; management actions → {action, address, enabled, breakpoints[]}; wait → {hit, already_paused, timeout_s, pc, reason, related_address, ticks, condition, condition_filtered, filtered_hits, storm_break}; trace → {hit, already_paused, address, access, timeout_s, hits: [{pc, related_address, reason, ticks, mem_hits?, registers?, backtrace?}], bp_removed, resumed, note}.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| log | No | Log flag (update / mem_set / mem_update only; None = don't change). For mem_set, defaults to False when None. | |
| read | No | Trigger on read access (mem_set only; None defaults to True). For mem_update, passing read triggers a merge query — omit to leave read unchanged. | |
| size | No | Memory breakpoint watch size in bytes (mem_set / mem_remove / mem_update; default 4). Fixed-width watches use 1/2/4; larger sizes are passed through to PPSSPP as a range watch. NOTE: PPSSPP matches a memory watchpoint by the exact address+size pair (BreakpointSubscriber.cpp) -- the size is part of the match key, not just bookkeeping. mem_remove therefore resolves the real size via mem_list first, because removing with the caller's size alone silently fails when it differs (e.g. a 16-byte watch removed with the default 4). | |
| write | No | Trigger on write access (mem_set only; None defaults to True). For mem_update, passing write triggers a merge query — omit to leave write unchanged. | |
| action | Yes | Breakpoint operation. Valid values: Consumption actions (lifecycle orchestration): - 'wait': STRICT-WAIT — block until any breakpoint is hit (arm nothing; set/mem_set first). Lock-free: concurrent reads keep working. The breakpoint stays armed. - 'stats': HIT-FREQUENCY — count breakpoint hits by pc over a time window; optionally samples probe value changes via state_observer. Read-only. - 'trace': HIT-SNAPSHOT-RESUME — arm a temporary MEMORY breakpoint at `address`, wait for the hit, capture pc/registers/backtrace, ALWAYS remove it, then resume (defaults to read access; narrow with read/write/size). For EXECUTION breakpoints use action='set' + 'wait' instead. CPU breakpoint actions: - 'set': add a CPU execution breakpoint (requires address; enabled? defaults to True; condition? optional — enforced MCP-side (falsy hits auto-resumed, counted in filtered_hits), NOT sent to PPSSPP). - 'remove': delete a CPU breakpoint by address. - 'list': list all current CPU breakpoints. - 'update': update a CPU breakpoint's enabled/log/condition/log_format (requires address; all other params optional; condition='' clears it). Memory breakpoint actions: - 'mem_set': add a memory access breakpoint (requires address; size?/read?/write?/enabled?/log?/condition?/log_format?). - 'mem_remove': delete a memory breakpoint by address. Delete semantics are STRICT: removing a non-existent memcheck is an ERROR (unlike ppsspp_state_observer action=clear, which is idempotent-ok). - 'mem_list': list all current memory breakpoints. - 'mem_update': update a memory breakpoint's enabled/log/condition/log_format (requires address). | |
| address | No | Required for set / remove / update / mem_set / mem_remove / mem_update. Breakpoint address, as a hex string (e.g. '0x08804000'). Not used by list / mem_list. The schema default of '0x0' exists for legacy callers -- do NOT rely on it when the action is one of the above. | 0x0 |
| enabled | No | Breakpoint enable flag. For action='set' / 'mem_set', defaults to True when None. For action='update' / 'mem_update', None means 'don't change'. Ignored for remove / list actions. | |
| condition | No | Break condition expression (set / update / mem_set / mem_update; None = don't send). Enforced MCP-side: PPSSPP's IR mode silently ignores register conditions, so the breakpoint is armed UNCONDITIONALLY and falsy hits are auto-resumed and counted in filtered_hits. | |
| timeout_s | No | Wait budget in seconds (wait / trace only; default 30, clamped to [0.5, 300]). On timeout: hit=false — NOT an error — so callers can poll. | |
| log_format | No | Log format string (update / mem_set / mem_update only; None = don't change). | |
| session_id | No | Active session ID; omit to auto-resolve when exactly one session is active. | |
| want_backtrace | No | Include the HLE backtrace in the hit (trace only; CPU is paused at the hit, so the trace is valid). | |
| want_registers | No | Include the full CPU register dump in the hit (trace only). |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| pc | No | ||
| hit | No | ||
| hits | No | ||
| mode | No | ||
| note | No | ||
| by_pc | No | ||
| ticks | No | ||
| access | No | ||
| action | No | 'set' / 'remove' / 'list' / 'update' / 'mem_set' / 'mem_remove' / 'mem_list' / 'mem_update'. | |
| reason | No | ||
| address | No | ||
| enabled | No | Enabled flag (set / mem_set / update / mem_update only). | |
| resumed | No | ||
| mem_hits | No | ||
| window_s | No | ||
| condition | No | ||
| timeout_s | No | ||
| bp_removed | No | ||
| total_hits | No | ||
| breakpoints | No | Breakpoint list (list / mem_list only). | |
| storm_break | No | ||
| filtered_hits | No | ||
| probe_changes | No | ||
| already_paused | No | ||
| related_address | No | ||
| condition_filtered | No |