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
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 frame->interrupt = false;
50 memcpy(frame->regs, regs, stack->registersSize);
51 return frame;
52}
53
54struct mStackFrame* mStackTraceGetFrame(struct mStackTrace* stack, uint32_t frame) {
55 size_t depth = mStackTraceGetDepth(stack);
56 if (frame >= depth) {
57 return NULL;
58 }
59 return mStackFramesGetPointer(&stack->stack, depth - frame - 1);
60}
61
62void mStackTraceFormatFrame(struct mStackTrace* stack, uint32_t frame, char* out, size_t* length) {
63 struct mStackFrame* stackFrame = mStackTraceGetFrame(stack, frame);
64 struct mStackFrame* prevFrame = mStackTraceGetFrame(stack, frame + 1);
65 size_t written = snprintf(out, *length, "#%d ", frame);
66 CHECK_LENGTH();
67 if (prevFrame) {
68 written += snprintf(out + written, *length - written, "%08X ", prevFrame->entryAddress);
69 CHECK_LENGTH();
70 }
71 if (!stackFrame) {
72 written += snprintf(out + written, *length - written, "no stack frame available)\n");
73 *length = written;
74 return;
75 } else if (stack->formatRegisters) {
76 written += snprintf(out + written, *length - written, "(");
77 CHECK_LENGTH();
78 char buffer[1024];
79 size_t formattedSize = sizeof(buffer) - 2;
80 stack->formatRegisters(stackFrame, buffer, &formattedSize);
81 written += snprintf(out + written, *length - written, "%s)\n ", buffer);
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 > 0) {
96 struct mStackFrame* frame = mStackFramesGetPointer(&stack->stack, depth - 1);
97 free(frame->regs);
98 mStackFramesResize(&stack->stack, -1);
99 }
100}