Commit Graph
164 Commits
Author SHA1 Message Date
Henrik RydgårdandClaude Opus 5 75ff0d406c Rename Memory::Lock() to Core_LockAgainstShutdown(), move it to Core
It stopped being about memory when CPU_Shutdown started holding it across the
whole teardown - it's what keeps kernel objects, the symbol map and the memory
map from being freed while another thread reads them. The old name invited the
reading that it locks memory *access*, which it has never done.

Memory::Reinit() now holds it across both halves rather than relying on
Memory::Shutdown()'s own acquire: between Shutdown() and Init() there is no
memory map at all, and a reader could slip into that gap.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
2026-08-17 13:11:16 +02:00
Henrik RydgårdandClaude Opus 5 f57a102027 Win32 debugger: take the shutdown lock everywhere core state is read
Seven GUI-thread readers held only g_frameMutex, which guards against the CPU
thread mutating state but not against the core being torn down - and two of
PSP_Shutdown's paths (EmuScreen::sendMessage, ProcessScreenSwitches) run outside
that span entirely. CtrlThreadList::reloadThreads walking kernel objects that
__KernelShutdown had freed was the concrete crash; the symbol map readers
(CDisasm::Show/NotifyMapLoaded, CtrlModuleList, CtrlWatchList) had the same
shape.

Always g_frameMutex first, then Memory::Lock().

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
2026-08-17 13:11:16 +02:00
Henrik RydgårdandClaude Opus 5 35a91b757a Move the temporary breakpoint out of the user's breakpoint list
step-over, step-out and run-until plant a one-shot breakpoint at the address
they want execution to return to. Keeping it in breakPoints_ alongside the
user's own meant the two kept colliding:

- Adding a log-only user breakpoint at the same address hijacked the temporary
  one. AddBreakPoint() didn't match across temp-ness so both existed, and then
  ChangeBreakPoint() looked up "the first enabled breakpoint at this address" -
  a log-only breakpoint isn't enabled, so the temporary one won and had its
  action overwritten to log-only. It lost PAUSE and the step never came back.
- RemoveBreakPoint() erased up to two entries per address to catch an
  overlapping temporary one, so deleting either deleted both - including the
  interpreter's cleanup path in CheckExecBreakpoints() taking the user's
  breakpoint with it.
- ExecBreakPoint() handled one breakpoint per address, so with both at the same
  address only one of them did anything: the step completed but the user's log
  line never printed.
- Nothing dropped it when something *else* stopped us first, so an interrupted
  step left a breakpoint armed at an address nobody was waiting for anymore,
  which later fired as a phantom stop.

It's a single TempBreakPoint member now, invisible to the breakpoint lists and
untouched by user edits. One is enough: step over/out and cross-thread step into
all require the CPU to already be stepping and resume it immediately, so only
one can be in flight, and run-until now replaces rather than stacking (two
pending run-untils had no coherent meaning, and the loser stayed armed).

Behavior follows what other debuggers do. Both breakpoints at an address are
evaluated independently and their actions combine, so a log-only breakpoint
logs without stopping and still lets the step finish. Core_Break() drops the
temporary breakpoint on any stop, whatever the reason - the same way gdb deletes
its step-resume breakpoint and lldb discards the thread plan.

Two things to be careful of, both covered by the new TempBreakpoints test:
HasBreakPoints() has to account for it, or the interpreter's checked run loop
and the JIT skip breakpoint checking entirely and a step with no user
breakpoints set never returns; and IsAddressBreakPoint() (user-facing, for the
lists and disassembly markers) is now separate from NeedsBreakCheckAt() (what
the JIT frontends and interpreter ask), since only the latter should see it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
2026-08-17 00:29:26 +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 f72709feb0 Legacy Win32 debugger: fix the painting problem with a frame-scoped mutex
Debugger windows (register list, disassembly view, memory view, breakpoint/
thread/module/stack lists, watch list) read CPU-thread-owned state directly
from the GUI thread's WM_PAINT/list-fill handlers, racing against the CPU
thread. Routing every read through Core_RunOnCPUThread would be too slow for
something invoked continuously on paint/list-refresh.

