mirror of https://github.com/mgba-emu/mgba.git
Debugger: Conditional watchpoints
This commit is contained in:
parent
0383c82b46
commit
0131a196d1
2
CHANGES
2
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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -29,6 +29,7 @@ struct ARMDebugBreakpoint {
|
|||
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 @@ 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,18 +452,11 @@ static void _source(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
|||
}
|
||||
#endif
|
||||
|
||||
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);
|
||||
return;
|
||||
}
|
||||
uint32_t address = dv->intValue;
|
||||
if (dv->next && dv->next->type == CLIDV_CHAR_TYPE) {
|
||||
static struct ParseTree* _parseTree(const char* string) {
|
||||
struct LexVector lv;
|
||||
bool error = false;
|
||||
LexVectorInit(&lv, 0);
|
||||
const char* string = dv->next->charValue;
|
||||
size_t length = strlen(dv->next->charValue);
|
||||
size_t length = strlen(string);
|
||||
size_t adjusted = lexExpression(&lv, string, length, NULL);
|
||||
struct ParseTree* tree = malloc(sizeof(*tree));
|
||||
if (!adjusted) {
|
||||
|
@ -484,9 +477,24 @@ static void _setBreakpoint(struct CLIDebugger* debugger, struct CLIDebugVector*
|
|||
if (error) {
|
||||
parseFree(tree);
|
||||
free(tree);
|
||||
debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS);
|
||||
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);
|
||||
return;
|
||||
}
|
||||
uint32_t address = dv->intValue;
|
||||
if (dv->next && dv->next->type == CLIDV_CHAR_TYPE) {
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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 @@ 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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue