From 07bdb3454a6b866aff97cb05fc9bb3a5c4b77586 Mon Sep 17 00:00:00 2001 From: Crementif <26669564+Crementif@users.noreply.github.com> Date: Sun, 1 Feb 2026 01:53:02 +0100 Subject: [PATCH] debugger: Various QoL improvements (#1777) * Ignore keyboard inputs when debugger is focused * Add workaround for slightly improved multi-thread debugging * debugger: Add interpreter mode switch and log-only memory breakpoints --- src/Cafe/HW/Espresso/Debugger/Debugger.cpp | 25 ++++++++++++++- src/Cafe/HW/Espresso/Debugger/Debugger.h | 1 + src/gui/interface/WindowSystem.h | 1 + src/gui/wxgui/CemuApp.cpp | 25 +++++++++++++++ src/gui/wxgui/debugger/DebuggerWindow2.cpp | 32 ++++++++++++++++++- src/gui/wxgui/debugger/DebuggerWindow2.h | 8 +++-- src/input/api/Keyboard/KeyboardController.cpp | 4 +++ 7 files changed, 92 insertions(+), 4 deletions(-) diff --git a/src/Cafe/HW/Espresso/Debugger/Debugger.cpp b/src/Cafe/HW/Espresso/Debugger/Debugger.cpp index fbfc94fe..02f23497 100644 --- a/src/Cafe/HW/Espresso/Debugger/Debugger.cpp +++ b/src/Cafe/HW/Espresso/Debugger/Debugger.cpp @@ -208,7 +208,24 @@ void debugger_handleSingleStepException(uint64 dr6) if (catchBP) { PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance(); - debugger_createCodeBreakpoint(hCPU->instructionPointer + 4, DEBUGGER_BP_T_ONE_SHOT); + if (debuggerState.logOnlyMemoryBreakpoints) + { + float memValueF = memory_readFloat(debuggerState.activeMemoryBreakpoint->address); + uint32 memValue = memory_readU32(debuggerState.activeMemoryBreakpoint->address); + cemuLog_log(LogType::Force, "[Debugger] 0x{:08X} was read/written! New Value: 0x{:08X} (float {}) IP: {:08X} LR: {:08X}", + debuggerState.activeMemoryBreakpoint->address, + memValue, + memValueF, + hCPU->instructionPointer, + hCPU->spr.LR + ); + if (cemuLog_advancedPPCLoggingEnabled()) + DebugLogStackTrace(coreinit::OSGetCurrentThread(), hCPU->gpr[1]); + } + else + { + debugger_createCodeBreakpoint(hCPU->instructionPointer + 4, DEBUGGER_BP_T_ONE_SHOT); + } } } @@ -543,6 +560,12 @@ void debugger_createPPCStateSnapshot(PPCInterpreter_t* hCPU) void debugger_enterTW(PPCInterpreter_t* hCPU) { + // Currently, we don't support multiple threads inside the debugger. Spin loop a thread if we already paused for another breakpoint hit. + while (debuggerState.debugSession.isTrapped) + { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + // handle logging points DebuggerBreakpoint* bp = debugger_getFirstBP(hCPU->instructionPointer); bool shouldBreak = debuggerBPChain_hasType(bp, DEBUGGER_BP_T_NORMAL) || debuggerBPChain_hasType(bp, DEBUGGER_BP_T_ONE_SHOT); diff --git a/src/Cafe/HW/Espresso/Debugger/Debugger.h b/src/Cafe/HW/Espresso/Debugger/Debugger.h index 8b09477b..03602ff0 100644 --- a/src/Cafe/HW/Espresso/Debugger/Debugger.h +++ b/src/Cafe/HW/Espresso/Debugger/Debugger.h @@ -142,6 +142,7 @@ struct PPCSnapshot typedef struct { bool breakOnEntry; + bool logOnlyMemoryBreakpoints; // breakpoints std::vector breakpoints; std::vector patches; diff --git a/src/gui/interface/WindowSystem.h b/src/gui/interface/WindowSystem.h index dc614691..89e35ee6 100644 --- a/src/gui/interface/WindowSystem.h +++ b/src/gui/interface/WindowSystem.h @@ -44,6 +44,7 @@ namespace WindowSystem std::atomic_int32_t restored_pad_width = -1, restored_pad_height = -1; std::atomic_bool is_fullscreen; + std::atomic_bool debugger_focused; void set_keystate(uint32 keycode, bool state) { diff --git a/src/gui/wxgui/CemuApp.cpp b/src/gui/wxgui/CemuApp.cpp index 1c803bf6..a900f10b 100644 --- a/src/gui/wxgui/CemuApp.cpp +++ b/src/gui/wxgui/CemuApp.cpp @@ -12,6 +12,7 @@ #include "wxgui/helpers/wxHelpers.h" #include "Cemu/ncrypto/ncrypto.h" #include "wxgui/input/HotkeySettings.h" +#include "wxgui/debugger/DebuggerWindow2.h" #include #if ( BOOST_OS_LINUX || BOOST_OS_BSD ) && HAS_WAYLAND @@ -434,6 +435,30 @@ int CemuApp::FilterEvent(wxEvent& event) g_window_info.set_keystatesup(); } + // track if debugger window or its child windows are focused + if (g_debugger_window && (event.GetEventType() == wxEVT_SET_FOCUS || event.GetEventType() == wxEVT_ACTIVATE)) + { + wxWindow* target_window = wxDynamicCast(event.GetEventObject(), wxWindow); + + if (target_window && event.GetEventType() == wxEVT_ACTIVATE && !((wxActivateEvent&)event).GetActive()) + target_window = nullptr; + + if (target_window) + { + g_window_info.debugger_focused = false; + wxWindow* window_it = target_window; + while (window_it) + { + if (window_it == g_debugger_window) g_window_info.debugger_focused = true; + window_it = window_it->GetParent(); + } + } + } + else if (!g_debugger_window) + { + g_window_info.debugger_focused = false; + } + return wxApp::FilterEvent(event); } diff --git a/src/gui/wxgui/debugger/DebuggerWindow2.cpp b/src/gui/wxgui/debugger/DebuggerWindow2.cpp index 0c3cdb47..4422b61a 100644 --- a/src/gui/wxgui/debugger/DebuggerWindow2.cpp +++ b/src/gui/wxgui/debugger/DebuggerWindow2.cpp @@ -20,6 +20,8 @@ #include "wxgui/debugger/BreakpointWindow.h" #include "wxgui/debugger/ModuleWindow.h" #include "util/helpers/helpers.h" +#include "Cafe/HW/Espresso/Recompiler/PPCRecompiler.h" +#include "Cemu/Logging/CemuLogging.h" #include "resource/embedded/resources.h" @@ -30,6 +32,8 @@ enum // settings MENU_ID_OPTIONS_PIN_TO_MAINWINDOW, MENU_ID_OPTIONS_BREAK_ON_START, + MENU_ID_OPTIONS_LOG_MEMORY_BREAKPOINTS, + MENU_ID_OPTIONS_SWITCH_CPU_MODE, // window MENU_ID_WINDOW_REGISTERS, MENU_ID_WINDOW_DUMP, @@ -75,12 +79,14 @@ wxBEGIN_EVENT_TABLE(DebuggerWindow2, wxFrame) EVT_MENU_RANGE(MENU_ID_WINDOW_REGISTERS, MENU_ID_WINDOW_MODULE, DebuggerWindow2::OnWindowMenu) wxEND_EVENT_TABLE() + DebuggerWindow2* g_debugger_window; void DebuggerConfig::Load(XMLConfigParser& parser) { pin_to_main = parser.get("PinToMainWindow", true); break_on_start = parser.get("break_on_start", true); + log_memory_breakpoints = parser.get("log_memory_breakpoints", false); auto window_parser = parser.get("Windows"); show_register = window_parser.get("Registers", true); @@ -95,7 +101,8 @@ void DebuggerConfig::Save(XMLConfigParser& parser) { parser.set("PinToMainWindow", pin_to_main); parser.set("break_on_start", break_on_start); - + parser.set("log_memory_breakpoints", log_memory_breakpoints); + auto window_parser = parser.set("Windows"); window_parser.set("Registers", show_register); window_parser.set("MemoryDump", show_dump); @@ -285,6 +292,7 @@ DebuggerWindow2::DebuggerWindow2(wxFrame& parent, const wxRect& display_size) m_config.Load(); debuggerState.breakOnEntry = m_config.data().break_on_start; + debuggerState.logOnlyMemoryBreakpoints = m_config.data().log_memory_breakpoints; m_main_position = parent.GetPosition(); m_main_size = parent.GetSize(); @@ -571,6 +579,26 @@ void DebuggerWindow2::OnOptionsInput(wxCommandEvent& event) debuggerState.breakOnEntry = value; break; } + case MENU_ID_OPTIONS_LOG_MEMORY_BREAKPOINTS: + { + const bool value = !m_config.data().log_memory_breakpoints; + m_config.data().log_memory_breakpoints = value; + debuggerState.logOnlyMemoryBreakpoints = value; + break; + } + case MENU_ID_OPTIONS_SWITCH_CPU_MODE: + { + if (ppcRecompilerEnabled) + { + ppcRecompilerEnabled = false; + cemuLog_log(LogType::Force, "Debugger: Switched CPU mode to interpreter"); + } + else { + ppcRecompilerEnabled = true; + cemuLog_log(LogType::Force, "Debugger: Switched CPU mode to recompiler"); + } + break; + } default: return; } @@ -662,6 +690,8 @@ void DebuggerWindow2::CreateMenuBar() wxMenu* options_menu = new wxMenu; options_menu->Append(MENU_ID_OPTIONS_PIN_TO_MAINWINDOW, _("&Pin to main window"), wxEmptyString, wxITEM_CHECK)->Check(m_config.data().pin_to_main); options_menu->Append(MENU_ID_OPTIONS_BREAK_ON_START, _("Break on &entry point"), wxEmptyString, wxITEM_CHECK)->Check(m_config.data().break_on_start); + options_menu->Append(MENU_ID_OPTIONS_LOG_MEMORY_BREAKPOINTS, _("Log only memory breakpoints"), wxEmptyString, wxITEM_CHECK)->Check(m_config.data().log_memory_breakpoints); + options_menu->Append(MENU_ID_OPTIONS_SWITCH_CPU_MODE, _("Switch to &interpreter CPU mode"), wxEmptyString, wxITEM_CHECK); menu_bar->Append(options_menu, _("&Options")); // window diff --git a/src/gui/wxgui/debugger/DebuggerWindow2.h b/src/gui/wxgui/debugger/DebuggerWindow2.h index b06601ba..b6395e06 100644 --- a/src/gui/wxgui/debugger/DebuggerWindow2.h +++ b/src/gui/wxgui/debugger/DebuggerWindow2.h @@ -26,13 +26,16 @@ wxDECLARE_EVENT(wxEVT_NOTIFY_MODULE_LOADED, wxCommandEvent); wxDECLARE_EVENT(wxEVT_NOTIFY_MODULE_UNLOADED, wxCommandEvent); wxDECLARE_EVENT(wxEVT_NOTIFY_GRAPHIC_PACKS_MODIFIED, wxCommandEvent); +extern class DebuggerWindow2* g_debugger_window; + struct DebuggerConfig { DebuggerConfig() - : pin_to_main(true), break_on_start(true), show_register(true), show_dump(true), show_stack(true), show_breakpoints(true), show_modules(true), show_symbols(true) {} - + : pin_to_main(true), break_on_start(true), log_memory_breakpoints(false), show_register(true), show_dump(true), show_stack(true), show_breakpoints(true), show_modules(true), show_symbols(true) {} + bool pin_to_main; bool break_on_start; + bool log_memory_breakpoints; bool show_register; bool show_dump; @@ -82,6 +85,7 @@ public: bool Show(bool show = true) override; std::wstring GetModuleStoragePath(std::string module_name, uint32_t crc_hash) const; + private: void OnBreakpointHit(wxCommandEvent& event); void OnRunProgram(wxCommandEvent& event); diff --git a/src/input/api/Keyboard/KeyboardController.cpp b/src/input/api/Keyboard/KeyboardController.cpp index fedabb9d..0514cd5d 100644 --- a/src/input/api/Keyboard/KeyboardController.cpp +++ b/src/input/api/Keyboard/KeyboardController.cpp @@ -17,6 +17,10 @@ std::string KeyboardController::get_button_name(uint64 button) const ControllerState KeyboardController::raw_state() { ControllerState result{}; + + if (WindowSystem::GetWindowInfo().debugger_focused) + return result; + boost::container::small_vector pressedKeys; WindowSystem::GetWindowInfo().iter_keystates([&pressedKeys](const std::pair& keyState) { if (keyState.second) pressedKeys.emplace_back(keyState.first); }); result.buttons.SetPressedButtons(pressedKeys);