Add g_frameMutex (Core.h/Core.cpp), held by NativeFrame() only across the
span where it actually touches that state (running the CPU, processing
breakpoints, running the ImGui debugger) - not across input handling or the
present/frame-pacing waits. Debugger windows now hold the same mutex while
reading, giving synchronized reads without the round-trip cost of queuing
to the CPU thread.

CtrlRegisterList::onPaint() goes back to always reading live values (now
safe under the lock) and grays them out by color alone while the core is
running, rather than the earlier snapshot-caching approach.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hqm11k99viLfbJm2MkH4BH
2026-08-08 17:22:12 +02:00
Henrik RydgårdandClaude Sonnet 5 25c3c90c94 Legacy Win32 debugger: route mutations through the CPU thread
The Win32 debugger dialogs mutate CPU-thread-owned state (breakpoints,
symbol map, registers, memory, kernel threads) directly from the GUI
thread with no synchronization, same problem the WebSocket debugger had.
Route all of these through Core_RunOnCPUThread instead, following the
same pattern used there.

Also drops two forced-pause (Core_Break/Core_WaitInactive/Core_Resume)
dances in CtrlMemView::onChar and DumpMemoryWindow's dump handler, now
unnecessary since Core_RunOnCPUThread works whether the CPU is running
or already stepping.

Modal dialogs (MessageBox, InputBox_GetString, BreakpointWindow::exec,
etc.) are kept outside any queued callback so they never block the CPU
thread on user input. Pure reads/painting (disassembly formatting,
search, list reloads, onPaint) are intentionally left as-is for now.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hqm11k99viLfbJm2MkH4BH
2026-08-08 17:22:12 +02:00
Henrik RydgårdandClaude Sonnet 5 b1f0112cef Debugger: Remove opcode-fusion display and fix cpu step size units
DisassemblyManager used to fuse lui+addiu/load/store into single pseudo-
instructions ("li", fused loads/stores) for display. This only applied to a
handful of opcodes, complicated DisassemblyManager, and was the root cause of
a stepping bug: Core_PerformCPUStep's Into/Over cases treated stepSize as a
byte count, while the WebSocket cpu.stepInto handler computed it as an
instruction count (needed to step over a whole fused macro in one go) - so a
plain, non-fused stepInto silently executed zero instructions.

Removed the fusion logic entirely (DisassemblyMacro, DISTYPE_MACRO) - every
disassembly line is now exactly one 4-byte instruction. With that,
"how many instructions does this line span" is always 1, so the
getInstructionSizeAt() byte-size queries in the legacy Windows and ImGui
debuggers are gone too; step requests just pass 1. Core_RequestCPUStep's
stepSize is now consistently in instructions everywhere.

