2007-02-23 08:36:11 -08:00
/*
This file is part of Warzone 2100.
2008-04-05 08:11:43 -07:00
Copyright ( C ) 2007 - 2008 Warzone Resurrection Project
2007-02-23 08:36:11 -08:00
Warzone 2100 is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
Warzone 2100 is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with Warzone 2100 ; if not , write to the Free Software
Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 USA
*/
2008-04-05 08:11:43 -07:00
# include "lib/framework/frame.h"
# include "exceptionhandler.h"
2008-04-14 16:17:51 -07:00
# include "dumpinfo.h"
2007-03-29 02:45:11 -07:00
2007-03-03 10:23:06 -08:00
# if defined(WZ_OS_WIN)
2007-02-23 08:36:11 -08:00
# include "dbghelp.h"
2008-04-05 09:15:14 -07:00
# include "exchndl.h"
2007-03-03 10:23:06 -08:00
2008-04-13 15:54:58 -07:00
# if !defined(WZ_CC_MINGW)
2008-04-05 15:14:16 -07:00
static LPTOP_LEVEL_EXCEPTION_FILTER prevExceptionHandler = NULL ;
2007-03-29 02:45:11 -07:00
2007-03-15 12:12:33 -07:00
/**
* Exception handling on Windows .
* Ask the user whether he wants to safe a Minidump and then dump it into the temp directory .
*
* \ param pExceptionInfo Information on the exception , passed from Windows
* \ return whether further exception handlers ( i . e . the Windows internal one ) should be invoked
*/
2007-02-23 08:36:11 -08:00
static LONG WINAPI windowsExceptionHandler ( PEXCEPTION_POINTERS pExceptionInfo )
{
2007-03-05 16:46:34 -08:00
LPCSTR applicationName = " Warzone 2100 " ;
2007-02-23 08:36:11 -08:00
2007-08-21 05:59:05 -07:00
char miniDumpPath [ PATH_MAX ] = { ' \0 ' } , resultMessage [ PATH_MAX ] = { ' \0 ' } ;
2007-02-23 08:36:11 -08:00
// Write to temp dir, to support unprivileged users
2008-05-25 06:46:49 -07:00
if ( ! GetTempPathA ( sizeof ( miniDumpPath ) , miniDumpPath ) )
2007-10-24 14:11:29 -07:00
{
2008-05-25 06:46:49 -07:00
sstrcpy ( miniDumpPath , " c: \\ temp \\ " ) ;
2007-10-24 14:11:29 -07:00
}
// Append the filename
2008-05-25 08:42:02 -07:00
sstrcat ( miniDumpPath , " warzone2100.mdmp " ) ;
2007-02-23 08:36:11 -08:00
/*
Alternative :
GetModuleFileName ( NULL , miniDumpPath , MAX_PATH ) ;
2007-10-24 14:11:29 -07:00
// Append extension
2008-05-25 06:46:49 -07:00
sstrcat ( miniDumpPath , " .mdmp " ) ;
2007-02-23 08:36:11 -08:00
*/
2007-03-05 16:46:34 -08:00
if ( MessageBoxA ( NULL , " Warzone crashed unexpectedly, would you like to save a diagnostic file? " , applicationName , MB_YESNO ) = = IDYES )
2007-02-23 08:36:11 -08:00
{
2007-03-05 16:46:34 -08:00
HANDLE miniDumpFile = CreateFileA ( miniDumpPath , GENERIC_WRITE , FILE_SHARE_WRITE , NULL , CREATE_ALWAYS , FILE_ATTRIBUTE_NORMAL , NULL ) ;
2007-02-23 08:36:11 -08:00
if ( miniDumpFile ! = INVALID_HANDLE_VALUE )
{
2007-12-12 10:34:30 -08:00
MINIDUMP_USER_STREAM uStream = { LastReservedStream + 1 , strlen ( PACKAGE_VERSION ) , PACKAGE_VERSION } ;
2007-02-23 08:36:11 -08:00
MINIDUMP_USER_STREAM_INFORMATION uInfo = { 1 , & uStream } ;
2008-03-24 09:51:17 -07:00
MINIDUMP_EXCEPTION_INFORMATION eInfo = { GetCurrentThreadId ( ) , pExceptionInfo , false } ;
2007-02-23 08:36:11 -08:00
if ( MiniDumpWriteDump (
GetCurrentProcess ( ) ,
GetCurrentProcessId ( ) ,
miniDumpFile ,
MiniDumpNormal ,
pExceptionInfo ? & eInfo : NULL ,
& uInfo ,
NULL ) )
{
2007-10-24 14:11:29 -07:00
snprintf ( resultMessage , sizeof ( resultMessage ) , " Saved dump file to '%s' " , miniDumpPath ) ;
2007-02-23 08:36:11 -08:00
}
else
{
2007-10-24 14:11:29 -07:00
snprintf ( resultMessage , sizeof ( resultMessage ) , " Failed to save dump file to '%s' (error %d) " , miniDumpPath , ( int ) GetLastError ( ) ) ;
2007-02-23 08:36:11 -08:00
}
2007-10-24 14:11:29 -07:00
2007-02-23 08:36:11 -08:00
CloseHandle ( miniDumpFile ) ;
}
else
{
2007-10-24 14:11:29 -07:00
snprintf ( resultMessage , sizeof ( resultMessage ) , " Failed to create dump file '%s' (error %d) " , miniDumpPath , ( int ) GetLastError ( ) ) ;
2007-02-23 08:36:11 -08:00
}
2007-10-25 16:32:20 -07:00
MessageBoxA ( NULL , resultMessage , applicationName , MB_OK ) ;
2007-02-23 08:36:11 -08:00
}
2008-04-05 15:14:16 -07:00
if ( prevExceptionHandler )
return prevExceptionHandler ( pExceptionInfo ) ;
else
return EXCEPTION_CONTINUE_SEARCH ;
2007-02-23 08:36:11 -08:00
}
2008-04-13 15:54:58 -07:00
# endif
2007-03-03 10:23:06 -08:00
2007-05-15 13:33:55 -07:00
# elif defined(WZ_OS_UNIX) && !defined(WZ_OS_MAC)
2007-03-29 02:45:11 -07:00
2007-03-03 10:23:06 -08:00
// C99 headers:
2007-03-04 16:24:23 -08:00
# include <stdint.h>
# include <signal.h>
# include <string.h>
2007-03-03 10:23:06 -08:00
// POSIX headers:
2007-03-04 16:24:23 -08:00
# include <unistd.h>
# include <fcntl.h>
2007-06-25 11:04:39 -07:00
# include <time.h>
2007-03-04 16:24:23 -08:00
# include <sys / types.h>
# include <sys / stat.h>
# include <sys / wait.h>
# include <sys / utsname.h>
2007-02-23 08:36:11 -08:00
2007-03-10 11:43:02 -08:00
// GNU extension for backtrace():
# if defined(__GLIBC__)
# include <execinfo.h>
# define MAX_BACKTRACE 20
# endif
2007-03-03 10:23:06 -08:00
2007-03-04 05:04:36 -08:00
2007-03-04 16:24:23 -08:00
# define MAX_PID_STRING 16
2007-06-25 11:04:39 -07:00
# define MAX_DATE_STRING 256
2007-02-24 10:21:22 -08:00
2007-03-13 06:48:59 -07:00
typedef void ( * SigActionHandler ) ( int , siginfo_t * , void * ) ;
2007-05-07 13:02:18 -07:00
# ifdef WZ_OS_MAC
static struct sigaction oldAction [ 32 ] ;
# else
2007-03-13 06:48:59 -07:00
static struct sigaction oldAction [ NSIG ] ;
2007-05-07 13:02:18 -07:00
# endif
2007-02-23 08:36:11 -08:00
2007-03-05 16:30:07 -08:00
static struct utsname sysInfo ;
2008-03-24 09:51:17 -07:00
static BOOL gdbIsAvailable = false , programIsAvailable = false , sysInfoValid = false ;
2007-06-25 10:35:09 -07:00
static char
2007-06-25 11:04:39 -07:00
executionDate [ MAX_DATE_STRING ] = { ' \0 ' } ,
2007-06-25 10:35:09 -07:00
programPID [ MAX_PID_STRING ] = { ' \0 ' } ,
2007-08-21 05:59:05 -07:00
programPath [ PATH_MAX ] = { ' \0 ' } ,
gdbPath [ PATH_MAX ] = { ' \0 ' } ;
2007-06-25 10:35:09 -07:00
static const char * gdmpPath = " /tmp/warzone2100.gdmp " ;
2007-03-05 16:30:07 -08:00
2007-03-04 05:52:41 -08:00
2007-03-15 12:12:33 -07:00
/**
* Signal number to string mapper .
* Also takes into account the signal code with details about the signal .
*
* \ param signum Signal number
* \ param sigcode Signal code
* \ return String with the description of the signal . " Unknown signal " when no description is available .
*/
2007-03-13 06:48:59 -07:00
static const char * wz_strsignal ( int signum , int sigcode )
2007-03-04 05:52:41 -08:00
{
2007-03-04 14:50:41 -08:00
switch ( signum )
{
2007-03-13 06:48:59 -07:00
case SIGABRT :
return " SIGABRT: Process abort signal " ;
case SIGALRM :
return " SIGALRM: Alarm clock " ;
case SIGBUS :
switch ( sigcode )
{
case BUS_ADRALN :
return " SIGBUS: Access to an undefined portion of a memory object: Invalid address alignment " ;
case BUS_ADRERR :
return " SIGBUS: Access to an undefined portion of a memory object: Nonexistent physical address " ;
case BUS_OBJERR :
return " SIGBUS: Access to an undefined portion of a memory object: Object-specific hardware error " ;
default :
return " SIGBUS: Access to an undefined portion of a memory object " ;
}
case SIGCHLD :
switch ( sigcode )
{
case CLD_EXITED :
return " SIGCHLD: Child process terminated, stopped, or continued: Child has exited " ;
case CLD_KILLED :
return " SIGCHLD: Child process terminated, stopped, or continued: Child has terminated abnormally and did not create a core file " ;
case CLD_DUMPED :
return " SIGCHLD: Child process terminated, stopped, or continued: Child has terminated abnormally and created a core file " ;
case CLD_TRAPPED :
return " SIGCHLD: Child process terminated, stopped, or continued: Traced child has trapped " ;
case CLD_STOPPED :
return " SIGCHLD: Child process terminated, stopped, or continued: Child has stopped " ;
case CLD_CONTINUED :
return " SIGCHLD: Child process terminated, stopped, or continued: Stopped child has continued " ;
}
case SIGCONT :
return " SIGCONT: Continue executing, if stopped " ;
case SIGFPE :
switch ( sigcode )
{
case FPE_INTDIV :
return " SIGFPE: Erroneous arithmetic operation: Integer divide by zero " ;
case FPE_INTOVF :
return " SIGFPE: Erroneous arithmetic operation: Integer overflow " ;
case FPE_FLTDIV :
return " SIGFPE: Erroneous arithmetic operation: Floating-point divide by zero " ;
case FPE_FLTOVF :
return " SIGFPE: Erroneous arithmetic operation: Floating-point overflow " ;
case FPE_FLTUND :
return " SIGFPE: Erroneous arithmetic operation: Floating-point underflow " ;
case FPE_FLTRES :
return " SIGFPE: Erroneous arithmetic operation: Floating-point inexact result " ;
case FPE_FLTINV :
return " SIGFPE: Erroneous arithmetic operation: Invalid floating-point operation " ;
case FPE_FLTSUB :
return " SIGFPE: Erroneous arithmetic operation: Subscript out of range " ;
default :
return " SIGFPE: Erroneous arithmetic operation " ;
} ;
case SIGHUP :
return " SIGHUP: Hangup " ;
case SIGILL :
switch ( sigcode )
{
case ILL_ILLOPC :
return " SIGILL: Illegal instruction: Illegal opcode " ;
case ILL_ILLOPN :
return " SIGILL: Illegal instruction: Illegal operand " ;
case ILL_ILLADR :
return " SIGILL: Illegal instruction: Illegal addressing mode " ;
case ILL_ILLTRP :
return " SIGILL: Illegal instruction: Illegal trap " ;
case ILL_PRVOPC :
return " SIGILL: Illegal instruction: Privileged opcode " ;
case ILL_PRVREG :
return " SIGILL: Illegal instruction: Privileged register " ;
case ILL_COPROC :
return " SIGILL: Illegal instruction: Coprocessor error " ;
case ILL_BADSTK :
return " SIGILL: Illegal instruction: Internal stack error " ;
default :
return " SIGILL: Illegal instruction " ;
}
case SIGINT :
return " SIGINT: Terminal interrupt signal " ;
case SIGKILL :
return " SIGKILL: Kill " ;
case SIGPIPE :
return " SIGPIPE: Write on a pipe with no one to read it " ;
case SIGQUIT :
return " SIGQUIT: Terminal quit signal " ;
case SIGSEGV :
switch ( sigcode )
{
case SEGV_MAPERR :
return " SIGSEGV: Invalid memory reference: Address not mapped to object " ;
case SEGV_ACCERR :
return " SIGSEGV: Invalid memory reference: Invalid permissions for mapped object " ;
default :
return " SIGSEGV: Invalid memory reference " ;
}
case SIGSTOP :
return " SIGSTOP: Stop executing " ;
case SIGTERM :
return " SIGTERM: Termination signal " ;
case SIGTSTP :
return " SIGTSTP: Terminal stop signal " ;
case SIGTTIN :
return " SIGTTIN: Background process attempting read " ;
case SIGTTOU :
return " SIGTTOU: Background process attempting write " ;
case SIGUSR1 :
return " SIGUSR1: User-defined signal 1 " ;
case SIGUSR2 :
return " SIGUSR2: User-defined signal 2 " ;
# if _XOPEN_UNIX
case SIGPOLL :
switch ( sigcode )
{
case POLL_IN :
return " SIGPOLL: Pollable event: Data input available " ;
case POLL_OUT :
return " SIGPOLL: Pollable event: Output buffers available " ;
case POLL_MSG :
return " SIGPOLL: Pollable event: Input message available " ;
case POLL_ERR :
return " SIGPOLL: Pollable event: I/O error " ;
case POLL_PRI :
return " SIGPOLL: Pollable event: High priority input available " ;
case POLL_HUP :
return " SIGPOLL: Pollable event: Device disconnected. " ;
default :
return " SIGPOLL: Pollable event " ;
}
case SIGPROF :
return " SIGPROF: Profiling timer expired " ;
case SIGSYS :
return " SIGSYS: Bad system call " ;
case SIGTRAP :
switch ( sigcode )
{
case TRAP_BRKPT :
return " SIGTRAP: Trace/breakpoint trap: Process breakpoint " ;
case TRAP_TRACE :
return " SIGTRAP: Trace/breakpoint trap: Process trace trap " ;
default :
return " SIGTRAP: Trace/breakpoint trap " ;
}
# endif // _XOPEN_UNIX
case SIGURG :
return " SIGURG: High bandwidth data is available at a socket " ;
# if _XOPEN_UNIX
case SIGVTALRM :
return " SIGVTALRM: Virtual timer expired " ;
case SIGXCPU :
return " SIGXCPU: CPU time limit exceeded " ;
case SIGXFSZ :
return " SIGXFSZ: File size limit exceeded " ;
# endif // _XOPEN_UNIX
default :
return " Unknown signal " ;
}
}
2007-03-04 16:24:23 -08:00
2007-03-15 12:12:33 -07:00
/**
* Set signal handlers for fatal signals on POSIX systems
*
* \ param signalHandler Pointer to the signal handler function
*/
2007-03-13 06:48:59 -07:00
static void setFatalSignalHandler ( SigActionHandler signalHandler )
{
struct sigaction new_handler ;
2007-03-04 16:24:23 -08:00
2007-03-13 06:48:59 -07:00
new_handler . sa_sigaction = signalHandler ;
sigemptyset ( & new_handler . sa_mask ) ;
new_handler . sa_flags = SA_SIGINFO ;
2007-03-04 16:24:23 -08:00
2007-03-13 06:48:59 -07:00
sigaction ( SIGABRT , NULL , & oldAction [ SIGABRT ] ) ;
if ( oldAction [ SIGABRT ] . sa_handler ! = SIG_IGN )
sigaction ( SIGABRT , & new_handler , NULL ) ;
2007-03-04 16:24:23 -08:00
2007-03-13 06:48:59 -07:00
sigaction ( SIGBUS , NULL , & oldAction [ SIGBUS ] ) ;
if ( oldAction [ SIGBUS ] . sa_handler ! = SIG_IGN )
sigaction ( SIGBUS , & new_handler , NULL ) ;
2007-03-04 16:24:23 -08:00
2007-03-13 06:48:59 -07:00
sigaction ( SIGFPE , NULL , & oldAction [ SIGFPE ] ) ;
if ( oldAction [ SIGFPE ] . sa_handler ! = SIG_IGN )
sigaction ( SIGFPE , & new_handler , NULL ) ;
2007-03-04 16:24:23 -08:00
2007-03-13 06:48:59 -07:00
sigaction ( SIGILL , NULL , & oldAction [ SIGILL ] ) ;
if ( oldAction [ SIGILL ] . sa_handler ! = SIG_IGN )
sigaction ( SIGILL , & new_handler , NULL ) ;
2007-03-04 16:24:23 -08:00
2007-03-13 06:48:59 -07:00
sigaction ( SIGQUIT , NULL , & oldAction [ SIGQUIT ] ) ;
if ( oldAction [ SIGQUIT ] . sa_handler ! = SIG_IGN )
sigaction ( SIGQUIT , & new_handler , NULL ) ;
2007-03-04 16:24:23 -08:00
2007-03-13 06:48:59 -07:00
sigaction ( SIGSEGV , NULL , & oldAction [ SIGSEGV ] ) ;
if ( oldAction [ SIGSEGV ] . sa_handler ! = SIG_IGN )
sigaction ( SIGSEGV , & new_handler , NULL ) ;
2007-03-04 16:24:23 -08:00
2007-03-13 06:48:59 -07:00
# if _XOPEN_UNIX
sigaction ( SIGSYS , NULL , & oldAction [ SIGSYS ] ) ;
if ( oldAction [ SIGSYS ] . sa_handler ! = SIG_IGN )
sigaction ( SIGSYS , & new_handler , NULL ) ;
2007-03-04 16:24:23 -08:00
2007-03-13 06:48:59 -07:00
sigaction ( SIGTRAP , NULL , & oldAction [ SIGTRAP ] ) ;
if ( oldAction [ SIGTRAP ] . sa_handler ! = SIG_IGN )
sigaction ( SIGTRAP , & new_handler , NULL ) ;
2007-03-04 14:50:41 -08:00
2007-03-13 06:48:59 -07:00
sigaction ( SIGXCPU , NULL , & oldAction [ SIGXCPU ] ) ;
if ( oldAction [ SIGXCPU ] . sa_handler ! = SIG_IGN )
sigaction ( SIGXCPU , & new_handler , NULL ) ;
2007-02-23 08:36:11 -08:00
2007-03-13 06:48:59 -07:00
sigaction ( SIGXFSZ , NULL , & oldAction [ SIGXFSZ ] ) ;
if ( oldAction [ SIGXFSZ ] . sa_handler ! = SIG_IGN )
sigaction ( SIGXFSZ , & new_handler , NULL ) ;
# endif // _XOPEN_UNIX
2007-02-23 08:36:11 -08:00
}
2007-03-15 12:12:33 -07:00
/**
2008-07-04 15:41:07 -07:00
* Dumps a backtrace of the stack to the given output stream .
2007-03-15 12:12:33 -07:00
*
2008-07-04 15:41:07 -07:00
* @ param dumpFile a POSIX file descriptor to write the resulting backtrace to .
2008-07-04 15:41:17 -07:00
*
* @ return false if any failure occurred , preventing a full " extended "
* backtrace .
2007-03-15 12:12:33 -07:00
*/
2008-07-04 15:41:17 -07:00
static bool gdbExtendedBacktrace ( int const dumpFile )
2007-02-23 08:36:11 -08:00
{
2008-07-04 15:41:17 -07:00
/* Check if the "bare minimum" is available: GDB and an absolute path
* to our program ' s binary .
*/
if ( ! programIsAvailable
| | ! gdbIsAvailable )
2007-03-04 04:05:53 -08:00
{
2008-07-04 15:41:17 -07:00
write ( dumpFile , " No extended backtrace dumped: \n " ,
strlen ( " No extended backtrace dumped: \n " ) ) ;
2008-07-04 15:41:07 -07:00
2008-07-04 15:41:17 -07:00
if ( ! programIsAvailable )
2007-03-04 04:05:53 -08:00
{
2008-07-04 15:41:17 -07:00
write ( dumpFile , " - Program path not available \n " ,
strlen ( " - Program path not available \n " ) ) ;
}
if ( ! gdbIsAvailable )
{
write ( dumpFile , " - GDB not available \n " ,
strlen ( " - GDB not available \n " ) ) ;
}
2007-03-04 04:05:53 -08:00
2008-07-04 15:41:17 -07:00
return false ;
}
2007-03-04 04:05:53 -08:00
2008-07-04 15:41:17 -07:00
// Create a pipe to use for communication with 'gdb'
int gdbPipe [ 2 ] ;
if ( pipe ( gdbPipe ) = = - 1 )
{
write ( dumpFile , " Pipe failed \n " ,
strlen ( " Pipe failed \n " ) ) ;
2007-03-04 04:05:53 -08:00
2008-07-04 15:41:17 -07:00
printf ( " Pipe failed \n " ) ;
2007-03-10 11:43:02 -08:00
2008-07-04 15:41:17 -07:00
return false ;
}
2008-07-04 15:41:00 -07:00
2008-07-04 15:41:17 -07:00
// Fork a new child process
const pid_t pid = fork ( ) ;
if ( pid = = - 1 )
{
write ( dumpFile , " Fork failed \n " ,
strlen ( " Fork failed \n " ) ) ;
2008-07-04 15:41:00 -07:00
2008-07-04 15:41:17 -07:00
printf ( " Fork failed \n " ) ;
2008-07-04 15:41:00 -07:00
2008-07-04 15:41:17 -07:00
// Clean up our pipe
close ( gdbPipe [ 0 ] ) ;
close ( gdbPipe [ 1 ] ) ;
2008-07-01 13:40:40 -07:00
2008-07-04 15:41:17 -07:00
return false ;
}
2007-03-04 04:05:53 -08:00
2008-07-04 15:41:17 -07:00
// Check to see if we're the child
if ( pid = = 0 )
{
char * gdbArgv [ ] = { gdbPath , programPath , programPID , NULL } ;
char * gdbEnv [ ] = { NULL } ;
2007-03-04 04:05:53 -08:00
2008-07-04 15:41:17 -07:00
close ( gdbPipe [ 1 ] ) ; // No output to pipe
2007-03-04 04:05:53 -08:00
2008-07-04 15:41:17 -07:00
dup2 ( gdbPipe [ 0 ] , STDIN_FILENO ) ; // STDIN from pipe
dup2 ( dumpFile , STDOUT_FILENO ) ; // STDOUT to dumpFile
write ( dumpFile , " GDB extended backtrace: \n " ,
strlen ( " GDB extended backtrace: \n " ) ) ;
/* If execve() is successful it effectively prevents further
* execution of this code .
*/
execve ( gdbPath , ( char * * ) gdbArgv , ( char * * ) gdbEnv ) ;
2007-03-04 04:05:53 -08:00
}
2008-07-04 15:41:17 -07:00
// PARENT: If we get here we're the parent
// Retrieve a full stack backtrace
static const char gdbCommands [ ] = " backtrace full \n "
// Move to the stack frame where we triggered the crash
" frame 4 \n "
// Show the assembly code associated with that stack frame
" disassemble \n "
// Show the content of all registers
" info registers \n "
" quit \n " ;
close ( gdbPipe [ 0 ] ) ; // No input from pipe
write ( gdbPipe [ 1 ] , gdbCommands , sizeof ( gdbCommands ) ) ;
/* Close our end of the pipe to force an EOF on GDB's side of the pipe.
* This will prevent any kind of buffering on GDB ' s end from causing
* infinite blocking on the next waitpid ( ) call .
*/
close ( gdbPipe [ 1 ] ) ;
if ( waitpid ( pid , NULL , 0 ) < 0 )
2007-03-04 04:05:53 -08:00
{
2008-07-04 15:41:17 -07:00
printf ( " GDB failed \n " ) ;
2007-03-04 04:05:53 -08:00
}
2008-07-04 15:41:17 -07:00
return true ;
2008-07-04 15:41:07 -07:00
}
/**
* Exception ( signal ) handling on POSIX systems .
* Dumps info about the system incl . backtrace ( when GLibC or GDB is present ) to / tmp / warzone2100 . gdmp
*
* \ param signum Signal number
* \ param siginfo Signal info
* \ param sigcontext Signal context
*/
static void posixExceptionHandler ( int signum , siginfo_t * siginfo , WZ_DECL_UNUSED void * sigcontext )
{
static sig_atomic_t allreadyRunning = 0 ;
if ( allreadyRunning )
raise ( signum ) ;
allreadyRunning = 1 ;
# if defined(__GLIBC__)
void * btBuffer [ MAX_BACKTRACE ] = { NULL } ;
uint32_t btSize = backtrace ( btBuffer , MAX_BACKTRACE ) ;
# endif
const int dumpFile = open ( gdmpPath , O_WRONLY | O_CREAT | O_TRUNC , S_IRUSR | S_IWUSR ) ;
if ( dumpFile = = - 1 )
{
printf ( " Failed to create dump file '%s' " , gdmpPath ) ;
return ;
}
// Dump a generic info header
dbgDumpHeader ( dumpFile ) ;
write ( dumpFile , " Dump caused by signal: " , strlen ( " Dump caused by signal: " ) ) ;
const char * signal = wz_strsignal ( siginfo - > si_signo , siginfo - > si_code ) ;
write ( dumpFile , signal , strlen ( signal ) ) ;
write ( dumpFile , " \n \n " , 2 ) ;
dumpLog ( dumpFile ) ; // dump out the last two log calls
# if defined(__GLIBC__)
// Dump raw backtrace in case GDB is not available or fails
write ( dumpFile , " GLIBC raw backtrace: \n " , strlen ( " GLIBC raw backtrace: \n " ) ) ;
backtrace_symbols_fd ( btBuffer , btSize , dumpFile ) ;
write ( dumpFile , " \n " , 1 ) ;
# else
write ( dumpFile , " GLIBC not available, no raw backtrace dumped \n \n " ,
strlen ( " GLIBC not available, no raw backtrace dumped \n \n " ) ) ;
# endif
// Make sure everything is written before letting GDB write to it
fsync ( dumpFile ) ;
2007-02-23 08:36:11 -08:00
2008-07-04 15:41:07 -07:00
// Use 'gdb' to provide an "extended" backtrace
gdbExtendedBacktrace ( dumpFile ) ;
2007-03-04 16:24:23 -08:00
printf ( " Saved dump file to '%s' \n " , gdmpPath ) ;
close ( dumpFile ) ;
2007-03-15 12:12:33 -07:00
sigaction ( signum , & oldAction [ signum ] , NULL ) ;
raise ( signum ) ;
2007-02-23 08:36:11 -08:00
}
2007-03-03 10:23:06 -08:00
2007-03-29 02:45:11 -07:00
2007-03-03 10:23:06 -08:00
# endif // WZ_OS_*
2007-02-23 08:36:11 -08:00
2008-07-04 15:40:20 -07:00
# if defined(WZ_OS_UNIX) && !defined(WZ_OS_MAC)
static bool fetchProgramPath ( char * const programPath , size_t const bufSize , const char * const programCommand )
{
// Construct the "which $(programCommand)" string
char whichProgramCommand [ PATH_MAX ] ;
snprintf ( whichProgramCommand , sizeof ( whichProgramCommand ) , " which %s " , programCommand ) ;
/* Fill the output buffer with zeroes so that we can rely on the output
* string being NUL - terminated .
*/
memset ( programPath , 0 , bufSize ) ;
/* Execute the "which" command (constructed above) and collect its
* output in programPath .
*/
FILE * const whichProgramStream = popen ( whichProgramCommand , " r " ) ;
size_t const bytesRead = fread ( programPath , 1 , bufSize , whichProgramStream ) ;
pclose ( whichProgramStream ) ;
// Check whether our buffer is too small, indicate failure if it is
if ( bytesRead = = bufSize )
{
debug ( LOG_WARNING , " Could not retrieve full path to \" %s \" , as our buffer is too small. This may prevent creation of an extended backtrace. " , programCommand ) ;
return false ;
}
// Cut of the linefeed (and everything following it) if it's present.
char * const linefeed = strchr ( programPath , ' \n ' ) ;
if ( linefeed )
{
* linefeed = ' \0 ' ;
}
// Check to see whether we retrieved any meaning ful result
if ( strlen ( programPath ) = = 0 )
{
debug ( LOG_WARNING , " Could not retrieve full path to \" %s \" . This may prevent creation of an extended backtrace. " , programCommand ) ;
return false ;
}
debug ( LOG_WZ , " Found program \" %s \" at path \" %s \" " , programCommand , programPath ) ;
return true ;
}
# endif
2007-02-23 08:36:11 -08:00
2007-03-15 12:12:33 -07:00
/**
* Setup the exception handler responsible for target OS .
*
2008-01-05 15:42:54 -08:00
* \ param programCommand Command used to launch this program . Only used for POSIX handler .
2007-03-15 12:12:33 -07:00
*/
2007-06-25 10:35:09 -07:00
void setupExceptionHandler ( const char * programCommand )
2007-02-23 08:36:11 -08:00
{
2008-07-04 15:40:43 -07:00
# if !defined(WZ_OS_MAC)
// Initialize info required for the debug dumper
2008-04-28 14:41:12 -07:00
dbgDumpInit ( programCommand ) ;
2008-07-04 15:40:43 -07:00
# endif
# if defined(WZ_OS_WIN)
2008-04-05 14:10:36 -07:00
# if defined(WZ_CC_MINGW)
2008-04-05 09:15:14 -07:00
ExchndlSetup ( ) ;
2008-04-05 14:10:36 -07:00
# else
2008-04-05 15:14:16 -07:00
prevExceptionHandler = SetUnhandledExceptionFilter ( windowsExceptionHandler ) ;
2008-04-05 14:10:36 -07:00
# endif // !defined(WZ_CC_MINGW)
2007-05-15 13:33:55 -07:00
# elif defined(WZ_OS_UNIX) && !defined(WZ_OS_MAC)
2007-06-25 10:35:09 -07:00
// Get full path to this program. Needed for gdb to find the binary.
2008-07-04 15:40:20 -07:00
programIsAvailable = fetchProgramPath ( programPath , sizeof ( programPath ) , programCommand ) ;
2007-06-25 10:35:09 -07:00
2007-03-04 05:04:36 -08:00
// Get full path to 'gdb'
2008-07-04 15:40:20 -07:00
gdbIsAvailable = fetchProgramPath ( gdbPath , sizeof ( gdbPath ) , " gdb " ) ;
2007-03-04 16:24:23 -08:00
2007-03-05 16:30:07 -08:00
sysInfoValid = ( uname ( & sysInfo ) = = 0 ) ;
2007-06-25 11:04:39 -07:00
time_t currentTime = time ( NULL ) ;
2008-05-25 06:46:49 -07:00
sstrcpy ( executionDate , ctime ( & currentTime ) ) ;
2007-06-25 11:04:39 -07:00
2007-10-27 08:47:17 -07:00
snprintf ( programPID , sizeof ( programPID ) , " %i " , getpid ( ) ) ;
2007-03-04 05:04:36 -08:00
2007-03-13 06:48:59 -07:00
setFatalSignalHandler ( posixExceptionHandler ) ;
2007-03-03 10:23:06 -08:00
# endif // WZ_OS_*
2007-02-23 08:36:11 -08:00
}