diff --git a/include/mgba/debugger/debugger.h b/include/mgba/debugger/debugger.h index 2a4bc2dce..cd4154994 100644 --- a/include/mgba/debugger/debugger.h +++ b/include/mgba/debugger/debugger.h @@ -13,6 +13,7 @@ CXX_GUARD_START #include #include #include +#include mLOG_DECLARE_CATEGORY(DEBUGGER); @@ -50,7 +51,8 @@ enum mDebuggerEntryReason { DEBUGGER_ENTER_ATTACHED, DEBUGGER_ENTER_BREAKPOINT, DEBUGGER_ENTER_WATCHPOINT, - DEBUGGER_ENTER_ILLEGAL_OP + DEBUGGER_ENTER_ILLEGAL_OP, + DEBUGGER_ENTER_STACK }; struct mDebuggerEntryInfo { @@ -67,6 +69,10 @@ struct mDebuggerEntryInfo { uint32_t opcode; enum mBreakpointType breakType; } bp; + + struct { + enum mStackTraceMode traceType; + } st; } type; ssize_t pointId; }; @@ -114,6 +120,9 @@ struct mDebuggerPlatform { bool (*getRegister)(struct mDebuggerPlatform*, const char* name, int32_t* value); bool (*setRegister)(struct mDebuggerPlatform*, const char* name, int32_t value); bool (*lookupIdentifier)(struct mDebuggerPlatform*, const char* name, int32_t* value, int* segment); + + uint32_t (*getStackTraceMode)(struct mDebuggerPlatform*); + void (*setStackTraceMode)(struct mDebuggerPlatform*, uint32_t mode); }; struct mDebugger { @@ -123,6 +132,7 @@ struct mDebugger { enum mDebuggerType type; struct mCore* core; struct mScriptBridge* bridge; + struct mStackTrace stackTrace; void (*init)(struct mDebugger*); void (*deinit)(struct mDebugger*); diff --git a/include/mgba/internal/arm/debugger/debugger.h b/include/mgba/internal/arm/debugger/debugger.h index 5dc73485c..1850478f9 100644 --- a/include/mgba/internal/arm/debugger/debugger.h +++ b/include/mgba/internal/arm/debugger/debugger.h @@ -36,6 +36,7 @@ struct ARMDebugger { struct ARMMemory originalMemory; ssize_t nextId; + enum mStackTraceMode stackTraceMode; void (*entered)(struct mDebugger*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*); diff --git a/src/arm/debugger/debugger.c b/src/arm/debugger/debugger.c index 7449ece33..a4dc9a30c 100644 --- a/src/arm/debugger/debugger.c +++ b/src/arm/debugger/debugger.c @@ -11,9 +11,69 @@ #include #include #include +#include DEFINE_VECTOR(ARMDebugBreakpointList, struct ARMDebugBreakpoint); +static bool _updateStackTrace(struct mDebuggerPlatform* d, uint32_t pc) { + struct ARMDebugger* debugger = (struct ARMDebugger*) d; + struct ARMCore* cpu = debugger->cpu; + struct mStackTrace* stack = &d->p->stackTrace; + struct ARMInstructionInfo info; + uint32_t instruction = cpu->prefetch[0]; + ARMDecodeARM(instruction, &info); + struct mStackFrame* frame = mStackTraceGetFrame(stack, 0); + bool isCall = (info.branchType & ARM_BRANCH_LINKED); + uint32_t destAddress; + + if (frame && frame->finished) { + mStackTracePop(stack); + frame = NULL; + } + + if (info.operandFormat & ARM_OPERAND_IMMEDIATE_1) { + if (!isCall) { + return false; + } + destAddress = info.op1.immediate + cpu->gprs[ARM_PC]; + } else if (info.operandFormat & ARM_OPERAND_REGISTER_1) { + if (!isCall && info.op1.reg != ARM_LR) { + return false; + } + destAddress = cpu->gprs[info.op1.reg]; + } else { + abort(); // Should be unreachable + } + + if (info.branchType & ARM_BRANCH_INDIRECT) { + destAddress = cpu->memory.load32(cpu, destAddress, 0); + } + + if (isCall) { + frame = mStackTracePush(stack, instruction, pc, destAddress, cpu->gprs[ARM_SP], &cpu->regs); + if (!(debugger->stackTraceMode & STACK_TRACE_BREAK_ON_CALL)) { + return false; + } + } else { + frame = mStackTraceGetFrame(stack, 0); + if (!frame) { + return false; + } + if (!frame->breakWhenFinished && !(debugger->stackTraceMode & STACK_TRACE_BREAK_ON_RETURN)) { + mStackTracePop(stack); + return false; + } + frame->finished = true; + } + struct mDebuggerEntryInfo debuggerInfo = { + .address = pc, + .type.st.traceType = isCall ? STACK_TRACE_BREAK_ON_CALL : STACK_TRACE_BREAK_ON_RETURN, + .pointId = 0 + }; + mDebuggerEnter(d->p, DEBUGGER_ENTER_STACK, &debuggerInfo); + return true; +} + static struct ARMDebugBreakpoint* _lookupBreakpoint(struct ARMDebugBreakpointList* breakpoints, uint32_t address) { size_t i; for (i = 0; i < ARMDebugBreakpointListSize(breakpoints); ++i) { @@ -47,6 +107,9 @@ static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform* d) { } else { instructionLength = WORD_SIZE_THUMB; } + if (_updateStackTrace(d, debugger->cpu->gprs[ARM_PC] - instructionLength)) { + return; + } struct ARMDebugBreakpoint* breakpoint = _lookupBreakpoint(&debugger->breakpoints, debugger->cpu->gprs[ARM_PC] - instructionLength); if (!breakpoint) { return; @@ -81,6 +144,8 @@ static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform*); static void ARMDebuggerTrace(struct mDebuggerPlatform*, char* out, size_t* length); static bool ARMDebuggerGetRegister(struct mDebuggerPlatform*, const char* name, int32_t* value); static bool ARMDebuggerSetRegister(struct mDebuggerPlatform*, const char* name, int32_t value); +static uint32_t ARMDebuggerGetStackTraceMode(struct mDebuggerPlatform*); +static void ARMDebuggerSetStackTraceMode(struct mDebuggerPlatform*, uint32_t); struct mDebuggerPlatform* ARMDebuggerPlatformCreate(void) { struct mDebuggerPlatform* platform = (struct mDebuggerPlatform*) malloc(sizeof(struct ARMDebugger)); @@ -97,6 +162,8 @@ struct mDebuggerPlatform* ARMDebuggerPlatformCreate(void) { platform->trace = ARMDebuggerTrace; platform->getRegister = ARMDebuggerGetRegister; platform->setRegister = ARMDebuggerSetRegister; + platform->getStackTraceMode = ARMDebuggerGetStackTraceMode; + platform->setStackTraceMode = ARMDebuggerSetStackTraceMode; return platform; } @@ -108,6 +175,10 @@ void ARMDebuggerInit(void* cpu, struct mDebuggerPlatform* platform) { ARMDebugBreakpointListInit(&debugger->breakpoints, 0); ARMDebugBreakpointListInit(&debugger->swBreakpoints, 0); mWatchpointListInit(&debugger->watchpoints, 0); + struct mStackTrace* stack = &platform->p->stackTrace; + mStackTraceInit(stack, sizeof(struct ARMRegisterFile)); + // TODO + stack->formatRegisters = NULL; } void ARMDebuggerDeinit(struct mDebuggerPlatform* platform) { @@ -133,6 +204,8 @@ void ARMDebuggerDeinit(struct mDebuggerPlatform* platform) { } ARMDebugBreakpointListDeinit(&debugger->swBreakpoints); mWatchpointListDeinit(&debugger->watchpoints); + + mStackTraceDeinit(&platform->p->stackTrace); } static void ARMDebuggerEnter(struct mDebuggerPlatform* platform, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) { @@ -261,7 +334,7 @@ static void ARMDebuggerListBreakpoints(struct mDebuggerPlatform* d, struct mBrea ++i; } else if (sw) { *b = sw->d; - ++s; + ++s; } else { abort(); // Should be unreachable } @@ -270,7 +343,7 @@ static void ARMDebuggerListBreakpoints(struct mDebuggerPlatform* d, struct mBrea static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform* d) { struct ARMDebugger* debugger = (struct ARMDebugger*) d; - return ARMDebugBreakpointListSize(&debugger->breakpoints) || mWatchpointListSize(&debugger->watchpoints); + return ARMDebugBreakpointListSize(&debugger->breakpoints) || mWatchpointListSize(&debugger->watchpoints) || debugger->stackTraceMode != STACK_TRACE_DISABLED; } static ssize_t ARMDebuggerSetWatchpoint(struct mDebuggerPlatform* d, const struct mWatchpoint* info) { @@ -403,3 +476,17 @@ bool ARMDebuggerSetRegister(struct mDebuggerPlatform* d, const char* name, int32 } return false; } + +static uint32_t ARMDebuggerGetStackTraceMode(struct mDebuggerPlatform* d) { + struct ARMDebugger* debugger = (struct ARMDebugger*) d; + return debugger->stackTraceMode; +} + +static void ARMDebuggerSetStackTraceMode(struct mDebuggerPlatform* d, uint32_t mode) { + struct ARMDebugger* debugger = (struct ARMDebugger*) d; + struct mStackTrace* stack = &d->p->stackTrace; + if (mode == STACK_TRACE_DISABLED && debugger->stackTraceMode != STACK_TRACE_DISABLED) { + mStackTraceClear(stack); + } + debugger->stackTraceMode = mode; +} diff --git a/src/sm83/debugger/debugger.c b/src/sm83/debugger/debugger.c index 8921ac095..1e540a66f 100644 --- a/src/sm83/debugger/debugger.c +++ b/src/sm83/debugger/debugger.c @@ -90,6 +90,8 @@ struct mDebuggerPlatform* SM83DebuggerPlatformCreate(void) { platform->d.trace = SM83DebuggerTrace; platform->d.getRegister = SM83DebuggerGetRegister; platform->d.setRegister = SM83DebuggerSetRegister; + platform->d.getStackTraceMode = NULL; + platform->d.setStackTraceMode = NULL; platform->printStatus = NULL; return &platform->d; }