Also fixes the PPSSPPHeadless build, broken since 0ed1f3e added
OpenWebDebugger() (which calls System_LaunchUrl) without a headless stub.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hqm11k99viLfbJm2MkH4BH
2026-08-08 11:16:36 +02:00
Henrik Rydgård b0a3d5753a Remove an obsolete setting 2026-03-07 09:19:31 +01:00
Henrik Rydgård 42b46e2098 If RetroAchievements' "Hardcore mode" is enabled, prevent debugger use
Fixes an easy way you could access the debugger windows.
2025-07-09 16:25:24 +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 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 39ffe92e0a LR->RA rename, fixes 2024-12-12 17:47:37 +01:00
Henrik Rydgård 5817f60346 Remove redundant code. Add "break after syscall" button, fix up 2024-12-10 22:43:31 +01:00
Henrik Rydgård 474e7acf54 Hook up the new (bare-bones) ImGui debug window to GPU stepping, fix stepping. 2024-12-05 00:51:59 +01:00
Henrik Rydgård ea43e07cce Move some stuff around, rename some stuff 2024-12-05 00:36:48 +01:00
Henrik Rydgård 0f97578a35 Replace a bad function 2024-12-03 20:33:12 +01:00
Henrik Rydgård 7992ff4627 Make CBreakpoints an object 2024-11-25 00:22:53 +01:00
Henrik Rydgård a8744f5992 Disable the native debugger when RetroAchievements hardcore mode is enabled 2024-11-10 19:19:03 +01:00
Henrik Rydgård cf6d06c56a Start work on imdbg 2024-11-07 11:10:12 +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 785ce86f76 Apply that to breaking execution as well. 2024-11-03 17:54:55 +01:00
Henrik Rydgård 5598228462 Better notification mechanism to scroll the disasm window after step 2024-11-03 17:54:55 +01:00
Henrik Rydgård 3ba3b911b4 Add disasm event WM_DEB_AFTEREVENT 2024-11-03 17:54:54 +01:00
Henrik Rydgård c851be26eb Decouple the last part of step() 2024-11-03 17:54:54 +01:00
Henrik Rydgård 828523165f Rework the step functions. 2024-11-03 17:54:54 +01:00
Henrik Rydgård a730f01c53 Unify the step functions 2024-11-03 17:54:54 +01:00
Henrik Rydgård 73aba606e7 Harmonize the various step functions in the disasm window 2024-11-03 17:54:54 +01:00
Henrik Rydgård 69177f7fa6 Same with Step Out 2024-11-03 17:54:54 +01:00
Henrik Rydgård 251fc8d905 Same with Step Over 2024-11-03 17:54:54 +01:00
Henrik Rydgård c787149472 Start moving the CPU debug-step logic to Core.cpp 2024-11-03 17:54:51 +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 770e5b4f4a Fix shortcut translation so we don't eat Ctrl+C for RAIntegration windows 2024-04-05 22:41:08 +02:00
Unknown W. Brackets f31a7a0582 Debugger: Update VFPU dialog more evenly.
Was previously not updating when you hit a breakpoint, stepped out, etc.
2023-04-09 10:12:54 -07:00
Unknown W. Brackets 879e91dbf6 Debugger: Add empty watch tab for debugger.
Currently, there's nothing shown here.
2023-04-09 00:01:42 -07:00
Unknown W. Brackets cbc1ed4bfe Debugger: Lock memory during stack walk.
In case of shutdown during it.
2023-02-22 21:14:33 -08:00
Henrik Rydgård b4a91dbce5 Disable dark mode in disasm window, and the scrollbar hack 2023-01-12 01:05:06 +01:00
Henrik Rydgård 62e5a5dfc3 Rudimentary support for Windows Dark Mode. Still undocumented for Win32 apps, so ugly. 2023-01-12 00:41:25 +01:00
Unknown W. Brackets 9cfcbc46e6 Global: Cleanup initialization/pointer checks.
Cleaning up a lot of cases of uninitialized data, unchecked return values
for failures, and similar.
2022-12-10 21:13:36 -08:00
Henrik Rydgård 858539c5b0 Merge pull request #15402 from unknownbrackets/debugger-highlight
GE Debugger: Highlight changed state values
2022-02-13 21:42:43 +01:00
Unknown W. Brackets b654ee9d44 Debugger: Allow custom draw and generic lists. 2022-02-13 10:22:38 -08:00
Unknown W. Brackets ea6c42aafb Debugger: Avoid scroll on breakpoint add. 2022-02-13 07:57:54 -08:00
Unknown W. Brackets 9435938ad9 Debugger: Cleanup some header pollution.
Also simplify just a bit.
2022-02-13 07:55:34 -08:00
Unknown W. Brackets 7fe9c4e519 Debugger: Debounce updates to disasm text.
This is a bit slow, so deferring it improves update speed.
2021-12-12 11:57:24 -08:00
Unknown W. Brackets 045fc0f2b4 Debugger: Never wait inactive on window update.
This blocks the UI, and we always get a message when stepping is actually
active anyway.  More importantly, we PostMessage() debugger state, so we
might've already resumed.
2021-12-12 11:36:06 -08:00
Unknown W. Brackets db2eddcf27 Debugger: Remove double debug window updates.
Core_EnableStepping() is already marking the debug windows for update,
there's no need to call SetDebugMode() again.
2021-12-12 11:24:03 -08:00
Unknown W. Brackets de1a6a93d8 Debugger: Debounce redraws using timers. 2021-12-12 10:22:21 -08:00
Unknown W. Brackets 96c1ae0f12 Debugger: Fix crash on stepping/break. 2021-11-14 09:31:43 -08:00
Henrik Rydgård fc26beca4b Also delay-load the VFPU dialog, just because 2021-11-13 22:24:30 +01:00
Henrik Rydgård c004e9ca9d Windows debugger: Load the dialogs on demand.
I think this is especially good for the Ge dialog since we now can avoid
initializing that extra GL context unless you open the dialog.
2021-11-13 22:10:37 +01:00