mirror of https://github.com/mgba-emu/mgba.git
Stack trace: WIP backtrace implementation
This commit is contained in:
parent
d0edc7dc76
commit
238c68f080
|
@ -36,7 +36,7 @@ struct ARMDebugger {
|
|||
struct ARMMemory originalMemory;
|
||||
|
||||
ssize_t nextId;
|
||||
enum mStackTraceMode stackTraceMode;
|
||||
uint32_t stackTraceMode;
|
||||
|
||||
void (*entered)(struct mDebugger*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*);
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ CXX_GUARD_START
|
|||
|
||||
// Bit 0: a register is involved with this operand
|
||||
// Bit 1: an immediate is invovled with this operand
|
||||
// Bit 2: a memory access is invovled with this operand
|
||||
// Bit 2: a memory access is involved with this operand
|
||||
// Bit 3: the destination of this operand is affected by this opcode
|
||||
// Bit 4: this operand is shifted by a register
|
||||
// Bit 5: this operand is shifted by an immediate
|
||||
|
|
|
@ -24,7 +24,6 @@ enum mStackTraceMode {
|
|||
};
|
||||
|
||||
struct mStackFrame {
|
||||
uint32_t instruction;
|
||||
uint32_t callAddress;
|
||||
uint32_t entryAddress;
|
||||
uint32_t frameBaseAddress;
|
||||
|
@ -47,8 +46,9 @@ void mStackTraceDeinit(struct mStackTrace* stack);
|
|||
|
||||
void mStackTraceClear(struct mStackTrace* stack);
|
||||
size_t mStackTraceGetDepth(struct mStackTrace* stack);
|
||||
struct mStackFrame* mStackTracePush(struct mStackTrace* stack, uint32_t instruction, uint32_t pc, uint32_t destAddress, uint32_t sp, void* regs);
|
||||
struct mStackFrame* mStackTraceGetFrame(struct mStackTrace* stack, size_t frame);
|
||||
struct mStackFrame* mStackTracePush(struct mStackTrace* stack, uint32_t pc, uint32_t destAddress, uint32_t sp, void* regs);
|
||||
struct mStackFrame* mStackTraceGetFrame(struct mStackTrace* stack, uint32_t frame);
|
||||
void mStackTraceFormatFrame(struct mStackTrace* stack, uint32_t frame, char* out, size_t* length);
|
||||
void mStackTracePop(struct mStackTrace* stack);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
|
|
@ -12,16 +12,21 @@
|
|||
#include <mgba/internal/arm/debugger/memory-debugger.h>
|
||||
#include <mgba/internal/debugger/parser.h>
|
||||
#include <mgba/internal/debugger/stack-trace.h>
|
||||
#include <stdint.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);
|
||||
if (info.branchType == ARM_BRANCH_NONE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct mStackTrace* stack = &d->p->stackTrace;
|
||||
struct mStackFrame* frame = mStackTraceGetFrame(stack, 0);
|
||||
bool isCall = (info.branchType & ARM_BRANCH_LINKED);
|
||||
uint32_t destAddress;
|
||||
|
@ -31,7 +36,24 @@ static bool _updateStackTrace(struct mDebuggerPlatform* d, uint32_t pc) {
|
|||
frame = NULL;
|
||||
}
|
||||
|
||||
if (info.operandFormat & ARM_OPERAND_IMMEDIATE_1) {
|
||||
if (info.operandFormat & ARM_OPERAND_MEMORY_1) {
|
||||
if (!isCall) {
|
||||
return false;
|
||||
}
|
||||
// count number of set bits, gcc/clang will convert to intrinsics
|
||||
int regCount = 0;
|
||||
uint32_t reglist = info.op1.immediate & 0xf;
|
||||
printf("%d\n", reglist);
|
||||
fflush(NULL);
|
||||
while (reglist != 0) {
|
||||
reglist &= reglist - 1;
|
||||
regCount++;
|
||||
}
|
||||
printf("->%d\n", regCount);
|
||||
fflush(NULL);
|
||||
uint32_t baseAddress = cpu->gprs[info.memory.baseReg] + ((regCount - 1) << 2);
|
||||
destAddress = cpu->memory.load32(cpu, baseAddress, NULL);
|
||||
} else if (info.operandFormat & ARM_OPERAND_IMMEDIATE_1) {
|
||||
if (!isCall) {
|
||||
return false;
|
||||
}
|
||||
|
@ -46,11 +68,11 @@ static bool _updateStackTrace(struct mDebuggerPlatform* d, uint32_t pc) {
|
|||
}
|
||||
|
||||
if (info.branchType & ARM_BRANCH_INDIRECT) {
|
||||
destAddress = cpu->memory.load32(cpu, destAddress, 0);
|
||||
destAddress = cpu->memory.load32(cpu, destAddress, NULL);
|
||||
}
|
||||
|
||||
if (isCall) {
|
||||
frame = mStackTracePush(stack, instruction, pc, destAddress, cpu->gprs[ARM_SP], &cpu->regs);
|
||||
frame = mStackTracePush(stack, pc, destAddress, cpu->gprs[ARM_SP], &cpu->regs);
|
||||
if (!(debugger->stackTraceMode & STACK_TRACE_BREAK_ON_CALL)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -107,10 +129,11 @@ static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform* d) {
|
|||
} else {
|
||||
instructionLength = WORD_SIZE_THUMB;
|
||||
}
|
||||
if (_updateStackTrace(d, debugger->cpu->gprs[ARM_PC] - instructionLength)) {
|
||||
uint32_t pc = debugger->cpu->gprs[ARM_PC] - instructionLength;
|
||||
if (debugger->stackTraceMode != STACK_TRACE_DISABLED && _updateStackTrace(d, pc)) {
|
||||
return;
|
||||
}
|
||||
struct ARMDebugBreakpoint* breakpoint = _lookupBreakpoint(&debugger->breakpoints, debugger->cpu->gprs[ARM_PC] - instructionLength);
|
||||
struct ARMDebugBreakpoint* breakpoint = _lookupBreakpoint(&debugger->breakpoints, pc);
|
||||
if (!breakpoint) {
|
||||
return;
|
||||
}
|
||||
|
@ -142,6 +165,8 @@ static void ARMDebuggerListWatchpoints(struct mDebuggerPlatform*, struct mWatchp
|
|||
static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform*);
|
||||
static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform*);
|
||||
static void ARMDebuggerTrace(struct mDebuggerPlatform*, char* out, size_t* length);
|
||||
static void ARMDebuggerFormatRegisters(struct ARMRegisterFile* regs, char* delimiter, char* disassembly, char* out, size_t* length);
|
||||
static void ARMDebuggerFrameFormatRegisters(struct mStackFrame* frame, 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*);
|
||||
|
@ -172,13 +197,13 @@ void ARMDebuggerInit(void* cpu, struct mDebuggerPlatform* platform) {
|
|||
debugger->cpu = cpu;
|
||||
debugger->originalMemory = debugger->cpu->memory;
|
||||
debugger->nextId = 1;
|
||||
debugger->stackTraceMode = STACK_TRACE_DISABLED;
|
||||
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;
|
||||
stack->formatRegisters = ARMDebuggerFrameFormatRegisters;
|
||||
}
|
||||
|
||||
void ARMDebuggerDeinit(struct mDebuggerPlatform* platform) {
|
||||
|
@ -393,12 +418,20 @@ static void ARMDebuggerTrace(struct mDebuggerPlatform* d, char* out, size_t* len
|
|||
}
|
||||
}
|
||||
|
||||
*length = snprintf(out, *length, "%08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X cpsr: %08X | %s",
|
||||
cpu->gprs[0], cpu->gprs[1], cpu->gprs[2], cpu->gprs[3],
|
||||
cpu->gprs[4], cpu->gprs[5], cpu->gprs[6], cpu->gprs[7],
|
||||
cpu->gprs[8], cpu->gprs[9], cpu->gprs[10], cpu->gprs[11],
|
||||
cpu->gprs[12], cpu->gprs[13], cpu->gprs[14], cpu->gprs[15],
|
||||
cpu->cpsr.packed, disassembly);
|
||||
ARMDebuggerFormatRegisters(&cpu->regs, " | ", disassembly, out, length);
|
||||
}
|
||||
|
||||
static void ARMDebuggerFormatRegisters(struct ARMRegisterFile* regs, char* delimiter, char* disassembly, char* out, size_t* length) {
|
||||
*length = snprintf(out, *length, "%08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X cpsr: %08X%s%s",
|
||||
regs->gprs[0], regs->gprs[1], regs->gprs[2], regs->gprs[3],
|
||||
regs->gprs[4], regs->gprs[5], regs->gprs[6], regs->gprs[7],
|
||||
regs->gprs[8], regs->gprs[9], regs->gprs[10], regs->gprs[11],
|
||||
regs->gprs[12], regs->gprs[13], regs->gprs[14], regs->gprs[15],
|
||||
regs->cpsr.packed, delimiter, disassembly);
|
||||
}
|
||||
|
||||
static void ARMDebuggerFrameFormatRegisters(struct mStackFrame* frame, char* out, size_t* length) {
|
||||
ARMDebuggerFormatRegisters((struct ARMRegisterFile*)&frame->regs, "", "", out, length);
|
||||
}
|
||||
|
||||
bool ARMDebuggerGetRegister(struct mDebuggerPlatform* d, const char* name, int32_t* value) {
|
||||
|
@ -489,4 +522,5 @@ static void ARMDebuggerSetStackTraceMode(struct mDebuggerPlatform* d, uint32_t m
|
|||
mStackTraceClear(stack);
|
||||
}
|
||||
debugger->stackTraceMode = mode;
|
||||
printf("SetStackTraceMode %d\n", mode);
|
||||
}
|
||||
|
|
|
@ -69,12 +69,17 @@ static void _dumpWord(struct CLIDebugger*, struct CLIDebugVector*);
|
|||
#ifdef ENABLE_SCRIPTING
|
||||
static void _source(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
#endif
|
||||
static void _backtrace(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _finish(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _setStackTraceMode(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
|
||||
static struct CLIDebuggerCommandSummary _debuggerCommands[] = {
|
||||
{ "backtrace", _backtrace, "i", "Print backtrace of all or specified frames" },
|
||||
{ "break", _setBreakpoint, "Is", "Set a breakpoint" },
|
||||
{ "continue", _continue, "", "Continue execution" },
|
||||
{ "delete", _clearBreakpoint, "I", "Delete a breakpoint or watchpoint" },
|
||||
{ "disassemble", _disassemble, "Ii", "Disassemble instructions" },
|
||||
{ "finish", _finish, "", "Execute until current stack frame returns" },
|
||||
{ "help", _printHelp, "S", "Print help" },
|
||||
{ "listb", _listBreakpoints, "", "List breakpoints" },
|
||||
{ "listw", _listWatchpoints, "", "List watchpoints" },
|
||||
|
@ -87,6 +92,7 @@ static struct CLIDebuggerCommandSummary _debuggerCommands[] = {
|
|||
{ "r/1", _readByte, "I", "Read a byte from a specified offset" },
|
||||
{ "r/2", _readHalfword, "I", "Read a halfword from a specified offset" },
|
||||
{ "r/4", _readWord, "I", "Read a word from a specified offset" },
|
||||
{ "stackmode", _setStackTraceMode, "S", "Changes the stack tracing mode" },
|
||||
{ "status", _printStatus, "", "Print the current status" },
|
||||
{ "trace", _trace, "Is", "Trace a number of instructions" },
|
||||
{ "w/1", _writeByte, "II", "Write a byte at a specified offset" },
|
||||
|
@ -111,6 +117,7 @@ static struct CLIDebuggerCommandSummary _debuggerCommands[] = {
|
|||
|
||||
static struct CLIDebuggerCommandAlias _debuggerCommandAliases[] = {
|
||||
{ "b", "break" },
|
||||
{ "bt", "backtrace" },
|
||||
{ "c", "continue" },
|
||||
{ "d", "delete" },
|
||||
{ "dis", "disassemble" },
|
||||
|
@ -979,7 +986,7 @@ static void _reportEntry(struct mDebugger* debugger, enum mDebuggerEntryReason r
|
|||
if (info->pointId > 0) {
|
||||
cliDebugger->backend->printf(cliDebugger->backend, "Hit breakpoint %" PRIz "i at 0x%08X\n", info->pointId, info->address);
|
||||
} else {
|
||||
cliDebugger->backend->printf(cliDebugger->backend, "Hit unknown breakpoint at 0x%08X\n", info->address);
|
||||
cliDebugger->backend->printf(cliDebugger->backend, "Hit unknown breakpoint at 0x%08X\n", info->address);
|
||||
}
|
||||
} else {
|
||||
cliDebugger->backend->printf(cliDebugger->backend, "Hit breakpoint\n");
|
||||
|
@ -1131,3 +1138,39 @@ bool CLIDebuggerTabComplete(struct CLIDebugger* debugger, const char* token, boo
|
|||
debugger->backend->lineAppend(debugger->backend, " ");
|
||||
return true;
|
||||
}
|
||||
|
||||
static void _backtrace(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
struct mDebuggerPlatform* platform = debugger->d.platform;
|
||||
if (!platform->getStackTraceMode) {
|
||||
debugger->backend->printf(debugger->backend, "Stack tracing is not supported by this platform.\n");
|
||||
return;
|
||||
} else if (platform->getStackTraceMode(platform) == STACK_TRACE_DISABLED) {
|
||||
debugger->backend->printf(debugger->backend, "Stack tracing is not enabled.\n");
|
||||
return;
|
||||
}
|
||||
struct mStackTrace* stack = &debugger->d.stackTrace;
|
||||
size_t frames = mStackTraceGetDepth(stack);
|
||||
if (dv && dv->type == CLIDV_INT_TYPE && dv->intValue < frames) {
|
||||
frames = dv->intValue;
|
||||
}
|
||||
size_t i;
|
||||
for (i = 0; i < frames; ++i) {
|
||||
char trace[1024];
|
||||
size_t traceSize = sizeof(trace) - 2;
|
||||
mStackTraceFormatFrame(stack, i, trace, &traceSize);
|
||||
debugger->backend->printf(debugger->backend, "%s", trace);
|
||||
}
|
||||
}
|
||||
|
||||
static void _finish(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
}
|
||||
|
||||
static void _setStackTraceMode(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
printf("setStackTraceMode\n");
|
||||
struct mDebuggerPlatform* platform = debugger->d.platform;
|
||||
if (!platform->setStackTraceMode) {
|
||||
debugger->backend->printf(debugger->backend, "Stack tracing is not supported by this platform.\n");
|
||||
return;
|
||||
}
|
||||
platform->setStackTraceMode(platform, STACK_TRACE_ENABLED);
|
||||
}
|
||||
|
|
|
@ -7,10 +7,17 @@
|
|||
|
||||
#include <mgba/core/core.h>
|
||||
|
||||
#define CHECK_LENGTH() \
|
||||
if (written >= *length) { \
|
||||
*length = written; \
|
||||
return; \
|
||||
}
|
||||
|
||||
DEFINE_VECTOR(mStackFrames, struct mStackFrame);
|
||||
|
||||
void mStackTraceInit(struct mStackTrace* stack, size_t registersSize) {
|
||||
mStackFramesInit(&stack->stack, 0);
|
||||
stack->registersSize = registersSize;
|
||||
}
|
||||
|
||||
void mStackTraceDeinit(struct mStackTrace* stack) {
|
||||
|
@ -24,15 +31,15 @@ void mStackTraceClear(struct mStackTrace* stack) {
|
|||
free(mStackTraceGetFrame(stack, i)->regs);
|
||||
--i;
|
||||
}
|
||||
mStackFramesClear(&stack->stack);
|
||||
}
|
||||
|
||||
size_t mStackTraceGetDepth(struct mStackTrace* stack) {
|
||||
return mStackFramesSize(&stack->stack);
|
||||
}
|
||||
|
||||
struct mStackFrame* mStackTracePush(struct mStackTrace* stack, uint32_t instruction, uint32_t pc, uint32_t destAddress, uint32_t sp, void* regs) {
|
||||
struct mStackFrame* mStackTracePush(struct mStackTrace* stack, uint32_t pc, uint32_t destAddress, uint32_t sp, void* regs) {
|
||||
struct mStackFrame* frame = mStackFramesAppend(&stack->stack);
|
||||
frame->instruction = instruction;
|
||||
frame->callAddress = pc;
|
||||
frame->entryAddress = destAddress;
|
||||
frame->frameBaseAddress = sp;
|
||||
|
@ -43,7 +50,7 @@ struct mStackFrame* mStackTracePush(struct mStackTrace* stack, uint32_t instruct
|
|||
return frame;
|
||||
}
|
||||
|
||||
struct mStackFrame* mStackTraceGetFrame(struct mStackTrace* stack, size_t frame) {
|
||||
struct mStackFrame* mStackTraceGetFrame(struct mStackTrace* stack, uint32_t frame) {
|
||||
size_t depth = mStackTraceGetDepth(stack);
|
||||
if (frame >= depth) {
|
||||
return NULL;
|
||||
|
@ -51,12 +58,44 @@ struct mStackFrame* mStackTraceGetFrame(struct mStackTrace* stack, size_t frame)
|
|||
return mStackFramesGetPointer(&stack->stack, depth - frame - 1);
|
||||
}
|
||||
|
||||
void mStackTraceFormatFrame(struct mStackTrace* stack, uint32_t frame, char* out, size_t* length) {
|
||||
struct mStackFrame* stackFrame = mStackTraceGetFrame(stack, frame);
|
||||
struct mStackFrame* prevFrame = mStackTraceGetFrame(stack, frame + 1);
|
||||
size_t written = snprintf(out, *length, "#%d ", frame);
|
||||
CHECK_LENGTH();
|
||||
if (prevFrame) {
|
||||
written += snprintf(out + written, *length - written, "%08X ", prevFrame->entryAddress);
|
||||
CHECK_LENGTH();
|
||||
}
|
||||
if (!stackFrame) {
|
||||
written += snprintf(out + written, *length - written, "no stack frame available)\n");
|
||||
*length = written;
|
||||
return;
|
||||
} else if (stack->formatRegisters) {
|
||||
written += snprintf(out + written, *length - written, "(");
|
||||
CHECK_LENGTH();
|
||||
size_t formattedSize = *length - written;
|
||||
stack->formatRegisters(stackFrame, out + written, &formattedSize);
|
||||
written += formattedSize;
|
||||
CHECK_LENGTH();
|
||||
written += snprintf(out + written, *length - written, ")\n ");
|
||||
CHECK_LENGTH();
|
||||
}
|
||||
if (prevFrame) {
|
||||
int32_t offset = stackFrame->callAddress - prevFrame->entryAddress;
|
||||
written += snprintf(out + written, *length - written, "at %08X [%08X+%d]\n", stackFrame->callAddress, prevFrame->entryAddress, offset);
|
||||
} else {
|
||||
written += snprintf(out + written, *length - written, "at %08X\n", stackFrame->callAddress);
|
||||
}
|
||||
*length = written;
|
||||
}
|
||||
|
||||
void mStackTracePop(struct mStackTrace* stack) {
|
||||
size_t depth = mStackTraceGetDepth(stack);
|
||||
if (depth == 0) {
|
||||
if (depth < 1) {
|
||||
return;
|
||||
}
|
||||
struct mStackFrame* frame = mStackFramesGetPointer(&stack->stack, depth - 1);
|
||||
free(frame->regs);
|
||||
mStackFramesResize(&stack->stack, depth - 1);
|
||||
mStackFramesResize(&stack->stack, -1);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue