src/debugger/stack-trace.c (view raw)
1/* Copyright (c) 2013-2016 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
8#include <mgba/core/core.h>
9
10#define CHECK_LENGTH() \
11 if (written >= *length) { \
12 *length = written; \
13 return; \
14 }
15
16DEFINE_VECTOR(mStackFrames, struct mStackFrame);
17
18void mStackTraceInit(struct mStackTrace* stack, size_t registersSize) {
19 mStackFramesInit(&stack->stack, 0);
20 stack->registersSize = registersSize;
21}
22
23void mStackTraceDeinit(struct mStackTrace* stack) {
24 mStackTraceClear(stack);
25 mStackFramesDeinit(&stack->stack);
26}
27
28void mStackTraceClear(struct mStackTrace* stack) {
29 ssize_t i = mStackTraceGetDepth(stack) - 1;
30 while (i >= 0) {
31 free(mStackTraceGetFrame(stack, i)->regs);
32 --i;
33 }
34 mStackFramesClear(&stack->stack);
35}
36
37size_t mStackTraceGetDepth(struct mStackTrace* stack) {
38 return mStackFramesSize(&stack->stack);
39}
40
41struct mStackFrame* mStackTracePush(struct mStackTrace* stack, uint32_t pc, uint32_t destAddress, uint32_t sp, void* regs) {
42 struct mStackFrame* frame = mStackFramesAppend(&stack->stack);
43 frame->callAddress = pc;
44 frame->entryAddress = destAddress;
45 frame->frameBaseAddress = sp;
46 frame->regs = malloc(stack->registersSize);
47 frame->finished = false;
48 frame->breakWhenFinished = false;
49 memcpy(frame->regs, regs, stack->registersSize);
50 return frame;
51}
52
53struct mStackFrame* mStackTraceGetFrame(struct mStackTrace* stack, uint32_t frame) {
54 size_t depth = mStackTraceGetDepth(stack);
55 if (frame >= depth) {
56 return NULL;
57 }
58 return mStackFramesGetPointer(&stack->stack, depth - frame - 1);
59}
60
61void mStackTraceFormatFrame(struct mStackTrace* stack, uint32_t frame, char* out, size_t* length) {
62 struct mStackFrame* stackFrame = mStackTraceGetFrame(stack, frame);
63 struct mStackFrame* prevFrame = mStackTraceGetFrame(stack, frame + 1);
64 size_t written = snprintf(out, *length, "#%d ", frame);
65 CHECK_LENGTH();
66 if (prevFrame) {
67 written += snprintf(out + written, *length - written, "%08X ", prevFrame->entryAddress);
68 CHECK_LENGTH();
69 }
70 if (!stackFrame) {
71 written += snprintf(out + written, *length - written, "no stack frame available)\n");
72 *length = written;
73 return;
74 } else if (stack->formatRegisters) {
75 written += snprintf(out + written, *length - written, "(");
76 CHECK_LENGTH();
77 size_t formattedSize = *length - written;
78 stack->formatRegisters(stackFrame, out + written, &formattedSize);
79 written += formattedSize;
80 CHECK_LENGTH();
81 written += snprintf(out + written, *length - written, ")\n ");
82 CHECK_LENGTH();
83 }
84 if (prevFrame) {
85 int32_t offset = stackFrame->callAddress - prevFrame->entryAddress;
86 written += snprintf(out + written, *length - written, "at %08X [%08X+%d]\n", stackFrame->callAddress, prevFrame->entryAddress, offset);
87 } else {
88 written += snprintf(out + written, *length - written, "at %08X\n", stackFrame->callAddress);
89 }
90 *length = written;
91}
92
93void mStackTracePop(struct mStackTrace* stack) {
94 size_t depth = mStackTraceGetDepth(stack);
95 if (depth < 1) {
96 return;
97 }
98 struct mStackFrame* frame = mStackFramesGetPointer(&stack->stack, depth - 1);
99 free(frame->regs);
100 mStackFramesResize(&stack->stack, -1);
101}