Permalink
Browse files

Debugger: Conditional breakpoints

  • Loading branch information...
endrift committed Dec 29, 2017
1 parent 178017a commit 0383c82b469e7ca38832f66d07418cd890ea68a5
View
@@ -11,6 +11,7 @@ Features:
- Automatic cheat loading and saving
- GameShark and Action Replay button support
- AGBPrint support
+ - Debugger: Conditional breakpoints
Bugfixes:
- GB Audio: Make audio unsigned with bias (fixes mgba.io/i/749)
- GB Serialize: Fix audio state loading
@@ -70,6 +70,7 @@ struct mDebuggerEntryInfo {
};
struct mDebugger;
+struct ParseTree;
struct mDebuggerPlatform {
struct mDebugger* p;
@@ -79,6 +80,7 @@ struct mDebuggerPlatform {
bool (*hasBreakpoints)(struct mDebuggerPlatform*);
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 (*clearWatchpoint)(struct mDebuggerPlatform*, uint32_t address, int segment);
@@ -15,8 +15,10 @@ CXX_GUARD_START
#include <mgba/internal/arm/arm.h>
#include <mgba-util/vector.h>
+struct ParseTree;
struct ARMDebugBreakpoint {
uint32_t address;
+ struct ParseTree* condition;
bool isSw;
struct {
uint32_t opcode;
@@ -12,6 +12,9 @@
CXX_GUARD_START
+struct Token;
+DECLARE_VECTOR(LexVector, struct Token);
+
enum Operation {
OP_ASSIGN,
OP_ADD,
@@ -54,15 +57,13 @@ struct Token {
};
};
-DECLARE_VECTOR(LexVector, struct Token);
-
struct ParseTree {
struct Token token;
struct ParseTree* lhs;
struct ParseTree* rhs;
};
-size_t lexExpression(struct LexVector* lv, const char* string, size_t length);
+size_t lexExpression(struct LexVector* lv, const char* string, size_t length, const char* eol);
void parseLexedExpression(struct ParseTree* tree, struct LexVector* lv);
void lexFree(struct LexVector* lv);
@@ -15,10 +15,11 @@ CXX_GUARD_START
#include <mgba/internal/lr35902/lr35902.h>
#include <mgba-util/vector.h>
-
+struct ParseTree;
struct LR35902DebugBreakpoint {
uint16_t address;
int segment;
+ struct ParseTree* condition;
};
struct LR35902DebugWatchpoint {
@@ -10,6 +10,7 @@
#include <mgba/internal/arm/decoder.h>
#include <mgba/internal/arm/isa-inlines.h>
#include <mgba/internal/arm/debugger/memory-debugger.h>
+#include <mgba/internal/debugger/parser.h>
DEFINE_VECTOR(ARMDebugBreakpointList, struct ARMDebugBreakpoint);
DEFINE_VECTOR(ARMDebugWatchpointList, struct ARMDebugWatchpoint);
@@ -24,6 +25,13 @@ static struct ARMDebugBreakpoint* _lookupBreakpoint(struct ARMDebugBreakpointLis
return 0;
}
+static void _destroyBreakpoint(struct ARMDebugBreakpoint* breakpoint) {
+ if (breakpoint->condition) {
+ parseFree(breakpoint->condition);
+ free(breakpoint->condition);
+ }
+}
+
static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform* d) {
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
int instructionLength;
@@ -37,6 +45,13 @@ static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform* d) {
if (!breakpoint) {
return;
}
+ if (breakpoint->condition) {
+ int32_t value;
+ int segment;
+ if (!mDebuggerEvaluateParseTree(d->p, breakpoint->condition, &value, &segment) || !(value || segment >= 0)) {
+ return;
+ }
+ }
struct mDebuggerEntryInfo info = {
.address = breakpoint->address,
.type.bp.breakType = BREAKPOINT_HARDWARE
@@ -50,6 +65,7 @@ static void ARMDebuggerDeinit(struct mDebuggerPlatform* platform);
static void ARMDebuggerEnter(struct mDebuggerPlatform* d, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info);
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 ARMDebuggerClearWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment);
@@ -65,6 +81,7 @@ struct mDebuggerPlatform* ARMDebuggerPlatformCreate(void) {
platform->init = ARMDebuggerInit;
platform->deinit = ARMDebuggerDeinit;
platform->setBreakpoint = ARMDebuggerSetBreakpoint;
+ platform->setConditionalBreakpoint = ARMDebuggerSetConditionalBreakpoint;
platform->clearBreakpoint = ARMDebuggerClearBreakpoint;
platform->setWatchpoint = ARMDebuggerSetWatchpoint;
platform->clearWatchpoint = ARMDebuggerClearWatchpoint;
@@ -97,6 +114,10 @@ void ARMDebuggerDeinit(struct mDebuggerPlatform* platform) {
}
ARMDebuggerRemoveMemoryShim(debugger);
+ size_t i;
+ for (i = 0; i < ARMDebugBreakpointListSize(&debugger->breakpoints); ++i) {
+ _destroyBreakpoint(ARMDebugBreakpointListGetPointer(&debugger->breakpoints, i));
+ }
ARMDebugBreakpointListDeinit(&debugger->breakpoints);
ARMDebugBreakpointListDeinit(&debugger->swBreakpoints);
ARMDebugWatchpointListDeinit(&debugger->watchpoints);
@@ -168,6 +189,16 @@ static void ARMDebuggerSetBreakpoint(struct mDebuggerPlatform* d, uint32_t addre
UNUSED(segment);
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListAppend(&debugger->breakpoints);
+ breakpoint->condition = NULL;
+ breakpoint->address = address;
+ breakpoint->isSw = false;
+}
+
+static void ARMDebuggerSetConditionalBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, struct ParseTree* condition) {
+ UNUSED(segment);
+ struct ARMDebugger* debugger = (struct ARMDebugger*) d;
+ struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListAppend(&debugger->breakpoints);
+ breakpoint->condition = condition;
breakpoint->address = address;
breakpoint->isSw = false;
}
@@ -179,6 +210,7 @@ static void ARMDebuggerClearBreakpoint(struct mDebuggerPlatform* d, uint32_t add
size_t i;
for (i = 0; i < ARMDebugBreakpointListSize(breakpoints); ++i) {
if (ARMDebugBreakpointListGetPointer(breakpoints, i)->address == address) {
+ _destroyBreakpoint(ARMDebugBreakpointListGetPointer(breakpoints, i));
ARMDebugBreakpointListShift(breakpoints, i, 1);
}
}
@@ -62,8 +62,8 @@ static void _source(struct CLIDebugger*, struct CLIDebugVector*);
#endif
static struct CLIDebuggerCommandSummary _debuggerCommands[] = {
- { "b", _setBreakpoint, "I", "Set a breakpoint" },
- { "break", _setBreakpoint, "I", "Set a breakpoint" },
+ { "b", _setBreakpoint, "Is", "Set a breakpoint" },
+ { "break", _setBreakpoint, "Is", "Set a breakpoint" },
{ "c", _continue, "", "Continue execution" },
{ "continue", _continue, "", "Continue execution" },
{ "d", _clearBreakpoint, "I", "Delete a breakpoint" },
@@ -458,7 +458,39 @@ static void _setBreakpoint(struct CLIDebugger* debugger, struct CLIDebugVector*
return;
}
uint32_t address = dv->intValue;
- debugger->d.platform->setBreakpoint(debugger->d.platform, address, dv->segmentValue);
+ 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 {
+ debugger->d.platform->setConditionalBreakpoint(debugger->d.platform, address, dv->segmentValue, tree);
+ }
+ } else {
+ debugger->d.platform->setBreakpoint(debugger->d.platform, address, dv->segmentValue);
+ }
}
static void _setWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
@@ -547,7 +579,7 @@ struct CLIDebugVector* CLIDVParse(struct CLIDebugger* debugger, const char* stri
struct LexVector lv;
LexVectorInit(&lv, 0);
- size_t adjusted = lexExpression(&lv, string, length);
+ size_t adjusted = lexExpression(&lv, string, length, " ");
if (adjusted > length) {
dvTemp.type = CLIDV_ERROR_TYPE;
}
@@ -562,8 +594,7 @@ struct CLIDebugVector* CLIDVParse(struct CLIDebugger* debugger, const char* stri
}
}
- parseFree(tree.lhs);
- parseFree(tree.rhs);
+ parseFree(&tree);
lexFree(&lv);
LexVectorDeinit(&lv);
View
@@ -166,13 +166,20 @@ static void _lexValue(struct LexVector* lv, char token, uint32_t next, enum LexS
lvNext->type = TOKEN_CLOSE_PAREN_TYPE;
*state = LEX_EXPECT_OPERATOR;
break;
+ case ' ':
+ case '\t':
+ lvNext = LexVectorAppend(lv);
+ lvNext->type = TOKEN_UINT_TYPE;
+ lvNext->uintValue = next;
+ *state = LEX_EXPECT_OPERATOR;
+ break;
default:
*state = LEX_ERROR;
break;
}
}
-size_t lexExpression(struct LexVector* lv, const char* string, size_t length) {
+size_t lexExpression(struct LexVector* lv, const char* string, size_t length, const char* eol) {
if (!string || length < 1) {
return 0;
}
@@ -184,7 +191,11 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length) {
const char* tokenStart = 0;
struct Token* lvNext;
- while (length > 0 && string[0] && string[0] != ' ' && state != LEX_ERROR) {
+ if (!eol) {
+ eol = " \r\n";
+ }
+
+ while (length > 0 && string[0] && !strchr(eol, string[0]) && state != LEX_ERROR) {
char token = string[0];
++string;
++adjusted;
@@ -236,6 +247,9 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length) {
lvNext = LexVectorAppend(lv);
lvNext->type = TOKEN_OPEN_PAREN_TYPE;
break;
+ case ' ':
+ case '\t':
+ break;
default:
if (tolower(token) >= 'a' && tolower(token <= 'z')) {
state = LEX_EXPECT_IDENTIFIER;
@@ -272,6 +286,13 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length) {
lvNext->type = TOKEN_CLOSE_PAREN_TYPE;
state = LEX_EXPECT_OPERATOR;
break;
+ case ' ':
+ case '\t':
+ lvNext = LexVectorAppend(lv);
+ lvNext->type = TOKEN_IDENTIFIER_TYPE;
+ lvNext->identifierValue = strndup(tokenStart, string - tokenStart - 1);
+ state = LEX_EXPECT_OPERATOR;
+ break;
default:
break;
}
@@ -412,7 +433,9 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length) {
case ')':
lvNext = LexVectorAppend(lv);
lvNext->type = TOKEN_CLOSE_PAREN_TYPE;
- state = LEX_EXPECT_OPERATOR;
+ break;
+ case ' ':
+ case '\t':
break;
default:
state = LEX_ERROR;
@@ -585,13 +608,18 @@ void parseFree(struct ParseTree* tree) {
return;
}
- parseFree(tree->lhs);
- parseFree(tree->rhs);
+ if (tree->lhs) {
+ parseFree(tree->lhs);
+ free(tree->lhs);
+ }
+ if (tree->rhs) {
+ parseFree(tree->rhs);
+ free(tree->rhs);
+ }
if (tree->token.type == TOKEN_IDENTIFIER_TYPE) {
free(tree->token.identifierValue);
}
- free(tree);
}
static bool _performOperation(enum Operation operation, int32_t current, int32_t next, int32_t* value) {
Oops, something went wrong.

0 comments on commit 0383c82

Please sign in to comment.