Skip to content

Commit

Permalink
Debugger: Add memory breakpoint management.
Browse files Browse the repository at this point in the history
  • Loading branch information
unknownbrackets committed Jun 8, 2018
1 parent 29d93c5 commit b2cc4a0
Show file tree
Hide file tree
Showing 9 changed files with 227 additions and 13 deletions.
17 changes: 15 additions & 2 deletions Core/Debugger/Breakpoints.cpp
Expand Up @@ -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<std::mutex> 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) static inline u32 NotCached(u32 val)
{ {
// Remove the cached part of the address. // Remove the cached part of the address.
return val & ~0x40000000; return val & ~0x40000000;
} }


MemCheck *CBreakPoints::GetMemCheck(u32 address, int size) { bool CBreakPoints::GetMemCheckInRange(u32 address, int size, MemCheck *check) {
std::lock_guard<std::mutex> guard(memCheckMutex_); std::lock_guard<std::mutex> 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) { MemCheck *CBreakPoints::GetMemCheckLocked(u32 address, int size) {
Expand Down
3 changes: 2 additions & 1 deletion Core/Debugger/Breakpoints.h
Expand Up @@ -149,7 +149,8 @@ class CBreakPoints


static void ChangeMemCheckLogFormat(u32 start, u32 end, const std::string &fmt); 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 ExecMemCheck(u32 address, bool write, int size, u32 pc);
static BreakAction ExecOpMemCheck(u32 address, u32 pc); static BreakAction ExecOpMemCheck(u32 address, u32 pc);


Expand Down
1 change: 0 additions & 1 deletion Core/Debugger/DisassemblyManager.cpp
Expand Up @@ -346,7 +346,6 @@ u32 DisassemblyManager::getNthNextAddress(u32 address, int n)
} }


DisassemblyManager::~DisassemblyManager() { DisassemblyManager::~DisassemblyManager() {
clear();
} }


void DisassemblyManager::clear() void DisassemblyManager::clear()
Expand Down
177 changes: 175 additions & 2 deletions Core/Debugger/WebSocket/BreakpointSubscriber.cpp
Expand Up @@ -17,6 +17,8 @@


#include "Common/StringUtils.h" #include "Common/StringUtils.h"
#include "Core/Debugger/Breakpoints.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/BreakpointSubscriber.h"
#include "Core/Debugger/WebSocket/WebSocketUtils.h" #include "Core/Debugger/WebSocket/WebSocketUtils.h"
#include "Core/MIPS/MIPSDebugInterface.h" #include "Core/MIPS/MIPSDebugInterface.h"
Expand All @@ -28,6 +30,11 @@ void *WebSocketBreakpointInit(DebuggerEventHandlerMap &map) {
map["cpu.breakpoint.remove"] = &WebSocketCPUBreakpointRemove; map["cpu.breakpoint.remove"] = &WebSocketCPUBreakpointRemove;
map["cpu.breakpoint.list"] = &WebSocketCPUBreakpointList; 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; return nullptr;
} }


