all repos — mgba @ 41211639ba654e54ad1a1212682e4f829f3aad33

mGBA Game Boy Advance Emulator

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 (stackFrame->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 (prevFrame->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}