Cemu/src/Common/ExceptionHandler/ExceptionHandler_posix.cpp
2023-04-06 07:33:41 +02:00

158 lines
4.5 KiB
C++

#include <signal.h>
#include <execinfo.h>
#include <string.h>
#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)
{
ELFSymbolTable symTable;
for (char** i = backtrace; i < backtrace + size; i++)
{
std::string_view traceLine{*i};
// basic check to see if the backtrace line matches expected format
size_t parenthesesOpen = traceLine.find_last_of('(');
size_t parenthesesClose = traceLine.find_last_of(')');
size_t offsetPlus = traceLine.find_last_of('+');
if (!parenthesesOpen || !parenthesesClose || !offsetPlus ||
offsetPlus < parenthesesOpen || offsetPlus > parenthesesClose)
{
// fall back to default string
CrashLog_WriteLine(traceLine);
continue;
}
// attempt to resolve symbol from regular symbol table if missing from dynamic symbol table
uint64 newOffset = -1;
std::string_view symbolName = traceLine.substr(parenthesesOpen+1, offsetPlus-parenthesesOpen-1);
if (symbolName.empty())
{
uint64 symbolOffset = StringHelpers::ToInt64(traceLine.substr(offsetPlus+1,offsetPlus+1-parenthesesClose-1));
symbolName = symTable.OffsetToSymbol(symbolOffset, newOffset);
}
CrashLog_WriteLine(traceLine.substr(0, parenthesesOpen+1), false);
CrashLog_WriteLine(boost::core::demangle(symbolName.empty() ? "" : symbolName.data()), false);
// print relative or existing symbol offset.
CrashLog_WriteLine("+", false);
if (newOffset != -1)
{
CrashLog_WriteLine(fmt::format("0x{:x}", newOffset), false);
CrashLog_WriteLine(traceLine.substr(parenthesesClose));
}
else
{
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, siginfo_t *info, void *context)
{
if(!CrashLog_Create())
return; // give up if crashlog was already created
char* sigName = strsignal(sig);
if (sigName)
{
printf("%s!\n", sigName);
}
else
{
// should never be the case
printf("Unknown core dumping signal!\n");
}
void* backtraceArray[128];
size_t size;
// get void*'s for all entries on the stack
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
CrashLog_WriteLine(fmt::format("Error: signal {}:", sig));
#if BOOST_OS_LINUX
char** symbol_trace = backtrace_symbols(backtraceArray, size);
if (symbol_trace)
{
DemangleAndPrintBacktrace(symbol_trace, size);
free(symbol_trace);
}
else
{
CrashLog_WriteLine("Failed to read backtrace");
}
#else
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
signal(sig, SIG_DFL);
raise(sig);
return;
}
// exit process ignoring all issues
_Exit(1);
}
void handler_SIGINT(int sig)
{
/*
* Received when pressing CTRL + C in a console
* Ideally should be exiting cleanly after saving settings but currently
* there's no clean exit pathway (at least on linux) and exiting the app
* by any mean ends up with a SIGABRT from the standard library destroying
* threads.
*/
_Exit(0);
}
void ExceptionHandler_Init()
{
struct sigaction action;
action.sa_flags = 0;
sigfillset(&action.sa_mask); // don't allow signals to be interrupted
action.sa_handler = handler_SIGINT;
sigaction(SIGINT, &action, nullptr);
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);
sigaction(SIGILL, &action, nullptr);
sigaction(SIGIOT, &action, nullptr);
sigaction(SIGQUIT, &action, nullptr);
sigaction(SIGSEGV, &action, nullptr);
sigaction(SIGSYS, &action, nullptr);
sigaction(SIGTRAP, &action, nullptr);
}