mirror of https://github.com/mgba-emu/mgba.git
Stack trace: add tracing to ARM debugger
This commit is contained in:
parent
fadd0391d6
commit
d0edc7dc76
|
@ -13,6 +13,7 @@ CXX_GUARD_START
|
||||||
#include <mgba/core/cpu.h>
|
#include <mgba/core/cpu.h>
|
||||||
#include <mgba/core/log.h>
|
#include <mgba/core/log.h>
|
||||||
#include <mgba-util/vector.h>
|
#include <mgba-util/vector.h>
|
||||||
|
#include <mgba/internal/debugger/stack-trace.h>
|
||||||
|
|
||||||
mLOG_DECLARE_CATEGORY(DEBUGGER);
|
mLOG_DECLARE_CATEGORY(DEBUGGER);
|
||||||
|
|
||||||
|
@ -50,7 +51,8 @@ enum mDebuggerEntryReason {
|
||||||
DEBUGGER_ENTER_ATTACHED,
|
DEBUGGER_ENTER_ATTACHED,
|
||||||
DEBUGGER_ENTER_BREAKPOINT,
|
DEBUGGER_ENTER_BREAKPOINT,
|
||||||
DEBUGGER_ENTER_WATCHPOINT,
|
DEBUGGER_ENTER_WATCHPOINT,
|
||||||
DEBUGGER_ENTER_ILLEGAL_OP
|
DEBUGGER_ENTER_ILLEGAL_OP,
|
||||||
|
DEBUGGER_ENTER_STACK
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mDebuggerEntryInfo {
|
struct mDebuggerEntryInfo {
|
||||||
|
@ -67,6 +69,10 @@ struct mDebuggerEntryInfo {
|
||||||
uint32_t opcode;
|
uint32_t opcode;
|
||||||
enum mBreakpointType breakType;
|
enum mBreakpointType breakType;
|
||||||
} bp;
|
} bp;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
enum mStackTraceMode traceType;
|
||||||
|
} st;
|
||||||
} type;
|
} type;
|
||||||
ssize_t pointId;
|
ssize_t pointId;
|
||||||
};
|
};
|
||||||
|
@ -114,6 +120,9 @@ struct mDebuggerPlatform {
|
||||||
bool (*getRegister)(struct mDebuggerPlatform*, const char* name, int32_t* value);
|
bool (*getRegister)(struct mDebuggerPlatform*, const char* name, int32_t* value);
|
||||||
bool (*setRegister)(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);
|
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 {
|
struct mDebugger {
|
||||||
|
@ -123,6 +132,7 @@ struct mDebugger {
|
||||||
enum mDebuggerType type;
|
enum mDebuggerType type;
|
||||||
struct mCore* core;
|
struct mCore* core;
|
||||||
struct mScriptBridge* bridge;
|
struct mScriptBridge* bridge;
|
||||||
|
struct mStackTrace stackTrace;
|
||||||
|
|
||||||
void (*init)(struct mDebugger*);
|
void (*init)(struct mDebugger*);
|
||||||
void (*deinit)(struct mDebugger*);
|
void (*deinit)(struct mDebugger*);
|
||||||
|
|
|
@ -36,6 +36,7 @@ struct ARMDebugger {
|
||||||
struct ARMMemory originalMemory;
|
struct ARMMemory originalMemory;
|
||||||
|
|
||||||
ssize_t nextId;
|
ssize_t nextId;
|
||||||
|
enum mStackTraceMode stackTraceMode;
|
||||||
|
|
||||||
void (*entered)(struct mDebugger*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*);
|
void (*entered)(struct mDebugger*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*);
|
||||||
|
|
||||||
|
|
|
@ -11,9 +11,69 @@
|
||||||
#include <mgba/internal/arm/isa-inlines.h>
|
#include <mgba/internal/arm/isa-inlines.h>
|
||||||
#include <mgba/internal/arm/debugger/memory-debugger.h>
|
#include <mgba/internal/arm/debugger/memory-debugger.h>
|
||||||
#include <mgba/internal/debugger/parser.h>
|
#include <mgba/internal/debugger/parser.h>
|
||||||
|
#include <mgba/internal/debugger/stack-trace.h>
|
||||||
|
|
||||||
DEFINE_VECTOR(ARMDebugBreakpointList, struct ARMDebugBreakpoint);
|
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) {
|
static struct ARMDebugBreakpoint* _lookupBreakpoint(struct ARMDebugBreakpointList* breakpoints, uint32_t address) {
|
||||||
size_t i;
|
size_t i;
|
||||||
for (i = 0; i < ARMDebugBreakpointListSize(breakpoints); ++i) {
|
for (i = 0; i < ARMDebugBreakpointListSize(breakpoints); ++i) {
|
||||||
|
@ -47,6 +107,9 @@ static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform* d) {
|
||||||
} else {
|
} else {
|
||||||
instructionLength = WORD_SIZE_THUMB;
|
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);
|
struct ARMDebugBreakpoint* breakpoint = _lookupBreakpoint(&debugger->breakpoints, debugger->cpu->gprs[ARM_PC] - instructionLength);
|
||||||
if (!breakpoint) {
|
if (!breakpoint) {
|
||||||
return;
|
return;
|
||||||
|
@ -81,6 +144,8 @@ static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform*);
|
||||||
static void ARMDebuggerTrace(struct mDebuggerPlatform*, char* out, size_t* length);
|
static void ARMDebuggerTrace(struct mDebuggerPlatform*, char* out, size_t* length);
|
||||||
static bool ARMDebuggerGetRegister(struct mDebuggerPlatform*, const char* name, int32_t* value);
|
static bool ARMDebuggerGetRegister(struct mDebuggerPlatform*, const char* name, int32_t* value);
|
||||||
static bool ARMDebuggerSetRegister(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* ARMDebuggerPlatformCreate(void) {
|
||||||
struct mDebuggerPlatform* platform = (struct mDebuggerPlatform*) malloc(sizeof(struct ARMDebugger));
|
struct mDebuggerPlatform* platform = (struct mDebuggerPlatform*) malloc(sizeof(struct ARMDebugger));
|
||||||
|
@ -97,6 +162,8 @@ struct mDebuggerPlatform* ARMDebuggerPlatformCreate(void) {
|
||||||
platform->trace = ARMDebuggerTrace;
|
platform->trace = ARMDebuggerTrace;
|
||||||
platform->getRegister = ARMDebuggerGetRegister;
|
platform->getRegister = ARMDebuggerGetRegister;
|
||||||
platform->setRegister = ARMDebuggerSetRegister;
|
platform->setRegister = ARMDebuggerSetRegister;
|
||||||
|
platform->getStackTraceMode = ARMDebuggerGetStackTraceMode;
|
||||||
|
platform->setStackTraceMode = ARMDebuggerSetStackTraceMode;
|
||||||
return platform;
|
return platform;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,6 +175,10 @@ void ARMDebuggerInit(void* cpu, struct mDebuggerPlatform* platform) {
|
||||||
ARMDebugBreakpointListInit(&debugger->breakpoints, 0);
|
ARMDebugBreakpointListInit(&debugger->breakpoints, 0);
|
||||||
ARMDebugBreakpointListInit(&debugger->swBreakpoints, 0);
|
ARMDebugBreakpointListInit(&debugger->swBreakpoints, 0);
|
||||||
mWatchpointListInit(&debugger->watchpoints, 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) {
|
void ARMDebuggerDeinit(struct mDebuggerPlatform* platform) {
|
||||||
|
@ -133,6 +204,8 @@ void ARMDebuggerDeinit(struct mDebuggerPlatform* platform) {
|
||||||
}
|
}
|
||||||
ARMDebugBreakpointListDeinit(&debugger->swBreakpoints);
|
ARMDebugBreakpointListDeinit(&debugger->swBreakpoints);
|
||||||
mWatchpointListDeinit(&debugger->watchpoints);
|
mWatchpointListDeinit(&debugger->watchpoints);
|
||||||
|
|
||||||
|
mStackTraceDeinit(&platform->p->stackTrace);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ARMDebuggerEnter(struct mDebuggerPlatform* platform, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) {
|
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;
|
++i;
|
||||||
} else if (sw) {
|
} else if (sw) {
|
||||||
*b = sw->d;
|
*b = sw->d;
|
||||||
++s;
|
++s;
|
||||||
} else {
|
} else {
|
||||||
abort(); // Should be unreachable
|
abort(); // Should be unreachable
|
||||||
}
|
}
|
||||||
|
@ -270,7 +343,7 @@ static void ARMDebuggerListBreakpoints(struct mDebuggerPlatform* d, struct mBrea
|
||||||
|
|
||||||
static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform* d) {
|
static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform* d) {
|
||||||
struct ARMDebugger* debugger = (struct ARMDebugger*) 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) {
|
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;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -90,6 +90,8 @@ struct mDebuggerPlatform* SM83DebuggerPlatformCreate(void) {
|
||||||
platform->d.trace = SM83DebuggerTrace;
|
platform->d.trace = SM83DebuggerTrace;
|
||||||
platform->d.getRegister = SM83DebuggerGetRegister;
|
platform->d.getRegister = SM83DebuggerGetRegister;
|
||||||
platform->d.setRegister = SM83DebuggerSetRegister;
|
platform->d.setRegister = SM83DebuggerSetRegister;
|
||||||
|
platform->d.getStackTraceMode = NULL;
|
||||||
|
platform->d.setStackTraceMode = NULL;
|
||||||
platform->printStatus = NULL;
|
platform->printStatus = NULL;
|
||||||
return &platform->d;
|
return &platform->d;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue