diff --git a/Core/Core.cpp b/Core/Core.cpp index 9f33616469..d37365d54b 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -780,6 +780,7 @@ const char *ExceptionTypeAsString(MIPSExceptionType type) { case MIPSExceptionType::BREAK: return "Break"; case MIPSExceptionType::BAD_EXEC_ADDR: return "Bad Execution Address"; case MIPSExceptionType::FPU: return "FPU Exception"; + case MIPSExceptionType::HLE: return "HLE Error"; default: return "N/A"; } } @@ -836,7 +837,7 @@ static std::string ModuleAddressSuffix(u32 address) { } } -void Core_MemoryException(u32 address, u32 accessSize, u32 pc, MemoryExceptionType type, std::string_view additionalInfo) { +void Core_MemoryException(u32 address, u32 accessSize, u32 pc, MemoryExceptionType type, std::string_view additionalInfo, bool halting) { // In jit, we only flush PC when bIgnoreBadMemAccess is off. char pcDetails[128]; pcDetails[0] = 0; @@ -877,14 +878,14 @@ void Core_MemoryException(u32 address, u32 accessSize, u32 pc, MemoryExceptionTy const char *desc = MemoryExceptionTypeAsString(type); char msg[512]; snprintf(msg, sizeof(msg), "%s: SIGSEGV at %08x%s (size: %d bytes) %s\nHost:%.*s", desc, address, addressSuffix.c_str(), accessSize, pcDetails, STR_VIEW(additionalInfo)); - if (action == ExceptionAction::Ignore) { + if (action == ExceptionAction::Ignore && !halting) { Core_SendDebugOutput(LogLevel::LWARNING, msg); return; } const std::string stackTrace = FormatStackTrace(WalkCurrentStack(-1)); // Do the most detailed logging we can. Core_SendDebugOutput(LogLevel::LERROR, StringFromFormat("%sMIPS call stack:\n%s", msg, stackTrace.c_str())); - if (action == ExceptionAction::Break) { + if (action == ExceptionAction::Break || halting) { MIPSExceptionInfo &e = g_exceptionInfo; e = {}; e.type = MIPSExceptionType::MEMORY; @@ -1061,6 +1062,20 @@ void Core_BreakException(u32 pc) { } } +void Core_HLEError(std::string_view message) { + const std::string stackTrace = FormatStackTrace(WalkCurrentStack(-1)); + Core_SendDebugOutput(LogLevel::LERROR, StringFromFormat("HLE error: %.*s\n%s", STR_VIEW(message), stackTrace.c_str())); + + MIPSExceptionInfo &e = g_exceptionInfo; + e = {}; + e.type = MIPSExceptionType::HLE; + e.info = message; + e.stackTrace = stackTrace; + e.pc = currentMIPS->pc; + e.ra = currentMIPS->r[MIPS_REG_RA]; + Core_UpdateState(CORE_RUNTIME_ERROR); +} + // We can't actually deliver the trap to the game's exception handler - nothing in PPSSPP emulates // the FPU exception vector - so all we can do is tell the user about it. Execution continues after // this returns, just without the destination register having been written (as on hardware). diff --git a/Core/Core.h b/Core/Core.h index 1fde9982a0..0645b5f4b9 100644 --- a/Core/Core.h +++ b/Core/Core.h @@ -281,9 +281,13 @@ enum class FPUExceptionType { DIVIDE_BY_ZERO, }; -void Core_MemoryException(u32 address, u32 accessSize, u32 pc, MemoryExceptionType type, std::string_view additionalInfo = ""); +// halting: the caller stops emulation whatever the configured action, so the exception is recorded +// for the crash screen even when the action is to ignore it. +void Core_MemoryException(u32 address, u32 accessSize, u32 pc, MemoryExceptionType type, std::string_view additionalInfo = "", bool halting = false); void Core_ExecException(u32 address, u32 pc, ExecExceptionType type); void Core_BreakException(u32 pc); +// For HLE that finds it can't go on (a corrupt callback stack, a failed LoadExec) and stops the core. +void Core_HLEError(std::string_view message); // Only called for exceptions the game has unmasked in fcr31 - a masked one just sets the flag bit // and produces the IEEE default result, without coming through here. void Core_FPUException(u32 pc, FPUExceptionType type); @@ -325,6 +329,7 @@ enum class MIPSExceptionType { BREAK, BAD_EXEC_ADDR, FPU, + HLE, }; struct MIPSExceptionInfo { diff --git a/Core/HLE/HLE.cpp b/Core/HLE/HLE.cpp index 2a41a58336..a15ae8f701 100644 --- a/Core/HLE/HLE.cpp +++ b/Core/HLE/HLE.cpp @@ -879,8 +879,7 @@ void HLEReturnFromMipsCall() { stackData.ptr = sp; if ((stackData->nextOff & 0x0000000F) != 0 || !Memory::IsValidAddress(sp + stackData->nextOff)) { - ERROR_LOG(Log::HLE, "Corrupt stack on HLE mips call return: %08x", stackData->nextOff); - Core_UpdateState(CORE_RUNTIME_ERROR); + Core_HLEError(StringFromFormat("Corrupt stack on HLE mips call return: %08x", stackData->nextOff)); hleNoLogVoid(); return; } @@ -896,8 +895,7 @@ void HLEReturnFromMipsCall() { } if (finalMarker->nextOff != 0xFFFFFFFF) { - ERROR_LOG(Log::HLE, "Corrupt stack on HLE mips call return action: %08x", finalMarker->nextOff); - Core_UpdateState(CORE_RUNTIME_ERROR); + Core_HLEError(StringFromFormat("Corrupt stack on HLE mips call return action: %08x", finalMarker->nextOff)); hleNoLogVoid(); return; } diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index 27db7a4645..43f32907c5 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -1046,7 +1046,7 @@ static int sceKernelLoadExecBufferVSHUsbWlan(int bufferSize, u32 bufferAddr, u32 const u8 *data = Memory::GetPointerUnchecked(bufferAddr); std::string error_string; if (!__KernelLoadExecFromBuffer(currentMIPS, data, (size_t)bufferSize, paramPtr, &error_string)) { - Core_UpdateState(CORE_RUNTIME_ERROR); + Core_HLEError("sceKernelLoadExecBufferVSHUsbWlan failed: " + error_string); return hleLogError(Log::sceKernel, -1, "failed: %s", error_string.c_str()); } if (gpu) { diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index fc39fa7135..dce0c2120b 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -2560,7 +2560,7 @@ int sceKernelLoadExec(const char *filename, u32 paramPtr) { DEBUG_LOG(Log::sceModule, "sceKernelLoadExec(name=%s,...): loading %s", filename, exec_filename.c_str()); std::string error_string; if (!__KernelLoadExec(currentMIPS, exec_filename.c_str(), paramPtr, &error_string)) { - Core_UpdateState(CORE_RUNTIME_ERROR); + Core_HLEError("sceKernelLoadExec failed: " + error_string); return hleLogError(Log::sceModule, -1, "failed: %s", error_string.c_str());; } if (gpu) { diff --git a/Core/MemFault.cpp b/Core/MemFault.cpp index 841e14916d..f86d5522d5 100644 --- a/Core/MemFault.cpp +++ b/Core/MemFault.cpp @@ -271,7 +271,7 @@ bool HandleFault(uintptr_t hostAddress, void *ctx) { uint32_t approximatePC = currentMIPS->pc; // TODO: Determine access size from the disassembled native instruction. We have some partial info already, // just need to clean it up. - Core_MemoryException(guestAddress, info.OperandSizeInBytes(), approximatePC, type, infoString); + Core_MemoryException(guestAddress, info.OperandSizeInBytes(), approximatePC, type, infoString, true); // There's a small chance we can resume from this type of crash. g_lastCrashAddress = codePtr; diff --git a/UI/DebugOverlay.cpp b/UI/DebugOverlay.cpp index 1b9ff7711f..0bf44c726c 100644 --- a/UI/DebugOverlay.cpp +++ b/UI/DebugOverlay.cpp @@ -419,6 +419,13 @@ PC: %08x )", info.pc); ctx->Draw()->DrawTextShadow(ubuntu24, statbuf, x, y, 0xFFFFFFFF); y += 180; + } else if (info.type == MIPSExceptionType::HLE) { + snprintf(statbuf, sizeof(statbuf), R"( +%s +PC: %08x +RA: %08x)", info.info.c_str(), info.pc, info.ra); + ctx->Draw()->DrawTextShadow(ubuntu24, statbuf, x, y, 0xFFFFFFFF); + y += 180; } else { snprintf(statbuf, sizeof(statbuf), R"( Invalid / Unknown (%d)