Misc shutdown fixes

* Destroy the ImGui debugger explicitly, not at static destruction time

* Windows: destroy the debugger windows before NativeShutdown()

* Clear the disassembly cache where it goes stale, not in view destructors
  (behind a no-op stub for libretro, which doesn't build DisassemblyManager.cpp)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Henrik Rydgård
2026-09-04 13:06:24 -06:00
co-authored by Claude Opus 5
parent 050cacbbdf
commit 1c7f393e2b
10 changed files with 29 additions and 15 deletions
+4
View File
@@ -40,6 +40,10 @@
DisassemblyManager g_disassemblyManager;
void ClearDisassemblyCache() {
g_disassemblyManager.clear();
}
bool isInInterval(u32 start, u32 size, u32 value) {
return start <= value && value <= (start+size-1);
}
+9
View File
@@ -204,6 +204,15 @@ private:
extern DisassemblyManager g_disassemblyManager;
// Drops the cached disassembly. Call this when what it describes - emulated memory and the symbol
// map - goes away. DisassemblyManager.cpp isn't built for libretro (see libretro/Makefile.common),
// so there it's a no-op, the same way the WebSocket debugger entry points are.
#ifdef __LIBRETRO__
inline void ClearDisassemblyCache() {}
#else
void ClearDisassemblyCache();
#endif
bool isInInterval(u32 start, u32 size, u32 value);
bool IsLikelyStringAt(uint32_t addr);
@@ -37,9 +37,6 @@ public:
WebSocketDisasmState() {
g_disassemblyManager.setCpu(currentDebugMIPS);
}
~WebSocketDisasmState() {
g_disassemblyManager.clear();
}
void Base(DebuggerRequest &req);
void Disasm(DebuggerRequest &req);
@@ -33,9 +33,6 @@ struct WebSocketSteppingState : public DebuggerSubscriber {
WebSocketSteppingState() {
g_disassemblyManager.setCpu(currentDebugMIPS);
}
~WebSocketSteppingState() {
g_disassemblyManager.clear();
}
void Into(DebuggerRequest &req);
void Over(DebuggerRequest &req);
+5
View File
@@ -50,6 +50,7 @@
#include "Core/MIPS/MIPS.h"
#include "Core/MIPS/MIPSAnalyst.h"
#include "Core/MIPS/MIPSVFPUUtils.h"
#include "Core/Debugger/DisassemblyManager.h"
#include "Core/Debugger/LineInfo.h"
#include "Core/Debugger/SymbolMap.h"
#include "Core/System.h"
@@ -616,6 +617,10 @@ void CPU_Shutdown(bool success) {
pspFileSystem.Shutdown(); // This unmounts all filesystems.
// Everything the disassembly cache describes - emulated memory and the symbol map - is about
// to go away, so drop it here rather than leaving it to whichever debugger UI closes last.
ClearDisassemblyCache();
mipsr4k.Shutdown();
Memory::Shutdown();
HLEPlugins::Shutdown();
-4
View File
@@ -38,10 +38,6 @@ ImDisasmView::ImDisasmView() {
displaySymbols_ = true;
}
ImDisasmView::~ImDisasmView() {
g_disassemblyManager.clear();
}
void ImDisasmView::ScanVisibleFunctions() {
g_disassemblyManager.analyze(windowStart_, g_disassemblyManager.getNthNextAddress(windowStart_, visibleRows_) - windowStart_);
}
-1
View File
@@ -23,7 +23,6 @@ class MIPSState;
class ImDisasmView {
public:
ImDisasmView();
~ImDisasmView();
// Public variables bounds to imgui checkboxes
bool followPC_ = true;
+6
View File
@@ -1046,7 +1046,13 @@ void NativeShutdownGraphics(GraphicsContext *graphicsContext) {
}
ImGui_ImplThin3d_DestroyDeviceObjects();
ImGui_ImplThin3d_Shutdown();
// Destroy the debugger here, while the things it refers to are still alive. If left to
// static destruction, ~ImDisasmView runs after Core's globals are gone and its
// g_disassemblyManager.clear() walks a destroyed map (and locks a destroyed mutex).
imDebugger_.reset();
ImGui::DestroyContext(ctx_);
ctx_ = nullptr;
imguiInited_ = false;
}
#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP)
-1
View File
@@ -209,7 +209,6 @@ CtrlDisAsmView::~CtrlDisAsmView()
{
DeleteObject(font);
DeleteObject(boldfont);
g_disassemblyManager.clear();
}
static COLORREF scaleColor(COLORREF color, float factor)
+5 -3
View File
@@ -1312,6 +1312,11 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin
_dbg_assert_(mainThread.joinable());
mainThread.join();
// The debugger windows reach into core state as they go away (CtrlDisAsmView touches
// g_disassemblyManager, for example), so tear them down while the core is still around.
MainWindow::DestroyDebugWindows();
DialogManager::DestroyAll();
// It's safe to call NativeShutdown, we've joined the main thread.
NativeShutdown();
@@ -1319,9 +1324,6 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin
// g_InputManager.StopPolling() is called in WM_DESTROY
MainWindow::DestroyDebugWindows();
DialogManager::DestroyAll();
TimeShutdown();
UnRegisterCMPTMFApis();