Debugger: Conditional watchpoints

This commit is contained in:
Vicki Pfau 2017-12-29 16:38:46 -05:00
parent 0383c82b46
commit 0131a196d1
9 changed files with 129 additions and 47 deletions

View File

@ -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

View File

@ -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);

View File

@ -29,6 +29,7 @@ struct ARMDebugBreakpoint {
struct ARMDebugWatchpoint {
uint32_t address;
enum mWatchpointType type;
struct ParseTree* condition;
};
DECLARE_VECTOR(ARMDebugBreakpointList, struct ARMDebugBreakpoint);

View File

@ -26,6 +26,7 @@ struct LR35902DebugWatchpoint {
uint16_t address;
int segment;
enum mWatchpointType type;
struct ParseTree* condition;
};
struct LR35902Segment {

View File

@ -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);
}
}

View File

@ -6,6 +6,7 @@
#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 @@ 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);

View File

@ -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) {

View File

@ -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) {

View File

@ -5,6 +5,7 @@
* 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 @@ 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;