From e2a69becf12116115e9f9c1fc023d6998d49a12e Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Fri, 27 Mar 2026 20:55:49 +0100 Subject: [PATCH] debugger: Rework PPC debugger This is a first pass for now, more work is needed mostly for UI polish. Changes in this commit: - Debugger now exposes a thread-safe API. Previously everything would just access internal debugger state directly without regard for race conditions - Reworked stepping logic to be more reliable. For example previously Cemu could get forever stuck in the middle of stepping to the next instruction - Support for debugging while using multi-threaded CPU emulation. Originally the debugger was added when only single core support existed in Cemu. It was possible to debug multi-threaded before but it was very very prone to state corruptions. - Debugger should now remember breakpoints correctly across sessions --- src/Cafe/HW/Espresso/Debugger/Debugger.cpp | 526 ++++++++++-------- src/Cafe/HW/Espresso/Debugger/Debugger.h | 101 ++-- src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp | 5 + src/Cafe/OS/libs/coreinit/coreinit_Thread.h | 1 + src/gui/wxgui/CemuApp.cpp | 6 +- src/gui/wxgui/MainWindow.cpp | 6 + src/gui/wxgui/debugger/BreakpointWindow.cpp | 128 +++-- src/gui/wxgui/debugger/DebuggerWindow2.cpp | 206 ++++--- src/gui/wxgui/debugger/DebuggerWindow2.h | 44 +- src/gui/wxgui/debugger/DisasmCtrl.cpp | 43 +- src/gui/wxgui/debugger/DumpCtrl.cpp | 2 - src/gui/wxgui/debugger/ModuleWindow.cpp | 3 +- src/gui/wxgui/debugger/RegisterWindow.cpp | 74 ++- src/gui/wxgui/debugger/SymbolCtrl.cpp | 3 +- 14 files changed, 659 insertions(+), 489 deletions(-) diff --git a/src/Cafe/HW/Espresso/Debugger/Debugger.cpp b/src/Cafe/HW/Espresso/Debugger/Debugger.cpp index 02f23497..40b8499a 100644 --- a/src/Cafe/HW/Espresso/Debugger/Debugger.cpp +++ b/src/Cafe/HW/Espresso/Debugger/Debugger.cpp @@ -13,13 +13,50 @@ #include #endif +void debugger_updateExecutionBreakpoint(uint32 address, bool forceRestore = false); + DebuggerDispatcher g_debuggerDispatcher; -debuggerState_t debuggerState{ }; +struct DebuggerState +{ + bool breakOnEntry{false}; + bool logOnlyMemoryBreakpoints{false}; + // breakpoints + std::recursive_mutex breakpointsMtx; + std::vector breakpoints; + // patches + std::vector patches; + // active memory breakpoint (only one for now) + DebuggerBreakpoint* activeMemoryBreakpoint{}; + // debugging session + struct + { + std::mutex debugSessionMtx; + std::atomic_bool shouldBreak; // request any thread to break asap + std::atomic_bool isTrapped{false}; + uint32 debuggedThreadMPTR{}; + PPCInterpreter_t* hCPU{}; + // step control + std::atomic stepCommand{DebuggerStepCommand::None}; + }debugSession; +}; + +DebuggerState s_debuggerState{}; + +std::vector& debugger_lockBreakpoints() +{ + s_debuggerState.breakpointsMtx.lock(); + return s_debuggerState.breakpoints; +} + +void debugger_unlockBreakpoints() +{ + s_debuggerState.breakpointsMtx.unlock(); +} DebuggerBreakpoint* debugger_getFirstBP(uint32 address) { - for (auto& it : debuggerState.breakpoints) + for (auto& it : s_debuggerState.breakpoints) { if (it->address == address) return it; @@ -29,7 +66,7 @@ DebuggerBreakpoint* debugger_getFirstBP(uint32 address) DebuggerBreakpoint* debugger_getFirstBP(uint32 address, uint8 bpType) { - for (auto& it : debuggerState.breakpoints) + for (auto& it : s_debuggerState.breakpoints) { if (it->address == address) { @@ -46,6 +83,21 @@ DebuggerBreakpoint* debugger_getFirstBP(uint32 address, uint8 bpType) return nullptr; } +DebuggerBreakpoint* debugger_getBreakpointById(BreakpointId bpId) +{ + for (auto& it : s_debuggerState.breakpoints) + { + DebuggerBreakpoint* chain = it; + while (chain) + { + if (chain->id == bpId) + return chain; + chain = chain->next; + } + } + return nullptr; +} + bool debuggerBPChain_hasType(DebuggerBreakpoint* bp, uint8 bpType) { while (bp) @@ -69,7 +121,7 @@ void debuggerBPChain_add(uint32 address, DebuggerBreakpoint* bp) return; } // no breakpoint chain exists for this address - debuggerState.breakpoints.push_back(bp); + s_debuggerState.breakpoints.push_back(bp); } uint32 debugger_getAddressOriginalOpcode(uint32 address) @@ -148,7 +200,7 @@ void debugger_updateMemoryBreakpoint(DebuggerBreakpoint* bp) std::vector schedulerThreadHandles = coreinit::OSGetSchedulerThreads(); #if BOOST_OS_WINDOWS - debuggerState.activeMemoryBreakpoint = bp; + s_debuggerState.activeMemoryBreakpoint = bp; for (auto& hThreadNH : schedulerThreadHandles) { HANDLE hThread = (HANDLE)hThreadNH; @@ -156,7 +208,7 @@ void debugger_updateMemoryBreakpoint(DebuggerBreakpoint* bp) ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS; SuspendThread(hThread); GetThreadContext(hThread, &ctx); - if (debuggerState.activeMemoryBreakpoint) + if (s_debuggerState.activeMemoryBreakpoint) { ctx.Dr0 = (DWORD64)memory_getPointerFromVirtualOffset(bp->address); ctx.Dr1 = (DWORD64)memory_getPointerFromVirtualOffset(bp->address); @@ -196,24 +248,24 @@ void debugger_handleSingleStepException(uint64 dr6) if (triggeredDR0 && triggeredDR1) { // write (and read) access - if (debuggerState.activeMemoryBreakpoint && debuggerState.activeMemoryBreakpoint->bpType == DEBUGGER_BP_T_MEMORY_WRITE) + if (s_debuggerState.activeMemoryBreakpoint && s_debuggerState.activeMemoryBreakpoint->bpType == DEBUGGER_BP_T_MEMORY_WRITE) catchBP = true; } else { // read access - if (debuggerState.activeMemoryBreakpoint && debuggerState.activeMemoryBreakpoint->bpType == DEBUGGER_BP_T_MEMORY_READ) + if (s_debuggerState.activeMemoryBreakpoint && s_debuggerState.activeMemoryBreakpoint->bpType == DEBUGGER_BP_T_MEMORY_READ) catchBP = true; } if (catchBP) { PPCInterpreter_t* hCPU = PPCInterpreter_getCurrentInstance(); - if (debuggerState.logOnlyMemoryBreakpoints) + if (s_debuggerState.logOnlyMemoryBreakpoints) { - float memValueF = memory_readFloat(debuggerState.activeMemoryBreakpoint->address); - uint32 memValue = memory_readU32(debuggerState.activeMemoryBreakpoint->address); + float memValueF = memory_readFloat(s_debuggerState.activeMemoryBreakpoint->address); + uint32 memValue = memory_readU32(s_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, + s_debuggerState.activeMemoryBreakpoint->address, memValue, memValueF, hCPU->instructionPointer, @@ -243,10 +295,10 @@ void debugger_createMemoryBreakpoint(uint32 address, bool onRead, bool onWrite) DebuggerBreakpoint* bp = new DebuggerBreakpoint(address, 0xFFFFFFFF, bpType, true); debuggerBPChain_add(address, bp); // disable any already existing memory breakpoint - if (debuggerState.activeMemoryBreakpoint) + if (s_debuggerState.activeMemoryBreakpoint) { - debuggerState.activeMemoryBreakpoint->enabled = false; - debuggerState.activeMemoryBreakpoint = nullptr; + s_debuggerState.activeMemoryBreakpoint->enabled = false; + s_debuggerState.activeMemoryBreakpoint = nullptr; } // activate new breakpoint debugger_updateMemoryBreakpoint(bp); @@ -254,42 +306,44 @@ void debugger_createMemoryBreakpoint(uint32 address, bool onRead, bool onWrite) void debugger_handleEntryBreakpoint(uint32 address) { - if (!debuggerState.breakOnEntry) + if (!s_debuggerState.breakOnEntry) return; debugger_createCodeBreakpoint(address, DEBUGGER_BP_T_NORMAL); } -void debugger_deleteBreakpoint(DebuggerBreakpoint* bp) +void debugger_deleteBreakpoint(BreakpointId bpId) { - for (auto& it : debuggerState.breakpoints) + std::unique_lock _l(s_debuggerState.breakpointsMtx); + DebuggerBreakpoint* bp = debugger_getBreakpointById(bpId); + if (!bp) + return; + for (auto& it : s_debuggerState.breakpoints) { if (it->address == bp->address) { // for execution breakpoints make sure the instruction is restored - if (bp->isExecuteBP()) + if (it->isExecuteBP()) { - bp->enabled = false; - debugger_updateExecutionBreakpoint(bp->address); + it->enabled = false; + debugger_updateExecutionBreakpoint(it->address); } // remove if (it == bp) { // remove first in list - debuggerState.breakpoints.erase(std::remove(debuggerState.breakpoints.begin(), debuggerState.breakpoints.end(), bp), debuggerState.breakpoints.end()); + s_debuggerState.breakpoints.erase(std::remove(s_debuggerState.breakpoints.begin(), s_debuggerState.breakpoints.end(), bp), s_debuggerState.breakpoints.end()); DebuggerBreakpoint* nextBP = bp->next; if (nextBP) - debuggerState.breakpoints.push_back(nextBP); + s_debuggerState.breakpoints.push_back(nextBP); } else { // remove from list DebuggerBreakpoint* bpItr = it; while (bpItr->next != bp) - { bpItr = bpItr->next; - } - cemu_assert_debug(bpItr->next != bp); + cemu_assert_debug(bpItr->next == bp); bpItr->next = bp->next; } delete bp; @@ -300,11 +354,12 @@ void debugger_deleteBreakpoint(DebuggerBreakpoint* bp) void debugger_toggleExecuteBreakpoint(uint32 address) { + std::unique_lock _l(s_debuggerState.breakpointsMtx); auto existingBP = debugger_getFirstBP(address, DEBUGGER_BP_T_NORMAL); if (existingBP) - { + { // delete existing breakpoint - debugger_deleteBreakpoint(existingBP); + debugger_deleteBreakpoint(existingBP->id); } else { @@ -315,11 +370,12 @@ void debugger_toggleExecuteBreakpoint(uint32 address) void debugger_toggleLoggingBreakpoint(uint32 address) { + std::unique_lock _l(s_debuggerState.breakpointsMtx); auto existingBP = debugger_getFirstBP(address, DEBUGGER_BP_T_LOGGING); if (existingBP) { // delete existing breakpoint - debugger_deleteBreakpoint(existingBP); + debugger_deleteBreakpoint(existingBP->id); } else { @@ -328,20 +384,55 @@ void debugger_toggleLoggingBreakpoint(uint32 address) } } -void debugger_forceBreak() -{ - debuggerState.debugSession.shouldBreak = true; -} - bool debugger_isTrapped() { - return debuggerState.debugSession.isTrapped; + return s_debuggerState.debugSession.isTrapped; +} + +PPCInterpreter_t* debugger_lockDebugSession() +{ + s_debuggerState.debugSession.debugSessionMtx.lock(); + if (!s_debuggerState.debugSession.isTrapped) + { + s_debuggerState.debugSession.debugSessionMtx.unlock(); + return nullptr; + } + return s_debuggerState.debugSession.hCPU; +} + +void debugger_unlockDebugSession(PPCInterpreter_t* hCPU) +{ + cemu_assert_debug(s_debuggerState.debugSession.isTrapped); + cemu_assert_debug(s_debuggerState.debugSession.hCPU == hCPU); + s_debuggerState.debugSession.debugSessionMtx.unlock(); +} + +PPCSnapshot debugger_getSnapshotFromSession(PPCInterpreter_t* hCPU) +{ + cemu_assert_debug(s_debuggerState.debugSession.isTrapped); + cemu_assert_debug(s_debuggerState.debugSession.hCPU == hCPU); + PPCSnapshot snapshot{}; + memcpy(snapshot.gpr, hCPU->gpr, sizeof(uint32) * 32); + memcpy(snapshot.fpr, hCPU->fpr, sizeof(FPR_t) * 32); + snapshot.spr_lr = hCPU->spr.LR; + for (uint32 i = 0; i < 32; i++) + snapshot.cr[i] = hCPU->cr[i]; + return snapshot; } void debugger_resume() { - // if there is a breakpoint on the current instruction then do a single 'step into' to skip it - debuggerState.debugSession.run = true; + s_debuggerState.debugSession.stepCommand = DebuggerStepCommand::Run; +} + +void debugger_stepCommand(DebuggerStepCommand stepCommand) +{ + s_debuggerState.debugSession.stepCommand = stepCommand; +} + +void debugger_requestBreak() +{ + s_debuggerState.debugSession.shouldBreak = true; } void debugger_toggleBreakpoint(uint32 address, bool state, DebuggerBreakpoint* bp) @@ -351,6 +442,8 @@ void debugger_toggleBreakpoint(uint32 address, bool state, DebuggerBreakpoint* b { if (bpItr == bp) { + if (bp->enabled == state) + return; if (bpItr->bpType == DEBUGGER_BP_T_NORMAL || bpItr->bpType == DEBUGGER_BP_T_LOGGING) { bp->enabled = state; @@ -360,13 +453,13 @@ void debugger_toggleBreakpoint(uint32 address, bool state, DebuggerBreakpoint* b else if (bpItr->isMemBP()) { // disable other memory breakpoints - for (auto& it : debuggerState.breakpoints) + for (auto& it : s_debuggerState.breakpoints) { DebuggerBreakpoint* bpItr2 = it; while (bpItr2) { if (bpItr2->isMemBP() && bpItr2 != bp) - { + { bpItr2->enabled = false; } bpItr2 = bpItr2->next; @@ -410,9 +503,9 @@ void debugger_createPatch(uint32 address, std::span patchData) } } // merge with existing patches if the ranges touch - for(sint32 i=0; i= patchItr->address && address <= patchItr->address + patchItr->length) { uint32 newAddress = std::min(patch->address, patchItr->address); @@ -434,11 +527,11 @@ void debugger_createPatch(uint32 address, std::span patchData) patch = newPatch; delete patchItr; // remove currently iterated patch - debuggerState.patches.erase(debuggerState.patches.begin()+i); + s_debuggerState.patches.erase(s_debuggerState.patches.begin()+i); i--; } } - debuggerState.patches.push_back(patch); + s_debuggerState.patches.push_back(patch); // apply patch (if breakpoints exist then update those instead of actual data) if ((address & 3) != 0) cemu_assert_debug(false); @@ -468,7 +561,7 @@ void debugger_createPatch(uint32 address, std::span patchData) bool debugger_hasPatch(uint32 address) { - for (auto& patch : debuggerState.patches) + for (auto& patch : s_debuggerState.patches) { if (address + 4 > patch->address && address < patch->address + patch->length) return true; @@ -478,15 +571,15 @@ bool debugger_hasPatch(uint32 address) void debugger_removePatch(uint32 address) { - for (sint32 i = 0; i < debuggerState.patches.size(); i++) + for (sint32 i = 0; i < s_debuggerState.patches.size(); i++) { - auto& patch = debuggerState.patches[i]; + auto& patch = s_debuggerState.patches[i]; if (address < patch->address || address >= (patch->address + patch->length)) continue; MPTR startAddress = patch->address; MPTR endAddress = patch->address + patch->length; // remove any breakpoints overlapping with the patch - for (auto& bp : debuggerState.breakpoints) + for (auto& bp : s_debuggerState.breakpoints) { if (bp->address + 4 > startAddress && bp->address < endAddress) { @@ -499,228 +592,205 @@ void debugger_removePatch(uint32 address) PPCRecompiler_invalidateRange(startAddress, endAddress); // remove patch delete patch; - debuggerState.patches.erase(debuggerState.patches.begin() + i); + s_debuggerState.patches.erase(s_debuggerState.patches.begin() + i); return; } } -void debugger_stepInto(PPCInterpreter_t* hCPU, bool updateDebuggerWindow = true) +bool debugger_CanStepOverInstruction(MPTR address) { - bool isRecEnabled = ppcRecompilerEnabled; - ppcRecompilerEnabled = false; - uint32 initialIP = debuggerState.debugSession.instructionPointer; - debugger_updateExecutionBreakpoint(initialIP, true); - PPCInterpreterSlim_executeInstruction(hCPU); - debugger_updateExecutionBreakpoint(initialIP); - debuggerState.debugSession.instructionPointer = hCPU->instructionPointer; - if(updateDebuggerWindow) - g_debuggerDispatcher.MoveIP(); - ppcRecompilerEnabled = isRecEnabled; -} - -bool debugger_stepOver(PPCInterpreter_t* hCPU) -{ - bool isRecEnabled = ppcRecompilerEnabled; - ppcRecompilerEnabled = false; - // disassemble current instruction PPCDisassembledInstruction disasmInstr = { 0 }; - uint32 initialIP = debuggerState.debugSession.instructionPointer; - debugger_updateExecutionBreakpoint(initialIP, true); - ppcAssembler_disassemble(initialIP, memory_readU32(initialIP), &disasmInstr); - if (disasmInstr.ppcAsmCode != PPCASM_OP_BL && - disasmInstr.ppcAsmCode != PPCASM_OP_BCTRL) + ppcAssembler_disassemble(address, debugger_getAddressOriginalOpcode(address), &disasmInstr); + return disasmInstr.ppcAsmCode == PPCASM_OP_BL || disasmInstr.ppcAsmCode == PPCASM_OP_BLA || disasmInstr.ppcAsmCode == PPCASM_OP_BCTRL; +} + +void debugger_handleLoggingBreakpoint(PPCInterpreter_t* hCPU, DebuggerBreakpoint* bp) +{ + std::string comment = !bp->comment.empty() ? boost::nowide::narrow(bp->comment) : fmt::format("Breakpoint at 0x{:08X} (no comment)", bp->address); + + auto replacePlaceholders = [&](const std::string& prefix, const auto& formatFunc) { - // nothing to skip, use step-into - debugger_stepInto(hCPU); - debugger_updateExecutionBreakpoint(initialIP); - g_debuggerDispatcher.MoveIP(); - ppcRecompilerEnabled = isRecEnabled; - return false; - } - // create one-shot breakpoint at next instruction - debugger_createCodeBreakpoint(initialIP + 4, DEBUGGER_BP_T_ONE_SHOT); - // step over current instruction (to avoid breakpoint) - debugger_stepInto(hCPU); - g_debuggerDispatcher.MoveIP(); - // restore breakpoints - debugger_updateExecutionBreakpoint(initialIP); - // run + size_t pos = 0; + while ((pos = comment.find(prefix, pos)) != std::string::npos) + { + size_t endPos = comment.find('}', pos); + if (endPos == std::string::npos) + break; + + try + { + if (int regNum = ConvertString(comment.substr(pos + prefix.length(), endPos - pos - prefix.length())); regNum >= 0 && regNum < 32) + { + std::string replacement = formatFunc(regNum); + comment.replace(pos, endPos - pos + 1, replacement); + pos += replacement.length(); + } + else + { + pos = endPos + 1; + } + } + catch (...) + { + pos = endPos + 1; + } + } + }; + + // Replace integer register placeholders {rX} + replacePlaceholders("{r", [&](int regNum) { + return fmt::format("0x{:08X}", hCPU->gpr[regNum]); + }); + + // Replace floating point register placeholders {fX} + replacePlaceholders("{f", [&](int regNum) { + return fmt::format("{}", hCPU->fpr[regNum].fpr); + }); + + std::string logName = "Breakpoint '" + comment + "'"; + std::string logContext = fmt::format("Thread: {:08x} LR: 0x{:08x}", MEMPTR(coreinit::OSGetCurrentThread()).GetMPTR(), hCPU->spr.LR, cemuLog_advancedPPCLoggingEnabled() ? " Stack Trace:" : ""); + cemuLog_log(LogType::Force, "[Debugger] {} was executed! {}", logName, logContext); + if (cemuLog_advancedPPCLoggingEnabled()) + DebugLogStackTrace(coreinit::OSGetCurrentThread(), hCPU->gpr[1]); +} + +// used to escape the breakpoint on the current instruction +void debugger_stepOverCurrentBreakpoint(PPCInterpreter_t* hCPU) +{ + std::unique_lock _l(s_debuggerState.breakpointsMtx); + bool isRecEnabled = ppcRecompilerEnabled; + ppcRecompilerEnabled = false; + MPTR bpAddress = hCPU->instructionPointer; + debugger_updateExecutionBreakpoint(bpAddress, true); + PPCInterpreterSlim_executeInstruction(hCPU); + debugger_updateExecutionBreakpoint(bpAddress); ppcRecompilerEnabled = isRecEnabled; - return true; } -void debugger_createPPCStateSnapshot(PPCInterpreter_t* hCPU) +void debugger_enterTW(PPCInterpreter_t* hCPU, bool isSingleStep) { - memcpy(debuggerState.debugSession.ppcSnapshot.gpr, hCPU->gpr, sizeof(uint32) * 32); - memcpy(debuggerState.debugSession.ppcSnapshot.fpr, hCPU->fpr, sizeof(FPR_t) * 32); - debuggerState.debugSession.ppcSnapshot.spr_lr = hCPU->spr.LR; - for (uint32 i = 0; i < 32; i++) - debuggerState.debugSession.ppcSnapshot.cr[i] = hCPU->cr[i]; -} - -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) + s_debuggerState.debugSession.debugSessionMtx.lock(); + while ( true ) { + bool trappedExpectedVal = false; + if ( s_debuggerState.debugSession.isTrapped.compare_exchange_strong(trappedExpectedVal, true) ) + break; + s_debuggerState.debugSession.debugSessionMtx.unlock(); std::this_thread::sleep_for(std::chrono::milliseconds(1)); + s_debuggerState.debugSession.debugSessionMtx.lock(); } - + // init debug state + s_debuggerState.debugSession.debuggedThreadMPTR = MEMPTR(coreinit::OSGetCurrentThread()).GetMPTR(); + s_debuggerState.debugSession.hCPU = hCPU; + s_debuggerState.debugSession.stepCommand = DebuggerStepCommand::None; + s_debuggerState.debugSession.debugSessionMtx.unlock(); // 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); while (bp) { if (bp->bpType == DEBUGGER_BP_T_LOGGING && bp->enabled) - { - std::string comment = !bp->comment.empty() ? boost::nowide::narrow(bp->comment) : fmt::format("Breakpoint at 0x{:08X} (no comment)", bp->address); - - auto replacePlaceholders = [&](const std::string& prefix, const auto& formatFunc) - { - size_t pos = 0; - while ((pos = comment.find(prefix, pos)) != std::string::npos) - { - size_t endPos = comment.find('}', pos); - if (endPos == std::string::npos) - break; - - try - { - if (int regNum = ConvertString(comment.substr(pos + prefix.length(), endPos - pos - prefix.length())); regNum >= 0 && regNum < 32) - { - std::string replacement = formatFunc(regNum); - comment.replace(pos, endPos - pos + 1, replacement); - pos += replacement.length(); - } - else - { - pos = endPos + 1; - } - } - catch (...) - { - pos = endPos + 1; - } - } - }; - - // Replace integer register placeholders {rX} - replacePlaceholders("{r", [&](int regNum) { - return fmt::format("0x{:08X}", hCPU->gpr[regNum]); - }); - - // Replace floating point register placeholders {fX} - replacePlaceholders("{f", [&](int regNum) { - return fmt::format("{}", hCPU->fpr[regNum].fpr); - }); - - std::string logName = "Breakpoint '" + comment + "'"; - std::string logContext = fmt::format("Thread: {:08x} LR: 0x{:08x}", MEMPTR(coreinit::OSGetCurrentThread()).GetMPTR(), hCPU->spr.LR, cemuLog_advancedPPCLoggingEnabled() ? " Stack Trace:" : ""); - cemuLog_log(LogType::Force, "[Debugger] {} was executed! {}", logName, logContext); - if (cemuLog_advancedPPCLoggingEnabled()) - DebugLogStackTrace(coreinit::OSGetCurrentThread(), hCPU->gpr[1]); - break; - } + debugger_handleLoggingBreakpoint(hCPU, bp); bp = bp->next; } - - // return early if it's only a non-pausing logging breakpoint to prevent a modified debugger state and GUI updates - if (!shouldBreak) + // for logging breakpoints skip the waiting and notifying of the UI + DebuggerStepCommand stepCommand = DebuggerStepCommand::Run; + if (shouldBreak || isSingleStep) { - uint32 backupIP = debuggerState.debugSession.instructionPointer; - debuggerState.debugSession.instructionPointer = hCPU->instructionPointer; - debugger_stepInto(hCPU, false); - PPCInterpreterSlim_executeInstruction(hCPU); - debuggerState.debugSession.instructionPointer = backupIP; - return; - } - - // handle breakpoints - debuggerState.debugSession.isTrapped = true; - debuggerState.debugSession.debuggedThreadMPTR = MEMPTR(coreinit::OSGetCurrentThread()).GetMPTR(); - debuggerState.debugSession.instructionPointer = hCPU->instructionPointer; - debuggerState.debugSession.hCPU = hCPU; - debugger_createPPCStateSnapshot(hCPU); - // remove one-shot breakpoint if it exists - DebuggerBreakpoint* singleshotBP = debugger_getFirstBP(debuggerState.debugSession.instructionPointer, DEBUGGER_BP_T_ONE_SHOT); - if (singleshotBP) - debugger_deleteBreakpoint(singleshotBP); - g_debuggerDispatcher.NotifyDebugBreakpointHit(); - g_debuggerDispatcher.UpdateViewThreadsafe(); - // reset step control - debuggerState.debugSession.stepInto = false; - debuggerState.debugSession.stepOver = false; - debuggerState.debugSession.run = false; - while (debuggerState.debugSession.isTrapped) - { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - // check for step commands - if (debuggerState.debugSession.stepOver) + // remove one-shot breakpoint if it exists + DebuggerBreakpoint* singleshotBP = debugger_getFirstBP(hCPU->instructionPointer, DEBUGGER_BP_T_ONE_SHOT); + if (singleshotBP) + debugger_deleteBreakpoint(singleshotBP->id); + g_debuggerDispatcher.NotifyDebugBreakpointHit(); + g_debuggerDispatcher.UpdateViewThreadsafe(); + while (s_debuggerState.debugSession.isTrapped) { - if (debugger_stepOver(hCPU)) + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + // check for step commands from UI + stepCommand = s_debuggerState.debugSession.stepCommand.exchange(DebuggerStepCommand::None); + if (stepCommand != DebuggerStepCommand::None) + break; + if (!coreinit::OSIsSchedulerActive()) { - debugger_createPPCStateSnapshot(hCPU); - break; // if true is returned, continue with execution + // quit if scheduler is being shutdown + stepCommand = DebuggerStepCommand::Run; + break; } - debugger_createPPCStateSnapshot(hCPU); - g_debuggerDispatcher.UpdateViewThreadsafe(); - debuggerState.debugSession.stepOver = false; } - if (debuggerState.debugSession.stepInto) + if (stepCommand == DebuggerStepCommand::StepOver && !debugger_CanStepOverInstruction(hCPU->instructionPointer)) { - debugger_stepInto(hCPU); - debugger_createPPCStateSnapshot(hCPU); - g_debuggerDispatcher.UpdateViewThreadsafe(); - debuggerState.debugSession.stepInto = false; - continue; - } - if (debuggerState.debugSession.run) - { - debugger_createPPCStateSnapshot(hCPU); - debugger_stepInto(hCPU, false); - PPCInterpreterSlim_executeInstruction(hCPU); - debuggerState.debugSession.instructionPointer = hCPU->instructionPointer; - debuggerState.debugSession.run = false; - break; + stepCommand = DebuggerStepCommand::StepInto; } + g_debuggerDispatcher.UpdateViewThreadsafe(); + g_debuggerDispatcher.NotifyRun(); } + s_debuggerState.debugSession.debugSessionMtx.lock(); + s_debuggerState.debugSession.hCPU = nullptr; + s_debuggerState.debugSession.isTrapped = false; + s_debuggerState.debugSession.debugSessionMtx.unlock(); + // leave the breakpointed instruction + MPTR initialIP = hCPU->instructionPointer; + debugger_stepOverCurrentBreakpoint(hCPU); - debuggerState.debugSession.isTrapped = false; - debuggerState.debugSession.hCPU = nullptr; - g_debuggerDispatcher.UpdateViewThreadsafe(); - g_debuggerDispatcher.NotifyRun(); -} + // todo - if the current instruction is a HLE gateway, then we need to handle it with special logic (instead of generic debugger_stepOverCurrentBreakpoint): + // First grab the HLE index from the original opcode and look up the function pointer + // And then call the HLE func (must happen last, since it may not return immediately or ever) -void debugger_shouldBreak(PPCInterpreter_t* hCPU) -{ - if(debuggerState.debugSession.shouldBreak - // exclude emulator trampoline area - && (hCPU->instructionPointer < MEMORY_CODE_TRAMPOLINE_AREA_ADDR || hCPU->instructionPointer > MEMORY_CODE_TRAMPOLINE_AREA_ADDR + MEMORY_CODE_TRAMPOLINE_AREA_SIZE)) + if (stepCommand == DebuggerStepCommand::StepInto) { - debuggerState.debugSession.shouldBreak = false; - - const uint32 address = (uint32)hCPU->instructionPointer; - assert_dbg(); - //debugger_createBreakpoint(address, DEBUGGER_BP_TYPE_ONE_SHOT); + debugger_enterTW(hCPU, true); + } + else if (stepCommand == DebuggerStepCommand::StepOver) + { + debugger_createCodeBreakpoint(initialIP + 4, DEBUGGER_BP_T_ONE_SHOT); } } void debugger_addParserSymbols(class ExpressionParser& ep) { - const auto module_count = RPLLoader_GetModuleCount(); - const auto module_list = RPLLoader_GetModuleList(); - - std::vector module_tmp(module_count); - for (int i = 0; i < module_count; i++) + const auto moduleCount = RPLLoader_GetModuleCount(); + const auto moduleList = RPLLoader_GetModuleList(); + std::vector moduleTmp(moduleCount); + for (sint32 i = 0; i < moduleCount; i++) { - const auto module = module_list[i]; + const auto module = moduleList[i]; if (module) { - module_tmp[i] = (double)module->regionMappingBase_text.GetMPTR(); - ep.AddConstant(module->moduleName2, module_tmp[i]); + moduleTmp[i] = (double)module->regionMappingBase_text.GetMPTR(); + ep.AddConstant(module->moduleName2, moduleTmp[i]); } } + PPCInterpreter_t* hCPU = debugger_lockDebugSession(); + if (hCPU) + { + PPCSnapshot snapshot = debugger_getSnapshotFromSession(hCPU); + for (sint32 i = 0; i < 32; i++) + ep.AddConstant(fmt::format("r{}", i), snapshot.gpr[i]); + debugger_unlockDebugSession(hCPU); + } +} - for (sint32 i = 0; i < 32; i++) - ep.AddConstant(fmt::format("r{}", i), debuggerState.debugSession.ppcSnapshot.gpr[i]); -} \ No newline at end of file +void debugger_jumpToAddressInDisasm(MPTR address) +{ + g_debuggerDispatcher.MoveToAddressInDisassembly(address); +} + +void debugger_setOptionBreakOnEntry(bool isEnabled) +{ + s_debuggerState.breakOnEntry = isEnabled; +} + +void debugger_setOptionLogOnlyMemoryBreakpoints(bool isEnabled) +{ + s_debuggerState.logOnlyMemoryBreakpoints = isEnabled; +} + +bool debugger_getOptionBreakOnEntry() +{ + return s_debuggerState.breakOnEntry; +} + +bool debugger_getOptionLogOnlyMemoryBreakpoints() +{ + return s_debuggerState.logOnlyMemoryBreakpoints; +} diff --git a/src/Cafe/HW/Espresso/Debugger/Debugger.h b/src/Cafe/HW/Espresso/Debugger/Debugger.h index 03602ff0..2f95f67d 100644 --- a/src/Cafe/HW/Espresso/Debugger/Debugger.h +++ b/src/Cafe/HW/Espresso/Debugger/Debugger.h @@ -21,9 +21,9 @@ class DebuggerCallbacks virtual void UpdateViewThreadsafe() {} virtual void NotifyDebugBreakpointHit() {} virtual void NotifyRun() {} - virtual void MoveIP() {} - virtual void NotifyModuleLoaded(void* module) {} - virtual void NotifyModuleUnloaded(void* module) {} + virtual void MoveToAddressInDisassembly(MPTR address) {} + virtual void NotifyModuleLoaded(struct RPLModule* module) {} + virtual void NotifyModuleUnloaded(struct RPLModule* module) {} virtual void NotifyGraphicPacksModified() {} virtual ~DebuggerCallbacks() = default; }; @@ -64,17 +64,17 @@ class DebuggerDispatcher m_callbacks->NotifyRun(); } - void MoveIP() + void MoveToAddressInDisassembly(MPTR address) { - m_callbacks->MoveIP(); + m_callbacks->MoveToAddressInDisassembly(address); } - void NotifyModuleLoaded(void* module) + void NotifyModuleLoaded(struct RPLModule* module) { m_callbacks->NotifyModuleLoaded(module); } - void NotifyModuleUnloaded(void* module) + void NotifyModuleUnloaded(struct RPLModule* module) { m_callbacks->NotifyModuleUnloaded(module); } @@ -85,8 +85,11 @@ class DebuggerDispatcher } } extern g_debuggerDispatcher; +using BreakpointId = uint64; + struct DebuggerBreakpoint { + BreakpointId id; uint32 address; uint32 originalOpcodeValue; mutable uint8 bpType; @@ -97,10 +100,11 @@ struct DebuggerBreakpoint DebuggerBreakpoint(uint32 address, uint32 originalOpcode, uint8 bpType = 0, bool enabled = true, std::wstring comment = std::wstring()) :address(address), originalOpcodeValue(originalOpcode), bpType(bpType), enabled(enabled), comment(std::move(comment)) { + static std::atomic bpIdGen{1}; + id = bpIdGen.fetch_add(1); next = nullptr; } - bool operator<(const DebuggerBreakpoint& rhs) const { return address < rhs.address; @@ -133,64 +137,53 @@ struct DebuggerPatch struct PPCSnapshot { - uint32 gpr[32]; - FPR_t fpr[32]; - uint8 cr[32]; - uint32 spr_lr; + uint32 gpr[32]{}; + FPR_t fpr[32]{}; + uint8 cr[32]{}; + uint32 spr_lr{}; }; -typedef struct +enum class DebuggerStepCommand : uint8 { - bool breakOnEntry; - bool logOnlyMemoryBreakpoints; - // breakpoints - std::vector breakpoints; - std::vector patches; - DebuggerBreakpoint* activeMemoryBreakpoint; - // debugging state - struct - { - volatile bool shouldBreak; // debugger window requests a break asap - volatile bool isTrapped; // if set, breakpoint is active and stepping through the code is possible - uint32 debuggedThreadMPTR; - volatile uint32 instructionPointer; - PPCInterpreter_t* hCPU; - // step control - volatile bool stepOver; - volatile bool stepInto; - volatile bool run; - // snapshot of PPC state - PPCSnapshot ppcSnapshot; - }debugSession; + None, + StepInto, + StepOver, + Run +}; -}debuggerState_t; - -extern debuggerState_t debuggerState; - -// new API -DebuggerBreakpoint* debugger_getFirstBP(uint32 address); +// breakpoint API void debugger_createCodeBreakpoint(uint32 address, uint8 bpType); +void debugger_createMemoryBreakpoint(uint32 address, bool onRead, bool onWrite); void debugger_toggleExecuteBreakpoint(uint32 address); // create/remove execute breakpoint void debugger_toggleLoggingBreakpoint(uint32 address); // create/remove logging breakpoint void debugger_toggleBreakpoint(uint32 address, bool state, DebuggerBreakpoint* bp); +void debugger_deleteBreakpoint(BreakpointId bpId); +std::vector& debugger_lockBreakpoints(); +DebuggerBreakpoint* debugger_getFirstBP(uint32 address); +DebuggerBreakpoint* debugger_getBreakpointById(BreakpointId bpId); +void debugger_unlockBreakpoints(); -void debugger_createMemoryBreakpoint(uint32 address, bool onRead, bool onWrite); - -void debugger_handleEntryBreakpoint(uint32 address); - -void debugger_deleteBreakpoint(DebuggerBreakpoint* bp); - -void debugger_updateExecutionBreakpoint(uint32 address, bool forceRestore = false); - +// patch API void debugger_createPatch(uint32 address, std::span patchData); bool debugger_hasPatch(uint32 address); void debugger_removePatch(uint32 address); -void debugger_forceBreak(); // force breakpoint at the next possible instruction +// debug session +PPCInterpreter_t* debugger_lockDebugSession(); +void debugger_unlockDebugSession(PPCInterpreter_t* hCPU); +PPCSnapshot debugger_getSnapshotFromSession(PPCInterpreter_t* hCPU); +void debugger_stepCommand(DebuggerStepCommand stepCommand); + +// misc +void debugger_requestBreak(); // break at the next possible instruction (any thread) bool debugger_isTrapped(); -void debugger_resume(); +void debugger_handleEntryBreakpoint(uint32 address); +void debugger_enterTW(PPCInterpreter_t* hCPU, bool isSingleStep = false); +void debugger_jumpToAddressInDisasm(MPTR address); +void debugger_addParserSymbols(class ExpressionParser& ep); -void debugger_enterTW(PPCInterpreter_t* hCPU); -void debugger_shouldBreak(PPCInterpreter_t* hCPU); - -void debugger_addParserSymbols(class ExpressionParser& ep); \ No newline at end of file +// options +void debugger_setOptionBreakOnEntry(bool isEnabled); +bool debugger_getOptionBreakOnEntry(); +void debugger_setOptionLogOnlyMemoryBreakpoints(bool isEnabled); +bool debugger_getOptionLogOnlyMemoryBreakpoints(); diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp b/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp index 8f3b9e0a..52dc0318 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_Thread.cpp @@ -1483,6 +1483,11 @@ namespace coreinit s_threadToFiber.clear(); } + bool OSIsSchedulerActive() + { + return sSchedulerActive; + } + SysAllocator s_defaultThreads; SysAllocator s_stack; diff --git a/src/Cafe/OS/libs/coreinit/coreinit_Thread.h b/src/Cafe/OS/libs/coreinit/coreinit_Thread.h index a0517d9a..c199e59f 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_Thread.h +++ b/src/Cafe/OS/libs/coreinit/coreinit_Thread.h @@ -610,6 +610,7 @@ namespace coreinit // scheduler void OSSchedulerBegin(sint32 numCPUEmulationThreads); void OSSchedulerEnd(); + bool OSIsSchedulerActive(); // internal void __OSAddReadyThreadToRunQueue(OSThread_t* thread); diff --git a/src/gui/wxgui/CemuApp.cpp b/src/gui/wxgui/CemuApp.cpp index a900f10b..7b381bc0 100644 --- a/src/gui/wxgui/CemuApp.cpp +++ b/src/gui/wxgui/CemuApp.cpp @@ -436,7 +436,7 @@ int CemuApp::FilterEvent(wxEvent& event) } // track if debugger window or its child windows are focused - if (g_debugger_window && (event.GetEventType() == wxEVT_SET_FOCUS || event.GetEventType() == wxEVT_ACTIVATE)) + if (s_debuggerWindow && (event.GetEventType() == wxEVT_SET_FOCUS || event.GetEventType() == wxEVT_ACTIVATE)) { wxWindow* target_window = wxDynamicCast(event.GetEventObject(), wxWindow); @@ -449,12 +449,12 @@ int CemuApp::FilterEvent(wxEvent& event) wxWindow* window_it = target_window; while (window_it) { - if (window_it == g_debugger_window) g_window_info.debugger_focused = true; + if (window_it == s_debuggerWindow) g_window_info.debugger_focused = true; window_it = window_it->GetParent(); } } } - else if (!g_debugger_window) + else if (!s_debuggerWindow) { g_window_info.debugger_focused = false; } diff --git a/src/gui/wxgui/MainWindow.cpp b/src/gui/wxgui/MainWindow.cpp index 4ef4fe08..d2deef7b 100644 --- a/src/gui/wxgui/MainWindow.cpp +++ b/src/gui/wxgui/MainWindow.cpp @@ -443,6 +443,12 @@ wxString MainWindow::GetInitialWindowTitle() void MainWindow::OnClose(wxCloseEvent& event) { + if (m_debugger_window) + { + m_debugger_window->CleanupForDestroy(); + m_debugger_window->Destroy(); + m_debugger_window = nullptr; + } if(m_game_list) m_game_list->OnClose(event); diff --git a/src/gui/wxgui/debugger/BreakpointWindow.cpp b/src/gui/wxgui/debugger/BreakpointWindow.cpp index 9e569a4e..8dc449d9 100644 --- a/src/gui/wxgui/debugger/BreakpointWindow.cpp +++ b/src/gui/wxgui/debugger/BreakpointWindow.cpp @@ -101,42 +101,40 @@ void BreakpointWindow::OnUpdateView() Freeze(); m_breakpoints->DeleteAllItems(); + std::vector& breakpoints = debugger_lockBreakpoints(); - if (!debuggerState.breakpoints.empty()) + uint32 i = 0; + for (const auto bpBase : breakpoints) { - uint32_t i = 0; - for (const auto bpBase : debuggerState.breakpoints) + DebuggerBreakpoint* bp = bpBase; + while (bp) { - DebuggerBreakpoint* bp = bpBase; - while (bp) - { - wxListItem item = {}; - item.SetId(i++); + wxListItem item = {}; + item.SetId(i++); - const auto index = m_breakpoints->InsertItem(item); - m_breakpoints->SetItem(index, ColumnAddress, wxString::Format("%08x", bp->address)); - const char* typeName = "UKN"; - if (bp->bpType == DEBUGGER_BP_T_NORMAL) - typeName = "X"; - else if (bp->bpType == DEBUGGER_BP_T_LOGGING) - typeName = "LOG"; - else if (bp->bpType == DEBUGGER_BP_T_ONE_SHOT) - typeName = "XS"; - else if (bp->bpType == DEBUGGER_BP_T_MEMORY_READ) - typeName = "R"; - else if (bp->bpType == DEBUGGER_BP_T_MEMORY_WRITE) - typeName = "W"; + const auto index = m_breakpoints->InsertItem(item); + m_breakpoints->SetItemPtrData(index, (uintptr_t)bp->id); + m_breakpoints->SetItem(index, ColumnAddress, wxString::Format("%08x", bp->address)); + const char* typeName = "UKN"; + if (bp->bpType == DEBUGGER_BP_T_NORMAL) + typeName = "X"; + else if (bp->bpType == DEBUGGER_BP_T_LOGGING) + typeName = "LOG"; + else if (bp->bpType == DEBUGGER_BP_T_ONE_SHOT) + typeName = "XS"; + else if (bp->bpType == DEBUGGER_BP_T_MEMORY_READ) + typeName = "R"; + else if (bp->bpType == DEBUGGER_BP_T_MEMORY_WRITE) + typeName = "W"; - m_breakpoints->SetItem(index, ColumnType, typeName); - m_breakpoints->SetItem(index, ColumnComment, bp->comment); - m_breakpoints->CheckItem(index, bp->enabled); - m_breakpoints->SetItemPtrData(index, (wxUIntPtr)bp); + m_breakpoints->SetItem(index, ColumnType, typeName); + m_breakpoints->SetItem(index, ColumnComment, bp->comment); + m_breakpoints->CheckItem(index, bp->enabled); - bp = bp->next; - } + bp = bp->next; } } - + debugger_unlockBreakpoints(); Thaw(); } @@ -147,15 +145,16 @@ void BreakpointWindow::OnGameLoaded() void BreakpointWindow::OnBreakpointToggled(wxListEvent& event) { - const int32_t index = event.GetIndex(); - if (0 <= index && index < m_breakpoints->GetItemCount()) + const sint32 index = event.GetIndex(); + uint64 bpId = (uint64)event.GetData(); + debugger_lockBreakpoints(); + DebuggerBreakpoint* bp = debugger_getBreakpointById(bpId); + if (bp) { const bool state = m_breakpoints->IsItemChecked(index); - wxString line = m_breakpoints->GetItemText(index, ColumnAddress); - DebuggerBreakpoint* bp = (DebuggerBreakpoint*)m_breakpoints->GetItemData(index); - const uint32 address = std::stoul(line.ToStdString(), nullptr, 16); - debugger_toggleBreakpoint(address, state, bp); + debugger_toggleBreakpoint(bp->address, state, bp); } + debugger_unlockBreakpoints(); } void BreakpointWindow::OnLeftDClick(wxMouseEvent& event) @@ -178,8 +177,7 @@ void BreakpointWindow::OnLeftDClick(wxMouseEvent& event) { const auto item = m_breakpoints->GetItemText(index, ColumnAddress); const auto address = std::stoul(item.ToStdString(), nullptr, 16); - debuggerState.debugSession.instructionPointer = address; - g_debuggerDispatcher.MoveIP(); + g_debuggerDispatcher.MoveToAddressInDisassembly(address); return; } @@ -192,22 +190,37 @@ void BreakpointWindow::OnLeftDClick(wxMouseEvent& event) const auto comment_width = m_breakpoints->GetColumnWidth(ColumnComment); if (x <= comment_width) { - if (index >= debuggerState.breakpoints.size()) + std::vector& breakpoints = debugger_lockBreakpoints(); + if (index >= breakpoints.size()) + { + debugger_unlockBreakpoints(); return; + } + DebuggerBreakpoint* bp = breakpoints[index]; + auto bpId = bp->id; const auto item = m_breakpoints->GetItemText(index, ColumnAddress); const auto address = std::stoul(item.ToStdString(), nullptr, 16); - auto it = debuggerState.breakpoints.begin(); - std::advance(it, index); - - const wxString dialogTitle = (*it)->bpType == DEBUGGER_BP_T_LOGGING ? _("Enter a new logging message") : _("Enter a new comment"); - const wxString dialogMessage = (*it)->bpType == DEBUGGER_BP_T_LOGGING ? _("Set logging message when code at address %08x is ran.\nUse placeholders like {r3} or {f3} to log register values") : _("Set comment for breakpoint at address %08x"); - wxTextEntryDialog set_comment_dialog(this, dialogMessage, dialogTitle, (*it)->comment); + bool isLoggingBP = bp->bpType == DEBUGGER_BP_T_LOGGING; + const wxString dialogTitle = isLoggingBP ? _("Enter a new logging message") : _("Enter a new comment"); + const wxString dialogMessage = isLoggingBP ? _("Set logging message when code at address %08x is ran.\nUse placeholders like {r3} or {f3} to log register values") : _("Set comment for breakpoint at address %08x"); + const wxString existingComment = bp->comment; + debugger_unlockBreakpoints(); + wxTextEntryDialog set_comment_dialog(this, dialogMessage, dialogTitle, existingComment); if (set_comment_dialog.ShowModal() == wxID_OK) { - (*it)->comment = set_comment_dialog.GetValue().ToStdWstring(); - m_breakpoints->SetItem(index, ColumnComment, set_comment_dialog.GetValue()); + if (isLoggingBP) + { + debugger_lockBreakpoints(); + DebuggerBreakpoint* bp = debugger_getBreakpointById(bpId); + if (bp) + { + bp->comment = set_comment_dialog.GetValue().ToStdWstring(); + m_breakpoints->SetItem(index, ColumnComment, set_comment_dialog.GetValue()); + } + debugger_unlockBreakpoints(); + } } } } @@ -242,20 +255,27 @@ void BreakpointWindow::OnRightDown(wxMouseEvent& event) void BreakpointWindow::OnContextMenuClickSelected(wxCommandEvent& evt) { + auto& breakpoints = debugger_lockBreakpoints(); + long selectedIndex = m_breakpoints->GetFirstSelected(); + if (selectedIndex == wxNOT_FOUND || selectedIndex < 0 || selectedIndex >= (long)breakpoints.size()) + { + debugger_unlockBreakpoints(); + return; + } + BreakpointId bpId = (uint64)m_breakpoints->GetItemData(selectedIndex); + DebuggerBreakpoint* bp = debugger_getBreakpointById(bpId); + if (!bp) + { + debugger_unlockBreakpoints(); + return; + } if (evt.GetId() == MENU_ID_DELETE_BP) { - long sel = m_breakpoints->GetFirstSelected(); - if (sel == wxNOT_FOUND || sel < 0 || sel >= m_breakpoints->GetItemCount()) - return; - - auto it = debuggerState.breakpoints.begin(); - std::advance(it, sel); - - debugger_deleteBreakpoint(*it); - + debugger_deleteBreakpoint(bpId); wxCommandEvent evt(wxEVT_BREAKPOINT_CHANGE); wxPostEvent(this->m_parent, evt); } + debugger_unlockBreakpoints(); } void BreakpointWindow::OnContextMenuClick(wxCommandEvent& evt) diff --git a/src/gui/wxgui/debugger/DebuggerWindow2.cpp b/src/gui/wxgui/debugger/DebuggerWindow2.cpp index 4422b61a..d2a253a0 100644 --- a/src/gui/wxgui/debugger/DebuggerWindow2.cpp +++ b/src/gui/wxgui/debugger/DebuggerWindow2.cpp @@ -54,7 +54,7 @@ wxDEFINE_EVENT(wxEVT_DEBUGGER_CLOSE, wxCloseEvent); wxDEFINE_EVENT(wxEVT_UPDATE_VIEW, wxCommandEvent); wxDEFINE_EVENT(wxEVT_BREAKPOINT_CHANGE, wxCommandEvent); wxDEFINE_EVENT(wxEVT_BREAKPOINT_HIT, wxCommandEvent); -wxDEFINE_EVENT(wxEVT_MOVE_IP, wxCommandEvent); +wxDEFINE_EVENT(wxEVT_MOVE_TO_DISASM_ADDR, wxCommandEvent); wxDEFINE_EVENT(wxEVT_RUN, wxCommandEvent); wxDEFINE_EVENT(wxEVT_NOTIFY_MODULE_LOADED, wxCommandEvent); wxDEFINE_EVENT(wxEVT_NOTIFY_MODULE_UNLOADED, wxCommandEvent); @@ -65,7 +65,7 @@ wxBEGIN_EVENT_TABLE(DebuggerWindow2, wxFrame) EVT_CLOSE(DebuggerWindow2::OnClose) EVT_COMMAND(wxID_ANY, wxEVT_UPDATE_VIEW, DebuggerWindow2::OnUpdateView) EVT_COMMAND(wxID_ANY, wxEVT_BREAKPOINT_CHANGE, DebuggerWindow2::OnBreakpointChange) - EVT_COMMAND(wxID_ANY, wxEVT_MOVE_IP, DebuggerWindow2::OnMoveIP) + EVT_COMMAND(wxID_ANY, wxEVT_MOVE_TO_DISASM_ADDR, DebuggerWindow2::OnMoveToDisasmAddr) EVT_COMMAND(wxID_ANY, wxEVT_COMMAND_TOOL_CLICKED, DebuggerWindow2::OnToolClicked) EVT_COMMAND(wxID_ANY, wxEVT_BREAKPOINT_HIT, DebuggerWindow2::OnBreakpointHit) EVT_COMMAND(wxID_ANY, wxEVT_RUN, DebuggerWindow2::OnRunProgram) @@ -79,8 +79,25 @@ wxBEGIN_EVENT_TABLE(DebuggerWindow2, wxFrame) EVT_MENU_RANGE(MENU_ID_WINDOW_REGISTERS, MENU_ID_WINDOW_MODULE, DebuggerWindow2::OnWindowMenu) wxEND_EVENT_TABLE() +DebuggerModuleInfo::DebuggerModuleInfo(RPLModule* module) +{ + moduleName = module->moduleName2; + patchCRC = module->patchCRC; + textArea.base = module->regionMappingBase_text.GetMPTR(); + textArea.size = module->regionSize_text; + dataArea.base = module->regionMappingBase_data; + dataArea.size = module->regionSize_data; -DebuggerWindow2* g_debugger_window; +} + +struct DebuggerModuleInfoNotify : public wxClientData +{ + DebuggerModuleInfo moduleInfo; + + DebuggerModuleInfoNotify(RPLModule* module) : moduleInfo(module) {}; +}; + +DebuggerWindow2* s_debuggerWindow; void DebuggerConfig::Load(XMLConfigParser& parser) { @@ -130,7 +147,7 @@ void DebuggerModuleStorage::Load(XMLConfigParser& parser) const auto comment = element.get("Comment", ""); // calculate absolute address - uint32 module_base_address = (type == DEBUGGER_BP_T_NORMAL || type == DEBUGGER_BP_T_LOGGING) ? this->rpl_module->regionMappingBase_text.GetMPTR() : this->rpl_module->regionMappingBase_data; + uint32 module_base_address = (type == DEBUGGER_BP_T_NORMAL || type == DEBUGGER_BP_T_LOGGING) ? this->moduleInfo.textArea.base : this->moduleInfo.dataArea.base; uint32 address = module_base_address + relative_address; // don't change anything if there's already a breakpoint @@ -179,49 +196,52 @@ void DebuggerModuleStorage::Load(XMLConfigParser& parser) void DebuggerModuleStorage::Save(XMLConfigParser& parser) { auto breakpoints_parser = parser.set("Breakpoints"); - for (const auto& bp : debuggerState.breakpoints) + auto& breakpoints = debugger_lockBreakpoints(); + for (const auto& bp : breakpoints) { // check breakpoint type if (bp->dbType != DEBUGGER_BP_T_DEBUGGER) continue; // check whether the breakpoint is part of the current module being saved - RPLModule* address_module; - if (bp->bpType == DEBUGGER_BP_T_NORMAL || bp->bpType == DEBUGGER_BP_T_LOGGING) address_module = RPLLoader_FindModuleByCodeAddr(bp->address); - else if (bp->isMemBP()) address_module = RPLLoader_FindModuleByDataAddr(bp->address); - else continue; - - if (!address_module || !(address_module->moduleName2 == this->module_name && address_module->patchCRC == this->crc_hash)) + uint32 relativeAddress; + if (moduleInfo.textArea.ContainsAddress(bp->address)) + relativeAddress = bp->address - moduleInfo.textArea.base; + else if (moduleInfo.dataArea.ContainsAddress(bp->address)) + relativeAddress = bp->address - moduleInfo.dataArea.base; + else continue; - uint32_t relative_address = bp->address - (bp->isMemBP() ? address_module->regionMappingBase_data : address_module->regionMappingBase_text.GetMPTR()); auto entry = breakpoints_parser.set("Entry"); - entry.set("Address", fmt::format("{:#10x}", relative_address)); + entry.set("Address", fmt::format("{:#10x}", relativeAddress)); entry.set("Comment", boost::nowide::narrow(bp->comment).c_str()); entry.set("Type", bp->bpType); entry.set("Enabled", bp->enabled); - if (this->delete_breakpoints_after_saving) debugger_deleteBreakpoint(bp); + if (this->delete_breakpoints_after_saving) + debugger_deleteBreakpoint(bp->id); this->delete_breakpoints_after_saving = false; } + debugger_unlockBreakpoints(); auto comments_parser = parser.set("Comments"); for (const auto& comment_entry : rplDebugSymbol_getSymbols()) { // check comment type - const auto comment_address = comment_entry.first; + const auto commentAddress = comment_entry.first; const auto comment = static_cast(comment_entry.second); if (!comment || comment->type != RplDebugSymbolComment) continue; - // check whether it's part of the current module being saved - RPLModule* address_module = RPLLoader_FindModuleByCodeAddr(comment_entry.first); - if (!address_module || !(address_module->moduleName2 == module_name && address_module->patchCRC == this->crc_hash)) + // check whether the comment is part of the current module being saved + uint32 relativeAddress; + if (moduleInfo.textArea.ContainsAddress(commentAddress)) + relativeAddress = commentAddress - moduleInfo.textArea.base; + else continue; - uint32_t relative_address = comment_address - address_module->regionMappingBase_text.GetMPTR(); auto entry = comments_parser.set("Entry"); - entry.set("Address", fmt::format("{:#10x}", relative_address)); + entry.set("Address", fmt::format("{:#10x}", relativeAddress)); entry.set("Comment", boost::nowide::narrow(comment->comment).c_str()); } } @@ -256,28 +276,29 @@ void DebuggerWindow2::CreateToolBar() } -void DebuggerWindow2::SaveModuleStorage(const RPLModule* module, bool delete_breakpoints) +void DebuggerWindow2::LoadModuleStorage(const DebuggerModuleInfo& moduleInfo) { - auto path = GetModuleStoragePath(module->moduleName2, module->patchCRC); - for (auto& module_storage : m_modules_storage) - { - if (module_storage->data().module_name == module->moduleName2 && module_storage->data().crc_hash == module->patchCRC) - { - module_storage->data().delete_breakpoints_after_saving = delete_breakpoints; - module_storage->Save(path); - if (delete_breakpoints) m_modules_storage.erase(std::find(m_modules_storage.begin(), m_modules_storage.end(), module_storage)); - } - } - -} - -void DebuggerWindow2::LoadModuleStorage(const RPLModule* module) -{ - auto path = GetModuleStoragePath(module->moduleName2, module->patchCRC); - bool already_loaded = std::any_of(m_modules_storage.begin(), m_modules_storage.end(), [path](const std::unique_ptr& debug) { return debug->GetFilename() == path; }); + auto path = GetModuleStoragePath(moduleInfo.moduleName, moduleInfo.patchCRC); + bool already_loaded = std::any_of(m_modulesStorage.begin(), m_modulesStorage.end(), [path](const std::unique_ptr& debug) { return debug->GetFilename() == path; }); if (!path.empty() && !already_loaded) { - m_modules_storage.emplace_back(new XMLDebuggerModuleConfig(path, { module->moduleName2, module->patchCRC, module, false }))->Load(); + m_modulesStorage.emplace_back(new XMLDebuggerModuleConfig(path, { moduleInfo, false }))->Load(); + } +} + +void DebuggerWindow2::SaveModuleStorage(const DebuggerModuleInfo& moduleInfo, bool deleteModuleStorage) +{ + auto path = GetModuleStoragePath(moduleInfo.moduleName, moduleInfo.patchCRC); + for (auto& moduleStorage : m_modulesStorage) + { + if (moduleStorage->data().moduleInfo.moduleName == moduleInfo.moduleName && moduleStorage->data().moduleInfo.patchCRC == moduleInfo.patchCRC) + { + moduleStorage->data().delete_breakpoints_after_saving = deleteModuleStorage; // make sure breakpoints are deleted when the storage is removed + moduleStorage->Save(path); + if (deleteModuleStorage) + m_modulesStorage.erase(std::find(m_modulesStorage.begin(), m_modulesStorage.end(), moduleStorage)); + break; + } } } @@ -291,8 +312,8 @@ DebuggerWindow2::DebuggerWindow2(wxFrame& parent, const wxRect& display_size) m_config.SetFilename(file.generic_wstring()); m_config.Load(); - debuggerState.breakOnEntry = m_config.data().break_on_start; - debuggerState.logOnlyMemoryBreakpoints = m_config.data().log_memory_breakpoints; + debugger_setOptionBreakOnEntry(m_config.data().break_on_start); + debugger_setOptionLogOnlyMemoryBreakpoints(m_config.data().log_memory_breakpoints); m_main_position = parent.GetPosition(); m_main_size = parent.GetSize(); @@ -305,21 +326,22 @@ DebuggerWindow2::DebuggerWindow2(wxFrame& parent, const wxRect& display_size) wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL); - // load configs for already loaded modules + // load configs and module storage for already loaded modules const auto module_count = RPLLoader_GetModuleCount(); const auto module_list = RPLLoader_GetModuleList(); for (sint32 i = 0; i < module_count; i++) { const auto module = module_list[i]; - LoadModuleStorage(module); + DebuggerModuleInfo moduleInfo(module); + LoadModuleStorage(moduleInfo); } wxString label_text = _("> no modules loaded"); if (module_count != 0) { - RPLModule* current_rpl_module = RPLLoader_FindModuleByCodeAddr(MEMORY_CODEAREA_ADDR); - if (current_rpl_module) - label_text = wxString::Format("> %s", current_rpl_module->moduleName2.c_str()); + RPLModule* currentModule = RPLLoader_FindModuleByCodeAddr(MEMORY_CODEAREA_ADDR); + if (currentModule) + label_text = wxString::Format("> %s", currentModule->moduleName2.c_str()); else label_text = _("> unknown module"); } @@ -345,26 +367,16 @@ DebuggerWindow2::DebuggerWindow2(wxFrame& parent, const wxRect& display_size) OnParentMove(m_main_position, m_main_size); m_config.data().pin_to_main = value; - g_debugger_window = this; + s_debuggerWindow = this; } DebuggerWindow2::~DebuggerWindow2() { g_debuggerDispatcher.ClearDebuggerCallbacks(); - debuggerState.breakOnEntry = false; - g_debugger_window = nullptr; + s_debuggerWindow = nullptr; - // save configs for all modules that are still loaded - // doesn't delete breakpoints since that should (in the future) be done by unloading the rpl modules when exiting the current game - const auto module_count = RPLLoader_GetModuleCount(); - const auto module_list = RPLLoader_GetModuleList(); - for (sint32 i = 0; i < module_count; i++) - { - const auto module = module_list[i]; - if (module) - SaveModuleStorage(module, false); - } + CleanupForDestroy(); if (m_register_window && m_register_window->IsShown()) m_register_window->Close(true); @@ -380,13 +392,23 @@ DebuggerWindow2::~DebuggerWindow2() if (m_symbol_window && m_symbol_window->IsShown()) m_symbol_window->Close(true); +} +// note: Can be called twice in some circumstances where early cleanup is needed. Will also always be called from destructor +void DebuggerWindow2::CleanupForDestroy() +{ + debugger_setOptionBreakOnEntry(false); + // save module storage + while (!m_modulesStorage.empty()) + { + SaveModuleStorage(m_modulesStorage[0]->data().moduleInfo, true); // deleting the module storage will also delete breakpoints belonging to the module's data + } m_config.Save(); } void DebuggerWindow2::OnClose(wxCloseEvent& event) { - debuggerState.breakOnEntry = false; + debugger_setOptionBreakOnEntry(false); const wxCloseEvent parentEvent(wxEVT_DEBUGGER_CLOSE); wxPostEvent(m_parent, parentEvent); @@ -394,11 +416,13 @@ void DebuggerWindow2::OnClose(wxCloseEvent& event) event.Skip(); } -void DebuggerWindow2::OnMoveIP(wxCommandEvent& event) +void DebuggerWindow2::OnMoveToDisasmAddr(wxCommandEvent& event) { - const auto ip = debuggerState.debugSession.instructionPointer; - UpdateModuleLabel(ip); - m_disasm_ctrl->CenterOffset(ip); + MPTR address = (uint32)(uint64)event.GetClientData(); + if (!address) + return; + UpdateModuleLabel(address); + m_disasm_ctrl->CenterOffset(address); } void DebuggerWindow2::OnDisasmCtrlGotoAddress(wxCommandEvent& event) @@ -431,8 +455,8 @@ void DebuggerWindow2::OnParentMove(const wxPoint& main_position, const wxSize& m void DebuggerWindow2::OnNotifyModuleLoaded(wxCommandEvent& event) { - RPLModule* module = (RPLModule*)event.GetClientData(); - LoadModuleStorage(module); + DebuggerModuleInfoNotify* moduleNotif = static_cast(event.GetClientObject()); + LoadModuleStorage(moduleNotif->moduleInfo); m_module_window->OnGameLoaded(); m_symbol_window->OnGameLoaded(); m_disasm_ctrl->Init(); @@ -440,8 +464,8 @@ void DebuggerWindow2::OnNotifyModuleLoaded(wxCommandEvent& event) void DebuggerWindow2::OnNotifyModuleUnloaded(wxCommandEvent& event) { - RPLModule* module = (RPLModule*)event.GetClientData(); // todo - the RPL module is already unloaded at this point. Find a better way to handle this - SaveModuleStorage(module, true); + DebuggerModuleInfoNotify* moduleNotif = static_cast(event.GetClientObject()); + SaveModuleStorage(moduleNotif->moduleInfo, true); m_module_window->OnGameLoaded(); m_symbol_window->OnGameLoaded(); m_disasm_ctrl->Init(); @@ -507,7 +531,12 @@ std::wstring DebuggerWindow2::GetModuleStoragePath(std::string module_name, uint void DebuggerWindow2::OnBreakpointHit(wxCommandEvent& event) { - const auto ip = debuggerState.debugSession.instructionPointer; + PPCInterpreter_t* hCPU = debugger_lockDebugSession(); + if (!hCPU) + return; + MPTR ip = hCPU->instructionPointer; + debugger_unlockDebugSession(hCPU); + UpdateModuleLabel(ip); m_toolbar->SetToolShortHelp(TOOL_ID_PAUSE, _("Run (F5)")); @@ -537,17 +566,17 @@ void DebuggerWindow2::OnToolClicked(wxCommandEvent& event) break; case TOOL_ID_PAUSE: if (debugger_isTrapped()) - debugger_resume(); + debugger_stepCommand(DebuggerStepCommand::Run); else - debugger_forceBreak(); + debugger_requestBreak(); break; case TOOL_ID_STEP_INTO: if (debugger_isTrapped()) - debuggerState.debugSession.stepInto = true; + debugger_stepCommand(DebuggerStepCommand::StepInto); break; case TOOL_ID_STEP_OVER: if (debugger_isTrapped()) - debuggerState.debugSession.stepOver = true; + debugger_stepCommand(DebuggerStepCommand::StepOver); break; } } @@ -576,14 +605,14 @@ void DebuggerWindow2::OnOptionsInput(wxCommandEvent& event) { const bool value = !m_config.data().break_on_start; m_config.data().break_on_start = value; - debuggerState.breakOnEntry = value; + debugger_setOptionBreakOnEntry(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; + debugger_setOptionLogOnlyMemoryBreakpoints(value); break; } case MENU_ID_OPTIONS_SWITCH_CPU_MODE: @@ -746,16 +775,10 @@ void DebuggerWindow2::NotifyRun() wxQueueEvent(this, evt); } -void DebuggerWindow2::MoveIP() +void DebuggerWindow2::MoveToAddressInDisassembly(MPTR address) { - auto* evt = new wxCommandEvent(wxEVT_MOVE_IP); - wxQueueEvent(this, evt); -} - -void DebuggerWindow2::NotifyModuleLoaded(void* module) -{ - auto* evt = new wxCommandEvent(wxEVT_NOTIFY_MODULE_LOADED); - evt->SetClientData(module); + auto* evt = new wxCommandEvent(wxEVT_MOVE_TO_DISASM_ADDR); + evt->SetClientData((void*)(uintptr_t)address); wxQueueEvent(this, evt); } @@ -765,9 +788,18 @@ void DebuggerWindow2::NotifyGraphicPacksModified() wxQueueEvent(this, evt); } -void DebuggerWindow2::NotifyModuleUnloaded(void* module) +void DebuggerWindow2::NotifyModuleLoaded(RPLModule* module) { - auto* evt = new wxCommandEvent(wxEVT_NOTIFY_MODULE_UNLOADED); - evt->SetClientData(module); + DebuggerModuleInfoNotify* notifData = new DebuggerModuleInfoNotify(module); + auto* evt = new wxCommandEvent(wxEVT_NOTIFY_MODULE_LOADED); + evt->SetClientObject(notifData); + wxQueueEvent(this, evt); +} + +void DebuggerWindow2::NotifyModuleUnloaded(RPLModule* module) +{ + DebuggerModuleInfoNotify* notifData = new DebuggerModuleInfoNotify(module); + auto* evt = new wxCommandEvent(wxEVT_NOTIFY_MODULE_UNLOADED); + evt->SetClientObject(notifData); wxQueueEvent(this, evt); } diff --git a/src/gui/wxgui/debugger/DebuggerWindow2.h b/src/gui/wxgui/debugger/DebuggerWindow2.h index b6395e06..9c274a80 100644 --- a/src/gui/wxgui/debugger/DebuggerWindow2.h +++ b/src/gui/wxgui/debugger/DebuggerWindow2.h @@ -21,12 +21,12 @@ wxDECLARE_EVENT(wxEVT_UPDATE_VIEW, wxCommandEvent); wxDECLARE_EVENT(wxEVT_BREAKPOINT_HIT, wxCommandEvent); wxDECLARE_EVENT(wxEVT_RUN, wxCommandEvent); wxDECLARE_EVENT(wxEVT_BREAKPOINT_CHANGE, wxCommandEvent); -wxDECLARE_EVENT(wxEVT_MOVE_IP, wxCommandEvent); +wxDECLARE_EVENT(wxEVT_MOVE_TO_DISASM_ADDR, wxCommandEvent); 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; +extern class DebuggerWindow2* s_debuggerWindow; struct DebuggerConfig { @@ -49,11 +49,30 @@ struct DebuggerConfig }; typedef XMLDataConfig XMLDebuggerConfig; +struct DebuggerModuleInfo +{ + struct ModuleArea + { + MPTR base; + uint32 size; + + bool ContainsAddress(MPTR addr) + { + return addr >= base && (addr < (base+size)); + } + }; + + std::string moduleName; + uint32 patchCRC; + ModuleArea textArea; + ModuleArea dataArea; + + DebuggerModuleInfo(RPLModule* module); +}; + struct DebuggerModuleStorage { - std::string module_name; - uint32_t crc_hash; - const RPLModule* rpl_module; + DebuggerModuleInfo moduleInfo; bool delete_breakpoints_after_saving; void Load(XMLConfigParser& parser); @@ -73,10 +92,11 @@ class DebuggerWindow2 : public wxFrame, public DebuggerCallbacks { public: void CreateToolBar(); - void LoadModuleStorage(const RPLModule* module); - void SaveModuleStorage(const RPLModule* module, bool delete_breakpoints); + void LoadModuleStorage(const struct DebuggerModuleInfo& moduleInfo); + void SaveModuleStorage(const struct DebuggerModuleInfo& moduleInfo, bool deleteModuleStorage); DebuggerWindow2(wxFrame& parent, const wxRect& display_size); ~DebuggerWindow2(); + void CleanupForDestroy(); void OnParentMove(const wxPoint& position, const wxSize& size); void OnGameLoaded(); @@ -97,7 +117,7 @@ private: void OnExit(wxCommandEvent& event); void OnShow(wxShowEvent& event); void OnClose(wxCloseEvent& event); - void OnMoveIP(wxCommandEvent& event); + void OnMoveToDisasmAddr(wxCommandEvent& event); void OnNotifyModuleLoaded(wxCommandEvent& event); void OnNotifyModuleUnloaded(wxCommandEvent& event); void OnNotifyGraphicPacksModified(wxCommandEvent& event); @@ -110,13 +130,13 @@ private: void UpdateViewThreadsafe() override; void NotifyDebugBreakpointHit() override; void NotifyRun() override; - void MoveIP() override; - void NotifyModuleLoaded(void* module) override; + void MoveToAddressInDisassembly(MPTR address) override; void NotifyGraphicPacksModified() override; - void NotifyModuleUnloaded(void* module) override; + void NotifyModuleLoaded(struct RPLModule* module) override; + void NotifyModuleUnloaded(struct RPLModule* module) override; XMLDebuggerConfig m_config; - std::vector> m_modules_storage; + std::vector> m_modulesStorage; wxPoint m_main_position; wxSize m_main_size; diff --git a/src/gui/wxgui/debugger/DisasmCtrl.cpp b/src/gui/wxgui/debugger/DisasmCtrl.cpp index 08f254d6..33da4774 100644 --- a/src/gui/wxgui/debugger/DisasmCtrl.cpp +++ b/src/gui/wxgui/debugger/DisasmCtrl.cpp @@ -189,13 +189,19 @@ void DisasmCtrl::DrawDisassemblyLine(wxDC& dc, const wxPoint& linePosition, MPTR ppcAssembler_disassemble(virtualAddress, opcode, &disasmInstr); - const bool is_active_bp = debuggerState.debugSession.isTrapped && debuggerState.debugSession.instructionPointer == virtualAddress; + bool hasActiveBP = false; + PPCInterpreter_t* sessionCpu = debugger_lockDebugSession(); + if (sessionCpu) + { + hasActiveBP = sessionCpu->instructionPointer == virtualAddress; + debugger_unlockDebugSession(sessionCpu); + } // write virtual address wxColour background_colour; - if (is_active_bp && bp != nullptr) + if (hasActiveBP && bp != nullptr) background_colour = theme_lineBreakpointAndCurrentInstruction; - else if (is_active_bp) + else if (hasActiveBP) background_colour = theme_lineCurrentInstruction; else if (bp != nullptr) background_colour = bp->bpType == DEBUGGER_BP_T_NORMAL ? theme_lineBreakpointSet : theme_lineLoggingBreakpointSet; @@ -219,7 +225,7 @@ void DisasmCtrl::DrawDisassemblyLine(wxDC& dc, const wxPoint& linePosition, MPTR position.x += OFFSET_ADDRESS_RELATIVE; // draw arrow to clearly indicate instruction pointer - if(is_active_bp) + if(hasActiveBP) dc.DrawBitmap(*g_ipArrowBitmap, wxPoint(position.x - 24, position.y + 2), false); // handle data symbols @@ -604,36 +610,35 @@ void DisasmCtrl::OnKeyPressed(sint32 key_code, const wxPoint& position) } } - // debugger currently in break state - if (debuggerState.debugSession.isTrapped) + PPCInterpreter_t* debugCpu = debugger_lockDebugSession(); + if (debugCpu) { switch (key_code) { - case WXK_F5: + case WXK_F5: { - debuggerState.debugSession.run = true; - return; + debugger_stepCommand(DebuggerStepCommand::Run); + break; } - case WXK_F10: + case WXK_F10: { - debuggerState.debugSession.stepOver = true; - return; + debugger_stepCommand(DebuggerStepCommand::StepOver); + break; } - case WXK_F11: + case WXK_F11: { - debuggerState.debugSession.stepInto = true; + debugger_stepCommand(DebuggerStepCommand::StepInto); + break; } } + debugger_unlockDebugSession(debugCpu); } else { - switch (key_code) + if (key_code == WXK_F5) { - case WXK_F5: - { - debuggerState.debugSession.shouldBreak = true; - } + debugger_requestBreak(); } } } diff --git a/src/gui/wxgui/debugger/DumpCtrl.cpp b/src/gui/wxgui/debugger/DumpCtrl.cpp index 838845a8..16167e39 100644 --- a/src/gui/wxgui/debugger/DumpCtrl.cpp +++ b/src/gui/wxgui/debugger/DumpCtrl.cpp @@ -252,8 +252,6 @@ void DumpCtrl::CenterOffset(uint32 offset) RefreshControl(); //RefreshLine(line); - - debug_printf("scroll to %x\n", debuggerState.debugSession.instructionPointer); } uint32 DumpCtrl::LineToOffset(uint32 line) diff --git a/src/gui/wxgui/debugger/ModuleWindow.cpp b/src/gui/wxgui/debugger/ModuleWindow.cpp index 8144eb2b..cadf2e6a 100644 --- a/src/gui/wxgui/debugger/ModuleWindow.cpp +++ b/src/gui/wxgui/debugger/ModuleWindow.cpp @@ -129,6 +129,5 @@ void ModuleWindow::OnLeftDClick(wxMouseEvent& event) const auto address = std::stoul(text.ToStdString(), nullptr, 16); if (address == 0) return; - debuggerState.debugSession.instructionPointer = address; - g_debuggerDispatcher.MoveIP(); + debugger_jumpToAddressInDisasm(address); } diff --git a/src/gui/wxgui/debugger/RegisterWindow.cpp b/src/gui/wxgui/debugger/RegisterWindow.cpp index c99be90c..17167176 100644 --- a/src/gui/wxgui/debugger/RegisterWindow.cpp +++ b/src/gui/wxgui/debugger/RegisterWindow.cpp @@ -231,10 +231,15 @@ void RegisterWindow::UpdateIntegerRegister(wxTextCtrl* label, wxTextCtrl* value, void RegisterWindow::OnUpdateView() { - // m_register_ctrl->RefreshControl(); + PPCSnapshot snapshot = {}; + if (PPCInterpreter_t* hCPU = debugger_lockDebugSession(); hCPU) + { + snapshot = debugger_getSnapshotFromSession(hCPU); + debugger_unlockDebugSession(hCPU); + } for (int i = 0; i < 32; ++i) { - const uint32 registerValue = debuggerState.debugSession.ppcSnapshot.gpr[i]; + const uint32 registerValue = snapshot.gpr[i]; const bool hasChanged = registerValue != m_prev_snapshot.gpr[i]; const auto value = dynamic_cast(FindWindow(kRegisterValueR0 + i)); wxASSERT(value); @@ -246,7 +251,7 @@ void RegisterWindow::OnUpdateView() // update LR { - const uint32 registerValue = debuggerState.debugSession.ppcSnapshot.spr_lr; + const uint32 registerValue = snapshot.spr_lr; const bool hasChanged = registerValue != m_prev_snapshot.spr_lr; const auto value = dynamic_cast(FindWindow(kRegisterValueLR)); wxASSERT(value); @@ -257,7 +262,7 @@ void RegisterWindow::OnUpdateView() for (int i = 0; i < 32; ++i) { - const uint64_t register_value = debuggerState.debugSession.ppcSnapshot.fpr[i].fp0int; + const uint64_t register_value = snapshot.fpr[i].fp0int; const auto value = dynamic_cast(FindWindow(kRegisterValueFPR0_0 + i)); wxASSERT(value); @@ -271,14 +276,14 @@ void RegisterWindow::OnUpdateView() continue; if(m_show_double_values) - value->ChangeValue(wxString::Format("%lf", debuggerState.debugSession.ppcSnapshot.fpr[i].fp0)); + value->ChangeValue(wxString::Format("%lf", snapshot.fpr[i].fp0)); else value->ChangeValue(wxString::Format("%016llx", register_value)); } for (int i = 0; i < 32; ++i) { - const uint64_t register_value = debuggerState.debugSession.ppcSnapshot.fpr[i].fp1int; + const uint64_t register_value = snapshot.fpr[i].fp1int; const auto value = dynamic_cast(FindWindow(kRegisterValueFPR1_0 + i)); wxASSERT(value); @@ -292,7 +297,7 @@ void RegisterWindow::OnUpdateView() continue; if (m_show_double_values) - value->ChangeValue(wxString::Format("%lf", debuggerState.debugSession.ppcSnapshot.fpr[i].fp1)); + value->ChangeValue(wxString::Format("%lf", snapshot.fpr[i].fp1)); else value->ChangeValue(wxString::Format("%016llx", register_value)); } @@ -303,7 +308,7 @@ void RegisterWindow::OnUpdateView() const auto value = dynamic_cast(FindWindow(kRegisterValueCR0 + i)); wxASSERT(value); - auto cr_bits_ptr = debuggerState.debugSession.ppcSnapshot.cr + i * 4; + auto cr_bits_ptr = snapshot.cr + i * 4; auto cr_bits_ptr_cmp = m_prev_snapshot.cr + i * 4; const bool has_changed = !std::equal(cr_bits_ptr, cr_bits_ptr + 4, cr_bits_ptr_cmp); @@ -330,44 +335,53 @@ void RegisterWindow::OnUpdateView() value->ChangeValue(fmt::format("{}", fmt::join(joinArray, ", "))); } - memcpy(&m_prev_snapshot, &debuggerState.debugSession.ppcSnapshot, sizeof(m_prev_snapshot)); + m_prev_snapshot = snapshot; } void RegisterWindow::OnMouseDClickEvent(wxMouseEvent& event) { - if (!debuggerState.debugSession.isTrapped) + PPCInterpreter_t* debugSession = debugger_lockDebugSession(); + if (!debugSession) { event.Skip(); return; } + PPCSnapshot ppcSnapshot = debugger_getSnapshotFromSession(debugSession); + debugger_unlockDebugSession(debugSession); + debugSession = nullptr; const auto id = event.GetId(); if(kRegisterValueR0 <= id && id < kRegisterValueR0 + 32) { const uint32 register_index = id - kRegisterValueR0; - const uint32 register_value = debuggerState.debugSession.ppcSnapshot.gpr[register_index]; + const uint32 register_value = ppcSnapshot.gpr[register_index]; wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), wxString::Format(_("Set R%d value"), register_index), wxString::Format("%08x", register_value)); if (set_value_dialog.ShowModal() == wxID_OK) { const uint32 new_value = std::stoul(set_value_dialog.GetValue().ToStdString(), nullptr, 16); - debuggerState.debugSession.hCPU->gpr[register_index] = new_value; - debuggerState.debugSession.ppcSnapshot.gpr[register_index] = new_value; + if (debugSession = debugger_lockDebugSession(); debugSession) + { + debugSession->gpr[register_index] = new_value; + debugger_unlockDebugSession(debugSession); + } OnUpdateView(); } - return; } if (kRegisterValueFPR0_0 <= id && id < kRegisterValueFPR0_0 + 32) { const uint32 register_index = id - kRegisterValueFPR0_0; - const double register_value = debuggerState.debugSession.ppcSnapshot.fpr[register_index].fp0; + const double register_value = ppcSnapshot.fpr[register_index].fp0; wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), wxString::Format(_("Set FP0_%d value"), register_index), wxString::Format("%lf", register_value)); if (set_value_dialog.ShowModal() == wxID_OK) { const double new_value = std::stod(set_value_dialog.GetValue().ToStdString()); - debuggerState.debugSession.hCPU->fpr[register_index].fp0 = new_value; - debuggerState.debugSession.ppcSnapshot.fpr[register_index].fp0 = new_value; + if (debugSession = debugger_lockDebugSession(); debugSession) + { + debugSession->fpr[register_index].fp0 = new_value; + debugger_unlockDebugSession(debugSession); + } OnUpdateView(); } @@ -377,16 +391,18 @@ void RegisterWindow::OnMouseDClickEvent(wxMouseEvent& event) if (kRegisterValueFPR1_0 <= id && id < kRegisterValueFPR1_0 + 32) { const uint32 register_index = id - kRegisterValueFPR1_0; - const double register_value = debuggerState.debugSession.ppcSnapshot.fpr[register_index].fp1; + const double register_value = ppcSnapshot.fpr[register_index].fp1; wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), wxString::Format(_("Set FP1_%d value"), register_index), wxString::Format("%lf", register_value)); if (set_value_dialog.ShowModal() == wxID_OK) { const double new_value = std::stod(set_value_dialog.GetValue().ToStdString()); - debuggerState.debugSession.hCPU->fpr[register_index].fp1 = new_value; - debuggerState.debugSession.ppcSnapshot.fpr[register_index].fp1 = new_value; + if (debugSession = debugger_lockDebugSession(); debugSession) + { + debugSession->fpr[register_index].fp1 = new_value; + debugger_unlockDebugSession(debugSession); + } OnUpdateView(); } - return; } @@ -396,7 +412,13 @@ void RegisterWindow::OnMouseDClickEvent(wxMouseEvent& event) void RegisterWindow::OnFPViewModePress(wxCommandEvent& event) { m_show_double_values = !m_show_double_values; - + + PPCInterpreter_t* debugSession = debugger_lockDebugSession(); + if (!debugSession) + return; + PPCSnapshot ppcSnapshot = debugger_getSnapshotFromSession(debugSession); + debugger_unlockDebugSession(debugSession); + for (int i = 0; i < 32; ++i) { const auto value0 = dynamic_cast(FindWindow(kRegisterValueFPR0_0 + i)); @@ -406,13 +428,13 @@ void RegisterWindow::OnFPViewModePress(wxCommandEvent& event) if (m_show_double_values) { - value0->ChangeValue(wxString::Format("%lf", debuggerState.debugSession.ppcSnapshot.fpr[i].fp0)); - value1->ChangeValue(wxString::Format("%lf", debuggerState.debugSession.ppcSnapshot.fpr[i].fp1)); + value0->ChangeValue(wxString::Format("%lf", ppcSnapshot.fpr[i].fp0)); + value1->ChangeValue(wxString::Format("%lf", ppcSnapshot.fpr[i].fp1)); } else { - value0->ChangeValue(wxString::Format("%016llx", debuggerState.debugSession.ppcSnapshot.fpr[i].fp0int)); - value1->ChangeValue(wxString::Format("%016llx", debuggerState.debugSession.ppcSnapshot.fpr[i].fp1int)); + value0->ChangeValue(wxString::Format("%016llx", ppcSnapshot.fpr[i].fp0int)); + value1->ChangeValue(wxString::Format("%016llx", ppcSnapshot.fpr[i].fp1int)); } } } diff --git a/src/gui/wxgui/debugger/SymbolCtrl.cpp b/src/gui/wxgui/debugger/SymbolCtrl.cpp index 8e70082b..77f55529 100644 --- a/src/gui/wxgui/debugger/SymbolCtrl.cpp +++ b/src/gui/wxgui/debugger/SymbolCtrl.cpp @@ -112,8 +112,7 @@ void SymbolListCtrl::OnLeftDClick(wxListEvent& event) const auto address = std::stoul(text.ToStdString(), nullptr, 16); if (address == 0) return; - debuggerState.debugSession.instructionPointer = address; - g_debuggerDispatcher.MoveIP(); + debugger_jumpToAddressInDisasm(address); } void SymbolListCtrl::OnRightClick(wxListEvent& event)