mirror of https://github.com/mgba-emu/mgba.git
Add range watchpoints.
These are accessible via the following new CLI debugger commands: - rw: watchr minAddr maxAddr [cond] - r: watchr/r minAddr maxAddr [cond] - w: watchr/w minAddr maxAddr [cond] - c: watchr/c minAddr maxAddr [cond] This also makes all watchpoints range watchpoints under the hood. Preliminary benchmark results: Time taken to run 10000 frames of Megaman Battle Network 1 (U) with a write watchpoint set at 0x02000000 in milliseconds, 10 runs each: control (no watchpoint): [4184, 4185, 4197, 4207, 4220, 4178, 4304, 4226, 4234, 4292] mean = 4223, stdev = 43.95 old (single address watchpoint): [4743, 4685, 4679, 4670, 4782, 4704, 4698, 4875, 4746, 4718] mean = 4730, stdev = 61.67 new (range watchpoint): [4683, 4691, 4693, 4706, 4782, 4674, 4746, 4768, 4770, 4776] mean = 4728, stdev = 43.36
This commit is contained in:
parent
5415cd72e2
commit
42f7876731
1
CHANGES
1
CHANGES
|
@ -1,6 +1,7 @@
|
|||
0.11.0: (Future)
|
||||
Features:
|
||||
- New unlicensed GB mappers: NT (older types 1 and 2), Li Cheng
|
||||
- Debugger: Add range watchpoints
|
||||
Other fixes:
|
||||
- Qt: Manually split filename to avoid overzealous splitting (fixes mgba.io/i/2681)
|
||||
- Qt: Expand criteria for tag branch names (fixes mgba.io/i/2679)
|
||||
|
|
|
@ -88,8 +88,9 @@ struct mBreakpoint {
|
|||
|
||||
struct mWatchpoint {
|
||||
ssize_t id;
|
||||
uint32_t address;
|
||||
int segment;
|
||||
uint32_t minAddress;
|
||||
uint32_t maxAddress;
|
||||
enum mWatchpointType type;
|
||||
struct ParseTree* condition;
|
||||
};
|
||||
|
|
|
@ -92,12 +92,13 @@ CREATE_MULTIPLE_WATCHPOINT_SHIM(storeMultiple, WATCHPOINT_WRITE)
|
|||
CREATE_SHIM(setActiveRegion, void, (struct ARMCore* cpu, uint32_t address), address)
|
||||
|
||||
static bool _checkWatchpoints(struct ARMDebugger* debugger, uint32_t address, struct mDebuggerEntryInfo* info, enum mWatchpointType type, uint32_t newValue, int width) {
|
||||
--width;
|
||||
struct mWatchpoint* watchpoint;
|
||||
size_t i;
|
||||
uint32_t minAddress = address & ~(width - 1);
|
||||
uint32_t maxAddress = minAddress + width;
|
||||
for (i = 0; i < mWatchpointListSize(&debugger->watchpoints); ++i) {
|
||||
watchpoint = mWatchpointListGetPointer(&debugger->watchpoints, i);
|
||||
if (!((watchpoint->address ^ address) & ~width) && watchpoint->type & type) {
|
||||
if (watchpoint->type & type && watchpoint->minAddress < maxAddress && minAddress < watchpoint->maxAddress) {
|
||||
if (watchpoint->condition) {
|
||||
int32_t value;
|
||||
int segment;
|
||||
|
@ -107,7 +108,7 @@ static bool _checkWatchpoints(struct ARMDebugger* debugger, uint32_t address, st
|
|||
}
|
||||
|
||||
uint32_t oldValue;
|
||||
switch (width + 1) {
|
||||
switch (width) {
|
||||
case 1:
|
||||
oldValue = debugger->originalMemory.load8(debugger->cpu, address, 0);
|
||||
break;
|
||||
|
|
|
@ -60,6 +60,10 @@ static void _setReadWriteWatchpoint(struct CLIDebugger*, struct CLIDebugVector*)
|
|||
static void _setReadWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _setWriteWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _setWriteChangedWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _setReadWriteRangeWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _setReadRangeWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _setWriteRangeWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _setWriteChangedRangeWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _listWatchpoints(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _trace(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _writeByte(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
|
@ -114,6 +118,10 @@ static struct CLIDebuggerCommandSummary _debuggerCommands[] = {
|
|||
{ "watch/c", _setWriteChangedWatchpoint, "Is", "Set a change watchpoint" },
|
||||
{ "watch/r", _setReadWatchpoint, "Is", "Set a read watchpoint" },
|
||||
{ "watch/w", _setWriteWatchpoint, "Is", "Set a write watchpoint" },
|
||||
{ "watch-range", _setReadWriteRangeWatchpoint, "IIs", "Set a range watchpoint" },
|
||||
{ "watch-range/c", _setWriteChangedRangeWatchpoint, "IIs", "Set a change range watchpoint" },
|
||||
{ "watch-range/r", _setReadRangeWatchpoint, "IIs", "Set a read range watchpoint" },
|
||||
{ "watch-range/w", _setWriteRangeWatchpoint, "IIs", "Set a write range watchpoint" },
|
||||
{ "x/1", _dumpByte, "Ii", "Examine bytes at a specified offset" },
|
||||
{ "x/2", _dumpHalfword, "Ii", "Examine halfwords at a specified offset" },
|
||||
{ "x/4", _dumpWord, "Ii", "Examine words at a specified offset" },
|
||||
|
@ -146,6 +154,10 @@ static struct CLIDebuggerCommandAlias _debuggerCommandAliases[] = {
|
|||
{ "p/x", "print/x" },
|
||||
{ "q", "quit" },
|
||||
{ "w", "watch" },
|
||||
{ "watchr", "watch-range" },
|
||||
{ "watchr/c", "watch-range/c" },
|
||||
{ "watchr/r", "watch-range/r" },
|
||||
{ "watchr/w", "watch-range/w" },
|
||||
{ ".", "source" },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
@ -647,8 +659,9 @@ static void _setWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector*
|
|||
return;
|
||||
}
|
||||
struct mWatchpoint watchpoint = {
|
||||
.address = dv->intValue,
|
||||
.segment = dv->segmentValue,
|
||||
.minAddress = dv->intValue,
|
||||
.maxAddress = dv->intValue + 1,
|
||||
.type = type
|
||||
};
|
||||
if (dv->next && dv->next->type == CLIDV_CHAR_TYPE) {
|
||||
|
@ -666,6 +679,48 @@ static void _setWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector*
|
|||
}
|
||||
}
|
||||
|
||||
static void _setRangeWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv, enum mWatchpointType type) {
|
||||
if (!dv || dv->type != CLIDV_INT_TYPE) {
|
||||
debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
|
||||
return;
|
||||
}
|
||||
if (!dv->next || dv->next->type != CLIDV_INT_TYPE) {
|
||||
debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
|
||||
return;
|
||||
}
|
||||
if (!debugger->d.platform->setWatchpoint) {
|
||||
debugger->backend->printf(debugger->backend, "Watchpoints are not supported by this platform.\n");
|
||||
return;
|
||||
}
|
||||
if (dv->intValue >= dv->next->intValue) {
|
||||
debugger->backend->printf(debugger->backend, "Range watchpoint end is before start. Note that the end of the range is not included.\n");
|
||||
return;
|
||||
}
|
||||
if (dv->segmentValue != dv->next->segmentValue) {
|
||||
debugger->backend->printf(debugger->backend, "Range watchpoint does not start and end in the same segment.\n");
|
||||
return;
|
||||
}
|
||||
struct mWatchpoint watchpoint = {
|
||||
.segment = dv->segmentValue,
|
||||
.minAddress = dv->intValue,
|
||||
.maxAddress = dv->next->intValue,
|
||||
.type = type
|
||||
};
|
||||
if (dv->next->next && dv->next->next->type == CLIDV_CHAR_TYPE) {
|
||||
struct ParseTree* tree = _parseTree((const char*[]) { dv->next->next->charValue, NULL });
|
||||
if (tree) {
|
||||
watchpoint.condition = tree;
|
||||
} else {
|
||||
debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS);
|
||||
return;
|
||||
}
|
||||
}
|
||||
ssize_t id = debugger->d.platform->setWatchpoint(debugger->d.platform, &watchpoint);
|
||||
if (id > 0) {
|
||||
debugger->backend->printf(debugger->backend, INFO_WATCHPOINT_ADDED, id);
|
||||
}
|
||||
}
|
||||
|
||||
static void _setReadWriteWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
_setWatchpoint(debugger, dv, WATCHPOINT_RW);
|
||||
}
|
||||
|
@ -682,6 +737,22 @@ static void _setWriteChangedWatchpoint(struct CLIDebugger* debugger, struct CLID
|
|||
_setWatchpoint(debugger, dv, WATCHPOINT_WRITE_CHANGE);
|
||||
}
|
||||
|
||||
static void _setReadWriteRangeWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
_setRangeWatchpoint(debugger, dv, WATCHPOINT_RW);
|
||||
}
|
||||
|
||||
static void _setReadRangeWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
_setRangeWatchpoint(debugger, dv, WATCHPOINT_READ);
|
||||
}
|
||||
|
||||
static void _setWriteRangeWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
_setRangeWatchpoint(debugger, dv, WATCHPOINT_WRITE);
|
||||
}
|
||||
|
||||
static void _setWriteChangedRangeWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
_setRangeWatchpoint(debugger, dv, WATCHPOINT_WRITE_CHANGE);
|
||||
}
|
||||
|
||||
static void _clearBreakpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
if (!dv || dv->type != CLIDV_INT_TYPE) {
|
||||
debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
|
||||
|
@ -717,9 +788,17 @@ static void _listWatchpoints(struct CLIDebugger* debugger, struct CLIDebugVector
|
|||
for (i = 0; i < mWatchpointListSize(&watchpoints); ++i) {
|
||||
struct mWatchpoint* watchpoint = mWatchpointListGetPointer(&watchpoints, i);
|
||||
if (watchpoint->segment >= 0) {
|
||||
debugger->backend->printf(debugger->backend, "%" PRIz "i: %02X:%X\n", watchpoint->id, watchpoint->segment, watchpoint->address);
|
||||
if (watchpoint->maxAddress == watchpoint->minAddress + 1) {
|
||||
debugger->backend->printf(debugger->backend, "%" PRIz "i: %02X:%X\n", watchpoint->id, watchpoint->segment, watchpoint->minAddress);
|
||||
} else {
|
||||
debugger->backend->printf(debugger->backend, "%" PRIz "i: %02X:%X-%X\n", watchpoint->id, watchpoint->segment, watchpoint->minAddress, watchpoint->maxAddress);
|
||||
}
|
||||
} else {
|
||||
debugger->backend->printf(debugger->backend, "%" PRIz "i: 0x%X\n", watchpoint->id, watchpoint->address);
|
||||
if (watchpoint->maxAddress == watchpoint->minAddress + 1) {
|
||||
debugger->backend->printf(debugger->backend, "%" PRIz "i: 0x%X\n", watchpoint->id, watchpoint->minAddress);
|
||||
} else {
|
||||
debugger->backend->printf(debugger->backend, "%" PRIz "i: 0x%X-0x%X\n", watchpoint->id, watchpoint->minAddress, watchpoint->maxAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
mWatchpointListDeinit(&watchpoints);
|
||||
|
|
|
@ -532,7 +532,7 @@ static void _processQReadCommand(struct GDBStub* stub, const char* message) {
|
|||
} else if (!strncmp("Xfer:memory-map:read::", message, 22)) {
|
||||
if (strlen(stub->memoryMapXml) == 0) {
|
||||
_generateMemoryMapXml(stub, stub->memoryMapXml);
|
||||
}
|
||||
}
|
||||
_processQXferCommand(stub, message + 22, stub->memoryMapXml);
|
||||
} else if (!strncmp("Supported:", message, 10)) {
|
||||
_processQSupportedCommand(stub, message + 10);
|
||||
|
@ -575,7 +575,8 @@ static void _setBreakpoint(struct GDBStub* stub, const char* message) {
|
|||
.type = BREAKPOINT_HARDWARE
|
||||
};
|
||||
struct mWatchpoint watchpoint = {
|
||||
.address = address
|
||||
.minAddress = address,
|
||||
.maxAddress = address + 1
|
||||
};
|
||||
|
||||
switch (message[0]) {
|
||||
|
@ -631,10 +632,11 @@ static void _clearBreakpoint(struct GDBStub* stub, const char* message) {
|
|||
mWatchpointListInit(&watchpoints, 0);
|
||||
stub->d.platform->listWatchpoints(stub->d.platform, &watchpoints);
|
||||
for (index = 0; index < mWatchpointListSize(&watchpoints); ++index) {
|
||||
if (mWatchpointListGetPointer(&watchpoints, index)->address != address) {
|
||||
struct mWatchpoint* watchpoint = mWatchpointListGetPointer(&watchpoints, index);
|
||||
if (address >= watchpoint->minAddress && address < watchpoint->maxAddress) {
|
||||
continue;
|
||||
}
|
||||
stub->d.platform->clearBreakpoint(stub->d.platform, mWatchpointListGetPointer(&watchpoints, index)->id);
|
||||
stub->d.platform->clearBreakpoint(stub->d.platform, watchpoint->id);
|
||||
}
|
||||
mWatchpointListDeinit(&watchpoints);
|
||||
break;
|
||||
|
|
|
@ -47,7 +47,7 @@ static bool _checkWatchpoints(struct SM83Debugger* debugger, uint16_t address, s
|
|||
size_t i;
|
||||
for (i = 0; i < mWatchpointListSize(&debugger->watchpoints); ++i) {
|
||||
watchpoint = mWatchpointListGetPointer(&debugger->watchpoints, i);
|
||||
if (watchpoint->address == address && (watchpoint->segment < 0 || watchpoint->segment == debugger->originalMemory.currentSegment(debugger->cpu, address)) && watchpoint->type & type) {
|
||||
if (watchpoint->type & type && address >= watchpoint->minAddress && address < watchpoint->maxAddress && (watchpoint->segment < 0 || watchpoint->segment == debugger->originalMemory.currentSegment(debugger->cpu, address))) {
|
||||
if (watchpoint->condition) {
|
||||
int32_t value;
|
||||
int segment;
|
||||
|
|
Loading…
Reference in New Issue