debugger: Add interpreter mode switch and log-only memory breakpoints

Unfortunately, we still don't really have a good way of doing multi-core interpreter breakpoints nor switching the 3 core recompiler mode into a single core interpreter mode. We can only toggle the interpreter on and off.

But its not a huge issue. Single-core recompiler is still way faster then interpreter to get in-game, and then at the moment of setting a memory breakpoint, you just have to switch on the interpreter mode for this single core.

Also, I added an option to have the memory breakpoints only log their values and their changes, instead of breaking every time. This allows for quicker analysis of how a memory value might change over a game's frame.
This commit is contained in:
Crementif 2026-01-10 02:04:11 +01:00
parent 99c6426a53
commit 2280f7f473
4 changed files with 53 additions and 4 deletions

View File

@ -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);
}
}
}

View File

@ -142,6 +142,7 @@ struct PPCSnapshot
typedef struct
{
bool breakOnEntry;
bool logOnlyMemoryBreakpoints;
// breakpoints
std::vector<DebuggerBreakpoint*> breakpoints;
std::vector<DebuggerPatch*> patches;

View File

@ -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

View File

@ -31,10 +31,11 @@ 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;