Expand Down Expand Up @@ -57,12 +64,12 @@ struct WebSocketCPUBreakpointParams {
if (hasEnabled) { if (hasEnabled) {
if (!req.ParamBool("enabled", &enabled)) if (!req.ParamBool("enabled", &enabled))
return false; return false;
} }
hasLog = req.HasParam("log"); hasLog = req.HasParam("log");
if (hasLog) { if (hasLog) {
if (!req.ParamBool("log", &log)) if (!req.ParamBool("log", &log))
return false; return false;
} }
hasCondition = req.HasParam("condition"); hasCondition = req.HasParam("condition");
if (hasCondition) { if (hasCondition) {
if (!req.ParamString("condition", &condition)) if (!req.ParamString("condition", &condition))
Expand Down Expand Up @@ -173,6 +180,172 @@ void WebSocketCPUBreakpointList(DebuggerRequest &req) {
json.writeString("logFormat", bp.logFormat); json.writeString("logFormat", bp.logFormat);
else else
json.writeNull("logFormat"); 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();
} }
json.pop(); json.pop();
Expand Down
5 changes: 5 additions & 0 deletions Core/Debugger/WebSocket/BreakpointSubscriber.h
Expand Up @@ -25,3 +25,8 @@ void WebSocketCPUBreakpointAdd(DebuggerRequest &req);
void WebSocketCPUBreakpointUpdate(DebuggerRequest &req); void WebSocketCPUBreakpointUpdate(DebuggerRequest &req);
void WebSocketCPUBreakpointRemove(DebuggerRequest &req); void WebSocketCPUBreakpointRemove(DebuggerRequest &req);
void WebSocketCPUBreakpointList(DebuggerRequest &req); void WebSocketCPUBreakpointList(DebuggerRequest &req);

void WebSocketMemoryBreakpointAdd(DebuggerRequest &req);
void WebSocketMemoryBreakpointUpdate(DebuggerRequest &req);
void WebSocketMemoryBreakpointRemove(DebuggerRequest &req);
void WebSocketMemoryBreakpointList(DebuggerRequest &req);
21 changes: 21 additions & 0 deletions Core/Debugger/WebSocket/DisasmSubscriber.cpp
Expand Up @@ -31,6 +31,9 @@ struct WebSocketDisasmState {
WebSocketDisasmState() { WebSocketDisasmState() {
disasm_.setCpu(currentDebugMIPS); disasm_.setCpu(currentDebugMIPS);
} }
~WebSocketDisasmState() {
disasm_.clear();
}


void Base(DebuggerRequest &req); void Base(DebuggerRequest &req);
void Disasm(DebuggerRequest &req); void Disasm(DebuggerRequest &req);
Expand Down Expand Up @@ -324,6 +327,16 @@ void WebSocketDisasmState::Disasm(DebuggerRequest &req) {
json.pop(); 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) { void WebSocketDisasmState::SearchDisasm(DebuggerRequest &req) {
if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) { if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) {
return req.Fail("CPU not started"); return req.Fail("CPU not started");
Expand Down Expand Up @@ -393,6 +406,14 @@ void WebSocketDisasmState::SearchDisasm(DebuggerRequest &req) {
json.writeNull("address"); 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) { void WebSocketDisasmState::Assemble(DebuggerRequest &req) {
if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) { if (!currentDebugMIPS->isAlive() || !Memory::IsActive()) {
return req.Fail("CPU not started"); return req.Fail("CPU not started");
Expand Down
3 changes: 3 additions & 0 deletions Core/Debugger/WebSocket/SteppingSubscriber.cpp
Expand Up @@ -32,6 +32,9 @@ struct WebSocketSteppingState {
WebSocketSteppingState() { WebSocketSteppingState() {
disasm_.setCpu(currentDebugMIPS); disasm_.setCpu(currentDebugMIPS);
} }
~WebSocketSteppingState() {
disasm_.clear();
}


void Into(DebuggerRequest &req); void Into(DebuggerRequest &req);
void Over(DebuggerRequest &req); void Over(DebuggerRequest &req);
Expand Down
12 changes: 5 additions & 7 deletions Core/MIPS/x86/JitSafeMem.cpp
Expand Up @@ -346,14 +346,12 @@ void JitSafeMem::Finish()
jit_->SetJumpTarget(*it); jit_->SetJumpTarget(*it);
} }


void JitSafeMem::MemCheckImm(MemoryOpType type) void JitSafeMem::MemCheckImm(MemoryOpType type) {
{ MemCheck check;
MemCheck *check = CBreakPoints::GetMemCheck(iaddr_, size_); if (CBreakPoints::GetMemCheckInRange(iaddr_, size_, &check)) {
if (check) if (!(check.cond & MEMCHECK_READ) && type == MEM_READ)
{
if (!(check->cond & MEMCHECK_READ) && type == MEM_READ)
return; return;
if (!(check->cond & MEMCHECK_WRITE) && type == MEM_WRITE) if (!(check.cond & MEMCHECK_WRITE) && type == MEM_WRITE)
return; return;


jit_->MOV(32, MIPSSTATE_VAR(pc), Imm32(jit_->GetCompilerPC())); jit_->MOV(32, MIPSSTATE_VAR(pc), Imm32(jit_->GetCompilerPC()));
Expand Down
1 change: 1 addition & 0 deletions Windows/Debugger/CtrlDisAsmView.cpp
Expand Up @@ -183,6 +183,7 @@ CtrlDisAsmView::~CtrlDisAsmView()
{ {
DeleteObject(font); DeleteObject(font);
DeleteObject(boldfont); DeleteObject(boldfont);
manager.clear();
} }


COLORREF scaleColor(COLORREF color, float factor) COLORREF scaleColor(COLORREF color, float factor)
Expand Down

0 comments on commit b2cc4a0

Please sign in to comment.