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
This commit is contained in:
Henrik Rydgård 2023-03-31 10:08:12 +02:00 committed by GitHub
parent 26bf40c497
commit 2814668cf5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 75 additions and 8 deletions

View File

@ -151,7 +151,6 @@ void ControlMapper::Update() {
} }
} }
inline bool IsAnalogStickKey(int key) { inline bool IsAnalogStickKey(int key) {
switch (key) { switch (key) {
case VIRTKEY_AXIS_X_MIN: case VIRTKEY_AXIS_X_MIN:

View File

@ -37,6 +37,7 @@
#include "Core/MemMap.h" #include "Core/MemMap.h"
#include "Core/SaveState.h" #include "Core/SaveState.h"
#include "Core/System.h" #include "Core/System.h"
#include "Core/MemFault.h"
#include "Core/Debugger/Breakpoints.h" #include "Core/Debugger/Breakpoints.h"
#include "Core/HW/Display.h" #include "Core/HW/Display.h"
#include "Core/MIPS/MIPS.h" #include "Core/MIPS/MIPS.h"
@ -443,6 +444,11 @@ void Core_MemoryException(u32 address, u32 accessSize, u32 pc, MemoryExceptionTy
} }
if (!g_Config.bIgnoreBadMemAccess) { if (!g_Config.bIgnoreBadMemAccess) {
// Try to fetch a call stack, to start with.
std::vector<MIPSStackWalk::StackFrame> stackFrames = WalkCurrentStack(-1);
std::string stackTrace = FormatStackTrace(stackFrames);
WARN_LOG(MEMMAP, "\n%s", stackTrace.c_str());
ExceptionInfo &e = g_exceptionInfo; ExceptionInfo &e = g_exceptionInfo;
e = {}; e = {};
e.type = ExceptionType::MEMORY; e.type = ExceptionType::MEMORY;
@ -450,6 +456,7 @@ void Core_MemoryException(u32 address, u32 accessSize, u32 pc, MemoryExceptionTy
e.memory_type = type; e.memory_type = type;
e.address = address; e.address = address;
e.accessSize = accessSize; e.accessSize = accessSize;
e.stackTrace = stackTrace;
e.pc = pc; e.pc = pc;
Core_EnableStepping(true, "memory.exception", address); 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) { if (!g_Config.bIgnoreBadMemAccess || forceReport) {
// Try to fetch a call stack, to start with.
std::vector<MIPSStackWalk::StackFrame> stackFrames = WalkCurrentStack(-1);
std::string stackTrace = FormatStackTrace(stackFrames);
WARN_LOG(MEMMAP, "\n%s", stackTrace.c_str());
ExceptionInfo &e = g_exceptionInfo; ExceptionInfo &e = g_exceptionInfo;
e = {}; e = {};
e.type = ExceptionType::MEMORY; e.type = ExceptionType::MEMORY;
e.info = additionalInfo; e.info = additionalInfo;
e.memory_type = type; e.memory_type = type;
e.address = address; e.address = address;
e.stackTrace = stackTrace;
e.pc = pc; e.pc = pc;
Core_EnableStepping(true, "memory.exception", address); Core_EnableStepping(true, "memory.exception", address);
} }

View File

@ -122,6 +122,7 @@ enum class ExceptionType {
struct ExceptionInfo { struct ExceptionInfo {
ExceptionType type; ExceptionType type;
std::string info; std::string info;
std::string stackTrace; // if available.
// Memory exception info // Memory exception info
MemoryExceptionType memory_type; MemoryExceptionType memory_type;

View File

@ -306,8 +306,7 @@ bool __KernelIsRunning() {
} }
std::string __KernelStateSummary() { std::string __KernelStateSummary() {
std::string threadSummary = __KernelThreadingSummary(); return __KernelThreadingSummary();
return StringFromFormat("%s", threadSummary.c_str());
} }

View File

@ -185,4 +185,6 @@ namespace MIPSStackWalk {
return frames; return frames;
} }
}; };

View File

@ -20,7 +20,9 @@
#include <cstdint> #include <cstdint>
#include <unordered_set> #include <unordered_set>
#include <mutex> #include <mutex>
#include <sstream>
#include "Common/StringUtils.h"
#include "Common/MachineContext.h" #include "Common/MachineContext.h"
#if PPSSPP_ARCH(AMD64) || PPSSPP_ARCH(X86) #if PPSSPP_ARCH(AMD64) || PPSSPP_ARCH(X86)
@ -38,6 +40,12 @@
#include "Core/MemFault.h" #include "Core/MemFault.h"
#include "Core/MemMap.h" #include "Core/MemMap.h"
#include "Core/MIPS/JitCommon/JitCommon.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 { namespace Memory {
@ -132,8 +140,7 @@ bool HandleFault(uintptr_t hostAddress, void *ctx) {
} }
} }
// OK, a guest executable did a bad access. Let's handle it.
// OK, a guest executable did a bad access. Take care of it.
uint32_t guestAddress = invalidHostAddress ? 0xFFFFFFFFUL : (uint32_t)(hostAddress - baseAddress); uint32_t guestAddress = invalidHostAddress ? 0xFFFFFFFFUL : (uint32_t)(hostAddress - baseAddress);
@ -317,3 +324,31 @@ bool HandleFault(uintptr_t hostAddress, void *ctx) {
#endif #endif
} // namespace Memory } // namespace Memory
std::vector<MIPSStackWalk::StackFrame> 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<MIPSStackWalk::StackFrame> &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();
}

View File

@ -2,6 +2,8 @@
#include <cstdint> #include <cstdint>
#include "Core/MIPS/MIPSStackWalk.h"
namespace Memory { namespace Memory {
void MemFault_Init(); void MemFault_Init();
@ -19,3 +21,10 @@ void MemFault_IgnoreLastCrash();
bool HandleFault(uintptr_t hostAddress, void *context); 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<MIPSStackWalk::StackFrame> WalkCurrentStack(int threadID);
std::string FormatStackTrace(const std::vector<MIPSStackWalk::StackFrame> &frames);

View File

@ -1201,7 +1201,7 @@ static void DrawCrashDump(UIContext *ctx, const Path &gamePath) {
ctx->PushScissor(Bounds(x, y, columnWidth, height)); 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 snprintf(statbuf, sizeof(statbuf), R"(%s
%s (%s) %s (%s)
@ -1262,6 +1262,14 @@ Invalid / Unknown (%d)
ctx->Draw()->DrawTextShadow(ubuntu24, kernelState.c_str(), x, y, 0xFFFFFFFF); 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(); ctx->PopScissor();
// Draw some additional stuff to the right. // Draw some additional stuff to the right.

View File

@ -650,6 +650,7 @@ SOURCES_CXX += \
$(COREDIR)/MIPS/MIPSInt.cpp \ $(COREDIR)/MIPS/MIPSInt.cpp \
$(COREDIR)/MIPS/MIPSIntVFPU.cpp \ $(COREDIR)/MIPS/MIPSIntVFPU.cpp \
$(COREDIR)/MIPS/MIPSTables.cpp \ $(COREDIR)/MIPS/MIPSTables.cpp \
$(COREDIR)/MIPS/MIPSStackWalk.cpp \
$(COREDIR)/MIPS/MIPSVFPUUtils.cpp \ $(COREDIR)/MIPS/MIPSVFPUUtils.cpp \
$(COREDIR)/MemFault.cpp \ $(COREDIR)/MemFault.cpp \
$(COREDIR)/MemMap.cpp \ $(COREDIR)/MemMap.cpp \