all repos — mgba @ d0edc7dc76ba5effca40e9e6e7caaff0e0bfcb38

mGBA Game Boy Advance Emulator

Stack trace: add tracing to ARM debugger
Adam Higerd chighland@gmail.com
Sun, 26 Jul 2020 23:56:46 -0500
commit

d0edc7dc76ba5effca40e9e6e7caaff0e0bfcb38

parent

fadd0391d6f2b143a470c97d6ae129346a789239

M include/mgba/debugger/debugger.hinclude/mgba/debugger/debugger.h

@@ -13,6 +13,7 @@

#include <mgba/core/cpu.h> #include <mgba/core/log.h> #include <mgba-util/vector.h> +#include <mgba/internal/debugger/stack-trace.h> mLOG_DECLARE_CATEGORY(DEBUGGER);

@@ -50,7 +51,8 @@ DEBUGGER_ENTER_MANUAL,

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 {

uint32_t opcode; enum mBreakpointType breakType; } bp; + + struct { + enum mStackTraceMode traceType; + } st; } type; ssize_t pointId; };

@@ -114,6 +120,9 @@

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 @@ enum mDebuggerState state;

enum mDebuggerType type; struct mCore* core; struct mScriptBridge* bridge; + struct mStackTrace stackTrace; void (*init)(struct mDebugger*); void (*deinit)(struct mDebugger*);
M include/mgba/internal/arm/debugger/debugger.hinclude/mgba/internal/arm/debugger/debugger.h

@@ -36,6 +36,7 @@ struct mWatchpointList watchpoints;

struct ARMMemory originalMemory; ssize_t nextId; + enum mStackTraceMode stackTraceMode; void (*entered)(struct mDebugger*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*);
M src/arm/debugger/debugger.csrc/arm/debugger/debugger.c

@@ -11,9 +11,69 @@ #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> +#include <mgba/internal/debugger/stack-trace.h> 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 @@ instructionLength = WORD_SIZE_ARM;

} 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 @@ platform->hasBreakpoints = ARMDebuggerHasBreakpoints;

platform->trace = ARMDebuggerTrace; platform->getRegister = ARMDebuggerGetRegister; platform->setRegister = ARMDebuggerSetRegister; + platform->getStackTraceMode = ARMDebuggerGetStackTraceMode; + platform->setStackTraceMode = ARMDebuggerSetStackTraceMode; return platform; }

@@ -108,6 +175,10 @@ debugger->nextId = 1;

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 @@ _destroyWatchpoint(mWatchpointListGetPointer(&debugger->watchpoints, i));

} 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 @@ *b = hw->d;

++i; } else if (sw) { *b = sw->d; - ++s; + ++s; } else { abort(); // Should be unreachable }

@@ -270,7 +343,7 @@ }

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 @@ return true;

} 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; +}
M src/sm83/debugger/debugger.csrc/sm83/debugger/debugger.c

@@ -90,6 +90,8 @@ platform->d.hasBreakpoints = SM83DebuggerHasBreakpoints;

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