Debugger: Track reason for entering stepping.

This commit is contained in:
Unknown W. Brackets 2021-10-23 16:56:15 -07:00
parent 067f45d560
commit 2bd13c5d9d
16 changed files with 75 additions and 63 deletions

View file

@ -19,6 +19,7 @@
#include <set>
#include <chrono>
#include <cstdint>
#include <mutex>
#include <condition_variable>
@ -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<CoreLifecycleFunc> lifecycleFuncs;
static std::set<CoreStopRequestFunc> 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);
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -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<uint8_t> value = Base64Decode(&encoded[0], encoded.size());
uint32_t size = (uint32_t)value.size();

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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