Merge pull request #59 from someone2639/master

Crash Screen: Report Name of Crashed Function + Stacktrace
This commit is contained in:
someone2639
2021-09-20 17:18:57 -04:00
committed by GitHub
6 changed files with 244 additions and 17 deletions

View File

@@ -108,6 +108,8 @@ else ifeq ($(VERSION),sh)
GRUCODE ?= f3dzex
endif
DEBUG_MAP_STACKTRACE_FLAG := -D DEBUG_MAP_STACKTRACE
TARGET := sm64.$(VERSION)
@@ -575,7 +577,7 @@ $(BUILD_DIR)/src/usb/usb.o: CFLAGS += -Wno-unused-variable -Wno-sign-compare -Wn
$(BUILD_DIR)/src/usb/debug.o: OPT_FLAGS := -O0
$(BUILD_DIR)/src/usb/debug.o: CFLAGS += -Wno-unused-parameter -Wno-maybe-uninitialized
ALL_DIRS := $(BUILD_DIR) $(addprefix $(BUILD_DIR)/,$(SRC_DIRS) $(GODDARD_SRC_DIRS) $(LIBZ_SRC_DIRS) $(ULTRA_BIN_DIRS) $(BIN_DIRS) $(TEXTURE_DIRS) $(TEXT_DIRS) $(SOUND_SAMPLE_DIRS) $(addprefix levels/,$(LEVEL_DIRS)) rsp include) $(YAY0_DIR) $(addprefix $(YAY0_DIR)/,$(VERSION)) $(SOUND_BIN_DIR) $(SOUND_BIN_DIR)/sequences/$(VERSION)
ALL_DIRS := $(BUILD_DIR) $(addprefix $(BUILD_DIR)/,$(SRC_DIRS) asm/debug $(GODDARD_SRC_DIRS) $(LIBZ_SRC_DIRS) $(ULTRA_BIN_DIRS) $(BIN_DIRS) $(TEXTURE_DIRS) $(TEXT_DIRS) $(SOUND_SAMPLE_DIRS) $(addprefix levels/,$(LEVEL_DIRS)) rsp include) $(YAY0_DIR) $(addprefix $(YAY0_DIR)/,$(VERSION)) $(SOUND_BIN_DIR) $(SOUND_BIN_DIR)/sequences/$(VERSION)
# Make sure build directory exists before compiling anything
DUMMY != mkdir -p $(ALL_DIRS)
@@ -766,7 +768,7 @@ $(BUILD_DIR)/rsp/%.bin $(BUILD_DIR)/rsp/%_data.bin: rsp/%.s
# Run linker script through the C preprocessor
$(BUILD_DIR)/$(LD_SCRIPT): $(LD_SCRIPT) $(BUILD_DIR)/goddard.txt
$(call print,Preprocessing linker script:,$<,$@)
$(V)$(CPP) $(CPPFLAGS) -DBUILD_DIR=$(BUILD_DIR) -MMD -MP -MT $@ -MF $@.d -o $@ $<
$(V)$(CPP) $(CPPFLAGS) -DBUILD_DIR=$(BUILD_DIR) $(DEBUG_MAP_STACKTRACE_FLAG) -MMD -MP -MT $@ -MF $@.d -o $@ $<
# Link libgoddard
$(BUILD_DIR)/libgoddard.a: $(GODDARD_O_FILES)
@@ -791,10 +793,15 @@ $(BUILD_DIR)/goddard.txt: $(BUILD_DIR)/sm64_prelim.elf
$(call print,Getting Goddard size...)
$(V)python3 tools/getGoddardSize.py $(BUILD_DIR)/sm64_prelim.map $(VERSION)
$(BUILD_DIR)/asm/debug/map.o: asm/debug/map.s $(BUILD_DIR)/sm64_prelim.elf
$(call print,Assembling:,$<,$@)
$(V)python3 tools/mapPacker.py $(BUILD_DIR)/sm64_prelim.map $(BUILD_DIR)/bin/addr.bin $(BUILD_DIR)/bin/name.bin
$(V)$(CROSS)gcc -c $(ASMFLAGS) $(foreach i,$(INCLUDE_DIRS),-Wa,-I$(i)) -x assembler-with-cpp -MMD -MF $(BUILD_DIR)/$*.d -o $@ $<
# Link SM64 ELF file
$(ELF): $(BUILD_DIR)/sm64_prelim.elf $(O_FILES) $(YAY0_OBJ_FILES) $(SEG_FILES) $(BUILD_DIR)/$(LD_SCRIPT) undefined_syms.txt $(BUILD_DIR)/libz.a $(BUILD_DIR)/libgoddard.a
$(ELF): $(BUILD_DIR)/sm64_prelim.elf $(BUILD_DIR)/asm/debug/map.o $(O_FILES) $(YAY0_OBJ_FILES) $(SEG_FILES) $(BUILD_DIR)/$(LD_SCRIPT) undefined_syms.txt $(BUILD_DIR)/libz.a $(BUILD_DIR)/libgoddard.a
@$(PRINT) "$(GREEN)Linking ELF file: $(BLUE)$@ $(NO_COL)\n"
$(V)$(LD) --gc-sections -L $(BUILD_DIR) -T undefined_syms.txt -T $(BUILD_DIR)/$(LD_SCRIPT) -T goddard.txt -Map $(BUILD_DIR)/sm64.$(VERSION).map --no-check-sections $(addprefix -R ,$(SEG_FILES)) -o $@ $(O_FILES) -L$(LIBS_DIR) -l$(ULTRALIB) -Llib $(LINK_LIBRARIES) -u sprintf -u osMapTLB -Llib/gcclib/$(LIBGCCDIR) -lgcc -lnustd -lhvqm2
$(V)$(LD) --gc-sections -L $(BUILD_DIR) -T undefined_syms.txt -T $(BUILD_DIR)/$(LD_SCRIPT) -T goddard.txt -Map $(BUILD_DIR)/sm64.$(VERSION).map --no-check-sections $(addprefix -R ,$(SEG_FILES)) -o $@ $(O_FILES) -L$(LIBS_DIR) -l$(ULTRALIB) -Llib $(LINK_LIBRARIES) -u sprintf -u osMapTLB -Llib/gcclib/$(LIBGCCDIR) -lgcc
# Build ROM
$(ROM): $(ELF)

