Debugger: Shaving recursive yaks takes a lot of work

This commit is contained in:
Vicki Pfau 2022-06-28 18:43:15 -07:00
parent 1bf1a97023
commit 0979380c74
8 changed files with 216 additions and 92 deletions

View File

@ -53,6 +53,7 @@ Other fixes:
- Core: Don't attempt to restore rewind diffs past start of rewind - Core: Don't attempt to restore rewind diffs past start of rewind
- Core: Fix the runloop resuming after a game has crashed (fixes mgba.io/i/2451) - Core: Fix the runloop resuming after a game has crashed (fixes mgba.io/i/2451)
- Core: Fix crash if library can't be opened - Core: Fix crash if library can't be opened
- Debugger: Fix crash with extremely long CLI strings
- FFmpeg: Fix crash when encoding audio with some containers - FFmpeg: Fix crash when encoding audio with some containers
- FFmpeg: Fix GIF recording (fixes mgba.io/i/2393) - FFmpeg: Fix GIF recording (fixes mgba.io/i/2393)
- GB: Fix temporary saves - GB: Fix temporary saves

View File

@ -98,6 +98,7 @@ CXX_GUARD_START
} \ } \
DECLARE_VECTOR(StringList, char*); DECLARE_VECTOR(StringList, char*);
DECLARE_VECTOR(IntList, int);
CXX_GUARD_END CXX_GUARD_END

View File

