Improve exception logging on posix

Fix compile errors
This commit is contained in:
Exzap 2023-04-06 06:08:14 +02:00
parent 3428e4dae6
commit 072c18a6e3
8 changed files with 225 additions and 159 deletions

View File

@ -92,13 +92,18 @@ void cemuLog_thread()
}
}
fs::path cemuLog_GetLogFilePath()
{
return ActiveSettings::GetUserDataPath("log.txt");
}
void cemuLog_createLogFile(bool triggeredByCrash)
{
std::unique_lock lock(LogContext.log_mutex);
if (LogContext.file_stream.is_open())
return;
const auto path = ActiveSettings::GetUserDataPath("log.txt");
const auto path = cemuLog_GetLogFilePath();
LogContext.file_stream.open(path, std::ios::out);
if (LogContext.file_stream.fail())
{

View File

@ -112,6 +112,7 @@ uint64 cemuLog_getFlag(LogType type);
void cafeLog_setLoggingFlagEnable(sint32 loggingType, bool isEnable);
bool cafeLog_isLoggingFlagEnabled(sint32 loggingType);
fs::path cemuLog_GetLogFilePath();
void cemuLog_createLogFile(bool triggeredByCrash);
[[nodiscard]] std::unique_lock<std::recursive_mutex> cafeLog_acquire();

View File

@ -3,6 +3,7 @@ add_library(CemuCommon
cpu_features.cpp
cpu_features.h
enumFlags.h
ExceptionHandler/ExceptionHandler.cpp
ExceptionHandler/ExceptionHandler.h
FileStream.h
GLInclude/glext.h

View File

@ -0,0 +1,161 @@
#include "config/ActiveSettings.h"
#include "config/CemuConfig.h"
#include "Cafe/CafeSystem.h"
#include "Cafe/OS/libs/coreinit/coreinit_Thread.h"
#include "Cafe/HW/Espresso/PPCState.h"
#include "Cafe/HW/Espresso/Debugger/GDBStub.h"
#include "ExceptionHandler.h"
void DebugLogStackTrace(OSThread_t* thread, MPTR sp);
bool crashLogCreated = false;
bool CrashLog_Create()
{
if (crashLogCreated)
return false; // only create one crashlog
crashLogCreated = true;
cemuLog_createLogFile(true);
CrashLog_SetOutputChannels(true, true);
return true;
}
static bool s_writeToStdErr{true};
static bool s_writeToLogTxt{true};
void CrashLog_SetOutputChannels(bool writeToStdErr, bool writeToLogTxt)
{
s_writeToStdErr = writeToStdErr;
s_writeToLogTxt = writeToLogTxt;
}
// outputs to both stderr and log.txt
void CrashLog_WriteLine(std::string_view text, bool newLine)
{
if(s_writeToLogTxt)
cemuLog_writeLineToLog(text, false, newLine);
if(s_writeToStdErr)
{
fwrite(text.data(), sizeof(char), text.size(), stderr);
if(newLine)
fwrite("\n", sizeof(char), 1, stderr);
}
}
void CrashLog_WriteHeader(const char* header)
{
CrashLog_WriteLine("-----------------------------------------");
CrashLog_WriteLine(" ", false);
CrashLog_WriteLine(header);
CrashLog_WriteLine("-----------------------------------------");
}
void ExceptionHandler_LogGeneralInfo()
{
char dumpLine[1024];
// info about game
CrashLog_WriteLine("");
CrashLog_WriteHeader("Game info");
if (CafeSystem::IsTitleRunning())
{
CrashLog_WriteLine("Game: ", false);
CrashLog_WriteLine(CafeSystem::GetForegroundTitleName());
// title id
CrashLog_WriteLine(fmt::format("TitleId: {:016x}", CafeSystem::GetForegroundTitleId()));
// rpx hash
sprintf(dumpLine, "RPXHash: %08x (Update: %08x)", CafeSystem::GetRPXHashBase(), CafeSystem::GetRPXHashUpdated());
CrashLog_WriteLine(dumpLine);
}
else
{
CrashLog_WriteLine("Not running");
}
// info about active PPC instance:
CrashLog_WriteLine("");
CrashLog_WriteHeader("Active PPC instance");
if (ppcInterpreterCurrentInstance)
{
OSThread_t* currentThread = coreinit::OSGetCurrentThread();
uint32 threadPtr = memory_getVirtualOffsetFromPointer(coreinit::OSGetCurrentThread());
sprintf(dumpLine, "IP 0x%08x LR 0x%08x Thread 0x%08x", ppcInterpreterCurrentInstance->instructionPointer, ppcInterpreterCurrentInstance->spr.LR, threadPtr);
CrashLog_WriteLine(dumpLine);
// GPR info
CrashLog_WriteLine("");
auto gprs = ppcInterpreterCurrentInstance->gpr;
sprintf(dumpLine, "r0 =%08x r1 =%08x r2 =%08x r3 =%08x r4 =%08x r5 =%08x r6 =%08x r7 =%08x", gprs[0], gprs[1], gprs[2], gprs[3], gprs[4], gprs[5], gprs[6], gprs[7]);
CrashLog_WriteLine(dumpLine);
sprintf(dumpLine, "r8 =%08x r9 =%08x r10=%08x r11=%08x r12=%08x r13=%08x r14=%08x r15=%08x", gprs[8], gprs[9], gprs[10], gprs[11], gprs[12], gprs[13], gprs[14], gprs[15]);
CrashLog_WriteLine(dumpLine);
sprintf(dumpLine, "r16=%08x r17=%08x r18=%08x r19=%08x r20=%08x r21=%08x r22=%08x r23=%08x", gprs[16], gprs[17], gprs[18], gprs[19], gprs[20], gprs[21], gprs[22], gprs[23]);
CrashLog_WriteLine(dumpLine);
sprintf(dumpLine, "r24=%08x r25=%08x r26=%08x r27=%08x r28=%08x r29=%08x r30=%08x r31=%08x", gprs[24], gprs[25], gprs[26], gprs[27], gprs[28], gprs[29], gprs[30], gprs[31]);
CrashLog_WriteLine(dumpLine);
// stack trace
MPTR currentStackVAddr = ppcInterpreterCurrentInstance->gpr[1];
CrashLog_WriteLine("");
CrashLog_WriteHeader("PPC stack trace");
DebugLogStackTrace(currentThread, currentStackVAddr);
// stack dump
CrashLog_WriteLine("");
CrashLog_WriteHeader("PPC stack dump");
for (uint32 i = 0; i < 16; i++)
{
MPTR lineAddr = currentStackVAddr + i * 8 * 4;
if (memory_isAddressRangeAccessible(lineAddr, 8 * 4))
{
sprintf(dumpLine, "[0x%08x] %08x %08x %08x %08x - %08x %08x %08x %08x", lineAddr, memory_readU32(lineAddr + 0), memory_readU32(lineAddr + 4), memory_readU32(lineAddr + 8), memory_readU32(lineAddr + 12), memory_readU32(lineAddr + 16), memory_readU32(lineAddr + 20), memory_readU32(lineAddr + 24), memory_readU32(lineAddr + 28));
CrashLog_WriteLine(dumpLine);
}
else
{
sprintf(dumpLine, "[0x%08x] ?", lineAddr);
CrashLog_WriteLine(dumpLine);
}
}
}
else
{
CrashLog_WriteLine("Not active");
}
// PPC thread log
CrashLog_WriteLine("");
CrashLog_WriteHeader("PPC threads");
if (activeThreadCount == 0)
{
CrashLog_WriteLine("None active");
}
for (sint32 i = 0; i < activeThreadCount; i++)
{
MPTR threadItrMPTR = activeThread[i];
OSThread_t* threadItrBE = (OSThread_t*)memory_getPointerFromVirtualOffset(threadItrMPTR);
// get thread state
OSThread_t::THREAD_STATE threadState = threadItrBE->state;
const char* threadStateStr = "UNDEFINED";
if (threadItrBE->suspendCounter != 0)
threadStateStr = "SUSPENDED";
else if (threadState == OSThread_t::THREAD_STATE::STATE_NONE)
threadStateStr = "NONE";
else if (threadState == OSThread_t::THREAD_STATE::STATE_READY)
threadStateStr = "READY";
else if (threadState == OSThread_t::THREAD_STATE::STATE_RUNNING)
threadStateStr = "RUNNING";
else if (threadState == OSThread_t::THREAD_STATE::STATE_WAITING)
threadStateStr = "WAITING";
else if (threadState == OSThread_t::THREAD_STATE::STATE_MORIBUND)
threadStateStr = "MORIBUND";
// generate log line
uint8 affinity = threadItrBE->attr;
sint32 effectivePriority = threadItrBE->effectivePriority;
const char* threadName = "NULL";
if (!threadItrBE->threadName.IsNull())
threadName = threadItrBE->threadName.GetPtr();
sprintf(dumpLine, "%08x Ent %08x IP %08x LR %08x %-9s Aff %d%d%d Pri %2d Name %s", threadItrMPTR, _swapEndianU32(threadItrBE->entrypoint), threadItrBE->context.srr0, _swapEndianU32(threadItrBE->context.lr), threadStateStr, (affinity >> 0) & 1, (affinity >> 1) & 1, (affinity >> 2) & 1, effectivePriority, threadName);
// write line to log
CrashLog_WriteLine(dumpLine);
}
}

View File

@ -1,3 +1,10 @@
#pragma once
void ExceptionHandler_init();
void ExceptionHandler_Init();
bool CrashLog_Create();
void CrashLog_SetOutputChannels(bool writeToStdErr, bool writeToLogTxt);
void CrashLog_WriteLine(std::string_view text, bool newLine = true);
void CrashLog_WriteHeader(const char* header);
void ExceptionHandler_LogGeneralInfo();

View File

@ -4,13 +4,14 @@
#include <string>
#include "config/CemuConfig.h"
#include "util/helpers/StringHelpers.h"
#include "ExceptionHandler.h"
#if BOOST_OS_LINUX
#include "ELFSymbolTable.h"
#endif
#if BOOST_OS_LINUX
void demangleAndPrintBacktrace(char** backtrace, size_t size)
void DemangleAndPrintBacktrace(char** backtrace, size_t size)
{
ELFSymbolTable symTable;
for (char** i = backtrace; i < backtrace + size; i++)
@ -25,7 +26,7 @@ void demangleAndPrintBacktrace(char** backtrace, size_t size)
offsetPlus < parenthesesOpen || offsetPlus > parenthesesClose)
{
// fall back to default string
std::cerr << traceLine << std::endl;
CrashLog_WriteLine(traceLine);
continue;
}
@ -38,29 +39,32 @@ void demangleAndPrintBacktrace(char** backtrace, size_t size)
symbolName = symTable.OffsetToSymbol(symbolOffset, newOffset);
}
std::cerr << traceLine.substr(0, parenthesesOpen+1);
CrashLog_WriteLine(traceLine.substr(0, parenthesesOpen+1), false);
std::cerr << boost::core::demangle(symbolName.empty() ? "" : symbolName.data());
CrashLog_WriteLine(boost::core::demangle(symbolName.empty() ? "" : symbolName.data()), false);
// print relative or existing symbol offset.
std::cerr << '+';
CrashLog_WriteLine("+", false);
if (newOffset != -1)
{
std::cerr << std::hex << newOffset;
std::cerr << traceLine.substr(parenthesesClose) << std::endl;
CrashLog_WriteLine(fmt::format("0x{:x}", newOffset), false);
CrashLog_WriteLine(traceLine.substr(parenthesesClose));
}
else
{
std::cerr << traceLine.substr(offsetPlus+1) << std::endl;
CrashLog_WriteLine(traceLine.substr(offsetPlus+1));
}
}
}
#endif
// handle signals that would dump core, print stacktrace and then dump depending on config
void handlerDumpingSignal(int sig)
void handlerDumpingSignal(int sig, siginfo_t *info, void *context)
{
char* sigName = strsignal(sig);
if(!CrashLog_Create())
return; // give up if crashlog was already created
char* sigName = strsignal(sig);
if (sigName)
{
printf("%s!\n", sigName);
@ -71,31 +75,42 @@ void handlerDumpingSignal(int sig)
printf("Unknown core dumping signal!\n");
}
void *array[128];
void* backtraceArray[128];
size_t size;
// get void*'s for all entries on the stack
size = backtrace(array, 128);
size = backtrace(backtraceArray, 128);
// replace the deepest entry with the actual crash address
#if defined(ARCH_X86_64) && BOOST_OS_LINUX > 0
ucontext_t *uc = (ucontext_t *)context;
backtraceArray[0] = (void *)uc->uc_mcontext.gregs[REG_RIP];
#endif
// print out all the frames to stderr
fprintf(stderr, "Error: signal %d:\n", sig);
CrashLog_WriteLine(fmt::format("Error: signal {}:", sig));
#if BOOST_OS_LINUX
char** symbol_trace = backtrace_symbols(array, size);
char** symbol_trace = backtrace_symbols(backtraceArray, size);
if (symbol_trace)
{
demangleAndPrintBacktrace(symbol_trace, size);
DemangleAndPrintBacktrace(symbol_trace, size);
free(symbol_trace);
}
else
{
std::cerr << "Failed to read backtrace" << std::endl;
CrashLog_WriteLine("Failed to read backtrace");
}
#else
backtrace_symbols_fd(array, size, STDERR_FILENO);
backtrace_symbols_fd(backtraceArray, size, STDERR_FILENO);
#endif
std::cerr << fmt::format("\nStacktrace and additional info written to:") << std::endl;
std::cerr << cemuLog_GetLogFilePath().generic_string() << std::endl;
CrashLog_SetOutputChannels(false, true);
ExceptionHandler_LogGeneralInfo();
CrashLog_SetOutputChannels(true, true);
if (GetConfig().crash_dump == CrashDump::Enabled)
{
// reset signal handler to default and re-raise signal to dump core
@ -119,7 +134,7 @@ void handler_SIGINT(int sig)
_Exit(0);
}
void ExceptionHandler_init()
void ExceptionHandler_Init()
{
struct sigaction action;
action.sa_flags = 0;
@ -128,7 +143,9 @@ void ExceptionHandler_init()
action.sa_handler = handler_SIGINT;
sigaction(SIGINT, &action, nullptr);
action.sa_handler = handlerDumpingSignal;
action.sa_flags = SA_SIGINFO;
action.sa_handler = nullptr;
action.sa_sigaction = handlerDumpingSignal;
sigaction(SIGABRT, &action, nullptr);
sigaction(SIGBUS, &action, nullptr);
sigaction(SIGFPE, &action, nullptr);

View File

@ -1,36 +1,22 @@
#include "Common/precompiled.h"
#include "Cafe/CafeSystem.h"
#include "ExceptionHandler.h"
#include <Windows.h>
#include <Dbghelp.h>
#include <shellapi.h>
#include "Config/ActiveSettings.h"
#include "Config/CemuConfig.h"
#include "config/ActiveSettings.h"
#include "config/CemuConfig.h"
#include "Cafe/OS/libs/coreinit/coreinit_Thread.h"
#include "Cafe/HW/Espresso/PPCState.h"
#include "Cafe/HW/Espresso/Debugger/GDBStub.h"
extern uint32 currentBaseApplicationHash;
extern uint32 currentUpdatedApplicationHash;
LONG handleException_SINGLE_STEP(PEXCEPTION_POINTERS pExceptionInfo)
{
return EXCEPTION_CONTINUE_SEARCH;
}
void crashlog_writeHeader(const char* header)
{
cemuLog_writePlainToLog("-----------------------------------------\n");
cemuLog_writePlainToLog(" ");
cemuLog_writePlainToLog(header);
cemuLog_writePlainToLog("\n");
cemuLog_writePlainToLog("-----------------------------------------\n");
}
bool crashLogCreated = false;
#include <boost/algorithm/string.hpp>
BOOL CALLBACK MyMiniDumpCallback(PVOID pParam, const PMINIDUMP_CALLBACK_INPUT pInput, PMINIDUMP_CALLBACK_OUTPUT pOutput)
{
@ -108,8 +94,6 @@ bool CreateMiniDump(CrashDump dump, EXCEPTION_POINTERS* pep)
return result != FALSE;
}
void DebugLogStackTrace(OSThread_t* thread, MPTR sp);
void DumpThreadStackTrace()
{
HANDLE process = GetCurrentProcess();
@ -123,7 +107,7 @@ void DumpThreadStackTrace()
symbol->MaxNameLen = 255;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
crashlog_writeHeader("Stack trace");
CrashLog_WriteHeader("Stack trace");
for (unsigned int i = 0; i < frames; i++)
{
DWORD64 stackTraceOffset = (DWORD64)stack[i];
@ -171,11 +155,8 @@ void DumpThreadStackTrace()
void createCrashlog(EXCEPTION_POINTERS* e, PCONTEXT context)
{
if (crashLogCreated)
return; // only create one crashlog
crashLogCreated = true;
cemuLog_createLogFile(true);
if(!CrashLog_Create())
return; // give up if crashlog was already created
const auto crash_dump = GetConfig().crash_dump.GetValue();
const auto dump_written = CreateMiniDump(crash_dump, e);
@ -236,117 +217,10 @@ void createCrashlog(EXCEPTION_POINTERS* e, PCONTEXT context)
cemuLog_writePlainToLog(dumpLine);
sprintf(dumpLine, "R12=%016I64x R13=%016I64x R14=%016I64x R15=%016I64x\n", context->R12, context->R13, context->R14, context->R15);
cemuLog_writePlainToLog(dumpLine);
// info about game
cemuLog_writePlainToLog("\n");
crashlog_writeHeader("Game info");
if (CafeSystem::IsTitleRunning())
{
cemuLog_writePlainToLog("Game: ");
cemuLog_writePlainToLog(CafeSystem::GetForegroundTitleName().c_str());
cemuLog_writePlainToLog("\n");
// title id
sprintf(dumpLine, "TitleId: %I64x\n", CafeSystem::GetForegroundTitleId());
cemuLog_writePlainToLog(dumpLine);
// rpx hash
sprintf(dumpLine, "RPXHash: %x\n", currentBaseApplicationHash);
cemuLog_writePlainToLog(dumpLine);
}
else
{
cemuLog_writePlainToLog("Not running\n");
}
// info about active PPC instance:
cemuLog_writePlainToLog("\n");
crashlog_writeHeader("Active PPC instance");
if (ppcInterpreterCurrentInstance)
{
OSThread_t* currentThread = coreinit::OSGetCurrentThread();
uint32 threadPtr = memory_getVirtualOffsetFromPointer(coreinit::OSGetCurrentThread());
sprintf(dumpLine, "IP 0x%08x LR 0x%08x Thread 0x%08x\n", ppcInterpreterCurrentInstance->instructionPointer, ppcInterpreterCurrentInstance->spr.LR, threadPtr);
cemuLog_writePlainToLog(dumpLine);
// GPR info
sprintf(dumpLine, "\n");
cemuLog_writePlainToLog(dumpLine);
auto gprs = ppcInterpreterCurrentInstance->gpr;
sprintf(dumpLine, "r0 =%08x r1 =%08x r2 =%08x r3 =%08x r4 =%08x r5 =%08x r6 =%08x r7 =%08x\n", gprs[0], gprs[1], gprs[2], gprs[3], gprs[4], gprs[5], gprs[6], gprs[7]);
cemuLog_writePlainToLog(dumpLine);
sprintf(dumpLine, "r8 =%08x r9 =%08x r10=%08x r11=%08x r12=%08x r13=%08x r14=%08x r15=%08x\n", gprs[8], gprs[9], gprs[10], gprs[11], gprs[12], gprs[13], gprs[14], gprs[15]);
cemuLog_writePlainToLog(dumpLine);
sprintf(dumpLine, "r16=%08x r17=%08x r18=%08x r19=%08x r20=%08x r21=%08x r22=%08x r23=%08x\n", gprs[16], gprs[17], gprs[18], gprs[19], gprs[20], gprs[21], gprs[22], gprs[23]);
cemuLog_writePlainToLog(dumpLine);
sprintf(dumpLine, "r24=%08x r25=%08x r26=%08x r27=%08x r28=%08x r29=%08x r30=%08x r31=%08x\n", gprs[24], gprs[25], gprs[26], gprs[27], gprs[28], gprs[29], gprs[30], gprs[31]);
cemuLog_writePlainToLog(dumpLine);
// write line to log
cemuLog_writePlainToLog(dumpLine);
// stack trace
MPTR currentStackVAddr = ppcInterpreterCurrentInstance->gpr[1];
cemuLog_writePlainToLog("\n");
crashlog_writeHeader("PPC stack trace");
DebugLogStackTrace(currentThread, currentStackVAddr);
// stack dump
cemuLog_writePlainToLog("\n");
crashlog_writeHeader("PPC stack dump");
for (uint32 i = 0; i < 16; i++)
{
MPTR lineAddr = currentStackVAddr + i * 8 * 4;
if (memory_isAddressRangeAccessible(lineAddr, 8 * 4))
{
sprintf(dumpLine, "[0x%08x] %08x %08x %08x %08x - %08x %08x %08x %08x\n", lineAddr, memory_readU32(lineAddr + 0), memory_readU32(lineAddr + 4), memory_readU32(lineAddr + 8), memory_readU32(lineAddr + 12), memory_readU32(lineAddr + 16), memory_readU32(lineAddr + 20), memory_readU32(lineAddr + 24), memory_readU32(lineAddr + 28));
cemuLog_writePlainToLog(dumpLine);
}
else
{
sprintf(dumpLine, "[0x%08x] ?\n", lineAddr);
cemuLog_writePlainToLog(dumpLine);
}
}
}
else
{
cemuLog_writePlainToLog("Not active\n");
}
// PPC thread log
cemuLog_writePlainToLog("\n");
crashlog_writeHeader("PPC threads");
if (activeThreadCount == 0)
{
cemuLog_writePlainToLog("None active\n");
}
for (sint32 i = 0; i < activeThreadCount; i++)
{
MPTR threadItrMPTR = activeThread[i];
OSThread_t* threadItrBE = (OSThread_t*)memory_getPointerFromVirtualOffset(threadItrMPTR);
// get thread state
OSThread_t::THREAD_STATE threadState = threadItrBE->state;
const char* threadStateStr = "UNDEFINED";
if (threadItrBE->suspendCounter != 0)
threadStateStr = "SUSPENDED";
else if (threadState == OSThread_t::THREAD_STATE::STATE_NONE)
threadStateStr = "NONE";
else if (threadState == OSThread_t::THREAD_STATE::STATE_READY)
threadStateStr = "READY";
else if (threadState == OSThread_t::THREAD_STATE::STATE_RUNNING)
threadStateStr = "RUNNING";
else if (threadState == OSThread_t::THREAD_STATE::STATE_WAITING)
threadStateStr = "WAITING";
else if (threadState == OSThread_t::THREAD_STATE::STATE_MORIBUND)
threadStateStr = "MORIBUND";
// generate log line
uint8 affinity = threadItrBE->attr;
sint32 effectivePriority = threadItrBE->effectivePriority;
const char* threadName = "NULL";
if (!threadItrBE->threadName.IsNull())
threadName = threadItrBE->threadName.GetPtr();
sprintf(dumpLine, "%08x Ent %08x IP %08x LR %08x %-9s Aff %d%d%d Pri %2d Name %s\n", threadItrMPTR, _swapEndianU32(threadItrBE->entrypoint), threadItrBE->context.srr0, _swapEndianU32(threadItrBE->context.lr), threadStateStr, (affinity >> 0) & 1, (affinity >> 1) & 1, (affinity >> 2) & 1, effectivePriority, threadName);
// write line to log
cemuLog_writePlainToLog(dumpLine);
}
CrashLog_SetOutputChannels(false, true);
ExceptionHandler_LogGeneralInfo();
CrashLog_SetOutputChannels(true, true);
cemuLog_waitForFlush();
@ -405,7 +279,7 @@ LONG WINAPI cemu_unhandledExceptionFilter(EXCEPTION_POINTERS* pExceptionInfo)
return EXCEPTION_NONCONTINUABLE_EXCEPTION;
}
void ExceptionHandler_init()
void ExceptionHandler_Init()
{
SetUnhandledExceptionFilter(cemu_unhandledExceptionFilter);
AddVectoredExceptionHandler(1, VectoredExceptionHandler);

View File

@ -179,7 +179,7 @@ void mainEmulatorCommonInit()
// set high priority
SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);
#endif
ExceptionHandler_init();
ExceptionHandler_Init();
// read config
g_config.Load();
if (NetworkConfig::XMLExists())