17
asm/debug/map.s Normal file
View File

@@ -0,0 +1,17 @@
.include "macros.inc"
.section .data
.balign 16
glabel gMapEntries
.incbin "bin/addr.bin"
glabel gMapEntryEnd
.balign 16
glabel gMapStrings
.incbin "bin/name.bin"
glabel gMapStringsEnd
.balign 16
glabel gMapEntrySize
.word (gMapEntryEnd - gMapEntries) / 4
glabel gMapStringSize
.word (gMapStringsEnd - gMapStrings)

31
sm64.ld
View File

@@ -144,12 +144,23 @@ SECTIONS
. = _hvqbufSegmentBssEnd;
#endif
/* hardcoded symbols to satisfy preliminary link for map parser */
#ifndef DEBUG_MAP_STACKTRACE
parse_map = 0x80345678;
find_function_in_stack = 0x80345678;
_mapDataSegmentRomStart = 0;
gMapEntries = 0;
gMapEntrySize = 0;
gMapStrings = 0;
#endif
BEGIN_SEG(main, .) SUBALIGN(16)
{
KEEP(BUILD_DIR/asm/entry.o(.text));
BUILD_DIR/src/boot*.o(.text);
BUILD_DIR/src/hvqm*.o(.text);
BUILD_DIR/src/usb*.o(.text);
BUILD_DIR/src/audio*.o(.text);
#ifdef S2DEX_TEXT_ENGINE
lib/libs2d_engine.a:*(.text);
@@ -165,9 +176,11 @@ SECTIONS
*/libhvqm2.a:*.o(.text);
BUILD_DIR/lib/rsp.o(.text);
lib/PR/hvqm/hvqm2sp1.o(.text);
_mainSegmentTextEnd = .;
/* data */
BUILD_DIR/src/boot*.o(.*data*);
BUILD_DIR/src/usb*.o(.*data*);
BUILD_DIR/src/audio*.o(.*data*);
#ifdef S2DEX_TEXT_ENGINE
lib/libs2d_engine.a:*(.*data*);
@@ -185,6 +198,7 @@ SECTIONS
/* rodata */
BUILD_DIR/src/boot*.o(.rodata*);
BUILD_DIR/src/usb*.o(.rodata*);
BUILD_DIR/src/audio*.o(.rodata*);
#ifdef S2DEX_TEXT_ENGINE
lib/libs2d_engine.a:*(.rodata*);
@@ -201,13 +215,11 @@ SECTIONS
lib/PR/hvqm/hvqm2sp1.o(.rodata*);
}
END_SEG(main)
#ifndef PRELIMINARY
ASSERT((_mainSegmentRomEnd <= 0x101000), "Error: Please shrink your main segment to under 1MB.")
#endif
BEGIN_NOLOAD(main)
{
BUILD_DIR/src/boot*.o(.*bss*);
BUILD_DIR/src/hvqm*.o(.*bss*);
BUILD_DIR/src/usb*.o(.*bss*);
BUILD_DIR/src/audio*.o(.*bss*);
#ifdef S2DEX_TEXT_ENGINE
lib/libs2d_engine.a:*(.*bss*);
@@ -233,23 +245,20 @@ SECTIONS
{
BUILD_DIR/src/game*.o(.text);
BUILD_DIR/src/engine*.o(.text);
BUILD_DIR/src/usb*.o(.text);
_engineSegmentTextEnd = .;
/* data */
BUILD_DIR/src/game*.o(.*data*);
BUILD_DIR/src/engine*.o(.data*);
BUILD_DIR/src/engine*.o(.sdata*);
BUILD_DIR/src/usb*.o(.*data*);
/* rodata */
BUILD_DIR/src/game*.o(.rodata*);
BUILD_DIR/src/engine*.o(.rodata*);
BUILD_DIR/src/usb*.o(.rodata*);
}
END_SEG(engine)
BEGIN_NOLOAD(engine)
{
BUILD_DIR/src/game*.o(.*bss*);
BUILD_DIR/src/engine*.o(.bss*);
BUILD_DIR/src/usb*.o(.*bss*);
. = ALIGN(0x40);
}
END_NOLOAD(engine)
@@ -330,6 +339,7 @@ SECTIONS
BEGIN_SEG(goddard, RAM_END - GODDARD_SIZE)
{
KEEP(BUILD_DIR/src/menu*.o(.text));
_goddardSegmentTextEnd = .;
KEEP(BUILD_DIR/src/menu*.o(.data*));
KEEP(BUILD_DIR/src/menu*.o(.rodata*));
#ifdef KEEP_MARIO_HEAD
@@ -497,6 +507,13 @@ SECTIONS
}
END_SEG(capcom)
#endif
#ifdef DEBUG_MAP_STACKTRACE
BEGIN_SEG(mapData, 0x80700000) {
KEEP(BUILD_DIR/asm/debug/map.o(.data*));
}
END_SEG(mapData)
#endif
/* DWARF debug sections.
Symbols in the DWARF debugging sections are relative to the beginning
of the section so we begin them at 0. */

View File

@@ -6,6 +6,7 @@
#include "types.h"
#include "puppyprint.h"
#include "audio/external.h"
#include "farcall.h"
#include "game_init.h"
#include "main.h"
@@ -14,8 +15,9 @@
#include "printf.h"
enum crashPages {
PAGE_STACK,
PAGE_CONTEXT,
PAGE_LOG,
PAGE_STACKTRACE,
PAGE_COUNT
};
@@ -65,6 +67,9 @@ char *gFpcsrDesc[6] = {
extern u64 osClockRate;
extern far char *parse_map(u32);
extern far void map_data_init(void);
extern far char *find_function_in_stack(u32 *);
struct {
OSThread thread;
@@ -165,7 +170,7 @@ void crash_screen_print_float_reg(s32 x, s32 y, s32 regNum, void *addr) {
if ((exponent >= -0x7E && exponent <= 0x7F) || bits == 0) {
crash_screen_print(x, y, "F%02d:%.3e", regNum, *(f32 *) addr);
} else {
crash_screen_print(x, y, "F%02d:---------", regNum);
crash_screen_print(x, y, "F%02d:%08XD", regNum, *(u32 *) addr);
}
}
@@ -184,7 +189,7 @@ void crash_screen_print_fpcsr(u32 fpcsr) {
}
}
void draw_crash_stack(OSThread *thread, s32 cause)
void draw_crash_context(OSThread *thread, s32 cause)
{
__OSThreadContext *tc = &thread->context;
@@ -193,7 +198,14 @@ void draw_crash_stack(OSThread *thread, s32 cause)
crash_screen_print(30, 35, "PC:%08XH SR:%08XH VA:%08XH", tc->pc, tc->sr, tc->badvaddr);
osWritebackDCacheAll();
crash_screen_draw_rect(25, 45, 270, 185);
crash_screen_print(30, 50, "AT:%08XH V0:%08XH V1:%08XH", (u32) tc->at, (u32) tc->v0, (u32) tc->v1);
if ((u32)parse_map == 0x80345678) {
crash_screen_print(30, 50, "AT:%08XH V0:%08XH V1:%08XH", (u32) tc->at, (u32) tc->v0,
(u32) tc->v1);
} else {
char *fname = parse_map(tc->pc);
crash_screen_print(30, 50, "CRASH AT: %s", fname == NULL ? "UNKNOWN" : fname);
}
// crash_screen_print(30, 50, "AT:%08XH V0:%08XH V1:%08XH", (u32) tc->at, (u32) tc->v0, (u32) tc->v1);
crash_screen_print(30, 60, "A0:%08XH A1:%08XH A2:%08XH", (u32) tc->a0, (u32) tc->a1, (u32) tc->a2);
crash_screen_print(30, 70, "A3:%08XH T0:%08XH T1:%08XH", (u32) tc->a3, (u32) tc->t0, (u32) tc->t1);
crash_screen_print(30, 80, "T2:%08XH T3:%08XH T4:%08XH", (u32) tc->t2, (u32) tc->t3, (u32) tc->t4);
@@ -241,6 +253,46 @@ void draw_crash_log(OSThread *thread, s32 cause)
}
// prints any function pointers it finds in the stack
// format:
// SP address: function name
void draw_stacktrace(OSThread *thread, s32 cause) {
__OSThreadContext *tc = &thread->context;
u32 temp_sp = tc->sp + 0x14;
crash_screen_draw_rect(25, 20, 270, 25);
crash_screen_print(30, 25, "STACK TRACE FROM %08X:", temp_sp);
if ((u32) parse_map == 0x80345678) {
crash_screen_print(30, 35, "CURRFUNC: NONE");
} else {
crash_screen_print(30, 35, "CURRFUNC: %s", parse_map(tc->pc));
}
osWritebackDCacheAll();
for (int i = 0; i < 18; i++) {
if ((u32) find_function_in_stack == 0x80345678) {
crash_screen_print(30, 45 + (i * 10), "STACK TRACE DISABLED");
break;
} else {
if ((u32) find_function_in_stack == 0x80345678) {
return;
}
char *fname = find_function_in_stack(&temp_sp);
if (fname == NULL || (*(u32*)temp_sp & 0x80000000 == 0)) {
crash_screen_print(30, 45 + (i * 10), "%08X: UNKNOWN", temp_sp);
} else {
crash_screen_print(30, 45 + (i * 10), "%08X: %s", temp_sp, fname);
}
}
}
}
void draw_crash_screen(OSThread *thread)
{
s32 cause;
@@ -282,8 +334,9 @@ void draw_crash_screen(OSThread *thread)
crash_screen_print(15, 10, "Page:%d L/Z: Left R: Right", crashPage);
switch (crashPage)
{
case PAGE_STACK: draw_crash_stack(thread, cause); break;
case PAGE_LOG: draw_crash_log(thread, cause); break;
case PAGE_CONTEXT: draw_crash_context(thread, cause); break;
case PAGE_LOG: draw_crash_log(thread, cause); break;
case PAGE_STACKTRACE: draw_stacktrace(thread, cause); break;
}
osWritebackDCacheAll();
@@ -321,6 +374,9 @@ void thread2_crash_screen(UNUSED void *arg) {
osSetEventMesg(OS_EVENT_FAULT, &gCrashScreen.mesgQueue, (OSMesg) 2);
goto finished;
reset:
if ((u32) map_data_init != 0x80345678) {
map_data_init();
}
gCrashScreen.thread.priority = 15;
stop_sounds_in_continuous_banks();
stop_background_music(sBackgroundMusicQueue[0].seqId);

88
src/game/map_parser.c Normal file
View File

@@ -0,0 +1,88 @@
#include <ultra64.h>
#include <PR/os_internal_error.h>
#include <stdarg.h>
#include <string.h>
#include "segments.h"
#define STACK_TRAVERSAL_LIMIT 100
struct MapEntry {
u32 addr;
u32 nm_offset;
u32 nm_len;
u32 pad;
};
extern u8 gMapStrings[];
extern struct MapEntry gMapEntries[];
extern u32 gMapEntrySize;
extern u8 _mapDataSegmentRomStart[];
// code provided by Wiseguy
static s32 headless_dma(u32 devAddr, void *dramAddr, u32 size)
{
register u32 stat;
stat = IO_READ(PI_STATUS_REG);
while (stat & (PI_STATUS_IO_BUSY | PI_STATUS_DMA_BUSY)) {
stat = IO_READ(PI_STATUS_REG);
}
IO_WRITE(PI_DRAM_ADDR_REG, K0_TO_PHYS(dramAddr));
IO_WRITE(PI_CART_ADDR_REG, K1_TO_PHYS((u32)osRomBase | devAddr));
IO_WRITE(PI_WR_LEN_REG, size - 1);
return 0;
}
static u32 headless_pi_status(void)
{
return IO_READ(PI_STATUS_REG);
}
// end of code provided by Wiseguy
void map_data_init(void) {
headless_dma(_mapDataSegmentRomStart, 0x80700000, 0x100000);
while (headless_pi_status() & (PI_STATUS_DMA_BUSY | PI_STATUS_ERROR));
}
char *parse_map(u32 pc) {
u32 i;
for (i = 0; i < gMapEntrySize; i++) {
if (gMapEntries[i].addr >= pc) break;
}
if (i == gMapEntrySize - 1) {
return NULL;
} else {
return (char*) ((u32)gMapStrings + gMapEntries[i - 1].nm_offset);
}
}
extern u8 _mainSegmentStart[];
extern u8 _mainSegmentTextEnd[];
extern u8 _engineSegmentStart[];
extern u8 _engineSegmentTextEnd[];
extern u8 _goddardSegmentStart[];
extern u8 _goddardSegmentTextEnd[];
char *find_function_in_stack(u32 *sp) {
for (int i = 0; i < STACK_TRAVERSAL_LIMIT; i++) {
u32 val = *sp;
val = *(u32 *)val;
*sp = *sp + 4;
if ((val >= (u32)_mainSegmentStart) && (val <= (u32)_mainSegmentTextEnd)) {
return parse_map(val);
}
else if ((val >= (u32)_engineSegmentStart) && (val <= (u32)_engineSegmentTextEnd)) {
return parse_map(val);
}
else if ((val >= (u32)_goddardSegmentStart) && (val <= (u32)_goddardSegmentTextEnd)) {
return parse_map(val);
}
}
return NULL;
}

42
tools/mapPacker.py Normal file
View File

@@ -0,0 +1,42 @@
import sys, struct
class MapEntry():
def __init__(self, nm, addr):
self.name = nm
self.addr = addr
self.strlen = (len(nm) + 4) & (~3)
def __str__(self):
return "%s %s %d" % (self.addr, self.name, self.strlen)
def __repr__(self):
return "%s %s %d" % (self.addr, self.name, self.strlen)
structDef = ">LLLL"
symNames = []
with open(sys.argv[1]) as f:
for line in f:
if "0x000000008" in line and "=" not in line and "." not in line and "*" not in line and "load address" not in line:
tokens = line.split()
symNames.append(MapEntry(tokens[1], int(tokens[0], 16)))
f1 = open(sys.argv[2], "wb+")
f2 = open(sys.argv[3], "wb+")
symNames.sort(key=lambda x: x.addr)
off = 0
for x in symNames:
f1.write(struct.pack(structDef, x.addr, off, len(x.name), 0))
f2.write(struct.pack(">%ds" % x.strlen, bytes(x.name, encoding="ascii")))
off += x.strlen
f1.close()
f2.close()
# print('\n'.join([str(hex(x.addr)) + " " + x.name for x in symNames]))