Debugger: Conditional watchpoints
jump to
@@ -11,7 +11,7 @@ - Map viewer
- Automatic cheat loading and saving - GameShark and Action Replay button support - AGBPrint support - - Debugger: Conditional breakpoints + - Debugger: Conditional breakpoints and watchpoints Bugfixes: - GB Audio: Make audio unsigned with bias (fixes mgba.io/i/749) - GB Serialize: Fix audio state loading
@@ -83,6 +83,7 @@ void (*setBreakpoint)(struct mDebuggerPlatform*, uint32_t address, int segment);
void (*setConditionalBreakpoint)(struct mDebuggerPlatform*, uint32_t address, int segment, struct ParseTree* condition); void (*clearBreakpoint)(struct mDebuggerPlatform*, uint32_t address, int segment); void (*setWatchpoint)(struct mDebuggerPlatform*, uint32_t address, int segment, enum mWatchpointType type); + void (*setConditionalWatchpoint)(struct mDebuggerPlatform*, uint32_t address, int segment, enum mWatchpointType type, struct ParseTree* condition); void (*clearWatchpoint)(struct mDebuggerPlatform*, uint32_t address, int segment); void (*checkBreakpoints)(struct mDebuggerPlatform*); void (*trace)(struct mDebuggerPlatform*, char* out, size_t* length);
@@ -29,6 +29,7 @@
struct ARMDebugWatchpoint { uint32_t address; enum mWatchpointType type; + struct ParseTree* condition; }; DECLARE_VECTOR(ARMDebugBreakpointList, struct ARMDebugBreakpoint);
@@ -26,6 +26,7 @@ struct LR35902DebugWatchpoint {
uint16_t address; int segment; enum mWatchpointType type; + struct ParseTree* condition; }; struct LR35902Segment {
@@ -32,6 +32,13 @@ free(breakpoint->condition);
} } +static void _destroyWatchpoint(struct ARMDebugWatchpoint* watchpoint) { + if (watchpoint->condition) { + parseFree(watchpoint->condition); + free(watchpoint->condition); + } +} + static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform* d) { struct ARMDebugger* debugger = (struct ARMDebugger*) d; int instructionLength;@@ -68,6 +75,7 @@ static void ARMDebuggerSetBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment);
static void ARMDebuggerSetConditionalBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment, struct ParseTree* condition); static void ARMDebuggerClearBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment); static void ARMDebuggerSetWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment, enum mWatchpointType type); +static void ARMDebuggerSetConditionalWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment, enum mWatchpointType type, struct ParseTree* condition); static void ARMDebuggerClearWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment); static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform*); static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform*);@@ -84,6 +92,7 @@ platform->setBreakpoint = ARMDebuggerSetBreakpoint;
platform->setConditionalBreakpoint = ARMDebuggerSetConditionalBreakpoint; platform->clearBreakpoint = ARMDebuggerClearBreakpoint; platform->setWatchpoint = ARMDebuggerSetWatchpoint; + platform->setConditionalWatchpoint = ARMDebuggerSetConditionalWatchpoint; platform->clearWatchpoint = ARMDebuggerClearWatchpoint; platform->checkBreakpoints = ARMDebuggerCheckBreakpoints; platform->hasBreakpoints = ARMDebuggerHasBreakpoints;@@ -119,6 +128,10 @@ for (i = 0; i < ARMDebugBreakpointListSize(&debugger->breakpoints); ++i) {
_destroyBreakpoint(ARMDebugBreakpointListGetPointer(&debugger->breakpoints, i)); } ARMDebugBreakpointListDeinit(&debugger->breakpoints); + + for (i = 0; i < ARMDebugWatchpointListSize(&debugger->watchpoints); ++i) { + _destroyWatchpoint(ARMDebugWatchpointListGetPointer(&debugger->watchpoints, i)); + } ARMDebugBreakpointListDeinit(&debugger->swBreakpoints); ARMDebugWatchpointListDeinit(&debugger->watchpoints); }@@ -186,12 +199,7 @@ }
} static void ARMDebuggerSetBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) { - UNUSED(segment); - struct ARMDebugger* debugger = (struct ARMDebugger*) d; - struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListAppend(&debugger->breakpoints); - breakpoint->condition = NULL; - breakpoint->address = address; - breakpoint->isSw = false; + ARMDebuggerSetConditionalBreakpoint(d, address, segment, NULL); } static void ARMDebuggerSetConditionalBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, struct ParseTree* condition) {@@ -222,6 +230,10 @@ return ARMDebugBreakpointListSize(&debugger->breakpoints) || ARMDebugWatchpointListSize(&debugger->watchpoints);
} static void ARMDebuggerSetWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, enum mWatchpointType type) { + ARMDebuggerSetConditionalWatchpoint(d, address, segment, type, NULL); +} + +static void ARMDebuggerSetConditionalWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, enum mWatchpointType type, struct ParseTree* condition) { UNUSED(segment); struct ARMDebugger* debugger = (struct ARMDebugger*) d; if (!ARMDebugWatchpointListSize(&debugger->watchpoints)) {@@ -230,6 +242,7 @@ }
struct ARMDebugWatchpoint* watchpoint = ARMDebugWatchpointListAppend(&debugger->watchpoints); watchpoint->address = address; watchpoint->type = type; + watchpoint->condition = condition; } static void ARMDebuggerClearWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) {@@ -239,6 +252,7 @@ struct ARMDebugWatchpointList* watchpoints = &debugger->watchpoints;
size_t i; for (i = 0; i < ARMDebugWatchpointListSize(watchpoints); ++i) { if (ARMDebugWatchpointListGetPointer(watchpoints, i)->address == address) { + _destroyWatchpoint(ARMDebugWatchpointListGetPointer(watchpoints, i)); ARMDebugWatchpointListShift(watchpoints, i, 1); } }
@@ -6,6 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/internal/arm/debugger/memory-debugger.h> #include <mgba/internal/arm/debugger/debugger.h> +#include <mgba/internal/debugger/parser.h> #include <mgba-util/math.h>@@ -97,6 +98,14 @@ size_t i;
for (i = 0; i < ARMDebugWatchpointListSize(&debugger->watchpoints); ++i) { watchpoint = ARMDebugWatchpointListGetPointer(&debugger->watchpoints, i); if (!((watchpoint->address ^ address) & ~width) && watchpoint->type & type) { + if (watchpoint->condition) { + int32_t value; + int segment; + if (!mDebuggerEvaluateParseTree(debugger->d.p, watchpoint->condition, &value, &segment) || !(value || segment >= 0)) { + return false; + } + } + switch (width + 1) { case 1: info->type.wp.oldValue = debugger->originalMemory.load8(debugger->cpu, address, 0);
@@ -91,14 +91,14 @@ { "r/2", _readHalfword, "I", "Read a halfword from a specified offset" },
{ "r/4", _readWord, "I", "Read a word from a specified offset" }, { "status", _printStatus, "", "Print the current status" }, { "trace", _trace, "I", "Trace a fixed number of instructions" }, - { "w", _setWatchpoint, "I", "Set a watchpoint" }, + { "w", _setWatchpoint, "Is", "Set a watchpoint" }, { "w/1", _writeByte, "II", "Write a byte at a specified offset" }, { "w/2", _writeHalfword, "II", "Write a halfword at a specified offset" }, { "w/r", _writeRegister, "SI", "Write a register" }, { "w/4", _writeWord, "II", "Write a word at a specified offset" }, - { "watch", _setWatchpoint, "I", "Set a watchpoint" }, - { "watch/r", _setReadWatchpoint, "I", "Set a read watchpoint" }, - { "watch/w", _setWriteWatchpoint, "I", "Set a write watchpoint" }, + { "watch", _setWatchpoint, "Is", "Set a watchpoint" }, + { "watch/r", _setReadWatchpoint, "Is", "Set a read watchpoint" }, + { "watch/w", _setWriteWatchpoint, "Is", "Set a write 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" },@@ -452,6 +452,37 @@ }
} #endif +static struct ParseTree* _parseTree(const char* string) { + struct LexVector lv; + bool error = false; + LexVectorInit(&lv, 0); + size_t length = strlen(string); + size_t adjusted = lexExpression(&lv, string, length, NULL); + struct ParseTree* tree = malloc(sizeof(*tree)); + if (!adjusted) { + error = true; + } else { + parseLexedExpression(tree, &lv); + + if (adjusted > length) { + error = true; + } else { + length -= adjusted; + string += adjusted; + } + } + lexFree(&lv); + LexVectorClear(&lv); + LexVectorDeinit(&lv); + if (error) { + parseFree(tree); + free(tree); + return NULL; + } else { + return tree; + } +} + static void _setBreakpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { if (!dv || dv->type != CLIDV_INT_TYPE) { debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);@@ -459,34 +490,11 @@ return;
} uint32_t address = dv->intValue; if (dv->next && dv->next->type == CLIDV_CHAR_TYPE) { - struct LexVector lv; - bool error = false; - LexVectorInit(&lv, 0); - const char* string = dv->next->charValue; - size_t length = strlen(dv->next->charValue); - size_t adjusted = lexExpression(&lv, string, length, NULL); - struct ParseTree* tree = malloc(sizeof(*tree)); - if (!adjusted) { - error = true; + struct ParseTree* tree = _parseTree(dv->next->charValue); + if (tree) { + debugger->d.platform->setConditionalBreakpoint(debugger->d.platform, address, dv->segmentValue, tree); } else { - parseLexedExpression(tree, &lv); - - if (adjusted > length) { - error = true; - } else { - length -= adjusted; - string += adjusted; - } - } - lexFree(&lv); - LexVectorClear(&lv); - LexVectorDeinit(&lv); - if (error) { - parseFree(tree); - free(tree); debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS); - } else { - debugger->d.platform->setConditionalBreakpoint(debugger->d.platform, address, dv->segmentValue, tree); } } else { debugger->d.platform->setBreakpoint(debugger->d.platform, address, dv->segmentValue);@@ -503,7 +511,16 @@ debugger->backend->printf(debugger->backend, "Watchpoints are not supported by this platform.\n");
return; } uint32_t address = dv->intValue; - debugger->d.platform->setWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_RW); + if (dv->next && dv->next->type == CLIDV_CHAR_TYPE) { + struct ParseTree* tree = _parseTree(dv->next->charValue); + if (tree) { + debugger->d.platform->setConditionalWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_RW, tree); + } else { + debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS); + } + } else { + debugger->d.platform->setWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_RW); + } } static void _setReadWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {@@ -516,7 +533,16 @@ debugger->backend->printf(debugger->backend, "Watchpoints are not supported by this platform.\n");
return; } uint32_t address = dv->intValue; - debugger->d.platform->setWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_READ); + if (dv->next && dv->next->type == CLIDV_CHAR_TYPE) { + struct ParseTree* tree = _parseTree(dv->next->charValue); + if (tree) { + debugger->d.platform->setConditionalWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_READ, tree); + } else { + debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS); + } + } else { + debugger->d.platform->setWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_READ); + } } static void _setWriteWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {@@ -529,8 +555,16 @@ debugger->backend->printf(debugger->backend, "Watchpoints are not supported by this platform.\n");
return; } uint32_t address = dv->intValue; - debugger->d.platform->setWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_WRITE); -} + if (dv->next && dv->next->type == CLIDV_CHAR_TYPE) { + struct ParseTree* tree = _parseTree(dv->next->charValue); + if (tree) { + debugger->d.platform->setConditionalWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_WRITE, tree); + } else { + debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS); + } + } else { + debugger->d.platform->setWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_WRITE); + }} static void _clearBreakpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { if (!dv || dv->type != CLIDV_INT_TYPE) {
@@ -31,6 +31,13 @@ free(breakpoint->condition);
} } +static void _destroyWatchpoint(struct LR35902DebugWatchpoint* watchpoint) { + if (watchpoint->condition) { + parseFree(watchpoint->condition); + free(watchpoint->condition); + } +} + static void LR35902DebuggerCheckBreakpoints(struct mDebuggerPlatform* d) { struct LR35902Debugger* debugger = (struct LR35902Debugger*) d; struct LR35902DebugBreakpoint* breakpoint = _lookupBreakpoint(&debugger->breakpoints, debugger->cpu->pc);@@ -62,6 +69,7 @@ static void LR35902DebuggerSetBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment);
static void LR35902DebuggerSetConditionalBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment, struct ParseTree* condition); static void LR35902DebuggerClearBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment); static void LR35902DebuggerSetWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment, enum mWatchpointType type); +static void LR35902DebuggerSetConditionalWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment, enum mWatchpointType type, struct ParseTree* condition); static void LR35902DebuggerClearWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment); static void LR35902DebuggerCheckBreakpoints(struct mDebuggerPlatform*); static bool LR35902DebuggerHasBreakpoints(struct mDebuggerPlatform*);@@ -78,6 +86,7 @@ platform->setBreakpoint = LR35902DebuggerSetBreakpoint;
platform->setConditionalBreakpoint = LR35902DebuggerSetConditionalBreakpoint; platform->clearBreakpoint = LR35902DebuggerClearBreakpoint; platform->setWatchpoint = LR35902DebuggerSetWatchpoint; + platform->setConditionalWatchpoint = LR35902DebuggerSetConditionalWatchpoint; platform->clearWatchpoint = LR35902DebuggerClearWatchpoint; platform->checkBreakpoints = LR35902DebuggerCheckBreakpoints; platform->hasBreakpoints = LR35902DebuggerHasBreakpoints;@@ -101,6 +110,10 @@ for (i = 0; i < LR35902DebugBreakpointListSize(&debugger->breakpoints); ++i) {
_destroyBreakpoint(LR35902DebugBreakpointListGetPointer(&debugger->breakpoints, i)); } LR35902DebugBreakpointListDeinit(&debugger->breakpoints); + + for (i = 0; i < LR35902DebugWatchpointListSize(&debugger->watchpoints); ++i) { + _destroyWatchpoint(LR35902DebugWatchpointListGetPointer(&debugger->watchpoints, i)); + } LR35902DebugWatchpointListDeinit(&debugger->watchpoints); }@@ -117,11 +130,7 @@ }
} static void LR35902DebuggerSetBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) { - struct LR35902Debugger* debugger = (struct LR35902Debugger*) d; - struct LR35902DebugBreakpoint* breakpoint = LR35902DebugBreakpointListAppend(&debugger->breakpoints); - breakpoint->address = address; - breakpoint->segment = segment; - breakpoint->condition = NULL; + LR35902DebuggerSetConditionalBreakpoint(d, address, segment, NULL); } static void LR35902DebuggerSetConditionalBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, struct ParseTree* condition) {@@ -151,6 +160,10 @@ return LR35902DebugBreakpointListSize(&debugger->breakpoints) || LR35902DebugWatchpointListSize(&debugger->watchpoints);
} static void LR35902DebuggerSetWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, enum mWatchpointType type) { + LR35902DebuggerSetConditionalWatchpoint(d, address, segment, type, NULL); +} + +static void LR35902DebuggerSetConditionalWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, enum mWatchpointType type, struct ParseTree* condition) { struct LR35902Debugger* debugger = (struct LR35902Debugger*) d; if (!LR35902DebugWatchpointListSize(&debugger->watchpoints)) { LR35902DebuggerInstallMemoryShim(debugger);@@ -159,6 +172,7 @@ struct LR35902DebugWatchpoint* watchpoint = LR35902DebugWatchpointListAppend(&debugger->watchpoints);
watchpoint->address = address; watchpoint->type = type; watchpoint->segment = segment; + watchpoint->condition = condition; } static void LR35902DebuggerClearWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) {
@@ -5,6 +5,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include <mgba/internal/lr35902/debugger/memory-debugger.h> +#include <mgba/internal/debugger/parser.h> #include <mgba/internal/lr35902/debugger/debugger.h> #include <mgba-util/math.h>@@ -47,6 +48,13 @@ size_t i;
for (i = 0; i < LR35902DebugWatchpointListSize(&debugger->watchpoints); ++i) { watchpoint = LR35902DebugWatchpointListGetPointer(&debugger->watchpoints, i); if (watchpoint->address == address && (watchpoint->segment < 0 || watchpoint->segment == debugger->originalMemory.currentSegment(debugger->cpu, address)) && watchpoint->type & type) { + if (watchpoint->condition) { + int32_t value; + int segment; + if (!mDebuggerEvaluateParseTree(debugger->d.p, watchpoint->condition, &value, &segment) || !(value || segment >= 0)) { + return false; + } + } info->type.wp.oldValue = debugger->originalMemory.load8(debugger->cpu, address); info->type.wp.newValue = newValue; info->address = address;