From 2814668cf5c919b2f3efe2fee1ed018efeecdd29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 31 Mar 2023 10:08:12 +0200 Subject: [PATCH] Show a MIPS stack trace on crash screen (#17211) * Print simple stack traces to log on crashes. * Display stack traces on crash screen * Show the in-function offset in the printed callstacks. * Libretro buildfix attempt --- Core/ControlMapper.cpp | 1 - Core/Core.cpp | 13 +++++++++++++ Core/Core.h | 1 + Core/HLE/sceKernel.cpp | 3 +-- Core/MIPS/MIPSStackWalk.cpp | 4 +++- Core/MIPS/MIPSStackWalk.h | 2 +- Core/MemFault.cpp | 39 +++++++++++++++++++++++++++++++++++-- Core/MemFault.h | 9 +++++++++ UI/EmuScreen.cpp | 10 +++++++++- libretro/Makefile.common | 1 + 10 files changed, 75 insertions(+), 8 deletions(-) diff --git a/Core/ControlMapper.cpp b/Core/ControlMapper.cpp index ad66020ff0..3cef1079f1 100644 --- a/Core/ControlMapper.cpp +++ b/Core/ControlMapper.cpp @@ -151,7 +151,6 @@ void ControlMapper::Update() { } } - inline bool IsAnalogStickKey(int key) { switch (key) { case VIRTKEY_AXIS_X_MIN: diff --git a/Core/Core.cpp b/Core/Core.cpp index 2b157e4e83..a1cf09cadc 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -37,6 +37,7 @@ #include "Core/MemMap.h" #include "Core/SaveState.h" #include "Core/System.h" +#include "Core/MemFault.h" #include "Core/Debugger/Breakpoints.h" #include "Core/HW/Display.h" #include "Core/MIPS/MIPS.h" @@ -443,6 +444,11 @@ void Core_MemoryException(u32 address, u32 accessSize, u32 pc, MemoryExceptionTy } if (!g_Config.bIgnoreBadMemAccess) { + // Try to fetch a call stack, to start with. + std::vector stackFrames = WalkCurrentStack(-1); + std::string stackTrace = FormatStackTrace(stackFrames); + WARN_LOG(MEMMAP, "\n%s", stackTrace.c_str()); + ExceptionInfo &e = g_exceptionInfo; e = {}; e.type = ExceptionType::MEMORY; @@ -450,6 +456,7 @@ void Core_MemoryException(u32 address, u32 accessSize, u32 pc, MemoryExceptionTy e.memory_type = type; e.address = address; e.accessSize = accessSize; + e.stackTrace = stackTrace; e.pc = pc; Core_EnableStepping(true, "memory.exception", address); } @@ -465,12 +472,18 @@ void Core_MemoryExceptionInfo(u32 address, u32 pc, u32 accessSize, MemoryExcepti } if (!g_Config.bIgnoreBadMemAccess || forceReport) { + // Try to fetch a call stack, to start with. + std::vector stackFrames = WalkCurrentStack(-1); + std::string stackTrace = FormatStackTrace(stackFrames); + WARN_LOG(MEMMAP, "\n%s", stackTrace.c_str()); + ExceptionInfo &e = g_exceptionInfo; e = {}; e.type = ExceptionType::MEMORY; e.info = additionalInfo; e.memory_type = type; e.address = address; + e.stackTrace = stackTrace; e.pc = pc; Core_EnableStepping(true, "memory.exception", address); } diff --git a/Core/Core.h b/Core/Core.h index 333dbd1b4a..5be45aac64 100644 --- a/Core/Core.h +++ b/Core/Core.h @@ -122,6 +122,7 @@ enum class ExceptionType { struct ExceptionInfo { ExceptionType type; std::string info; + std::string stackTrace; // if available. // Memory exception info MemoryExceptionType memory_type; diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index f0e0456995..01bc74979b 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -306,8 +306,7 @@ bool __KernelIsRunning() { } std::string __KernelStateSummary() { - std::string threadSummary = __KernelThreadingSummary(); - return StringFromFormat("%s", threadSummary.c_str()); + return __KernelThreadingSummary(); } diff --git a/Core/MIPS/MIPSStackWalk.cpp b/Core/MIPS/MIPSStackWalk.cpp index 40e9267008..8c6305f4f8 100644 --- a/Core/MIPS/MIPSStackWalk.cpp +++ b/Core/MIPS/MIPSStackWalk.cpp @@ -185,4 +185,6 @@ namespace MIPSStackWalk { return frames; } -}; \ No newline at end of file + + +}; diff --git a/Core/MIPS/MIPSStackWalk.h b/Core/MIPS/MIPSStackWalk.h index f380b5881f..a924c9dddc 100644 --- a/Core/MIPS/MIPSStackWalk.h +++ b/Core/MIPS/MIPSStackWalk.h @@ -33,4 +33,4 @@ namespace MIPSStackWalk { }; std::vector Walk(u32 pc, u32 ra, u32 sp, u32 threadEntry, u32 threadStackTop); -}; \ No newline at end of file +}; diff --git a/Core/MemFault.cpp b/Core/MemFault.cpp index 5cedc51738..fe413d6a26 100644 --- a/Core/MemFault.cpp +++ b/Core/MemFault.cpp @@ -20,7 +20,9 @@ #include #include #include +#include +#include "Common/StringUtils.h" #include "Common/MachineContext.h" #if PPSSPP_ARCH(AMD64) || PPSSPP_ARCH(X86) @@ -38,6 +40,12 @@ #include "Core/MemFault.h" #include "Core/MemMap.h" #include "Core/MIPS/JitCommon/JitCommon.h" +#include "Core/Debugger/SymbolMap.h" + +// Stack walking stuff +#include "Core/MIPS/MIPSStackWalk.h" +#include "Core/MIPS/MIPSDebugInterface.h" +#include "Core/HLE/sceKernelThread.h" namespace Memory { @@ -132,8 +140,7 @@ bool HandleFault(uintptr_t hostAddress, void *ctx) { } } - - // OK, a guest executable did a bad access. Take care of it. + // OK, a guest executable did a bad access. Let's handle it. uint32_t guestAddress = invalidHostAddress ? 0xFFFFFFFFUL : (uint32_t)(hostAddress - baseAddress); @@ -317,3 +324,31 @@ bool HandleFault(uintptr_t hostAddress, void *ctx) { #endif } // namespace Memory + +std::vector WalkCurrentStack(int threadID) { + DebugInterface *cpuDebug = currentDebugMIPS; + + auto threads = GetThreadsInfo(); + uint32_t entry = cpuDebug->GetPC(); + uint32_t stackTop = 0; + for (const DebugThreadInfo &th : threads) { + if ((threadID == -1 && th.isCurrent) || th.id == threadID) { + entry = th.entrypoint; + stackTop = th.initialStack; + break; + } + } + + uint32_t ra = cpuDebug->GetRegValue(0, MIPS_REG_RA); + uint32_t sp = cpuDebug->GetRegValue(0, MIPS_REG_SP); + return MIPSStackWalk::Walk(cpuDebug->GetPC(), ra, sp, entry, stackTop); +} + +std::string FormatStackTrace(const std::vector &frames) { + std::stringstream str; + for (const auto &frame : frames) { + std::string desc = g_symbolMap->GetDescription(frame.entry); + str << StringFromFormat("%s (%08x+%03x, pc: %08x sp: %08x)\n", desc.c_str(), frame.entry, frame.pc - frame.entry, frame.pc, frame.sp); + } + return str.str(); +} diff --git a/Core/MemFault.h b/Core/MemFault.h index 4de4cbdf6b..3e825d2e53 100644 --- a/Core/MemFault.h +++ b/Core/MemFault.h @@ -2,6 +2,8 @@ #include +#include "Core/MIPS/MIPSStackWalk.h" + namespace Memory { void MemFault_Init(); @@ -19,3 +21,10 @@ void MemFault_IgnoreLastCrash(); bool HandleFault(uintptr_t hostAddress, void *context); } + +// Stack walk utility function, walks from the current state. Useful in the debugger and crash report screens etc. +// Doesn't exactly belong here maybe, but can think of worse locations. +// threadID can be -1 for current. +std::vector WalkCurrentStack(int threadID); + +std::string FormatStackTrace(const std::vector &frames); diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index cf659e9f2e..af665bdd6c 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -1201,7 +1201,7 @@ static void DrawCrashDump(UIContext *ctx, const Path &gamePath) { ctx->PushScissor(Bounds(x, y, columnWidth, height)); - INFO_LOG(SYSTEM, "DrawCrashDump (%d %d %d %d)", x, y, columnWidth, height); + // INFO_LOG(SYSTEM, "DrawCrashDump (%d %d %d %d)", x, y, columnWidth, height); snprintf(statbuf, sizeof(statbuf), R"(%s %s (%s) @@ -1262,6 +1262,14 @@ Invalid / Unknown (%d) ctx->Draw()->DrawTextShadow(ubuntu24, kernelState.c_str(), x, y, 0xFFFFFFFF); + y += 40; + + ctx->Draw()->SetFontScale(.5f, .5f); + + ctx->Draw()->DrawTextShadow(ubuntu24, info.stackTrace.c_str(), x, y, 0xFFFFFFFF); + + ctx->Draw()->SetFontScale(.7f, .7f); + ctx->PopScissor(); // Draw some additional stuff to the right. diff --git a/libretro/Makefile.common b/libretro/Makefile.common index 39e68cc1d2..325571d3cf 100644 --- a/libretro/Makefile.common +++ b/libretro/Makefile.common @@ -650,6 +650,7 @@ SOURCES_CXX += \ $(COREDIR)/MIPS/MIPSInt.cpp \ $(COREDIR)/MIPS/MIPSIntVFPU.cpp \ $(COREDIR)/MIPS/MIPSTables.cpp \ + $(COREDIR)/MIPS/MIPSStackWalk.cpp \ $(COREDIR)/MIPS/MIPSVFPUUtils.cpp \ $(COREDIR)/MemFault.cpp \ $(COREDIR)/MemMap.cpp \