diff --git a/Core/Core.cpp b/Core/Core.cpp index b6af6456fd..1c49545941 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -56,6 +57,8 @@ static std::condition_variable m_InactiveCond; static std::mutex m_hInactiveMutex; static bool singleStepPending = false; static int steppingCounter = 0; +static const char *steppingReason = ""; +static uint32_t steppingAddress = 0; static std::set lifecycleFuncs; static std::set stopFuncs; static bool windowHidden = false; @@ -358,11 +361,14 @@ void Core_Run(GraphicsContext *ctx) { } } -void Core_EnableStepping(bool step) { +void Core_EnableStepping(bool step, const char *reason, u32 relatedAddress) { if (step) { host->SetDebugMode(true); Core_UpdateState(CORE_STEPPING); steppingCounter++; + _assert_msg_(reason != nullptr, "No reason specified for break"); + steppingReason = reason; + steppingAddress = relatedAddress; } else { host->SetDebugMode(false); // Clear the exception if we resume. @@ -433,7 +439,7 @@ void Core_MemoryException(u32 address, u32 pc, MemoryExceptionType type) { e.memory_type = type; e.address = address; e.pc = pc; - Core_EnableStepping(true); + Core_EnableStepping(true, "memory.exception", address); host->SetDebugMode(true); } } @@ -455,14 +461,14 @@ void Core_MemoryExceptionInfo(u32 address, u32 pc, MemoryExceptionType type, std e.memory_type = type; e.address = address; e.pc = pc; - Core_EnableStepping(true); + Core_EnableStepping(true, "memory.exception", address); host->SetDebugMode(true); } } void Core_ExecException(u32 address, u32 pc, ExecExceptionType type) { const char *desc = ExecExceptionTypeAsString(type); - WARN_LOG(MEMMAP, "%s: Invalid destination %08x PC %08x LR %08x", desc, address, currentMIPS->pc, currentMIPS->r[MIPS_REG_RA]); + WARN_LOG(MEMMAP, "%s: Invalid destination %08x PC %08x LR %08x", desc, address, pc, currentMIPS->r[MIPS_REG_RA]); ExceptionInfo &e = g_exceptionInfo; e = {}; @@ -471,7 +477,7 @@ void Core_ExecException(u32 address, u32 pc, ExecExceptionType type) { e.exec_type = type; e.address = address; e.pc = pc; - Core_EnableStepping(true); + Core_EnableStepping(true, "cpu.exception", pc); host->SetDebugMode(true); } @@ -484,7 +490,7 @@ void Core_Break() { e.info = ""; if (!g_Config.bIgnoreBadMemAccess) { - Core_EnableStepping(true); + Core_EnableStepping(true, "cpu.breakInstruction", currentMIPS->pc); host->SetDebugMode(true); } } diff --git a/Core/Core.h b/Core/Core.h index a2acc66918..5ba51c10ba 100644 --- a/Core/Core.h +++ b/Core/Core.h @@ -33,7 +33,7 @@ void Core_Stop(); void Core_SetGraphicsContext(GraphicsContext *ctx); // called from gui -void Core_EnableStepping(bool step); +void Core_EnableStepping(bool step, const char *reason = nullptr, u32 relatedAddress = 0); bool Core_NextFrame(); void Core_DoSingleStep(); diff --git a/Core/CoreTiming.cpp b/Core/CoreTiming.cpp index d7c725d4f7..006d7137e3 100644 --- a/Core/CoreTiming.cpp +++ b/Core/CoreTiming.cpp @@ -182,7 +182,7 @@ int RegisterEvent(const char *name, TimedCallback callback) { void AntiCrashCallback(u64 userdata, int cyclesLate) { ERROR_LOG(SAVESTATE, "Savestate broken: an unregistered event was called."); - Core_EnableStepping(true); + Core_EnableStepping(true, "savestate.crash", 0); } void RestoreRegisterEvent(int &event_type, const char *name, TimedCallback callback) { diff --git a/Core/Debugger/Breakpoints.cpp b/Core/Debugger/Breakpoints.cpp index ddb2d2baf6..0eeed33210 100644 --- a/Core/Debugger/Breakpoints.cpp +++ b/Core/Debugger/Breakpoints.cpp @@ -68,7 +68,7 @@ BreakAction MemCheck::Action(u32 addr, bool write, int size, u32 pc, const char if (cond & mask) { Log(addr, write, size, pc, reason); if ((result & BREAK_ACTION_PAUSE) && coreState != CORE_POWERUP) { - Core_EnableStepping(true); + Core_EnableStepping(true, "memory.breakpoint", start); host->SetDebugMode(true); } @@ -93,7 +93,7 @@ void MemCheck::JitBeforeApply(u32 addr, bool write, int size, u32 pc) { void MemCheck::JitBeforeAction(u32 addr, bool write, int size, u32 pc) { if (lastAddr) { // We have to break to find out if it changed. - Core_EnableStepping(true); + Core_EnableStepping(true, "memory.breakpoint.check", start); } else { Action(addr, write, size, pc, "CPU"); } @@ -367,7 +367,7 @@ BreakAction CBreakPoints::ExecBreakPoint(u32 addr) { } } if ((info.result & BREAK_ACTION_PAUSE) && coreState != CORE_POWERUP) { - Core_EnableStepping(true); + Core_EnableStepping(true, "cpu.breakpoint", info.addr); host->SetDebugMode(true); } @@ -640,7 +640,7 @@ void CBreakPoints::Update(u32 addr) bool resume = false; if (Core_IsStepping() == false) { - Core_EnableStepping(true); + Core_EnableStepping(true, "cpu.breakpoint.update", addr); Core_WaitInactive(200); resume = true; } diff --git a/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp index 6f371e5b85..4a83788ef8 100644 --- a/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp +++ b/Core/Debugger/WebSocket/CPUCoreSubscriber.cpp @@ -70,7 +70,7 @@ void WebSocketCPUStepping(DebuggerRequest &req) { return req.Fail("CPU not started"); } if (!Core_IsStepping() && Core_IsActive()) { - Core_EnableStepping(true); + Core_EnableStepping(true, "cpu.stepping", 0); } } diff --git a/Core/Debugger/WebSocket/MemorySubscriber.cpp b/Core/Debugger/WebSocket/MemorySubscriber.cpp index 2d7741fad8..815eeba161 100644 --- a/Core/Debugger/WebSocket/MemorySubscriber.cpp +++ b/Core/Debugger/WebSocket/MemorySubscriber.cpp @@ -53,12 +53,12 @@ struct AutoDisabledReplacements { }; // Important: Only use keepReplacements when reading, not writing. -static AutoDisabledReplacements LockMemoryAndCPU(bool keepReplacements) { +static AutoDisabledReplacements LockMemoryAndCPU(uint32_t addr, bool keepReplacements) { AutoDisabledReplacements result; if (Core_IsStepping()) { result.wasStepping = true; } else { - Core_EnableStepping(true); + Core_EnableStepping(true, "memory.access", addr); Core_WaitInactive(); } @@ -92,15 +92,15 @@ AutoDisabledReplacements::~AutoDisabledReplacements() { // Response (same event name): // - value: unsigned integer void WebSocketMemoryReadU8(DebuggerRequest &req) { - auto memLock = LockMemoryAndCPU(true); - if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) - return req.Fail("CPU not started"); - uint32_t addr; if (!req.ParamU32("address", &addr, false)) { return; } + auto memLock = LockMemoryAndCPU(addr, true); + if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) + return req.Fail("CPU not started"); + if (!Memory::IsValidAddress(addr)) { req.Fail("Invalid address"); return; @@ -118,15 +118,15 @@ void WebSocketMemoryReadU8(DebuggerRequest &req) { // Response (same event name): // - value: unsigned integer void WebSocketMemoryReadU16(DebuggerRequest &req) { - auto memLock = LockMemoryAndCPU(true); - if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) - return req.Fail("CPU not started"); - uint32_t addr; if (!req.ParamU32("address", &addr, false)) { return; } + auto memLock = LockMemoryAndCPU(addr, true); + if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) + return req.Fail("CPU not started"); + if (!Memory::IsValidAddress(addr)) { req.Fail("Invalid address"); return; @@ -144,15 +144,15 @@ void WebSocketMemoryReadU16(DebuggerRequest &req) { // Response (same event name): // - value: unsigned integer void WebSocketMemoryReadU32(DebuggerRequest &req) { - auto memLock = LockMemoryAndCPU(true); - if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) - return req.Fail("CPU not started"); - uint32_t addr; if (!req.ParamU32("address", &addr, false)) { return; } + auto memLock = LockMemoryAndCPU(addr, true); + if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) + return req.Fail("CPU not started"); + if (!Memory::IsValidAddress(addr)) { req.Fail("Invalid address"); return; @@ -182,7 +182,7 @@ void WebSocketMemoryRead(DebuggerRequest &req) { if (!req.ParamBool("replacements", &replacements, DebuggerParamType::OPTIONAL)) return; - auto memLock = LockMemoryAndCPU(replacements); + auto memLock = LockMemoryAndCPU(addr, replacements); if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) return req.Fail("CPU not started"); @@ -219,13 +219,14 @@ void WebSocketMemoryRead(DebuggerRequest &req) { // Response (same event name) for 'base64': // - base64: base64 encode of binary data, not including NUL. void WebSocketMemoryReadString(DebuggerRequest &req) { - auto memLock = LockMemoryAndCPU(true); - if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) - return req.Fail("CPU not started"); - uint32_t addr; if (!req.ParamU32("address", &addr)) return; + + auto memLock = LockMemoryAndCPU(addr, true); + if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) + return req.Fail("CPU not started"); + std::string type = "utf-8"; if (!req.ParamString("type", &type, DebuggerParamType::OPTIONAL)) return; @@ -257,10 +258,6 @@ void WebSocketMemoryReadString(DebuggerRequest &req) { // Response (same event name): // - value: new value, unsigned integer void WebSocketMemoryWriteU8(DebuggerRequest &req) { - auto memLock = LockMemoryAndCPU(true); - if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) - return req.Fail("CPU not started"); - uint32_t addr, val; if (!req.ParamU32("address", &addr, false)) { return; @@ -269,6 +266,10 @@ void WebSocketMemoryWriteU8(DebuggerRequest &req) { return; } + auto memLock = LockMemoryAndCPU(addr, true); + if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) + return req.Fail("CPU not started"); + if (!Memory::IsValidAddress(addr)) { req.Fail("Invalid address"); return; @@ -289,10 +290,6 @@ void WebSocketMemoryWriteU8(DebuggerRequest &req) { // Response (same event name): // - value: new value, unsigned integer void WebSocketMemoryWriteU16(DebuggerRequest &req) { - auto memLock = LockMemoryAndCPU(true); - if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) - return req.Fail("CPU not started"); - uint32_t addr, val; if (!req.ParamU32("address", &addr, false)) { return; @@ -301,6 +298,10 @@ void WebSocketMemoryWriteU16(DebuggerRequest &req) { return; } + auto memLock = LockMemoryAndCPU(addr, true); + if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) + return req.Fail("CPU not started"); + if (!Memory::IsValidAddress(addr)) { req.Fail("Invalid address"); return; @@ -321,10 +322,6 @@ void WebSocketMemoryWriteU16(DebuggerRequest &req) { // Response (same event name): // - value: new value, unsigned integer void WebSocketMemoryWriteU32(DebuggerRequest &req) { - auto memLock = LockMemoryAndCPU(true); - if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) - return req.Fail("CPU not started"); - uint32_t addr, val; if (!req.ParamU32("address", &addr, false)) { return; @@ -333,6 +330,10 @@ void WebSocketMemoryWriteU32(DebuggerRequest &req) { return; } + auto memLock = LockMemoryAndCPU(addr, true); + if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) + return req.Fail("CPU not started"); + if (!Memory::IsValidAddress(addr)) { req.Fail("Invalid address"); return; @@ -352,10 +353,6 @@ void WebSocketMemoryWriteU32(DebuggerRequest &req) { // // Response (same event name) with no extra data. void WebSocketMemoryWrite(DebuggerRequest &req) { - auto memLock = LockMemoryAndCPU(true); - if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) - return req.Fail("CPU not started"); - uint32_t addr; if (!req.ParamU32("address", &addr)) return; @@ -363,6 +360,10 @@ void WebSocketMemoryWrite(DebuggerRequest &req) { if (!req.ParamString("base64", &encoded)) return; + auto memLock = LockMemoryAndCPU(addr, true); + if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) + return req.Fail("CPU not started"); + std::vector value = Base64Decode(&encoded[0], encoded.size()); uint32_t size = (uint32_t)value.size(); diff --git a/Core/Debugger/WebSocket/SteppingSubscriber.cpp b/Core/Debugger/WebSocket/SteppingSubscriber.cpp index 19c545de86..fcb9296985 100644 --- a/Core/Debugger/WebSocket/SteppingSubscriber.cpp +++ b/Core/Debugger/WebSocket/SteppingSubscriber.cpp @@ -93,7 +93,7 @@ void WebSocketSteppingState::Into(DebuggerRequest &req) { if (!currentDebugMIPS->isAlive()) return req.Fail("CPU not started"); if (!Core_IsStepping()) { - Core_EnableStepping(true); + Core_EnableStepping(true, "cpu.stepInto", 0); return; } diff --git a/Core/HLE/HLE.cpp b/Core/HLE/HLE.cpp index be27e632c5..1c0957e20d 100644 --- a/Core/HLE/HLE.cpp +++ b/Core/HLE/HLE.cpp @@ -68,6 +68,7 @@ static int delayedResultEvent = -1; static int hleAfterSyscall = HLE_AFTER_NOTHING; static const char *hleAfterSyscallReschedReason; static const HLEFunction *latestSyscall = nullptr; +static uint32_t latestSyscallPC = 0; static int idleOp; struct HLEMipsCallInfo { @@ -128,6 +129,7 @@ void HLEDoState(PointerWrap &p) { // Can't be inside a syscall, reset this so errors aren't misleading. latestSyscall = nullptr; + latestSyscallPC = 0; Do(p, delayedResultEvent); CoreTiming::RestoreRegisterEvent(delayedResultEvent, "HLEDelayedResult", hleDelayResultFinish); @@ -153,6 +155,7 @@ void HLEDoState(PointerWrap &p) { void HLEShutdown() { hleAfterSyscall = HLE_AFTER_NOTHING; latestSyscall = nullptr; + latestSyscallPC = 0; moduleDB.clear(); enqueuedMipsCalls.clear(); for (auto p : mipsCallActions) { @@ -360,7 +363,7 @@ bool hleExecuteDebugBreak(const HLEFunction &func) return false; } - Core_EnableStepping(true); + Core_EnableStepping(true, "hle.step", latestSyscallPC); host->SetDebugMode(true); return true; } @@ -625,6 +628,7 @@ static void updateSyscallStats(int modulenum, int funcnum, double total) inline void CallSyscallWithFlags(const HLEFunction *info) { latestSyscall = info; + latestSyscallPC = currentMIPS->pc; const u32 flags = info->flags; if (flags & HLE_CLEAR_STACK_BYTES) { @@ -651,6 +655,7 @@ inline void CallSyscallWithFlags(const HLEFunction *info) inline void CallSyscallWithoutFlags(const HLEFunction *info) { latestSyscall = info; + latestSyscallPC = currentMIPS->pc; info->func(); if (hleAfterSyscall != HLE_AFTER_NOTHING) diff --git a/Core/MIPS/MIPSTables.cpp b/Core/MIPS/MIPSTables.cpp index 607da6aa4d..6a411f42ba 100644 --- a/Core/MIPS/MIPSTables.cpp +++ b/Core/MIPS/MIPSTables.cpp @@ -981,7 +981,7 @@ int MIPSInterpret_RunUntil(u64 globalTicks) auto cond = CBreakPoints::GetBreakPointCondition(currentMIPS->pc); if (!cond || cond->Evaluate()) { - Core_EnableStepping(true); + Core_EnableStepping(true, "cpu.breakpoint", curMips->pc); if (CBreakPoints::IsTempBreakPoint(curMips->pc)) CBreakPoints::RemoveBreakPoint(curMips->pc); break; diff --git a/Core/SaveState.cpp b/Core/SaveState.cpp index 1b02abe199..a046b63607 100644 --- a/Core/SaveState.cpp +++ b/Core/SaveState.cpp @@ -336,14 +336,14 @@ namespace SaveState void Load(const Path &filename, int slot, Callback callback, void *cbUserData) { if (coreState == CoreState::CORE_RUNTIME_ERROR) - Core_EnableStepping(true); + Core_EnableStepping(true, "savestate.load", 0); Enqueue(Operation(SAVESTATE_LOAD, filename, slot, callback, cbUserData)); } void Save(const Path &filename, int slot, Callback callback, void *cbUserData) { if (coreState == CoreState::CORE_RUNTIME_ERROR) - Core_EnableStepping(true); + Core_EnableStepping(true, "savestate.save", 0); Enqueue(Operation(SAVESTATE_SAVE, filename, slot, callback, cbUserData)); } @@ -355,7 +355,7 @@ namespace SaveState void Rewind(Callback callback, void *cbUserData) { if (coreState == CoreState::CORE_RUNTIME_ERROR) - Core_EnableStepping(true); + Core_EnableStepping(true, "savestate.rewind", 0); Enqueue(Operation(SAVESTATE_REWIND, Path(), -1, callback, cbUserData)); } diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index b8dac68c5b..4be893cb0f 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -110,7 +110,7 @@ static void __EmuScreenVblank() if (frameStep_ && lastNumFlips != gpuStats.numFlips) { frameStep_ = false; - Core_EnableStepping(true); + Core_EnableStepping(true, "ui.frameAdvance", 0); lastNumFlips = gpuStats.numFlips; } #ifndef MOBILE_DEVICE @@ -611,7 +611,7 @@ void EmuScreen::onVKeyDown(int virtualKeyCode) { } else if (!frameStep_) { - Core_EnableStepping(true); + Core_EnableStepping(true, "ui.frameAdvance", 0); } break; diff --git a/Windows/Debugger/CtrlMemView.cpp b/Windows/Debugger/CtrlMemView.cpp index 5d9f4a0b27..eeb7ad2d3e 100644 --- a/Windows/Debugger/CtrlMemView.cpp +++ b/Windows/Debugger/CtrlMemView.cpp @@ -421,7 +421,7 @@ void CtrlMemView::onChar(WPARAM wParam, LPARAM lParam) } bool active = Core_IsActive(); - if (active) Core_EnableStepping(true); + if (active) Core_EnableStepping(true, "memory.access", curAddress); if (asciiSelected) { diff --git a/Windows/Debugger/Debugger_Disasm.cpp b/Windows/Debugger/Debugger_Disasm.cpp index 99828b2be8..833a9dbe0d 100644 --- a/Windows/Debugger/Debugger_Disasm.cpp +++ b/Windows/Debugger/Debugger_Disasm.cpp @@ -420,7 +420,7 @@ BOOL CDisasm::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) if (isRunning) { SetDebugMode(true, false); - Core_EnableStepping(true); + Core_EnableStepping(true, "cpu.breakpoint.add", 0); Core_WaitInactive(200); } @@ -523,7 +523,7 @@ BOOL CDisasm::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { ptr->setDontRedraw(false); SetDebugMode(true, true); - Core_EnableStepping(true); + Core_EnableStepping(true, "ui.break", 0); Sleep(1); //let cpu catch up ptr->gotoPC(); UpdateDialog(); diff --git a/Windows/Debugger/DumpMemoryWindow.cpp b/Windows/Debugger/DumpMemoryWindow.cpp index 67b95ce8fe..a55337b149 100644 --- a/Windows/Debugger/DumpMemoryWindow.cpp +++ b/Windows/Debugger/DumpMemoryWindow.cpp @@ -81,7 +81,7 @@ INT_PTR CALLBACK DumpMemoryWindow::dlgFunc(HWND hwnd, UINT iMsg, WPARAM wParam, bool priorDumpWasStepping = Core_IsStepping(); if (!priorDumpWasStepping && PSP_IsInited()) { // If emulator isn't paused force paused state, but wait before locking. - Core_EnableStepping(true); + Core_EnableStepping(true, "memory.access", bp->start); Core_WaitInactive(); } diff --git a/Windows/MainWindow.cpp b/Windows/MainWindow.cpp index 56d953d79b..e583846e6c 100644 --- a/Windows/MainWindow.cpp +++ b/Windows/MainWindow.cpp @@ -724,7 +724,7 @@ namespace MainWindow if (disasmWindow) SendMessage(disasmWindow->GetDlgHandle(), WM_COMMAND, IDC_STOPGO, 0); else - Core_EnableStepping(pause); + Core_EnableStepping(pause, "ui.lost_focus", 0); } } diff --git a/Windows/MainWindowMenu.cpp b/Windows/MainWindowMenu.cpp index a79eb127c2..acda99fdbb 100644 --- a/Windows/MainWindowMenu.cpp +++ b/Windows/MainWindowMenu.cpp @@ -381,7 +381,7 @@ namespace MainWindow { if (GetUIState() == UISTATE_INGAME) { browsePauseAfter = Core_IsStepping(); if (!browsePauseAfter) - Core_EnableStepping(true); + Core_EnableStepping(true, "ui.boot", 0); } W32Util::MakeTopMost(GetHWND(), false); @@ -601,7 +601,7 @@ namespace MainWindow { if (disasmWindow) SendMessage(disasmWindow->GetDlgHandle(), WM_COMMAND, IDC_STOPGO, 0); else - Core_EnableStepping(true); + Core_EnableStepping(true, "ui.break", 0); } noFocusPause = !noFocusPause; // If we pause, override pause on lost focus break;