2021-12-05 17:54:36 +00:00
# include "Diagnostics.h"
2022-11-27 21:47:14 +00:00
# include "Game.h"
2023-10-21 23:31:55 +00:00
# include "Logger.h"
2021-12-05 17:54:36 +00:00
// If we're on Win32, we'll include our minidump writer
# ifdef _WIN32
2021-12-05 23:59:50 +00:00
2021-12-05 17:54:36 +00:00
# include <Windows.h>
2021-12-05 23:59:50 +00:00
# include <Dbghelp.h>
2021-12-05 17:54:36 +00:00
# include "Game.h"
2023-10-21 23:31:55 +00:00
# include "Logger.h"
2021-12-05 17:54:36 +00:00
void make_minidump ( EXCEPTION_POINTERS * e ) {
auto hDbgHelp = LoadLibraryA ( " dbghelp " ) ;
if ( hDbgHelp = = nullptr )
return ;
auto pMiniDumpWriteDump = ( decltype ( & MiniDumpWriteDump ) ) GetProcAddress ( hDbgHelp , " MiniDumpWriteDump " ) ;
if ( pMiniDumpWriteDump = = nullptr )
return ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
char name [ MAX_PATH ] ;
{
auto nameEnd = name + GetModuleFileNameA ( GetModuleHandleA ( 0 ) , name , MAX_PATH ) ;
SYSTEMTIME t ;
GetSystemTime ( & t ) ;
wsprintfA ( nameEnd - strlen ( " .exe " ) ,
" _%4d%02d%02d_%02d%02d%02d.dmp " ,
t . wYear , t . wMonth , t . wDay , t . wHour , t . wMinute , t . wSecond ) ;
}
2023-10-21 23:31:55 +00:00
LOG ( " Creating crash dump %s " , name ) ;
2021-12-05 17:54:36 +00:00
auto hFile = CreateFileA ( name , GENERIC_WRITE , FILE_SHARE_READ , 0 , CREATE_ALWAYS , FILE_ATTRIBUTE_NORMAL , 0 ) ;
if ( hFile = = INVALID_HANDLE_VALUE )
return ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
MINIDUMP_EXCEPTION_INFORMATION exceptionInfo ;
exceptionInfo . ThreadId = GetCurrentThreadId ( ) ;
exceptionInfo . ExceptionPointers = e ;
exceptionInfo . ClientPointers = FALSE ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
auto dumped = pMiniDumpWriteDump (
GetCurrentProcess ( ) ,
GetCurrentProcessId ( ) ,
hFile ,
MINIDUMP_TYPE ( MiniDumpWithIndirectlyReferencedMemory | MiniDumpScanMemory ) ,
e ? & exceptionInfo : nullptr ,
nullptr ,
nullptr ) ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
CloseHandle ( hFile ) ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
return ;
}
LONG CALLBACK unhandled_handler ( EXCEPTION_POINTERS * e ) {
make_minidump ( e ) ;
if ( Game : : logger )
Game : : logger - > Flush ( ) ; // Flush our log if we have one, before exiting.
return EXCEPTION_CONTINUE_SEARCH ;
}
# endif
# if defined(__linux__) //&& !defined(__clang__) // backtrace is a gcc exclusive system library
# include <execinfo.h>
# include <ucontext.h>
# include <unistd.h>
# include <csignal>
# include <cstdio>
# include <cstdlib>
# include <cstring>
# include <exception>
2023-12-31 06:26:49 +00:00
# if defined(INCLUDE_BACKTRACE)
2021-12-05 17:54:36 +00:00
# include <backtrace.h>
# include <backtrace-supported.h>
struct bt_ctx {
struct backtrace_state * state ;
int error ;
} ;
static inline void Bt ( struct backtrace_state * state ) {
std : : string fileName = Diagnostics : : GetOutDirectory ( ) + " crash_ " + Diagnostics : : GetProcessName ( ) + " _ " + std : : to_string ( getpid ( ) ) + " .log " ;
2023-10-21 23:31:55 +00:00
LOG ( " backtrace is enabled, crash dump located at %s " , fileName . c_str ( ) ) ;
2021-12-05 17:54:36 +00:00
FILE * file = fopen ( fileName . c_str ( ) , " w+ " ) ;
if ( file ! = nullptr ) {
backtrace_print ( state , 2 , file ) ;
fclose ( file ) ;
}
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
backtrace_print ( state , 2 , stdout ) ;
}
static void ErrorCallback ( void * data , const char * msg , int errnum ) {
auto * ctx = ( struct bt_ctx * ) data ;
fprintf ( stderr , " ERROR: %s (%d) " , msg , errnum ) ;
ctx - > error = 1 ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
std : : string fileName = Diagnostics : : GetOutDirectory ( ) + " crash_ " + Diagnostics : : GetProcessName ( ) + " _ " + std : : to_string ( getpid ( ) ) + " .log " ;
FILE * file = fopen ( fileName . c_str ( ) , " w+ " ) ;
if ( file ! = nullptr ) {
fprintf ( file , " ERROR: %s (%d) " , msg , errnum ) ;
fclose ( file ) ;
}
}
# endif
2023-10-09 20:31:43 +00:00
# include "Demangler.h"
2021-12-05 17:54:36 +00:00
void GenerateDump ( ) {
std : : string cmd = " sudo gcore " + std : : to_string ( getpid ( ) ) ;
2023-04-12 16:48:20 +00:00
int ret = system ( cmd . c_str ( ) ) ; // Saving a return just to prevent warning
2021-12-05 17:54:36 +00:00
}
void CatchUnhandled ( int sig ) {
2023-12-30 07:04:26 +00:00
std : : exception_ptr eptr = std : : current_exception ( ) ;
try {
if ( eptr ) std : : rethrow_exception ( eptr ) ;
} catch ( const std : : exception & e ) {
LOG ( " Caught exception: '%s' " , e . what ( ) ) ;
}
2024-01-01 23:34:43 +00:00
Game : : logger - > Flush ( ) ;
2023-12-30 07:04:26 +00:00
2023-12-31 06:26:49 +00:00
# ifndef INCLUDE_BACKTRACE
2021-12-05 17:54:36 +00:00
2022-11-27 21:47:14 +00:00
std : : string fileName = Diagnostics : : GetOutDirectory ( ) + " crash_ " + Diagnostics : : GetProcessName ( ) + " _ " + std : : to_string ( getpid ( ) ) + " .log " ;
2023-10-21 23:31:55 +00:00
LOG ( " Encountered signal %i, creating crash dump %s " , sig , fileName . c_str ( ) ) ;
2021-12-05 17:54:36 +00:00
if ( Diagnostics : : GetProduceMemoryDump ( ) ) {
GenerateDump ( ) ;
}
2023-10-09 20:31:43 +00:00
constexpr uint8_t MaxStackTrace = 32 ;
void * array [ MaxStackTrace ] ;
2021-12-05 17:54:36 +00:00
size_t size ;
// get void*'s for all entries on the stack
2023-10-09 20:31:43 +00:00
size = backtrace ( array , MaxStackTrace ) ;
2021-12-05 17:54:36 +00:00
2023-10-09 20:31:43 +00:00
# if defined(__GNUG__)
2021-12-05 17:54:36 +00:00
// Loop through the returned addresses, and get the symbols to be demangled
char * * strings = backtrace_symbols ( array , size ) ;
2022-07-28 13:39:57 +00:00
2023-10-23 00:58:19 +00:00
FILE * file = fopen ( fileName . c_str ( ) , " w+ " ) ;
if ( file ! = NULL ) {
fprintf ( file , " Error: signal %d: \n " , sig ) ;
}
2021-12-05 17:54:36 +00:00
// Print the stack trace
for ( size_t i = 0 ; i < size ; i + + ) {
2023-10-09 20:31:43 +00:00
// Take a string like './WorldServer(_ZN19SlashCommandHandler17HandleChatCommandERKSbIDsSt11char_traitsIDsESaIDsEEP6EntityRK13SystemAddress+0x6187) [0x55869c44ecf7]'
// and extract '_ZN19SlashCommandHandler17HandleChatCommandERKSbIDsSt11char_traitsIDsESaIDsEEP6EntityRK13SystemAddress' from it to be demangled into a proper name
2021-12-05 17:54:36 +00:00
std : : string functionName = strings [ i ] ;
std : : string : : size_type start = functionName . find ( ' ( ' ) ;
std : : string : : size_type end = functionName . find ( ' + ' ) ;
if ( start ! = std : : string : : npos & & end ! = std : : string : : npos ) {
std : : string demangled = functionName . substr ( start + 1 , end - start - 1 ) ;
2022-07-28 13:39:57 +00:00
2023-10-09 20:31:43 +00:00
demangled = Demangler : : Demangle ( demangled . c_str ( ) ) ;
2022-07-28 13:39:57 +00:00
2023-10-09 20:31:43 +00:00
// If the demangled string is not empty, then we can replace the mangled string with the demangled one
if ( ! demangled . empty ( ) ) {
demangled . push_back ( ' ( ' ) ;
demangled + = functionName . substr ( end ) ;
functionName = demangled ;
2021-12-05 17:54:36 +00:00
}
}
2023-10-09 20:31:43 +00:00
2023-10-21 23:31:55 +00:00
LOG ( " [%02zu] %s " , i , functionName . c_str ( ) ) ;
2023-10-23 00:58:19 +00:00
if ( file ! = NULL ) {
fprintf ( file , " [%02zu] %s \n " , i , functionName . c_str ( ) ) ;
}
2021-12-05 17:54:36 +00:00
}
2023-10-09 20:31:43 +00:00
# else // defined(__GNUG__)
2021-12-05 17:54:36 +00:00
backtrace_symbols_fd ( array , size , STDOUT_FILENO ) ;
2023-10-09 20:31:43 +00:00
# endif // defined(__GNUG__)
2021-12-05 17:54:36 +00:00
2023-12-31 06:26:49 +00:00
# else // INCLUDE_BACKTRACE
2021-12-05 17:54:36 +00:00
struct backtrace_state * state = backtrace_create_state (
Diagnostics : : GetProcessFileName ( ) . c_str ( ) ,
BACKTRACE_SUPPORTS_THREADS ,
ErrorCallback ,
nullptr ) ;
struct bt_ctx ctx = { state , 0 } ;
Bt ( state ) ;
2023-12-31 06:26:49 +00:00
# endif // INCLUDE_BACKTRACE
2024-01-01 23:34:43 +00:00
Game : : logger - > Flush ( ) ;
2021-12-05 17:54:36 +00:00
exit ( EXIT_FAILURE ) ;
}
void CritErrHdlr ( int sig_num , siginfo_t * info , void * ucontext ) {
CatchUnhandled ( sig_num ) ;
}
void OnTerminate ( ) {
CatchUnhandled ( - 1 ) ;
}
void MakeBacktrace ( ) {
struct sigaction sigact ;
sigact . sa_sigaction = CritErrHdlr ;
sigact . sa_flags = SA_RESTART | SA_SIGINFO ;
2023-12-28 04:18:20 +00:00
if ( sigaction ( SIGSEGV , & sigact , nullptr ) ! = 0 | |
sigaction ( SIGFPE , & sigact , nullptr ) ! = 0 | |
sigaction ( SIGABRT , & sigact , nullptr ) ! = 0 | |
sigaction ( SIGILL , & sigact , nullptr ) ! = 0 ) {
2021-12-05 17:54:36 +00:00
fprintf ( stderr , " error setting signal handler for %d (%s) \n " ,
SIGSEGV ,
strsignal ( SIGSEGV ) ) ;
exit ( EXIT_FAILURE ) ;
}
std : : set_terminate ( OnTerminate ) ;
}
# endif
void Diagnostics : : Initialize ( ) {
# ifdef _WIN32
SetUnhandledExceptionFilter ( unhandled_handler ) ;
# elif defined(__linux__) //&& !defined(__clang__)
MakeBacktrace ( ) ;
# else
fprintf ( stderr , " Diagnostics not supported on this platform. \n " ) ;
# endif
}
std : : string Diagnostics : : m_ProcessName { } ;
std : : string Diagnostics : : m_ProcessFileName { } ;
std : : string Diagnostics : : m_OutDirectory { } ;
bool Diagnostics : : m_ProduceMemoryDump { } ;
void Diagnostics : : SetProcessName ( const std : : string & name ) {
m_ProcessName = name ;
}
void Diagnostics : : SetProcessFileName ( const std : : string & name ) {
m_ProcessFileName = name ;
}
void Diagnostics : : SetOutDirectory ( const std : : string & path ) {
m_OutDirectory = path ;
}
void Diagnostics : : SetProduceMemoryDump ( bool value ) {
m_ProduceMemoryDump = value ;
}
const std : : string & Diagnostics : : GetProcessName ( ) {
return m_ProcessName ;
}
const std : : string & Diagnostics : : GetProcessFileName ( ) {
return m_ProcessFileName ;
}
const std : : string & Diagnostics : : GetOutDirectory ( ) {
return m_OutDirectory ;
}
bool Diagnostics : : GetProduceMemoryDump ( ) {
return m_ProduceMemoryDump ;
}