@ -43,7 +43,7 @@ enum Operation {
struct Token { struct Token {
enum TokenType { enum TokenType {
TOKEN_ERROR_TYPE, TOKEN_ERROR_TYPE = 0,
TOKEN_UINT_TYPE, TOKEN_UINT_TYPE,
TOKEN_IDENTIFIER_TYPE, TOKEN_IDENTIFIER_TYPE,
TOKEN_OPERATOR_TYPE, TOKEN_OPERATOR_TYPE,
@ -60,8 +60,10 @@ struct Token {
struct ParseTree { struct ParseTree {
struct Token token; struct Token token;
struct ParseTree* p;
struct ParseTree* lhs; struct ParseTree* lhs;
struct ParseTree* rhs; struct ParseTree* rhs;
int precedence;
}; };
size_t lexExpression(struct LexVector* lv, const char* string, size_t length, const char* eol); size_t lexExpression(struct LexVector* lv, const char* string, size_t length, const char* eol);

View File

@ -190,14 +190,12 @@ static struct ARMDebugBreakpoint* _lookupBreakpoint(struct ARMDebugBreakpointLis
static void _destroyBreakpoint(struct ARMDebugBreakpoint* breakpoint) { static void _destroyBreakpoint(struct ARMDebugBreakpoint* breakpoint) {
if (breakpoint->d.condition) { if (breakpoint->d.condition) {
parseFree(breakpoint->d.condition); parseFree(breakpoint->d.condition);
free(breakpoint->d.condition);
} }
} }
static void _destroyWatchpoint(struct mWatchpoint* watchpoint) { static void _destroyWatchpoint(struct mWatchpoint* watchpoint) {
if (watchpoint->condition) { if (watchpoint->condition) {
parseFree(watchpoint->condition); parseFree(watchpoint->condition);
free(watchpoint->condition);
} }
} }

View File

@ -226,11 +226,9 @@ static bool _parseExpression(struct mDebugger* debugger, struct CLIDebugVector*
} }
if (!mDebuggerEvaluateParseTree(debugger, tree, intValue, segmentValue)) { if (!mDebuggerEvaluateParseTree(debugger, tree, intValue, segmentValue)) {
parseFree(tree); parseFree(tree);
free(tree);
return false; return false;
} }
parseFree(tree); parseFree(tree);
free(tree);
return true; return true;
} }
@ -599,7 +597,6 @@ static struct ParseTree* _parseTree(const char** string) {
if (error) { if (error) {
if (tree) { if (tree) {
parseFree(tree); parseFree(tree);
free(tree);
} }
return NULL; return NULL;
} else { } else {

View File

@ -11,6 +11,8 @@
DEFINE_VECTOR(LexVector, struct Token); DEFINE_VECTOR(LexVector, struct Token);
DEFINE_VECTOR(IntList, int32_t);
enum LexState { enum LexState {
LEX_ERROR = -1, LEX_ERROR = -1,
LEX_ROOT = 0, LEX_ROOT = 0,
@ -493,16 +495,21 @@ static const int _operatorPrecedence[] = {
[OP_DEREFERENCE] = 2, [OP_DEREFERENCE] = 2,
}; };
static struct ParseTree* _parseTreeCreate() { static struct ParseTree* _parseTreeCreate(void) {
struct ParseTree* tree = malloc(sizeof(struct ParseTree)); struct ParseTree* tree = malloc(sizeof(struct ParseTree));
tree->token.type = TOKEN_ERROR_TYPE; tree->token.type = TOKEN_ERROR_TYPE;
tree->rhs = 0; tree->p = NULL;
tree->lhs = 0; tree->rhs = NULL;
tree->lhs = NULL;
tree->precedence = INT_MAX;
return tree; return tree;
} }
static size_t _parseExpression(struct ParseTree* tree, struct LexVector* lv, size_t i, int precedence, int* openParens) { static size_t _parseExpression(struct ParseTree* tree, struct LexVector* lv, int* openParens) {
struct ParseTree* newTree = 0; struct ParseTree* newTree = NULL;
bool pop = false;
int precedence = INT_MAX;
size_t i = 0;
while (i < LexVectorSize(lv)) { while (i < LexVectorSize(lv)) {
struct Token* token = LexVectorGetPointer(lv, i); struct Token* token = LexVectorGetPointer(lv, i);
int newPrecedence; int newPrecedence;
@ -517,27 +524,36 @@ static size_t _parseExpression(struct ParseTree* tree, struct LexVector* lv, siz
++i; ++i;
} else { } else {
tree->token.type = TOKEN_ERROR_TYPE; tree->token.type = TOKEN_ERROR_TYPE;
return i + 1; ++i;
pop = true;
} }
break; break;
case TOKEN_SEGMENT_TYPE: case TOKEN_SEGMENT_TYPE:
tree->lhs = _parseTreeCreate(); tree->lhs = _parseTreeCreate();
tree->lhs->token.type = TOKEN_UINT_TYPE; tree->lhs->token.type = TOKEN_UINT_TYPE;
tree->lhs->token.uintValue = token->uintValue; tree->lhs->token.uintValue = token->uintValue;
tree->lhs->p = tree;
tree->lhs->precedence = precedence;
tree->rhs = _parseTreeCreate(); tree->rhs = _parseTreeCreate();
tree->rhs->p = tree;
tree->rhs->precedence = precedence;
tree->token.type = TOKEN_SEGMENT_TYPE; tree->token.type = TOKEN_SEGMENT_TYPE;
i = _parseExpression(tree->rhs, lv, i + 1, precedence, openParens); tree = tree->rhs;
++i;
break; break;
case TOKEN_OPEN_PAREN_TYPE: case TOKEN_OPEN_PAREN_TYPE:
++*openParens; ++*openParens;
i = _parseExpression(tree, lv, i + 1, INT_MAX, openParens); precedence = INT_MAX;
++i;
break; break;
case TOKEN_CLOSE_PAREN_TYPE: case TOKEN_CLOSE_PAREN_TYPE:
if (*openParens <= 0) { if (*openParens <= 0) {
tree->token.type = TOKEN_ERROR_TYPE; tree->token.type = TOKEN_ERROR_TYPE;
} }
--*openParens; --*openParens;
return i + 1; ++i;
pop = true;
break;
case TOKEN_OPERATOR_TYPE: case TOKEN_OPERATOR_TYPE:
if (tree->token.type == TOKEN_ERROR_TYPE) { if (tree->token.type == TOKEN_ERROR_TYPE) {
switch (token->operatorValue) { switch (token->operatorValue) {
@ -557,21 +573,44 @@ static size_t _parseExpression(struct ParseTree* tree, struct LexVector* lv, siz
newPrecedence = _operatorPrecedence[token->operatorValue]; newPrecedence = _operatorPrecedence[token->operatorValue];
if (newPrecedence < precedence) { if (newPrecedence < precedence) {
newTree = _parseTreeCreate(); newTree = _parseTreeCreate();
*newTree = *tree; memcpy(newTree, tree, sizeof(*tree));
if (newTree->lhs) {
newTree->lhs->p = newTree;
}
if (newTree->rhs) {
newTree->rhs->p = newTree;
}
newTree->p = tree;
tree->lhs = newTree; tree->lhs = newTree;
tree->rhs = _parseTreeCreate(); tree->rhs = _parseTreeCreate();
tree->rhs->p = tree;
tree->rhs->precedence = newPrecedence;
precedence = newPrecedence;
tree->token = *token; tree->token = *token;
i = _parseExpression(tree->rhs, lv, i + 1, newPrecedence, openParens); tree = tree->rhs;
if (tree->token.type == TOKEN_ERROR_TYPE) { ++i;
tree->token.type = TOKEN_ERROR_TYPE;
}
} else { } else {
return i; pop = true;
} }
break; break;
case TOKEN_ERROR_TYPE: case TOKEN_ERROR_TYPE:
tree->token.type = TOKEN_ERROR_TYPE; tree->token.type = TOKEN_ERROR_TYPE;
return i + 1; ++i;
pop = true;
break;
}
if (pop) {
if (tree->token.type == TOKEN_ERROR_TYPE && tree->p) {
tree->p->token.type = TOKEN_ERROR_TYPE;
}
tree = tree->p;
pop = false;
if (!tree) {
break;
} else {
precedence = tree->precedence;
}
} }
} }
@ -584,11 +623,13 @@ void parseLexedExpression(struct ParseTree* tree, struct LexVector* lv) {
} }
tree->token.type = TOKEN_ERROR_TYPE; tree->token.type = TOKEN_ERROR_TYPE;
tree->lhs = 0; tree->lhs = NULL;
tree->rhs = 0; tree->rhs = NULL;
tree->p = NULL;
tree->precedence = INT_MAX;
int openParens = 0; int openParens = 0;
_parseExpression(tree, lv, 0, INT_MAX, &openParens); _parseExpression(tree, lv, &openParens);
if (openParens) { if (openParens) {
if (tree->token.type == TOKEN_IDENTIFIER_TYPE) { if (tree->token.type == TOKEN_IDENTIFIER_TYPE) {
free(tree->token.identifierValue); free(tree->token.identifierValue);
@ -607,23 +648,40 @@ void lexFree(struct LexVector* lv) {
} }
} }
void parseFree(struct ParseTree* tree) { static void _freeTree(struct ParseTree* tree) {
if (!tree) {
return;
}
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) { if (tree->token.type == TOKEN_IDENTIFIER_TYPE) {
free(tree->token.identifierValue); free(tree->token.identifierValue);
} }
free(tree);
}
void parseFree(struct ParseTree* tree) {
while (tree) {
if (tree->lhs) {
tree = tree->lhs;
continue;
}
if (tree->rhs) {
tree = tree->rhs;
continue;
}
if (tree->p) {
if (tree->p->lhs == tree) {
tree = tree->p;
_freeTree(tree->lhs);
tree->lhs = NULL;
} else if (tree->p->rhs == tree) {
tree = tree->p;
_freeTree(tree->rhs);
tree->rhs = NULL;
} else {
abort();
}
} else {
_freeTree(tree);
break;
}
}
} }
static bool _performOperation(struct mDebugger* debugger, enum Operation operation, int32_t current, int32_t next, int32_t* value, int* segment) { static bool _performOperation(struct mDebugger* debugger, enum Operation operation, int32_t current, int32_t next, int32_t* value, int* segment) {
@ -721,19 +779,22 @@ bool mDebuggerEvaluateParseTree(struct mDebugger* debugger, struct ParseTree* tr
if (!value) { if (!value) {
return false; return false;
} }
int32_t lhs, rhs; struct IntList stack;
int nextBranch;
bool ok = true;
int32_t tmpVal, tmpSegment;
IntListInit(&stack, 0);
while (ok) {
switch (tree->token.type) { switch (tree->token.type) {
case TOKEN_UINT_TYPE: case TOKEN_UINT_TYPE:
if (segment) { nextBranch = 2;
*segment = -1; tmpSegment = -1;
} tmpVal = tree->token.uintValue;
*value = tree->token.uintValue; break;
return true;
case TOKEN_SEGMENT_TYPE: case TOKEN_SEGMENT_TYPE:
if (!mDebuggerEvaluateParseTree(debugger, tree->rhs, value, segment)) { nextBranch = 0;
return false; break;
}
return mDebuggerEvaluateParseTree(debugger, tree->lhs, segment, NULL);
case TOKEN_OPERATOR_TYPE: case TOKEN_OPERATOR_TYPE:
switch (tree->token.operatorValue) { switch (tree->token.operatorValue) {
case OP_ASSIGN: case OP_ASSIGN:
@ -755,22 +816,87 @@ bool mDebuggerEvaluateParseTree(struct mDebugger* debugger, struct ParseTree* tr
case OP_GE: case OP_GE:
case OP_SHIFT_L: case OP_SHIFT_L:
case OP_SHIFT_R: case OP_SHIFT_R:
if (!mDebuggerEvaluateParseTree(debugger, tree->lhs, &lhs, segment)) { nextBranch = 0;
return false; break;
}
// Fall through
default: default:
if (!mDebuggerEvaluateParseTree(debugger, tree->rhs, &rhs, segment)) { nextBranch = 1;
return false;
}
break; break;
} }
return _performOperation(debugger, tree->token.operatorValue, lhs, rhs, value, segment); break;
case TOKEN_IDENTIFIER_TYPE: case TOKEN_IDENTIFIER_TYPE:
return mDebuggerLookupIdentifier(debugger, tree->token.identifierValue, value, segment); if (!mDebuggerLookupIdentifier(debugger, tree->token.identifierValue, &tmpVal, &tmpSegment)) {
ok = false;
}
nextBranch = 2;
break;
case TOKEN_ERROR_TYPE: case TOKEN_ERROR_TYPE:
default: default:
ok = false;
break; break;
} }
return false; if (!ok) {
break;
}
bool gotTree = false;
while (!gotTree && tree) {
int32_t lhs, rhs;
switch (nextBranch) {
case 0:
*IntListAppend(&stack) = tmpVal;
*IntListAppend(&stack) = tmpSegment;
*IntListAppend(&stack) = nextBranch;
tree = tree->lhs;
gotTree = true;
break;
case 1:
*IntListAppend(&stack) = tmpVal;
*IntListAppend(&stack) = tmpSegment;
*IntListAppend(&stack) = nextBranch;
tree = tree->rhs;
gotTree = true;
break;
case 2:
if (!IntListSize(&stack)) {
tree = NULL;
break;
}
nextBranch = *IntListGetPointer(&stack, IntListSize(&stack) - 1);
IntListResize(&stack, -1);
tree = tree->p;
if (nextBranch == 0) {
++nextBranch;
} else if (tree) {
nextBranch = 2;
switch (tree->token.type) {
case TOKEN_OPERATOR_TYPE:
rhs = tmpVal;
lhs = *IntListGetPointer(&stack, IntListSize(&stack) - 2);
tmpSegment = *IntListGetPointer(&stack, IntListSize(&stack) - 1);
ok = _performOperation(debugger, tree->token.operatorValue, lhs, rhs, &tmpVal, &tmpSegment);
break;
case TOKEN_SEGMENT_TYPE:
tmpSegment = *IntListGetPointer(&stack, IntListSize(&stack) - 2);
break;
default:
break;
}
}
IntListResize(&stack, -2);
break;
}
}
if (!tree) {
break;
}
}
IntListDeinit(&stack);
if (ok) {
*value = tmpVal;
if (segment) {
*segment = tmpSegment;
}
}
return ok;
} }

View File

@ -9,7 +9,7 @@
struct LPTest { struct LPTest {
struct LexVector lv; struct LexVector lv;
struct ParseTree tree; struct ParseTree* tree;
}; };
#define PARSE(STR) \ #define PARSE(STR) \
@ -18,7 +18,8 @@ struct LPTest {
LexVectorClear(&lp->lv); \ LexVectorClear(&lp->lv); \
size_t adjusted = lexExpression(&lp->lv, STR, strlen(STR), ""); \ size_t adjusted = lexExpression(&lp->lv, STR, strlen(STR), ""); \
assert_false(adjusted > strlen(STR)); \ assert_false(adjusted > strlen(STR)); \
struct ParseTree* tree = &lp->tree; \ lp->tree = malloc(sizeof(*lp->tree)); \
struct ParseTree* tree = lp->tree; \
parseLexedExpression(tree, &lp->lv) parseLexedExpression(tree, &lp->lv)
static int parseSetup(void** state) { static int parseSetup(void** state) {
@ -30,7 +31,7 @@ static int parseSetup(void** state) {
static int parseTeardown(void** state) { static int parseTeardown(void** state) {
struct LPTest* lp = *state; struct LPTest* lp = *state;
parseFree(&lp->tree); parseFree(lp->tree);
lexFree(&lp->lv); lexFree(&lp->lv);
LexVectorDeinit(&lp->lv); LexVectorDeinit(&lp->lv);
free(lp); free(lp);

View File

@ -28,14 +28,12 @@ static struct mBreakpoint* _lookupBreakpoint(struct mBreakpointList* breakpoints
static void _destroyBreakpoint(struct mBreakpoint* breakpoint) { static void _destroyBreakpoint(struct mBreakpoint* breakpoint) {
if (breakpoint->condition) { if (breakpoint->condition) {
parseFree(breakpoint->condition); parseFree(breakpoint->condition);
free(breakpoint->condition);
} }
} }
static void _destroyWatchpoint(struct mWatchpoint* watchpoint) { static void _destroyWatchpoint(struct mWatchpoint* watchpoint) {
if (watchpoint->condition) { if (watchpoint->condition) {
parseFree(watchpoint->condition); parseFree(watchpoint->condition);
free(watchpoint->condition);
} }
} }