Commit Graph
32 Commits
Author SHA1 Message Date
Henrik RydgårdandClaude Opus 5 256de40a30 Tidy up the breakpoint skip-first mechanism
The tick basis was actually sound - CoreTiming::GetTicks() is continuous across
Advance(), so "ticks unchanged" really does mean "no instruction retired since",
which is exactly the window the suppression needs. The plumbing around it was
the problem:

- ExecRegBreakpoint() applied the skip only to the pause, so stepping off a
  log+pause register breakpoint printed it again and counted a second hit. The
  check now sits at the top of ExecBreakPoint(), ExecMemCheck(), ExecOpMemCheck()
  and ExecRegBreakpoint() instead of being repeated at seven call sites across
  the interpreter and four JIT frontends, where one of them had it wrong and
  another checked a different address than the rest.
- Address 0 doubled as "nothing to skip" (ClearSkipFirst() existed but was dead
  code; the JITs cleared by calling SetSkipFirst(0)), so a breakpoint at 0 would
  have been permanently suppressed. There's an explicit valid flag now, and
  ClearSkipFirst() is what clears it.
- The marker was set from five places and never cleared when execution stopped,
  so one could outlive the resume that armed it. Core_Break() clears it now, and
  the two WebSocket subscribers that set it immediately before asking for a
  step - which sets it again itself - no longer do.
- SetSkipFirst() now only arms when some breakpoint machinery actually exists,
  so a stale marker can't sit around waiting to swallow a breakpoint added later.

CheckSkipFirst() returning an address (compared against pc by each caller) is
replaced by ShouldSkipBreakpoint(addr), which compares against both addr and
currentMIPS->pc - under a JIT those differ, and only some callers knew that.

Covered by the Breakpoints unit test, including that a suppressed breakpoint
neither logs nor counts a hit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
2026-08-17 23:25:56 +02:00
Henrik RydgårdandClaude Opus 5 59eb8a9a81 Report emulated time in cpu.status, and note headless/wsdbg traps in AGENTS.md
cpu.status only reported raw CPU ticks, which a client can't turn into a time:
the PSP's clock frequency is changeable and games do change it, so the
ticks-per-second ratio isn't fixed over a run. CrossCraft Classic runs at
333MHz, so assuming the default 222MHz reads 9.1s where the truth is 6.3s -
enough to put scripted input injection in the wrong place entirely.

Adds "us" (emulated microseconds) and "clockHz" alongside "ticks".
CoreTiming::GetGlobalTimeUs() can't be used directly for this: it rebases its
own internal counters as a side effect, and cpu.status is deliberately served
straight from the WebSocket thread rather than queued to the CPU thread (it's
meant to be cheap and frequently pollable). So PeekGlobalTimeUs() computes the
same value without the rebasing.

AGENTS.md picks up the things that cost time while driving headless over the
websocket API: --sync silently desynchronises on raw JSON lines because only
wsdbg's key=value shorthand gets a ticket; headless reports HAS_DEBUGGER as
false so anything gated on it silently does nothing there; the memstick is
hardcoded next to the executable; a leftover headless process turns a build
into an LNK1168 that looks like a compile error; wrapping the launcher in
`timeout` kills the emulator along with it, losing the crash you stopped at;
response field names aren't uniform (value vs uintValue); broadcast.config.set
rejects two of the four keys the docs list.

Also writes down the ELF-as-oracle technique that cracked the relocation bug -
when homebrew ships app.elf next to app.prx, the pre-link ELF still has the
symbols and the relocation symbol indices the PRX format discards, so the
loader's output can be checked exhaustively offline instead of by re-running.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
2026-08-17 18:51:54 +02:00
Henrik Rydgård 14405c08cf Remove the concept of stepSize from the debugger 2026-08-16 16:59:54 +02:00
Henrik Rydgård e9a3449ede More MIPSState * plumbing (manual) 2026-08-12 14:02:19 +02:00
Henrik RydgårdandClaude Sonnet 5 c32df3d931 Debugger: Route BreakpointSubscriber, DisasmSubscriber, and most of
CPUCoreSubscriber through the CPU thread

Continues the pattern started with SteppingSubscriber: route breakpoint,
disassembly, symbol, and register access through Core_RunOnCPUThread()
instead of touching that state directly from the WebSocket handler thread.

Two intentional exceptions, matching the reasoning already used for
cpu.stepInto's "not currently stepping" branch:
- cpu.stepping's Core_Break() call stays unqueued - it's what makes the CPU
  thread start reaching the queue drain point in the first place.
- cpu.status stays unqueued - it's meant to be a cheap, frequently-pollable
  status check, and its "pc" field is already documented as inaccurate unless
  stepping. Matches how SteppingBroadcaster already reads the same state
  directly from the WebSocket thread.

