From 0131a196d17494abcabf22dd6d07f6c582b6d806 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 29 Dec 2017 16:38:46 -0500 Subject: [PATCH] Debugger: Conditional watchpoints --- CHANGES | 2 +- include/mgba/debugger/debugger.h | 1 + include/mgba/internal/arm/debugger/debugger.h | 1 + .../mgba/internal/lr35902/debugger/debugger.h | 1 + src/arm/debugger/debugger.c | 26 ++++- src/arm/debugger/memory-debugger.c | 9 ++ src/debugger/cli-debugger.c | 104 ++++++++++++------ src/lr35902/debugger/debugger.c | 24 +++- src/lr35902/debugger/memory-debugger.c | 8 ++ 9 files changed, 129 insertions(+), 47 deletions(-) diff --git a/CHANGES b/CHANGES index 5e4ff85a2..f11e81b95 100644 --- a/CHANGES +++ b/CHANGES @@ -11,7 +11,7 @@ Features: - 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 diff --git a/include/mgba/debugger/debugger.h b/include/mgba/debugger/debugger.h index a8e808b32..8de7d4c00 100644 --- a/include/mgba/debugger/debugger.h +++ b/include/mgba/debugger/debugger.h @@ -83,6 +83,7 @@ struct mDebuggerPlatform { 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); diff --git a/include/mgba/internal/arm/debugger/debugger.h b/include/mgba/internal/arm/debugger/debugger.h index 0941b0dd5..5c1852243 100644 --- a/include/mgba/internal/arm/debugger/debugger.h +++ b/include/mgba/internal/arm/debugger/debugger.h @@ -29,6 +29,7 @@ struct ARMDebugBreakpoint { struct ARMDebugWatchpoint { uint32_t address; enum mWatchpointType type; + struct ParseTree* condition; }; DECLARE_VECTOR(ARMDebugBreakpointList, struct ARMDebugBreakpoint); diff --git a/include/mgba/internal/lr35902/debugger/debugger.h b/include/mgba/internal/lr35902/debugger/debugger.h index 6fb7dd734..6b66c715a 100644 --- a/include/mgba/internal/lr35902/debugger/debugger.h +++ b/include/mgba/internal/lr35902/debugger/debugger.h @@ -26,6 +26,7 @@ struct LR35902DebugWatchpoint { uint16_t address; int segment; enum mWatchpointType type; + struct ParseTree* condition; }; struct LR35902Segment { diff --git a/src/arm/debugger/debugger.c b/src/arm/debugger/debugger.c index 1476f8c8a..55105a17d 100644 --- a/src/arm/debugger/debugger.c +++ b/src/arm/debugger/debugger.c @@ -32,6 +32,13 @@ static void _destroyBreakpoint(struct ARMDebugBreakpoint* breakpoint) { } } +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 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 @@ struct mDebuggerPlatform* ARMDebuggerPlatformCreate(void) { 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 @@ void ARMDebuggerDeinit(struct mDebuggerPlatform* platform) { _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 @@ void ARMDebuggerClearSoftwareBreakpoint(struct mDebuggerPlatform* d, uint32_t ad } 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 @@ static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform* d) { } 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 @@ static void ARMDebuggerSetWatchpoint(struct mDebuggerPlatform* d, uint32_t addre 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 @@ static void ARMDebuggerClearWatchpoint(struct mDebuggerPlatform* d, uint32_t add size_t i; for (i = 0; i < ARMDebugWatchpointListSize(watchpoints); ++i) { if (ARMDebugWatchpointListGetPointer(watchpoints, i)->address == address) { + _destroyWatchpoint(ARMDebugWatchpointListGetPointer(watchpoints, i)); ARMDebugWatchpointListShift(watchpoints, i, 1); } } diff --git a/src/arm/debugger/memory-debugger.c b/src/arm/debugger/memory-debugger.c index 88a8518b6..2faca99a6 100644 --- a/src/arm/debugger/memory-debugger.c +++ b/src/arm/debugger/memory-debugger.c @@ -6,6 +6,7 @@ #include #include +#include #include @@ -97,6 +98,14 @@ static bool _checkWatchpoints(struct ARMDebugger* debugger, uint32_t address, st 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); diff --git a/src/debugger/cli-debugger.c b/src/debugger/cli-debugger.c index bf975d2aa..d0ed6b672 100644 --- a/src/debugger/cli-debugger.c +++ b/src/debugger/cli-debugger.c @@ -91,14 +91,14 @@ static struct CLIDebuggerCommandSummary _debuggerCommands[] = { { "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 @@ static void _source(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { } #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 @@ static void _setBreakpoint(struct CLIDebugger* debugger, struct CLIDebugVector* } 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; - } 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 { + struct ParseTree* tree = _parseTree(dv->next->charValue); + if (tree) { debugger->d.platform->setConditionalBreakpoint(debugger->d.platform, address, dv->segmentValue, tree); + } else { + debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS); } } else { debugger->d.platform->setBreakpoint(debugger->d.platform, address, dv->segmentValue); @@ -503,7 +511,16 @@ static void _setWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* 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 @@ static void _setReadWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVect 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 @@ static void _setWriteWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVec 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) { diff --git a/src/lr35902/debugger/debugger.c b/src/lr35902/debugger/debugger.c index fa2c77620..4f899ae1e 100644 --- a/src/lr35902/debugger/debugger.c +++ b/src/lr35902/debugger/debugger.c @@ -31,6 +31,13 @@ static void _destroyBreakpoint(struct LR35902DebugBreakpoint* breakpoint) { } } +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 add 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 @@ struct mDebuggerPlatform* LR35902DebuggerPlatformCreate(void) { 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 @@ void LR35902DebuggerDeinit(struct mDebuggerPlatform* platform) { _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 LR35902DebuggerEnter(struct mDebuggerPlatform* platform, enum mDebug } 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 @@ static bool LR35902DebuggerHasBreakpoints(struct mDebuggerPlatform* d) { } 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 @@ static void LR35902DebuggerSetWatchpoint(struct mDebuggerPlatform* d, uint32_t a watchpoint->address = address; watchpoint->type = type; watchpoint->segment = segment; + watchpoint->condition = condition; } static void LR35902DebuggerClearWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) { diff --git a/src/lr35902/debugger/memory-debugger.c b/src/lr35902/debugger/memory-debugger.c index 3caf5ac72..b5c36a81e 100644 --- a/src/lr35902/debugger/memory-debugger.c +++ b/src/lr35902/debugger/memory-debugger.c @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include +#include #include #include @@ -47,6 +48,13 @@ static bool _checkWatchpoints(struct LR35902Debugger* debugger, uint16_t address 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;