diff --git a/Core/Core.cpp b/Core/Core.cpp index bdaf7e9cdf..fab19df3cc 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -213,7 +213,7 @@ const char *BreakReasonToString(BreakReason reason) { case BreakReason::SavestateCrash: return "savestate.crash"; case BreakReason::MemoryBreakpoint: return "memory.breakpoint"; case BreakReason::CpuBreakpoint: return "cpu.breakpoint"; - case BreakReason::GPRBreakpoint: return "cpu.gprBreakpoint"; + case BreakReason::RegBreakpoint: return "cpu.regBreakpoint"; case BreakReason::MemoryAccess: return "memory.access"; // ??? case BreakReason::JitBranchDebug: return "jit.branchdebug"; case BreakReason::RABreak: return "ra.break"; diff --git a/Core/Core.h b/Core/Core.h index 00db0f0d8d..2bd2ee72f6 100644 --- a/Core/Core.h +++ b/Core/Core.h @@ -64,7 +64,7 @@ enum class BreakReason { SavestateCrash, MemoryBreakpoint, CpuBreakpoint, - GPRBreakpoint, + RegBreakpoint, MemoryAccess, // ??? JitBranchDebug, BreakOnBoot, diff --git a/Core/Debugger/Breakpoints.cpp b/Core/Debugger/Breakpoints.cpp index cabdbef020..2ee645b174 100644 --- a/Core/Debugger/Breakpoints.cpp +++ b/Core/Debugger/Breakpoints.cpp @@ -94,13 +94,13 @@ size_t BreakpointManager::FindMemCheck(u32 start, u32 end) { return INVALID_MEMCHECK; } -size_t BreakpointManager::FindGPRBreakpoint(int reg) { - for (size_t i = 0; i < gprBreakpoints_.size(); ++i) { - if (gprBreakpoints_[i].reg == reg) +size_t BreakpointManager::FindRegBreakpoint(int reg) { + for (size_t i = 0; i < regBreakpoints_.size(); ++i) { + if (regBreakpoints_[i].reg == reg) return i; } - return INVALID_GPR_BREAKPOINT; + return INVALID_REG_BREAKPOINT; } bool BreakpointManager::IsAddressBreakPoint(u32 addr) @@ -506,30 +506,30 @@ BreakAction BreakpointManager::ExecOpMemCheck(u32 address, u32 pc) { return BREAK_ACTION_IGNORE; } -void BreakpointManager::RecomputeGPRBreakpointMask() { +void BreakpointManager::RecomputeRegBreakpointMask() { u32 mask = 0; - for (const auto &bp : gprBreakpoints_) { + for (const auto &bp : regBreakpoints_) { if (bp.result != BREAK_ACTION_IGNORE) mask |= 1u << bp.reg; } - gprBreakpointMask_ = mask; + regBreakpointMask_ = mask; } -int BreakpointManager::AddGPRBreakpoint(int reg) { - size_t bp = FindGPRBreakpoint(reg); - if (bp == INVALID_GPR_BREAKPOINT) { - GPRBreakpoint pt; +int BreakpointManager::AddRegBreakpoint(int reg) { + size_t bp = FindRegBreakpoint(reg); + if (bp == INVALID_REG_BREAKPOINT) { + RegBreakpoint pt; pt.reg = reg; pt.result |= BREAK_ACTION_PAUSE; - gprBreakpoints_.push_back(pt); - RecomputeGPRBreakpointMask(); + regBreakpoints_.push_back(pt); + RecomputeRegBreakpointMask(); Update(INVALID_ADDRESS); // Not baked into JIT code, no cache invalidation needed. - return (int)gprBreakpoints_.size() - 1; - } else if (!gprBreakpoints_[bp].IsEnabled()) { - gprBreakpoints_[bp].result |= BREAK_ACTION_PAUSE; - gprBreakpoints_[bp].hasCond = false; - RecomputeGPRBreakpointMask(); + return (int)regBreakpoints_.size() - 1; + } else if (!regBreakpoints_[bp].IsEnabled()) { + regBreakpoints_[bp].result |= BREAK_ACTION_PAUSE; + regBreakpoints_[bp].hasCond = false; + RecomputeRegBreakpointMask(); Update(INVALID_ADDRESS); return (int)bp; } else { @@ -537,106 +537,106 @@ int BreakpointManager::AddGPRBreakpoint(int reg) { } } -void BreakpointManager::RemoveGPRBreakpoint(int reg) { - size_t bp = FindGPRBreakpoint(reg); - if (bp != INVALID_GPR_BREAKPOINT) { - gprBreakpoints_.erase(gprBreakpoints_.begin() + bp); - RecomputeGPRBreakpointMask(); +void BreakpointManager::RemoveRegBreakpoint(int reg) { + size_t bp = FindRegBreakpoint(reg); + if (bp != INVALID_REG_BREAKPOINT) { + regBreakpoints_.erase(regBreakpoints_.begin() + bp); + RecomputeRegBreakpointMask(); Update(INVALID_ADDRESS); } } -void BreakpointManager::ChangeGPRBreakpoint(int reg, bool status) { - size_t bp = FindGPRBreakpoint(reg); - if (bp != INVALID_GPR_BREAKPOINT) { +void BreakpointManager::ChangeRegBreakpoint(int reg, bool status) { + size_t bp = FindRegBreakpoint(reg); + if (bp != INVALID_REG_BREAKPOINT) { if (status) - gprBreakpoints_[bp].result |= BREAK_ACTION_PAUSE; + regBreakpoints_[bp].result |= BREAK_ACTION_PAUSE; else - gprBreakpoints_[bp].result = BreakAction(gprBreakpoints_[bp].result & ~BREAK_ACTION_PAUSE); - RecomputeGPRBreakpointMask(); + regBreakpoints_[bp].result = BreakAction(regBreakpoints_[bp].result & ~BREAK_ACTION_PAUSE); + RecomputeRegBreakpointMask(); Update(INVALID_ADDRESS); } } -void BreakpointManager::ChangeGPRBreakpoint(int reg, BreakAction result) { - size_t bp = FindGPRBreakpoint(reg); - if (bp != INVALID_GPR_BREAKPOINT) { - gprBreakpoints_[bp].result = result; - RecomputeGPRBreakpointMask(); +void BreakpointManager::ChangeRegBreakpoint(int reg, BreakAction result) { + size_t bp = FindRegBreakpoint(reg); + if (bp != INVALID_REG_BREAKPOINT) { + regBreakpoints_[bp].result = result; + RecomputeRegBreakpointMask(); Update(INVALID_ADDRESS); } } -void BreakpointManager::ClearAllGPRBreakpoints() { - if (!gprBreakpoints_.empty()) { - gprBreakpoints_.clear(); - gprBreakpointMask_ = 0; +void BreakpointManager::ClearAllRegBreakpoints() { + if (!regBreakpoints_.empty()) { + regBreakpoints_.clear(); + regBreakpointMask_ = 0; Update(INVALID_ADDRESS); } } -void BreakpointManager::ChangeGPRBreakpointAddCond(int reg, const BreakPointCond &cond) { - size_t bp = FindGPRBreakpoint(reg); - if (bp != INVALID_GPR_BREAKPOINT) { - gprBreakpoints_[bp].hasCond = true; - gprBreakpoints_[bp].cond = cond; +void BreakpointManager::ChangeRegBreakpointAddCond(int reg, const BreakPointCond &cond) { + size_t bp = FindRegBreakpoint(reg); + if (bp != INVALID_REG_BREAKPOINT) { + regBreakpoints_[bp].hasCond = true; + regBreakpoints_[bp].cond = cond; // No need to update jit for a condition add/remove, they're not baked in. Update(INVALID_ADDRESS); } } -void BreakpointManager::ChangeGPRBreakpointRemoveCond(int reg) { - size_t bp = FindGPRBreakpoint(reg); - if (bp != INVALID_GPR_BREAKPOINT) { - gprBreakpoints_[bp].hasCond = false; +void BreakpointManager::ChangeRegBreakpointRemoveCond(int reg) { + size_t bp = FindRegBreakpoint(reg); + if (bp != INVALID_REG_BREAKPOINT) { + regBreakpoints_[bp].hasCond = false; Update(INVALID_ADDRESS); } } -BreakPointCond *BreakpointManager::GetGPRBreakpointCondition(int reg) { - size_t bp = FindGPRBreakpoint(reg); - if (bp != INVALID_GPR_BREAKPOINT && gprBreakpoints_[bp].hasCond) - return &gprBreakpoints_[bp].cond; +BreakPointCond *BreakpointManager::GetRegBreakpointCondition(int reg) { + size_t bp = FindRegBreakpoint(reg); + if (bp != INVALID_REG_BREAKPOINT && regBreakpoints_[bp].hasCond) + return ®Breakpoints_[bp].cond; return nullptr; } -void BreakpointManager::ChangeGPRBreakpointLogFormat(int reg, const std::string &fmt) { - size_t bp = FindGPRBreakpoint(reg); - if (bp != INVALID_GPR_BREAKPOINT) { - gprBreakpoints_[bp].logFormat = fmt; +void BreakpointManager::ChangeRegBreakpointLogFormat(int reg, const std::string &fmt) { + size_t bp = FindRegBreakpoint(reg); + if (bp != INVALID_REG_BREAKPOINT) { + regBreakpoints_[bp].logFormat = fmt; Update(INVALID_ADDRESS); } } -bool BreakpointManager::IsGPRBreakpoint(int reg) { - return (gprBreakpointMask_ & (1u << reg)) != 0; +bool BreakpointManager::IsRegBreakpoint(int reg) { + return (regBreakpointMask_ & (1u << reg)) != 0; } -bool BreakpointManager::GetGPRBreakpoint(int reg, GPRBreakpoint *check) { - size_t bp = FindGPRBreakpoint(reg); - if (bp != INVALID_GPR_BREAKPOINT) { - *check = gprBreakpoints_[bp]; +bool BreakpointManager::GetRegBreakpoint(int reg, RegBreakpoint *check) { + size_t bp = FindRegBreakpoint(reg); + if (bp != INVALID_REG_BREAKPOINT) { + *check = regBreakpoints_[bp]; return true; } return false; } -std::vector BreakpointManager::GetGPRBreakpoints() { - return gprBreakpoints_; +std::vector BreakpointManager::GetRegBreakpoints() { + return regBreakpoints_; } -BreakAction BreakpointManager::ExecGPRBreakpoint(int reg, u32 pc) { - // Callers are expected to have already checked GetGPRBreakpointMask() themselves (that's +BreakAction BreakpointManager::ExecRegBreakpoint(int reg, u32 pc) { + // Callers are expected to have already checked GetRegBreakpointMask() themselves (that's // the whole point of exposing it - a single shift+and in the hot interpreter loop, skipping // a function call entirely in the overwhelmingly common no-breakpoint case), but check again // here too since this is also reachable directly. - if ((gprBreakpointMask_ & (1u << reg)) == 0) + if ((regBreakpointMask_ & (1u << reg)) == 0) return BREAK_ACTION_IGNORE; - size_t bp = FindGPRBreakpoint(reg); - if (bp == INVALID_GPR_BREAKPOINT) + size_t bp = FindRegBreakpoint(reg); + if (bp == INVALID_REG_BREAKPOINT) return BREAK_ACTION_IGNORE; - GPRBreakpoint &info = gprBreakpoints_[bp]; + RegBreakpoint &info = regBreakpoints_[bp]; if (info.result == BREAK_ACTION_IGNORE) return BREAK_ACTION_IGNORE; @@ -647,15 +647,15 @@ BreakAction BreakpointManager::ExecGPRBreakpoint(int reg, u32 pc) { if (info.result & BREAK_ACTION_LOG) { if (info.logFormat.empty()) { - NOTICE_LOG(Log::JIT, "BKP GPR write r%d, PC=%08x (%s)", reg, pc, g_symbolMap->GetDescription(pc).c_str()); + NOTICE_LOG(Log::JIT, "BKP reg write r%d, PC=%08x (%s)", reg, pc, g_symbolMap->GetDescription(pc).c_str()); } else { std::string formatted; BreakpointManager::EvaluateLogFormat(currentDebugMIPS, info.logFormat, formatted); - NOTICE_LOG(Log::JIT, "BKP GPR write r%d, PC=%08x: %s", reg, pc, formatted.c_str()); + NOTICE_LOG(Log::JIT, "BKP reg write r%d, PC=%08x: %s", reg, pc, formatted.c_str()); } } if (info.result & BREAK_ACTION_PAUSE) { - Core_Break(BreakReason::GPRBreakpoint, pc); + Core_Break(BreakReason::RegBreakpoint, pc); } return info.result; diff --git a/Core/Debugger/Breakpoints.h b/Core/Debugger/Breakpoints.h index 1deee65e68..cf4c70a48b 100644 --- a/Core/Debugger/Breakpoints.h +++ b/Core/Debugger/Breakpoints.h @@ -114,11 +114,13 @@ struct MemCheck { } }; -// A breakpoint that trips whenever a general-purpose register is written to by an -// instruction, regardless of address - identified by register index (0-31), not addr/range. +// A breakpoint that trips whenever a register is written to by an instruction, regardless of +// address - identified by register index (0-31), not addr/range. Currently only GPRs are +// supported (reg is a GPR index), but the naming is kept general since this is expected to grow +// to cover other register files too (e.g. FPU registers like $f10). // Interpreter-only for now (see RunUntilDowncountZeroWithChecks in MIPSTables.cpp) - the JITs // don't check this at all, so it has no effect unless running with the plain interpreter core. -struct GPRBreakpoint { +struct RegBreakpoint { int reg = 0; // 0-31, general-purpose register index (matches OUT_RT/OUT_RD/OUT_RA fields). BreakAction result = BREAK_ACTION_IGNORE; @@ -133,7 +135,7 @@ struct GPRBreakpoint { return (result & BREAK_ACTION_PAUSE) != 0; } - bool operator == (const GPRBreakpoint &other) const { + bool operator == (const RegBreakpoint &other) const { return reg == other.reg; } }; @@ -145,7 +147,7 @@ class BreakpointManager { public: static const size_t INVALID_BREAKPOINT = -1; static const size_t INVALID_MEMCHECK = -1; - static const size_t INVALID_GPR_BREAKPOINT = -1; + static const size_t INVALID_REG_BREAKPOINT = -1; bool IsAddressBreakPoint(u32 addr); bool IsAddressBreakPoint(u32 addr, bool* enabled); @@ -183,26 +185,26 @@ public: BreakAction ExecMemCheck(u32 address, bool write, int size, u32 pc, const char *reason); BreakAction ExecOpMemCheck(u32 address, u32 pc); - // GPR write breakpoints - see GPRBreakpoint above. reg is a 0-31 GPR index. - int AddGPRBreakpoint(int reg); // Returns the breakpoint index. - void RemoveGPRBreakpoint(int reg); - void ChangeGPRBreakpoint(int reg, bool enable); - void ChangeGPRBreakpoint(int reg, BreakAction result); - void ClearAllGPRBreakpoints(); + // Register write breakpoints - see RegBreakpoint above. reg is a 0-31 GPR index. + int AddRegBreakpoint(int reg); // Returns the breakpoint index. + void RemoveRegBreakpoint(int reg); + void ChangeRegBreakpoint(int reg, bool enable); + void ChangeRegBreakpoint(int reg, BreakAction result); + void ClearAllRegBreakpoints(); - void ChangeGPRBreakpointAddCond(int reg, const BreakPointCond &cond); - void ChangeGPRBreakpointRemoveCond(int reg); - BreakPointCond *GetGPRBreakpointCondition(int reg); + void ChangeRegBreakpointAddCond(int reg, const BreakPointCond &cond); + void ChangeRegBreakpointRemoveCond(int reg); + BreakPointCond *GetRegBreakpointCondition(int reg); - void ChangeGPRBreakpointLogFormat(int reg, const std::string &fmt); + void ChangeRegBreakpointLogFormat(int reg, const std::string &fmt); - bool IsGPRBreakpoint(int reg); - bool GetGPRBreakpoint(int reg, GPRBreakpoint *bp); - std::vector GetGPRBreakpoints(); + bool IsRegBreakpoint(int reg); + bool GetRegBreakpoint(int reg, RegBreakpoint *bp); + std::vector GetRegBreakpoints(); // Called from the interpreter (RunUntilDowncountZeroWithChecks) right before executing an // instruction that would write to reg - does not itself execute the instruction. - BreakAction ExecGPRBreakpoint(int reg, u32 pc); + BreakAction ExecRegBreakpoint(int reg, u32 pc); void SetSkipFirst(u32 pc); u32 CheckSkipFirst(); @@ -228,14 +230,14 @@ public: bool HasMemChecks() const { return anyMemChecks_; } - bool HasGPRBreakpoints() const { - return gprBreakpointMask_ != 0; + bool HasRegBreakpoints() const { + return regBreakpointMask_ != 0; } - // Bit i set means register i has an active (non-ignored) GPR breakpoint - a cheap way for - // the interpreter's hot per-instruction loop to test "would this write trip anything" with - // a single shift+and, without touching gprBreakpoints_ at all in the common no-match case. - u32 GetGPRBreakpointMask() const { - return gprBreakpointMask_; + // Bit i set means register i has an active (non-ignored) register breakpoint - a cheap way + // for the interpreter's hot per-instruction loop to test "would this write trip anything" + // with a single shift+and, without touching regBreakpoints_ at all in the common no-match case. + u32 GetRegBreakpointMask() const { + return regBreakpointMask_; } void Frame(); @@ -255,12 +257,12 @@ private: // Finds a memcheck covering (part of) a range, unlike FindMemCheck() above. MemCheck *FindMemCheckInRange(u32 address, int size); void UpdateCachedMemCheckRanges(); - size_t FindGPRBreakpoint(int reg); - void RecomputeGPRBreakpointMask(); + size_t FindRegBreakpoint(int reg); + void RecomputeRegBreakpointMask(); std::atomic anyBreakPoints_; std::atomic anyMemChecks_; - std::atomic gprBreakpointMask_; + std::atomic regBreakpointMask_; std::vector breakPoints_; u32 breakSkipFirstAt_ = 0; @@ -270,7 +272,7 @@ private: std::vector memCheckRangesRead_; std::vector memCheckRangesWrite_; - std::vector gprBreakpoints_; + std::vector regBreakpoints_; bool needsUpdate_ = true; u32 updateAddr_ = 0; diff --git a/Core/Debugger/WebSocket/BreakpointSubscriber.cpp b/Core/Debugger/WebSocket/BreakpointSubscriber.cpp index 7c48b7e5ec..0580ea7a9c 100644 --- a/Core/Debugger/WebSocket/BreakpointSubscriber.cpp +++ b/Core/Debugger/WebSocket/BreakpointSubscriber.cpp @@ -36,17 +36,17 @@ DebuggerSubscriber *WebSocketBreakpointInit(DebuggerEventHandlerMap &map) { map["memory.breakpoint.remove"] = &WebSocketMemoryBreakpointRemove; map["memory.breakpoint.list"] = &WebSocketMemoryBreakpointList; - map["cpu.gprBreakpoint.add"] = &WebSocketGPRBreakpointAdd; - map["cpu.gprBreakpoint.update"] = &WebSocketGPRBreakpointUpdate; - map["cpu.gprBreakpoint.remove"] = &WebSocketGPRBreakpointRemove; - map["cpu.gprBreakpoint.list"] = &WebSocketGPRBreakpointList; + map["cpu.regBreakpoint.add"] = &WebSocketRegBreakpointAdd; + map["cpu.regBreakpoint.update"] = &WebSocketRegBreakpointUpdate; + map["cpu.regBreakpoint.remove"] = &WebSocketRegBreakpointRemove; + map["cpu.regBreakpoint.list"] = &WebSocketRegBreakpointList; return nullptr; } // Resolves a GPR by name (e.g. "s3", case-insensitive) or 0-31 index. Interpreter-only feature - -// see GPRBreakpoint in Breakpoints.h - has no effect while running under a JIT backend. -static bool ParseGPRBreakpointReg(DebuggerRequest &req, int *reg) { +// see RegBreakpoint in Breakpoints.h - has no effect while running under a JIT backend. +static bool ParseRegBreakpointReg(DebuggerRequest &req, int *reg) { if (req.HasParam("name")) { std::string name; if (!req.ParamString("name", &name)) @@ -550,7 +550,7 @@ void WebSocketMemoryBreakpointList(DebuggerRequest &req) { }); } -struct WebSocketGPRBreakpointParams { +struct WebSocketRegBreakpointParams { int reg = 0; bool hasEnabled = false; bool hasLog = false; @@ -569,7 +569,7 @@ struct WebSocketGPRBreakpointParams { return false; } - if (!ParseGPRBreakpointReg(req, ®)) + if (!ParseRegBreakpointReg(req, ®)) return false; hasEnabled = req.HasParam("enabled"); @@ -606,18 +606,18 @@ struct WebSocketGPRBreakpointParams { cond.debug = currentDebugMIPS; cond.expressionString = condition; cond.expression = compiledCondition; - g_breakpoints.ChangeGPRBreakpointAddCond(reg, cond); + g_breakpoints.ChangeRegBreakpointAddCond(reg, cond); } else if (hasCondition && condition.empty()) { - g_breakpoints.ChangeGPRBreakpointRemoveCond(reg); + g_breakpoints.ChangeRegBreakpointRemoveCond(reg); } if (hasLogFormat) { - g_breakpoints.ChangeGPRBreakpointLogFormat(reg, logFormat); + g_breakpoints.ChangeRegBreakpointLogFormat(reg, logFormat); } if (hasLog && !hasEnabled) { - GPRBreakpoint bp; - if (g_breakpoints.GetGPRBreakpoint(reg, &bp)) + RegBreakpoint bp; + if (g_breakpoints.GetRegBreakpoint(reg, &bp)) enabled = bp.IsEnabled(); hasEnabled = true; } @@ -627,16 +627,16 @@ struct WebSocketGPRBreakpointParams { result |= BREAK_ACTION_LOG; if (enabled) result |= BREAK_ACTION_PAUSE; - g_breakpoints.ChangeGPRBreakpoint(reg, result); + g_breakpoints.ChangeRegBreakpoint(reg, result); } else if (hasEnabled) { - g_breakpoints.ChangeGPRBreakpoint(reg, enabled); + g_breakpoints.ChangeRegBreakpoint(reg, enabled); } } }; -// Add a new GPR write breakpoint (cpu.gprBreakpoint.add) +// Add a new register write breakpoint (cpu.regBreakpoint.add) // -// Interpreter-only for now - see GPRBreakpoint in Core/Debugger/Breakpoints.h. Has no effect +// Interpreter-only for now - see RegBreakpoint in Core/Debugger/Breakpoints.h. Has no effect // while running under a JIT backend (force the interpreter core, e.g. -i on the command line). // // Parameters: @@ -649,28 +649,28 @@ struct WebSocketGPRBreakpointParams { // // Response (same event name) with no extra data. // -// Note: will replace any GPR breakpoint already set on the same register. -void WebSocketGPRBreakpointAdd(DebuggerRequest &req) { - WebSocketGPRBreakpointParams params; +// Note: will replace any register breakpoint already set on the same register. +void WebSocketRegBreakpointAdd(DebuggerRequest &req) { + WebSocketRegBreakpointParams params; if (!params.Parse(req)) return; // Route the actual breakpoint manipulation to the CPU thread instead of poking at it directly // from this WebSocket handler thread - see Core_RunOnCPUThread() in Core.h. Core_RunOnCPUThread([&] { - g_breakpoints.AddGPRBreakpoint(params.reg); + g_breakpoints.AddRegBreakpoint(params.reg); params.Apply(); }); req.Respond(); } -// Update a GPR write breakpoint (cpu.gprBreakpoint.update) +// Update a register write breakpoint (cpu.regBreakpoint.update) // -// Parameters: same as cpu.gprBreakpoint.add. +// Parameters: same as cpu.regBreakpoint.add. // // Response (same event name) with no extra data. -void WebSocketGPRBreakpointUpdate(DebuggerRequest &req) { - WebSocketGPRBreakpointParams params; +void WebSocketRegBreakpointUpdate(DebuggerRequest &req) { + WebSocketRegBreakpointParams params; if (!params.Parse(req)) return; @@ -678,8 +678,8 @@ void WebSocketGPRBreakpointUpdate(DebuggerRequest &req) { // from this WebSocket handler thread - see Core_RunOnCPUThread() in Core.h. bool found = false; Core_RunOnCPUThread([&] { - GPRBreakpoint bp; - found = g_breakpoints.GetGPRBreakpoint(params.reg, &bp); + RegBreakpoint bp; + found = g_breakpoints.GetRegBreakpoint(params.reg, &bp); if (found) params.Apply(); }); @@ -689,31 +689,31 @@ void WebSocketGPRBreakpointUpdate(DebuggerRequest &req) { req.Respond(); } -// Remove a GPR write breakpoint (cpu.gprBreakpoint.remove) +// Remove a register write breakpoint (cpu.regBreakpoint.remove) // // Parameters: // - register: unsigned integer 0-31 GPR index. Ignored if name given. // - name: string register name (e.g. "s3"), case-insensitive. Takes priority over 'register'. // // Response (same event name) with no extra data. -void WebSocketGPRBreakpointRemove(DebuggerRequest &req) { +void WebSocketRegBreakpointRemove(DebuggerRequest &req) { if (!currentDebugMIPS->isAlive()) { return req.Fail("CPU not started"); } int reg; - if (!ParseGPRBreakpointReg(req, ®)) + if (!ParseRegBreakpointReg(req, ®)) return; // Route the actual breakpoint manipulation to the CPU thread instead of poking at it directly // from this WebSocket handler thread - see Core_RunOnCPUThread() in Core.h. Core_RunOnCPUThread([&] { - g_breakpoints.RemoveGPRBreakpoint(reg); + g_breakpoints.RemoveRegBreakpoint(reg); }); req.Respond(); } -// List all GPR write breakpoints (cpu.gprBreakpoint.list) +// List all register write breakpoints (cpu.regBreakpoint.list) // // No parameters. // @@ -727,7 +727,7 @@ void WebSocketGPRBreakpointRemove(DebuggerRequest &req) { // whether it paused - i.e. even with enabled false, if log is true.) // - condition: null, or string expression to evaluate - breakpoint does not trip if false. // - logFormat: null, or string to log when breakpoint trips, may include {expression} parts. -void WebSocketGPRBreakpointList(DebuggerRequest &req) { +void WebSocketRegBreakpointList(DebuggerRequest &req) { if (!currentDebugMIPS->isAlive()) { return req.Fail("CPU not started"); } @@ -737,8 +737,8 @@ void WebSocketGPRBreakpointList(DebuggerRequest &req) { Core_RunOnCPUThread([&] { JsonWriter &json = req.Respond(); json.pushArray("breakpoints"); - std::vector bps = g_breakpoints.GetGPRBreakpoints(); - for (const GPRBreakpoint &bp : bps) { + std::vector bps = g_breakpoints.GetRegBreakpoints(); + for (const RegBreakpoint &bp : bps) { json.pushDict(); json.writeInt("register", bp.reg); json.writeString("name", MIPSDebugInterface::GetRegName(0, bp.reg)); diff --git a/Core/Debugger/WebSocket/BreakpointSubscriber.h b/Core/Debugger/WebSocket/BreakpointSubscriber.h index 367985b970..9f70e0337e 100644 --- a/Core/Debugger/WebSocket/BreakpointSubscriber.h +++ b/Core/Debugger/WebSocket/BreakpointSubscriber.h @@ -31,7 +31,7 @@ void WebSocketMemoryBreakpointUpdate(DebuggerRequest &req); void WebSocketMemoryBreakpointRemove(DebuggerRequest &req); void WebSocketMemoryBreakpointList(DebuggerRequest &req); -void WebSocketGPRBreakpointAdd(DebuggerRequest &req); -void WebSocketGPRBreakpointUpdate(DebuggerRequest &req); -void WebSocketGPRBreakpointRemove(DebuggerRequest &req); -void WebSocketGPRBreakpointList(DebuggerRequest &req); +void WebSocketRegBreakpointAdd(DebuggerRequest &req); +void WebSocketRegBreakpointUpdate(DebuggerRequest &req); +void WebSocketRegBreakpointRemove(DebuggerRequest &req); +void WebSocketRegBreakpointList(DebuggerRequest &req); diff --git a/Core/MIPS/MIPSTables.cpp b/Core/MIPS/MIPSTables.cpp index f32b2ecb95..bf3fc0e861 100644 --- a/Core/MIPS/MIPSTables.cpp +++ b/Core/MIPS/MIPSTables.cpp @@ -1158,8 +1158,8 @@ static inline int GetGPRWriteTarget(const MIPSInstruction *instr, MIPSOpcode op) static void RunUntilDowncountZeroWithChecks(MIPSState *mips, u64 globalTicks) { bool hasBPs = g_breakpoints.HasBreakPoints(); bool hasMCs = g_breakpoints.HasMemChecks(); - // Bit i set means register i has an active GPR breakpoint - see GetGPRBreakpointMask(). - u32 gprBPMask = g_breakpoints.GetGPRBreakpointMask(); + // Bit i set means register i has an active register breakpoint - see GetRegBreakpointMask(). + u32 regBPMask = g_breakpoints.GetRegBreakpointMask(); while (mips->downcount >= 0 && coreState == CORE_RUNNING_CPU) { // Don't stop in a delay slot! Well, unless we hit a memcheck in one, of course. do { @@ -1196,10 +1196,10 @@ static void RunUntilDowncountZeroWithChecks(MIPSState *mips, u64 globalTicks) { if (coreState == CORE_STEPPING_CPU) break; } - if (gprBPMask != 0 && (instr->flags & (OUT_RT | OUT_RD | OUT_RA)) != 0 && g_breakpoints.CheckSkipFirst() != mips->pc) { - int gprTarget = GetGPRWriteTarget(instr, op); - if (gprTarget >= 0 && (gprBPMask & (1u << gprTarget)) != 0) { - g_breakpoints.ExecGPRBreakpoint(gprTarget, mips->pc); + if (regBPMask != 0 && (instr->flags & (OUT_RT | OUT_RD | OUT_RA)) != 0 && g_breakpoints.CheckSkipFirst() != mips->pc) { + int regTarget = GetGPRWriteTarget(instr, op); + if (regTarget >= 0 && (regBPMask & (1u << regTarget)) != 0) { + g_breakpoints.ExecRegBreakpoint(regTarget, mips->pc); // If it tripped, bail without running - same convention as memchecks above. if (coreState == CORE_STEPPING_CPU) break; @@ -1228,7 +1228,7 @@ int MIPSInterpret_RunUntil(MIPSState *mips, u64 globalTicks) { CoreTiming::Advance(mips); uint64_t ticksLeft = globalTicks - CoreTiming::GetTicks(mips); - if (g_breakpoints.HasBreakPoints() || g_breakpoints.HasMemChecks() || g_breakpoints.HasGPRBreakpoints() || ticksLeft <= mips->downcount) { + if (g_breakpoints.HasBreakPoints() || g_breakpoints.HasMemChecks() || g_breakpoints.HasRegBreakpoints() || ticksLeft <= mips->downcount) { RunUntilDowncountZeroWithChecks(mips, globalTicks); } else { RunUntilDowncountZeroFast(mips); diff --git a/docs/WebSocketDebugger.md b/docs/WebSocketDebugger.md index f208c34a87..8ddaf456fc 100644 --- a/docs/WebSocketDebugger.md +++ b/docs/WebSocketDebugger.md @@ -105,7 +105,7 @@ file - this is just an index. | Game/version | `game.reset`, `game.status`, `version` | `GameSubscriber.cpp` | | CPU core | `cpu.stepping`, `cpu.resume`, `cpu.status`, `cpu.getAllRegs`, `cpu.getReg`, `cpu.setReg`, `cpu.evaluate` | `CPUCoreSubscriber.cpp` | | Stepping | `cpu.stepInto`, `cpu.stepOver`, `cpu.stepOut`, `cpu.runUntil`, `cpu.nextHLE` | `SteppingSubscriber.cpp` | -| Breakpoints | `cpu.breakpoint.add/update/remove/list`, `memory.breakpoint.add/update/remove/list`, `cpu.gprBreakpoint.add/update/remove/list` (break when a GPR is written to, by any instruction anywhere - interpreter-only, no effect under a JIT backend) | `BreakpointSubscriber.cpp` | +| Breakpoints | `cpu.breakpoint.add/update/remove/list`, `memory.breakpoint.add/update/remove/list`, `cpu.regBreakpoint.add/update/remove/list` (break when a register is written to, by any instruction anywhere - currently GPRs only; interpreter-only, no effect under a JIT backend) | `BreakpointSubscriber.cpp` | | Memory read/write | `memory.read_u8/u16/u32`, `memory.read`, `memory.readString`, `memory.write_u8/u16/u32`, `memory.write` | `MemorySubscriber.cpp` | | Memory search | `memory.search` - scan a range for a `u8`/`u16`/`u32`/`float` value or a `bytes` pattern (with an optional wildcard mask), for narrowing down where an unknown value lives (Cheat Engine style) | `MemorySubscriber.cpp` | | Memory info/annotations | `memory.mapping`, `memory.info.config/set/list/search` | `MemoryInfoSubscriber.cpp` |