diff --git a/Core/Debugger/Breakpoints.cpp b/Core/Debugger/Breakpoints.cpp index c5515f4f34..d1b293c578 100644 --- a/Core/Debugger/Breakpoints.cpp +++ b/Core/Debugger/Breakpoints.cpp @@ -452,15 +452,28 @@ void CBreakPoints::ChangeMemCheckLogFormat(u32 start, u32 end, const std::string } } +bool CBreakPoints::GetMemCheck(u32 start, u32 end, MemCheck *check) { + std::lock_guard guard(memCheckMutex_); + size_t mc = FindMemCheck(start, end); + if (mc != INVALID_MEMCHECK) { + *check = memChecks_[mc]; + return true; + } + return false; +} + static inline u32 NotCached(u32 val) { // Remove the cached part of the address. return val & ~0x40000000; } -MemCheck *CBreakPoints::GetMemCheck(u32 address, int size) { +bool CBreakPoints::GetMemCheckInRange(u32 address, int size, MemCheck *check) { std::lock_guard guard(memCheckMutex_); - return GetMemCheckLocked(address, size); + auto result = GetMemCheckLocked(address, size); + if (result) + *check = *result; + return result != nullptr; } MemCheck *CBreakPoints::GetMemCheckLocked(u32 address, int size) { diff --git a/Core/Debugger/Breakpoints.h b/Core/Debugger/Breakpoints.h index 5c58042904..45f91d3cc8 100644 --- a/Core/Debugger/Breakpoints.h +++ b/Core/Debugger/Breakpoints.h @@ -149,7 +149,8 @@ public: static void ChangeMemCheckLogFormat(u32 start, u32 end, const std::string &fmt); - static MemCheck *GetMemCheck(u32 address, int size); + static bool GetMemCheck(u32 start, u32 end, MemCheck *check); + static bool GetMemCheckInRange(u32 address, int size, MemCheck *check); static BreakAction ExecMemCheck(u32 address, bool write, int size, u32 pc); static BreakAction ExecOpMemCheck(u32 address, u32 pc); diff --git a/Core/Debugger/DisassemblyManager.cpp b/Core/Debugger/DisassemblyManager.cpp index 660ce2aa89..c5ab4b2e91 100644 --- a/Core/Debugger/DisassemblyManager.cpp +++ b/Core/Debugger/DisassemblyManager.cpp @@ -346,7 +346,6 @@ u32 DisassemblyManager::getNthNextAddress(u32 address, int n) } DisassemblyManager::~DisassemblyManager() { - clear(); } void DisassemblyManager::clear() diff --git a/Core/Debugger/WebSocket/BreakpointSubscriber.cpp b/Core/Debugger/WebSocket/BreakpointSubscriber.cpp index d93bd34051..aa6681cb82 100644 --- a/Core/Debugger/WebSocket/BreakpointSubscriber.cpp +++ b/Core/Debugger/WebSocket/BreakpointSubscriber.cpp @@ -17,6 +17,8 @@ #include "Common/StringUtils.h" #include "Core/Debugger/Breakpoints.h" +#include "Core/Debugger/DisassemblyManager.h" +#include "Core/Debugger/SymbolMap.h" #include "Core/Debugger/WebSocket/BreakpointSubscriber.h" #include "Core/Debugger/WebSocket/WebSocketUtils.h" #include "Core/MIPS/MIPSDebugInterface.h" @@ -28,6 +30,11 @@ void *WebSocketBreakpointInit(DebuggerEventHandlerMap &map) { map["cpu.breakpoint.remove"] = &WebSocketCPUBreakpointRemove; map["cpu.breakpoint.list"] = &WebSocketCPUBreakpointList; + map["memory.breakpoint.add"] = &WebSocketMemoryBreakpointAdd; + map["memory.breakpoint.update"] = &WebSocketMemoryBreakpointUpdate; + map["memory.breakpoint.remove"] = &WebSocketMemoryBreakpointRemove; + map["memory.breakpoint.list"] = &WebSocketMemoryBreakpointList; + return nullptr; } @@ -57,12 +64,12 @@ struct WebSocketCPUBreakpointParams { if (hasEnabled) { if (!req.ParamBool("enabled", &enabled)) return false; - } + } hasLog = req.HasParam("log"); if (hasLog) { if (!req.ParamBool("log", &log)) return false; - } + } hasCondition = req.HasParam("condition"); if (hasCondition) { if (!req.ParamString("condition", &condition)) @@ -173,6 +180,172 @@ void WebSocketCPUBreakpointList(DebuggerRequest &req) { json.writeString("logFormat", bp.logFormat); else json.writeNull("logFormat"); + std::string symbol = g_symbolMap->GetLabelString(bp.addr); + if (symbol.empty()) + json.writeNull("symbol"); + else + json.writeString("symbol", symbol); + + DisassemblyManager manager; + DisassemblyLineInfo line; + manager.getLine(manager.getStartAddress(bp.addr), true, line); + json.writeString("code", line.name + " " + line.params); + + json.pop(); + } + json.pop(); +} + +struct WebSocketMemoryBreakpointParams { + uint32_t address = 0; + uint32_t end = 0; + bool hasEnabled = false; + bool hasLog = false; + bool hasCond = false; + bool hasLogFormat = false; + + bool enabled = true; + bool log = true; + MemCheckCondition cond = MEMCHECK_READWRITE; + std::string logFormat; + + bool Parse(DebuggerRequest &req) { + if (!currentDebugMIPS->isAlive()) { + req.Fail("CPU not started"); + return false; + } + + if (!req.ParamU32("address", &address)) + return false; + uint32_t size; + if (!req.ParamU32("size", &size)) + return false; + if (address + size < address) { + req.Fail("Size is too large"); + return false; + } + end = size == 0 ? 0 : address + size; + + hasEnabled = req.HasParam("enabled"); + if (hasEnabled) { + if (!req.ParamBool("enabled", &enabled)) + return false; + } + hasLog = req.HasParam("log"); + if (hasLog) { + if (!req.ParamBool("log", &log)) + return false; + } + hasCond = req.HasParam("read") || req.HasParam("write") || req.HasParam("change"); + if (hasCond) { + bool read, write, change; + if (!req.ParamBool("read", &read) || !req.ParamBool("write", &write) || !req.ParamBool("change", &change)) + return false; + int bits = (read ? MEMCHECK_READ : 0) | (write ? MEMCHECK_WRITE : 0) | (change ? MEMCHECK_WRITE_ONCHANGE : 0); + cond = MemCheckCondition(bits); + } + hasLogFormat = req.HasParam("logFormat"); + if (hasLogFormat) { + if (!req.ParamString("logFormat", &logFormat)) + return false; + } + + return true; + } + + BreakAction Result(bool adding) { + int bits = MEMCHECK_READWRITE; + if (adding || (hasLog && hasEnabled)) { + bits = (enabled ? BREAK_ACTION_PAUSE : 0) | (log ? BREAK_ACTION_LOG : 0); + } else { + MemCheck prev; + if (CBreakPoints::GetMemCheck(address, end, &prev)) + bits = prev.result; + + if (hasEnabled) + bits = (bits & ~BREAK_ACTION_PAUSE) | (enabled ? BREAK_ACTION_PAUSE : 0); + if (hasLog) + bits = (bits & ~BREAK_ACTION_LOG) | (log ? BREAK_ACTION_LOG : 0); + } + + return BreakAction(bits); + } + + void Apply() { + if (hasLogFormat) { + CBreakPoints::ChangeMemCheckLogFormat(address, end, logFormat); + } + } +}; + +void WebSocketMemoryBreakpointAdd(DebuggerRequest &req) { + WebSocketMemoryBreakpointParams params; + if (!params.Parse(req)) + return; + + CBreakPoints::AddMemCheck(params.address, params.end, params.cond, params.Result(true)); + params.Apply(); + req.Respond(); +} + +void WebSocketMemoryBreakpointUpdate(DebuggerRequest &req) { + WebSocketMemoryBreakpointParams params; + if (!params.Parse(req)) + return; + + MemCheck mc; + if (!CBreakPoints::GetMemCheck(params.address, params.end, &mc)) + return req.Fail("Breakpoint not found"); + + CBreakPoints::ChangeMemCheck(params.address, params.end, params.cond, params.Result(true)); + params.Apply(); + req.Respond(); +} + +void WebSocketMemoryBreakpointRemove(DebuggerRequest &req) { + if (!currentDebugMIPS->isAlive()) { + return req.Fail("CPU not started"); + } + + uint32_t address; + if (!req.ParamU32("address", &address)) + return; + uint32_t size; + if (!req.ParamU32("size", &size)) + return; + + CBreakPoints::RemoveMemCheck(address, size == 0 ? 0 : address + size); + req.Respond(); +} + +void WebSocketMemoryBreakpointList(DebuggerRequest &req) { + if (!currentDebugMIPS->isAlive()) { + return req.Fail("CPU not started"); + } + + JsonWriter &json = req.Respond(); + json.pushArray("breakpoints"); + auto mcs = CBreakPoints::GetMemChecks(); + for (const auto &mc : mcs) { + json.pushDict(); + json.writeUint("address", mc.start); + json.writeUint("size", mc.end == 0 ? 0 : mc.end - mc.start); + json.writeBool("enabled", mc.IsEnabled()); + json.writeBool("log", (mc.result & BREAK_ACTION_LOG) != 0); + json.writeBool("read", (mc.cond & MEMCHECK_READ) != 0); + json.writeBool("write", (mc.cond & MEMCHECK_WRITE) != 0); + json.writeBool("change", (mc.cond & MEMCHECK_WRITE_ONCHANGE) != 0); + json.writeUint("hits", mc.numHits); + if (!mc.logFormat.empty()) + json.writeString("logFormat", mc.logFormat); + else + json.writeNull("logFormat"); + std::string symbol = g_symbolMap->GetLabelString(mc.start); + if (symbol.empty()) + json.writeNull("symbol"); + else + json.writeString("symbol", symbol); + json.pop(); } json.pop(); diff --git a/Core/Debugger/WebSocket/BreakpointSubscriber.h b/Core/Debugger/WebSocket/BreakpointSubscriber.h index eccf23c3cb..c9c2e1effc 100644 --- a/Core/Debugger/WebSocket/BreakpointSubscriber.h +++ b/Core/Debugger/WebSocket/BreakpointSubscriber.h @@ -25,3 +25,8 @@ void WebSocketCPUBreakpointAdd(DebuggerRequest &req); void WebSocketCPUBreakpointUpdate(DebuggerRequest &req); void WebSocketCPUBreakpointRemove(DebuggerRequest &req); void WebSocketCPUBreakpointList(DebuggerRequest &req); + +void WebSocketMemoryBreakpointAdd(DebuggerRequest &req); +void WebSocketMemoryBreakpointUpdate(DebuggerRequest &req); +void WebSocketMemoryBreakpointRemove(DebuggerRequest &req); +void WebSocketMemoryBreakpointList(DebuggerRequest &req); diff --git a/Core/Debugger/WebSocket/DisasmSubscriber.cpp b/Core/Debugger/WebSocket/DisasmSubscriber.cpp index 6f928c4a7e..95dddcebb8 100644 --- a/Core/Debugger/WebSocket/DisasmSubscriber.cpp +++ b/Core/Debugger/WebSocket/DisasmSubscriber.cpp @@ -31,6 +31,9 @@ struct WebSocketDisasmState { WebSocketDisasmState() { disasm_.setCpu(currentDebugMIPS); } + ~WebSocketDisasmState() { + disasm_.clear(); + } void Base(DebuggerRequest &req); void Disasm(DebuggerRequest &req); @@ -324,6 +327,16 @@ void WebSocketDisasmState::Disasm(DebuggerRequest &req) { json.pop(); } +// Search disassembly for some text (cpu.searchDisasm) +// +// Parameters: +// - address: starting address as a number. +// - end: optional end address as a number (otherwise uses start.) +// - match: string to search for. +// - displaySymbols: optional, specify false to hide symbols in the searched parameters. +// +// Response (same event name): +// - address: number address of match or null if none was found. void WebSocketDisasmState::SearchDisasm(DebuggerRequest &req) { if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) { return req.Fail("CPU not started"); @@ -393,6 +406,14 @@ void WebSocketDisasmState::SearchDisasm(DebuggerRequest &req) { json.writeNull("address"); } +// Assemble an instruction (cpu.assemble) +// +// Parameters: +// - address: number indicating the address to write to. +// - code: string containing the instruction to assemble. +// +// Response (same event name): +// - encoding: resulting encoding at this address. Always returns one value, even for macros. void WebSocketDisasmState::Assemble(DebuggerRequest &req) { if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) { return req.Fail("CPU not started"); diff --git a/Core/Debugger/WebSocket/SteppingSubscriber.cpp b/Core/Debugger/WebSocket/SteppingSubscriber.cpp index 3b3842353a..062064d9cd 100644 --- a/Core/Debugger/WebSocket/SteppingSubscriber.cpp +++ b/Core/Debugger/WebSocket/SteppingSubscriber.cpp @@ -32,6 +32,9 @@ struct WebSocketSteppingState { WebSocketSteppingState() { disasm_.setCpu(currentDebugMIPS); } + ~WebSocketSteppingState() { + disasm_.clear(); + } void Into(DebuggerRequest &req); void Over(DebuggerRequest &req); diff --git a/Core/MIPS/x86/JitSafeMem.cpp b/Core/MIPS/x86/JitSafeMem.cpp index 104b6579a0..676f8b1ced 100644 --- a/Core/MIPS/x86/JitSafeMem.cpp +++ b/Core/MIPS/x86/JitSafeMem.cpp @@ -346,14 +346,12 @@ void JitSafeMem::Finish() jit_->SetJumpTarget(*it); } -void JitSafeMem::MemCheckImm(MemoryOpType type) -{ - MemCheck *check = CBreakPoints::GetMemCheck(iaddr_, size_); - if (check) - { - if (!(check->cond & MEMCHECK_READ) && type == MEM_READ) +void JitSafeMem::MemCheckImm(MemoryOpType type) { + MemCheck check; + if (CBreakPoints::GetMemCheckInRange(iaddr_, size_, &check)) { + if (!(check.cond & MEMCHECK_READ) && type == MEM_READ) return; - if (!(check->cond & MEMCHECK_WRITE) && type == MEM_WRITE) + if (!(check.cond & MEMCHECK_WRITE) && type == MEM_WRITE) return; jit_->MOV(32, MIPSSTATE_VAR(pc), Imm32(jit_->GetCompilerPC())); diff --git a/Windows/Debugger/CtrlDisAsmView.cpp b/Windows/Debugger/CtrlDisAsmView.cpp index fecc7de397..7b8ea04406 100644 --- a/Windows/Debugger/CtrlDisAsmView.cpp +++ b/Windows/Debugger/CtrlDisAsmView.cpp @@ -183,6 +183,7 @@ CtrlDisAsmView::~CtrlDisAsmView() { DeleteObject(font); DeleteObject(boldfont); + manager.clear(); } COLORREF scaleColor(COLORREF color, float factor)