git-svn-id: https://clonekeenplus.svn.sourceforge.net/svnroot/clonekeenplus/cgenius/trunk@87 4df4b0f3-56ce-47cb-b001-ed939b7d65a6
541 lines
15 KiB
C++
541 lines
15 KiB
C++
/*
|
|
OpenLieroX CrashHandler
|
|
|
|
registers a crash handler in the OS and handles the crashes
|
|
|
|
code under LGPL
|
|
created 09-07-2008 by Albert Zeyer
|
|
*/
|
|
|
|
#include "CrashHandler.h"
|
|
#include "StringUtils.h"
|
|
#include "Debug.h"
|
|
|
|
|
|
#ifndef WIN32
|
|
#include <setjmp.h>
|
|
sigjmp_buf longJumpBuffer;
|
|
#endif
|
|
|
|
|
|
// note: important to have const char* here because std::string is too dynamic, could be screwed up when returning
|
|
void SetCrashHandlerReturnPoint(const char* name) {
|
|
#ifndef WIN32
|
|
if(sigsetjmp(longJumpBuffer, true) != 0) {
|
|
hints << "returned from sigsetjmp in " << name << endl;
|
|
if(/*tLXOptions->bFullscreen*/ false) {
|
|
notes << "we are in fullscreen, going to window mode now" << endl;
|
|
// tLXOptions->bFullscreen = false;
|
|
// doSetVideoModeInMainThread();
|
|
notes << "back in window mode" << endl;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// WIN 32
|
|
//
|
|
|
|
#if defined(_MSC_VER)
|
|
|
|
#define itoa _itoa
|
|
|
|
#include <DbgHelp.h>
|
|
#include <ShlObj.h>
|
|
#include "FindFile.h" // for IsFileAvailable and mkdir
|
|
|
|
LONG WINAPI CustomUnhandledExceptionFilter(PEXCEPTION_POINTERS pExInfo);
|
|
|
|
// Crash handling
|
|
class CrashHandlerImpl : public CrashHandler {
|
|
public:
|
|
CrashHandlerImpl() {
|
|
#ifdef _DEBUG
|
|
#ifdef USE_DEFAULT_MSC_DELEAKER
|
|
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
|
|
#endif
|
|
#endif // _DEBUG
|
|
|
|
SetUnhandledExceptionFilter(CustomUnhandledExceptionFilter);
|
|
|
|
notes << "Win32 Exception Filter installed" << endl;
|
|
}
|
|
|
|
};
|
|
|
|
extern void OlxWriteCoreDump_Win32(const char* fileName, PEXCEPTION_POINTERS pExInfo);
|
|
|
|
///////////////////
|
|
// This callback function is called whenever an unhandled exception occurs
|
|
LONG WINAPI CustomUnhandledExceptionFilter(PEXCEPTION_POINTERS pExInfo)
|
|
{
|
|
// Get the path
|
|
char buf[1024];
|
|
if (!SHGetSpecialFolderPath(NULL, buf, CSIDL_PERSONAL, false)) {
|
|
buf[0] = '\0';
|
|
strcpy(buf, "bug_reports");
|
|
} else {
|
|
size_t len = strnlen(buf, sizeof(buf));
|
|
if (buf[len - 1] != '\\' && buf[len - 1] != '/')
|
|
strncat(buf, "\\OpenLieroX", sizeof(buf));
|
|
else
|
|
strncat(buf, "OpenLieroX", sizeof(buf));
|
|
CreateDirectory(buf, NULL); // If the crash occurs at first startup, the OpenLieroX dir doesn't have to exist
|
|
strncat(buf, "\\bug_reports", sizeof(buf));
|
|
fix_markend(buf);
|
|
}
|
|
CreateDirectory(buf, NULL);
|
|
strncat(buf, "\\", sizeof(buf));
|
|
|
|
// Get the file name
|
|
char checkname[1024];
|
|
|
|
char tmp[32];
|
|
FILE *f = NULL;
|
|
for (int i=1;1;i++) {
|
|
itoa(i, tmp, 10);
|
|
fix_markend(tmp);
|
|
strncpy(checkname, buf, sizeof(checkname));
|
|
strncat(checkname, "report", sizeof(checkname));
|
|
strncat(checkname, tmp, sizeof(checkname));
|
|
strncat(checkname, ".dmp", sizeof(checkname));
|
|
f = fopen(checkname, "rb");
|
|
if (!f)
|
|
break;
|
|
fclose(f);
|
|
}
|
|
|
|
OlxWriteCoreDump_Win32(checkname, pExInfo);
|
|
|
|
// Try to free the cache, it eats a lot of memory
|
|
__try {
|
|
cCache.Clear();
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER) {}
|
|
|
|
// Quit SDL
|
|
__try {
|
|
SDL_Quit();
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER) {}
|
|
|
|
// End conversation logging (to make the XML valid)
|
|
__try {
|
|
if (convoLogger)
|
|
delete convoLogger;
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER) {}
|
|
|
|
// Close all opened files
|
|
fcloseall();
|
|
|
|
|
|
strncpy(&buf[1], checkname, sizeof(buf) - 1);
|
|
buf[0] = '\"';
|
|
strncat(buf, "\"", sizeof(buf));
|
|
fix_markend(buf);
|
|
|
|
// If ded server is running as service user won't see any dialog anyway
|
|
if (!bDedicated)
|
|
ShellExecute(NULL,"open","BugReport.exe",buf,NULL,SW_SHOWNORMAL);
|
|
|
|
// If running as a dedicated server, restart the application (there usually isn't any person sitting
|
|
// at the computer to fix this problem)
|
|
// If ded server is running as service it's restarted automatically
|
|
#ifdef DEDICATED_ONLY
|
|
//ShellExecute(NULL, "open", GetAppPath(), "-dedicated", NULL, SW_SHOWNORMAL);
|
|
#else
|
|
if (bDedicated) {
|
|
//ShellExecute(NULL, "open", GetAppPath(), "-dedicated", NULL, SW_SHOWNORMAL);
|
|
}
|
|
#endif
|
|
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
}
|
|
|
|
|
|
|
|
#elif !defined(WIN32) // MacOSX, Linux, Unix
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <signal.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/wait.h>
|
|
#include <ctype.h>
|
|
#include <unistd.h>
|
|
|
|
#if defined(__linux__) || defined(__APPLE__)
|
|
// TODO: why is execinfo needed here? at least on MacOSX, it's not needed here
|
|
//#include <execinfo.h>
|
|
/* get REG_EIP / REG_RIP from ucontext.h */
|
|
#include <ucontext.h>
|
|
#endif
|
|
|
|
#ifndef EIP
|
|
#define EIP 14
|
|
#endif
|
|
|
|
#if (defined (__x86_64__))
|
|
#ifndef REG_RIP
|
|
#define REG_RIP REG_INDEX(rip) /* seems to be 16 */
|
|
#endif
|
|
#endif
|
|
|
|
|
|
struct signal_def { char name[10]; int id; char description[40]; } ;
|
|
|
|
static signal_def signal_data[] =
|
|
{
|
|
{ "SIGHUP", SIGHUP, "Hangup (POSIX)" },
|
|
{ "SIGINT", SIGINT, "Interrupt (ANSI)" },
|
|
{ "SIGQUIT", SIGQUIT, "Quit (POSIX)" },
|
|
{ "SIGILL", SIGILL, "Illegal instruction (ANSI)" },
|
|
{ "SIGTRAP", SIGTRAP, "Trace trap (POSIX)" },
|
|
{ "SIGABRT", SIGABRT, "Abort (ANSI)" },
|
|
{ "SIGIOT", SIGIOT, "IOT trap (4.2 BSD)" },
|
|
{ "SIGBUS", SIGBUS, "BUS error (4.2 BSD)" },
|
|
{ "SIGFPE", SIGFPE, "Floating-point exception (ANSI)" },
|
|
{ "SIGKILL", SIGKILL, "Kill, unblockable (POSIX)" },
|
|
{ "SIGUSR1", SIGUSR1, "User-defined signal 1 (POSIX)" },
|
|
{ "SIGSEGV", SIGSEGV, "Segmentation violation (ANSI)" },
|
|
{ "SIGUSR2", SIGUSR2, "User-defined signal 2 (POSIX)" },
|
|
{ "SIGPIPE", SIGPIPE, "Broken pipe (POSIX)" },
|
|
{ "SIGALRM", SIGALRM, "Alarm clock (POSIX)" },
|
|
{ "SIGTERM", SIGTERM, "Termination (ANSI)" },
|
|
//{ "SIGSTKFLT", SIGSTKFLT, "Stack fault" },
|
|
{ "SIGCHLD", SIGCHLD, "Child status has changed (POSIX)" },
|
|
//{ "SIGCLD", SIGCLD, "Same as SIGCHLD (System V)" },
|
|
{ "SIGCONT", SIGCONT, "Continue (POSIX)" },
|
|
{ "SIGSTOP", SIGSTOP, "Stop, unblockable (POSIX)" },
|
|
{ "SIGTSTP", SIGTSTP, "Keyboard stop (POSIX)" },
|
|
{ "SIGTTIN", SIGTTIN, "Background read from tty (POSIX)" },
|
|
{ "SIGTTOU", SIGTTOU, "Background write to tty (POSIX)" },
|
|
{ "SIGURG", SIGURG, "Urgent condition on socket (4.2 BSD)" },
|
|
{ "SIGXCPU", SIGXCPU, "CPU limit exceeded (4.2 BSD)" },
|
|
{ "SIGXFSZ", SIGXFSZ, "File size limit exceeded (4.2 BSD)" },
|
|
{ "SIGVTALRM", SIGVTALRM, "Virtual alarm clock (4.2 BSD)" },
|
|
{ "SIGPROF", SIGPROF, "Profiling alarm clock (4.2 BSD)" },
|
|
{ "SIGWINCH", SIGWINCH, "Window size change (4.3 BSD, Sun)" },
|
|
{ "SIGIO", SIGIO, "I/O now possible (4.2 BSD)" },
|
|
//{ "SIGPOLL", SIGPOLL, "Pollable event occurred (System V)" },
|
|
//{ "SIGPWR", SIGPWR, "Power failure restart (System V)" },
|
|
{ "SIGSYS", SIGSYS, "Bad system call" },
|
|
};
|
|
|
|
static int handlerSignalList[] = {
|
|
SIGSEGV, SIGTRAP, SIGABRT, SIGHUP, SIGBUS, SIGILL, SIGFPE, SIGSYS, SIGUSR1, SIGUSR2
|
|
};
|
|
|
|
typedef const char * cchar;
|
|
|
|
bool CrashHandler_RecoverAfterCrash = false;
|
|
|
|
class CrashHandlerImpl : public CrashHandler {
|
|
public:
|
|
CrashHandlerImpl() {
|
|
if(CrashHandler_RecoverAfterCrash) {
|
|
setSignalHandlers();
|
|
DumpCallstack(NullOut); // dummy call to force loading dynamic lib at this point (with sane heap) for backtrace and friends
|
|
|
|
notes << "registered simple resuming signal handler" << endl;
|
|
}
|
|
else
|
|
notes << "no signal handler with these settings" << endl;
|
|
}
|
|
|
|
|
|
static void setSignalHandlers() {
|
|
struct sigaction sa;
|
|
|
|
sa.sa_sigaction = SimpleSignalHandler;
|
|
sigemptyset (&sa.sa_mask);
|
|
sa.sa_flags = SA_RESTART | SA_SIGINFO;
|
|
|
|
for (unsigned int i = 0; i < sizeof(handlerSignalList) / sizeof(int); i++)
|
|
sigaction(handlerSignalList[i], &sa, NULL);
|
|
}
|
|
|
|
static void unsetSignalHandlers() {
|
|
for (unsigned int i = 0; i < sizeof(handlerSignalList) / sizeof(int); i++)
|
|
signal(handlerSignalList[i], SIG_DFL);
|
|
}
|
|
|
|
void enable() { if(CrashHandler_RecoverAfterCrash) setSignalHandlers(); }
|
|
void disable() { unsetSignalHandlers(); }
|
|
|
|
static void SimpleSignalHandler(int signr, siginfo_t *info, void *secret) {
|
|
signal(signr, SIG_IGN); // discard all remaining signals
|
|
|
|
signal_def *d = NULL;
|
|
for (unsigned int i = 0; i < sizeof(signal_data) / sizeof(signal_def); i++)
|
|
if (signr == signal_data[i].id)
|
|
{ d = &signal_data[i]; break; }
|
|
if (d)
|
|
printf("Got signal 0x%02X (%s): %s\n", signr, d->name, d->description);
|
|
else
|
|
printf("Got signal 0x%02X\n", signr);
|
|
|
|
/*
|
|
see this article for further details: (thanks also for some code snippets)
|
|
http://www.linuxjournal.com/article/6391 */
|
|
|
|
void *pnt = NULL;
|
|
#if defined(__APPLE__)
|
|
# if defined(__x86_64__)
|
|
ucontext_t* uc = (ucontext_t*) secret;
|
|
pnt = (void*) uc->uc_mcontext->__ss.__rip ;
|
|
# elif defined(__hppa__)
|
|
ucontext_t* uc = (ucontext_t*) secret;
|
|
pnt = (void*) uc->uc_mcontext.sc_iaoq[0] & ~0x3UL ;
|
|
# elif (defined (__ppc__)) || (defined (__powerpc__))
|
|
ucontext_t* uc = (ucontext_t*) secret;
|
|
# if __DARWIN_UNIX03
|
|
pnt = (void*) uc->uc_mcontext->__ss.__srr0 ;
|
|
# else
|
|
pnt = (void*) uc->uc_mcontext->ss.srr0 ;
|
|
# endif
|
|
# elif defined(__sparc__)
|
|
struct sigcontext* sc = (struct sigcontext*) secret;
|
|
# if __WORDSIZE == 64
|
|
pnt = (void*) scp->sigc_regs.tpc ;
|
|
# else
|
|
pnt = (void*) scp->si_regs.pc ;
|
|
# endif
|
|
# elif defined(__i386__)
|
|
ucontext_t* uc = (ucontext_t*) secret;
|
|
# if __DARWIN_UNIX03
|
|
pnt = (void*) uc->uc_mcontext->__ss.__eip ;
|
|
# else
|
|
pnt = (void*) uc->uc_mcontext->ss.eip ;
|
|
# endif
|
|
# else
|
|
# warning mcontext is not defined for this arch, thus a dumped backtrace could be crippled
|
|
# endif
|
|
#elif defined(__linux__)
|
|
# if defined(__x86_64__)
|
|
ucontext_t* uc = (ucontext_t*) secret;
|
|
pnt = (void*) uc->uc_mcontext.gregs[REG_RIP] ;
|
|
# elif defined(__hppa__)
|
|
ucontext_t* uc = (ucontext_t*) secret;
|
|
pnt = (void*) uc->uc_mcontext.sc_iaoq[0] & ~0x3UL ;
|
|
# elif (defined (__ppc__)) || (defined (__powerpc__))
|
|
ucontext_t* uc = (ucontext_t*) secret;
|
|
pnt = (void*) uc->uc_mcontext.regs->nip ;
|
|
# elif defined(__sparc__)
|
|
struct sigcontext* sc = (struct sigcontext*) secret;
|
|
# if __WORDSIZE == 64
|
|
pnt = (void*) scp->sigc_regs.tpc ;
|
|
# else
|
|
pnt = (void*) scp->si_regs.pc ;
|
|
# endif
|
|
# elif defined(__i386__)
|
|
ucontext_t* uc = (ucontext_t*) secret;
|
|
pnt = (void*) uc->uc_mcontext.gregs[REG_EIP] ;
|
|
# else
|
|
# warning mcontext is not defined for this arch, thus a dumped backtrace could be crippled
|
|
# endif
|
|
#else
|
|
# warning mcontest is not defined for this system, thus a dumped backtraced could be crippled
|
|
#endif
|
|
|
|
/* potentially correct for other archs:
|
|
* alpha: ucp->m_context.sc_pc
|
|
* arm: ucp->m_context.ctx.arm_pc
|
|
* ia64: ucp->m_context.sc_ip & ~0x3UL
|
|
* mips: ucp->m_context.sc_pc
|
|
* s390: ucp->m_context.sregs->regs.psw.addr
|
|
*/
|
|
|
|
if (signr == SIGSEGV || signr == SIGBUS)
|
|
printf("Faulty address is %p, called from %p\n", info->si_addr, pnt);
|
|
|
|
/* The first two entries in the stack frame chain when you
|
|
* get into the signal handler contain, respectively, a
|
|
* return address inside your signal handler and one inside
|
|
* sigaction() in libc. The stack frame of the last function
|
|
* called before the signal (which, in case of fault signals,
|
|
* also is the one that supposedly caused the problem) is lost.
|
|
*/
|
|
|
|
/* the third parameter to the signal handler points to an
|
|
* ucontext_t structure that contains the values of the CPU
|
|
* registers when the signal was raised.
|
|
*/
|
|
|
|
// WARNING: dont use cout here in this function, it sometimes screws the cout up
|
|
// look at signal(2) for a list of safe functions
|
|
|
|
DumpCallstackPrintf(pnt);
|
|
|
|
#ifdef DEBUG
|
|
// commented out for now because it still doesn't work that good
|
|
//OlxWriteCoreDump(d ? d->name : NULL);
|
|
#endif
|
|
|
|
if(!CrashHandler_RecoverAfterCrash)
|
|
{
|
|
fflush(stdout);
|
|
abort();
|
|
#ifdef DEBUG
|
|
// raise(SIGQUIT);
|
|
#else
|
|
// exit(-1);
|
|
#endif
|
|
return;
|
|
}
|
|
setSignalHandlers(); // reset handler
|
|
printf("resuming ...\n");
|
|
fflush(stdout);
|
|
|
|
setSignalHandlers();
|
|
siglongjmp(longJumpBuffer, 1); // jump back to main loop, maybe we'll be able to continue somehow
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Attempts to cleanup and call drkonqi to process the crash
|
|
// (code taken from Lgi (LGPL) and modified)
|
|
//
|
|
static void DrKonqiSignalHandler(int Sig) {
|
|
// Don't get into an infinite loop
|
|
signal(SIGSEGV, SIG_DFL);
|
|
|
|
// Our pid
|
|
int MyPid = getpid();
|
|
printf("CrashHandler trigger MyPid=%i\n", MyPid);
|
|
|
|
// Fork to run the crash handler
|
|
pid_t Pid = fork();
|
|
if (Pid <= 0)
|
|
{
|
|
// Pass our state down to the crash handler...
|
|
int Args = 0;
|
|
cchar Arg[32];
|
|
memset(Arg, 0, sizeof(Arg));
|
|
char SigName[16], PidName[16], Version[32];
|
|
|
|
// TODO: sprintf allocates memory on the heap internally, is it safe to do it here?
|
|
sprintf(SigName, "%i", Sig);
|
|
sprintf(PidName, "%i", MyPid);
|
|
// strcpy( Version, GetFullGameName() );
|
|
|
|
Arg[Args++] = "drkonqi";
|
|
//Arg[Args++] = "--display";
|
|
//Arg[Args++] = XDisplayString(o.XDisplay());
|
|
Arg[Args++] = "--appname";
|
|
// Arg[Args++] = GetFullGameName();
|
|
Arg[Args++] = "--programname";
|
|
// Arg[Args++] = GetFullGameName();
|
|
Arg[Args++] = "--appversion";
|
|
Arg[Args++] = Version;
|
|
Arg[Args++] = "--apppath";
|
|
// Arg[Args++] = GetAppPath(); // should be save to call
|
|
Arg[Args++] = "--signal";
|
|
Arg[Args++] = SigName;
|
|
Arg[Args++] = "--pid";
|
|
Arg[Args++] = PidName;
|
|
Arg[Args++] = "--bugaddress";
|
|
Arg[Args++] = "openlierox@az2000.de";
|
|
|
|
setgid(getgid());
|
|
setuid(getuid());
|
|
|
|
execvp("drkonqi", (char* const*)Arg);
|
|
}
|
|
else
|
|
{
|
|
// Wait for child to exit
|
|
waitpid(Pid, NULL, 0);
|
|
_exit(253);
|
|
}
|
|
}
|
|
|
|
// Attempts to cleanup and call bug-buddy to process the crash
|
|
static void BugBuddySignalHandler(int Sig) {
|
|
// Don't get into an infinite loop
|
|
signal(SIGSEGV, SIG_DFL);
|
|
|
|
// Our pid
|
|
int MyPid = getpid();
|
|
printf("CrashHandler trigger MyPid=%i\n", MyPid);
|
|
|
|
// Fork to run the crash handler
|
|
pid_t Pid = fork();
|
|
if (Pid <= 0)
|
|
{
|
|
// Pass our state down to the crash handler...
|
|
int Args = 0;
|
|
cchar Arg[32];
|
|
memset(Arg, 0, sizeof(Arg));
|
|
char SigName[16], PidName[16], Version[40];
|
|
|
|
sprintf(SigName, "%i", Sig);
|
|
sprintf(PidName, "%i", MyPid);
|
|
// strcpy( Version, GetFullGameName() );
|
|
|
|
Arg[Args++] = "bug-buddy";
|
|
//Arg[Args++] = "--display";
|
|
//Arg[Args++] = XDisplayString(o.XDisplay());
|
|
Arg[Args++] = "--appname";
|
|
Arg[Args++] = Version;
|
|
Arg[Args++] = "--pid";
|
|
Arg[Args++] = PidName;
|
|
Arg[Args++] = "--name";
|
|
Arg[Args++] = "Albert Zeyer";
|
|
Arg[Args++] = "--email";
|
|
Arg[Args++] = "openlierox@az2000.de";
|
|
|
|
setgid(getgid());
|
|
setuid(getuid());
|
|
|
|
execvp("bug-buddy", (char* const*)Arg);
|
|
}
|
|
else
|
|
{
|
|
// Wait for child to exit
|
|
waitpid(Pid, NULL, 0);
|
|
_exit(253);
|
|
}
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
#else
|
|
class CrashHandlerImpl : public CrashHandler {
|
|
public:
|
|
CrashHandlerImpl() {
|
|
notes << "Dummy CrashHandler implementation" << endl;
|
|
}
|
|
};
|
|
|
|
#endif
|
|
|
|
CrashHandlerImpl* crashHandlerInstance = NULL;
|
|
|
|
void CrashHandler::init() {
|
|
if(crashHandlerInstance) {
|
|
warnings << "CrashHandler tried to init twice" << endl;
|
|
return;
|
|
}
|
|
notes << "Installing CrashHandler .. ";
|
|
crashHandlerInstance = new CrashHandlerImpl();
|
|
}
|
|
|
|
void CrashHandler::uninit() {
|
|
if(crashHandlerInstance) {
|
|
delete crashHandlerInstance;
|
|
crashHandlerInstance = NULL;
|
|
}
|
|
}
|
|
|
|
CrashHandler* CrashHandler::get() {
|
|
return crashHandlerInstance;
|
|
}
|