src/debugger/stack-trace.c (view raw)
1/* Copyright (c) 2013-2020 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include <mgba/internal/debugger/stack-trace.h>
7#include <mgba/internal/debugger/symbols.h>
8
9#include <mgba/core/core.h>
10
11#define CHECK_LENGTH() \
12 if (written >= *length) { \
13 *length = written; \
14 return; \
15 }
16
17DEFINE_VECTOR(mStackFrames, struct mStackFrame);
18
19void mStackTraceInit(struct mStackTrace* stack, size_t registersSize) {
20 mStackFramesInit(&stack->stack, 0);
21 stack->registersSize = registersSize;
22}
23
24void mStackTraceDeinit(struct mStackTrace* stack) {
25 mStackTraceClear(stack);
26 mStackFramesDeinit(&stack->stack);
27}
28
29void mStackTraceClear(struct mStackTrace* stack) {
30 ssize_t i = mStackTraceGetDepth(stack) - 1;
31 while (i >= 0) {
32 free(mStackTraceGetFrame(stack, i)->regs);
33 --i;
34 }
35 mStackFramesClear(&stack->stack);
36}
37
38size_t mStackTraceGetDepth(struct mStackTrace* stack) {
39 return mStackFramesSize(&stack->stack);
40}
41
42struct mStackFrame* mStackTracePush(struct mStackTrace* stack, uint32_t pc, uint32_t destAddress, uint32_t sp, void* regs) {
43 struct mStackFrame* frame = mStackFramesAppend(&stack->stack);
44 frame->callSegment = -1;
45 frame->callAddress = pc;
46 frame->entrySegment = -1;
47 frame->entryAddress = destAddress;
48 frame->frameBaseSegment = -1;
49 frame->frameBaseAddress = sp;
50 frame->regs = malloc(stack->registersSize);
51 frame->finished = false;
52 frame->breakWhenFinished = false;
53 frame->interrupt = false;
54 memcpy(frame->regs, regs, stack->registersSize);
55 return frame;
56}
57
58struct mStackFrame* mStackTracePushSegmented(struct mStackTrace* stack, int pcSegment, uint32_t pc, int destSegment, uint32_t destAddress, int spSegment, uint32_t sp, void* regs) {
59 struct mStackFrame* frame = mStackTracePush(stack, pc, destAddress, sp, regs);
60 frame->callSegment = pcSegment;
61 frame->entrySegment = destSegment;
62 frame->frameBaseSegment = spSegment;
63 return frame;
64}
65
66struct mStackFrame* mStackTraceGetFrame(struct mStackTrace* stack, uint32_t frame) {
67 size_t depth = mStackTraceGetDepth(stack);
68 if (frame >= depth) {
69 return NULL;
70 }
71 return mStackFramesGetPointer(&stack->stack, depth - frame - 1);
72}
73
74void mStackTraceFormatFrame(struct mStackTrace* stack, struct mDebuggerSymbols* st, uint32_t frame, char* out, size_t* length) {
75 struct mStackFrame* stackFrame = mStackTraceGetFrame(stack, frame);
76 struct mStackFrame* prevFrame = mStackTraceGetFrame(stack, frame + 1);
77 size_t written = snprintf(out, *length, "#%d ", frame);
78 CHECK_LENGTH();
79 if (!stackFrame) {
80 written += snprintf(out + written, *length - written, "(no stack frame available)\n");
81 *length = written;
82 return;
83 }
84 const char* functionName = mDebuggerSymbolReverseLookup(st, stackFrame->entryAddress, stackFrame->entrySegment);
85 if (functionName) {
86 written += snprintf(out + written, *length - written, "%s ", functionName);
87 } else if (prevFrame->entrySegment >= 0) {
88 written += snprintf(out + written, *length - written, "0x%02X:%08X ", stackFrame->entrySegment, stackFrame->entryAddress);
89 } else {
90 written += snprintf(out + written, *length - written, "0x%08X ", stackFrame->entryAddress);
91 }
92 CHECK_LENGTH();
93 if (stack->formatRegisters) {
94 written += snprintf(out + written, *length - written, "(");
95 CHECK_LENGTH();
96 char buffer[1024];
97 size_t formattedSize = sizeof(buffer) - 2;
98 stack->formatRegisters(stackFrame, buffer, &formattedSize);
99 written += snprintf(out + written, *length - written, "%s)\n ", buffer);
100 CHECK_LENGTH();
101 }
102 if (stackFrame->callSegment >= 0) {
103 written += snprintf(out + written, *length - written, "at 0x%02X:%08X", stackFrame->callSegment, stackFrame->callAddress);
104 } else {
105 written += snprintf(out + written, *length - written, "at 0x%08X", stackFrame->callAddress);
106 }
107 CHECK_LENGTH();
108 if (prevFrame) {
109 int32_t offset = stackFrame->callAddress - prevFrame->entryAddress;
110 if (offset >= 0) {
111 functionName = mDebuggerSymbolReverseLookup(st, prevFrame->entryAddress, prevFrame->entrySegment);
112 if (functionName) {
113 written += snprintf(out + written, *length - written, " [%s+%d]", functionName, offset);
114 } else if (stackFrame->entrySegment >= 0) {
115 written += snprintf(out + written, *length - written, " [0x%02X:%08X+%d]", prevFrame->entrySegment, prevFrame->entryAddress, offset);
116 } else {
117 written += snprintf(out + written, *length - written, " [0x%08X+%d]", prevFrame->entryAddress, offset);
118 }
119 }
120 }
121 CHECK_LENGTH();
122 written += snprintf(out + written, *length - written, "\n");
123 *length = written;
124}
125
126void mStackTracePop(struct mStackTrace* stack) {
127 size_t depth = mStackTraceGetDepth(stack);
128 if (depth > 0) {
129 struct mStackFrame* frame = mStackFramesGetPointer(&stack->stack, depth - 1);
130 free(frame->regs);
131 mStackFramesResize(&stack->stack, -1);
132 }
133}