Where a handler's response doesn't depend on anything the queued lambda
computed (plain add/remove endpoints), moved req.Respond() back out after
the Core_RunOnCPUThread() call for readability - the JSON building and
socket write happen later in Finish() regardless of where Respond() is
called, so there's no thread-safety difference either way, just clarity
about what actually needs to run on the CPU thread.

memory.searchDisasm carries the same caveat flagged for memory.search: its
scan range has no size cap, so if the CPU is stepping, a very large range
will now block the CPU thread's own frame pump for the scan's duration
rather than running unqueued on the WebSocket thread as before. Not fixed
here - noted in a comment at the call site.

Replaced remaining `auto` locals in these three files with concrete types.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hqm11k99viLfbJm2MkH4BH
2026-08-08 11:56:32 +02:00
Henrik Rydgård d6ebfed432 Fix some bugs and warnings from a pass of static analysis 2026-01-29 17:41:54 +01:00
Nemoumbra 846c265972 Fixed the docs + better wording 2025-08-10 17:44:38 +03:00
Henrik Rydgård 088a02bfdb Cleanup a lot of the bootup state management. 2025-03-30 14:02:29 +02:00
Henrik Rydgård 31cf5771f4 Turn the break reason into an enum, fix some minor issues 2025-02-19 16:01:11 -06:00
Henrik Rydgård 3e198c53b2 More include cleanup 2024-12-18 13:57:26 +01:00
Henrik Rydgård 20a17a0e8d Reorganize DebugInterface etc a bit.
KernelThreadDebugInterface no longer has a useless copy of a MIPSDebugInterface.
2024-12-12 18:54:46 +01:00
Henrik Rydgård ea43e07cce Move some stuff around, rename some stuff 2024-12-05 00:36:48 +01:00
Henrik Rydgård 7992ff4627 Make CBreakpoints an object 2024-11-25 00:22:53 +01:00
Henrik Rydgård 3a5968ba33 Don't block the render thread while the CPU is paused. This is a prereq for imgui debuggers. 2024-11-05 12:53:21 +01:00
Henrik Rydgård d3e9398cb3 Split Core_EnableStepping into Core_Break and Core_Resume 2024-11-03 17:53:42 +01:00
Henrik Rydgård 9253bf9cb5 Add a new screen for managing installed Adreno drivers 2024-01-17 10:51:08 +01:00
Unknown W. Brackets 1233d1d376 Reporting: Disable when debugger methods used.
This won't catch all means of hacking memory, but will catch some.
Trying to reduce noise in reporting from debugging.
2022-07-30 18:21:32 -07:00
Unknown W. Brackets 2bd13c5d9d Debugger: Track reason for entering stepping. 2021-10-23 16:56:15 -07:00
Unknown W. Brackets 4ce2b64ef7 Debugger: Refactor to allow simpler broadcasting.
It's okay to mix a bit for simple ones, I think.
2018-09-01 10:15:22 -07:00
Unknown W. Brackets 5670fc03ae Debugger: Add debug interfaces for threads.
This way we can switch context.
2018-06-08 06:59:18 -07:00
Unknown W. Brackets 5b132c904a Debugger: Fix resume not skipping breakpoints. 2018-06-08 06:59:18 -07:00
Unknown W. Brackets 65feb5f09c Debugger: Include assembler API. 2018-06-08 06:59:18 -07:00
Unknown W. Brackets fc8ad3b47b json: Optimize writing a bit.
This improves the responsiveness of certain APIs.
2018-06-08 06:59:17 -07:00
Unknown W. Brackets e746a2d106 Debugger: Add stepping to WebSocket API. 2018-06-08 06:59:17 -07:00
Unknown W. Brackets f66738eac7 Debugger: Add cpu.evaluate method.
Probably useful for a bunch of things, really.
2018-06-08 06:59:17 -07:00
Unknown W. Brackets 944948a5f4 Debugger: Initial disasm API. 2018-06-08 06:59:17 -07:00
Unknown W. Brackets 05c560b52d Debugger: Include PC and ticks in stepping events. 2018-06-08 06:59:17 -07:00
Unknown W. Brackets d67a1d4a3a Debugger: Allow pinging current status.
Helps especially when reconnecting.
2018-06-08 06:59:16 -07:00
Unknown W. Brackets a341994622 Debugger: Add cpu.stepping and cpu.resume. 2018-06-08 06:59:16 -07:00
Unknown W. Brackets a4044fd6a0 Debugger: Reorganize state handling.
Looking like this will be needed for disasm caches, etc.
2018-06-08 06:59:16 -07:00
Unknown W. Brackets 0a21063525 Debugger: Cleanup, add initial API docs. 2018-06-08 06:59:16 -07:00
Unknown W. Brackets 1f987bf1c1 Debugger: Initial register get/set APIs. 2018-06-08 06:59:16 -07:00