From db2d248b4a678a6e17820ddbca8bcd1c93146124 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 13 Aug 2026 16:09:51 +0200 Subject: [PATCH] Rename GPRBreakpoint/gprBreakpoint to RegBreakpoint/regBreakpoint The struct and its API only handle GPR indices today, but the naming should stay general since this is expected to grow to cover other register files too (e.g. FPU registers like $f10). Pure rename - no behavior change: - Core/Debugger/Breakpoints.{h,cpp}: RegBreakpoint struct, all BreakpointManager Add/Remove/Change/Get/Exec/Has/Find*RegBreakpoint* methods, regBreakpoints_/regBreakpointMask_ members. - Core/Core.{h,cpp}: BreakReason::RegBreakpoint, "cpu.regBreakpoint" break-reason string. - Core/Debugger/WebSocket/BreakpointSubscriber.{h,cpp}: WebSocket events cpu.gprBreakpoint.* -> cpu.regBreakpoint.*, matching Add/Update/Remove/List handlers and params struct. - Core/MIPS/MIPSTables.cpp: local variable names in the interpreter's per-instruction breakpoint check. - docs/WebSocketDebugger.md updated to match. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9 --- Core/Core.cpp | 2 +- Core/Core.h | 2 +- Core/Debugger/Breakpoints.cpp | 144 +++++++++--------- Core/Debugger/Breakpoints.h | 62 ++++---- .../WebSocket/BreakpointSubscriber.cpp | 70 ++++----- .../Debugger/WebSocket/BreakpointSubscriber.h | 8 +- Core/MIPS/MIPSTables.cpp | 14 +- docs/WebSocketDebugger.md | 2 +- 8 files changed, 153 insertions(+), 151 deletions(-) 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` |