/*
* Ascent MMORPG Server
* Copyright (C) 2005-2007 Ascent Team
*
* This program 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 3 of the License, or
* any later version.
*
* This program 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 this program. If not, see .
*
*/
#include
#include
#include "CrashHandler.h"
#include "Log.h"
void OutputCrashLogLine(const char * format, ...)
{
std::string s = FormatOutputString("logs", "CrashLog", true);
FILE * m_file = fopen(s.c_str(), "a");
if(!m_file) return;
va_list ap;
va_start(ap, format);
vfprintf(m_file, format, ap);
fprintf(m_file, "\n");
fclose(m_file);
va_end(ap);
}
#ifdef WIN32
/* *
@file CrashHandler.h
Handles crashes/exceptions on a win32 based platform, writes a dump file,
for later bug fixing.
*/
# pragma warning( disable : 4311 )
#include
#include
#include
#include "Log.h"
#include
bool ON_CRASH_BREAK_DEBUGGER;
void StartCrashHandler()
{
// Firstly, check if there is a debugger present. There isn't any point in
// handling crashes internally if we have a debugger attached, that would
// just piss us off. :P
DWORD code;
// Check for a debugger.
__asm
{
MOV EAX, FS:[0x18]
MOV EAX, DWORD PTR [EAX + 0x30]
MOV ECX, DWORD PTR [EAX]
MOV [DWORD PTR code], ECX
}
if(code & 0x00010000)
{
// We got a debugger. We'll tell it to not exit on a crash but instead break into debugger.
ON_CRASH_BREAK_DEBUGGER = true;
}
else
{
// No debugger. On crash, we'll call OnCrash to save etc.
ON_CRASH_BREAK_DEBUGGER = false;
}
//ON_CRASH_BREAK_DEBUGGER = IsDebuggerPresent();
}
///////////////////////////////////////////////////////////////////////////////
// GetExceptionDescription
// Translate the exception code into something human readable
static const TCHAR *GetExceptionDescription(DWORD ExceptionCode)
{
struct ExceptionNames
{
DWORD ExceptionCode;
TCHAR * ExceptionName;
};
#if 0 // from winnt.h
#define STATUS_WAIT_0 ((DWORD )0x00000000L)
#define STATUS_ABANDONED_WAIT_0 ((DWORD )0x00000080L)
#define STATUS_USER_APC ((DWORD )0x000000C0L)
#define STATUS_TIMEOUT ((DWORD )0x00000102L)
#define STATUS_PENDING ((DWORD )0x00000103L)
#define STATUS_SEGMENT_NOTIFICATION ((DWORD )0x40000005L)
#define STATUS_GUARD_PAGE_VIOLATION ((DWORD )0x80000001L)
#define STATUS_DATATYPE_MISALIGNMENT ((DWORD )0x80000002L)
#define STATUS_BREAKPOINT ((DWORD )0x80000003L)
#define STATUS_SINGLE_STEP ((DWORD )0x80000004L)
#define STATUS_ACCESS_VIOLATION ((DWORD )0xC0000005L)
#define STATUS_IN_PAGE_ERROR ((DWORD )0xC0000006L)
#define STATUS_INVALID_HANDLE ((DWORD )0xC0000008L)
#define STATUS_NO_MEMORY ((DWORD )0xC0000017L)
#define STATUS_ILLEGAL_INSTRUCTION ((DWORD )0xC000001DL)
#define STATUS_NONCONTINUABLE_EXCEPTION ((DWORD )0xC0000025L)
#define STATUS_INVALID_DISPOSITION ((DWORD )0xC0000026L)
#define STATUS_ARRAY_BOUNDS_EXCEEDED ((DWORD )0xC000008CL)
#define STATUS_FLOAT_DENORMAL_OPERAND ((DWORD )0xC000008DL)
#define STATUS_FLOAT_DIVIDE_BY_ZERO ((DWORD )0xC000008EL)
#define STATUS_FLOAT_INEXACT_RESULT ((DWORD )0xC000008FL)
#define STATUS_FLOAT_INVALID_OPERATION ((DWORD )0xC0000090L)
#define STATUS_FLOAT_OVERFLOW ((DWORD )0xC0000091L)
#define STATUS_FLOAT_STACK_CHECK ((DWORD )0xC0000092L)
#define STATUS_FLOAT_UNDERFLOW ((DWORD )0xC0000093L)
#define STATUS_INTEGER_DIVIDE_BY_ZERO ((DWORD )0xC0000094L)
#define STATUS_INTEGER_OVERFLOW ((DWORD )0xC0000095L)
#define STATUS_PRIVILEGED_INSTRUCTION ((DWORD )0xC0000096L)
#define STATUS_STACK_OVERFLOW ((DWORD )0xC00000FDL)
#define STATUS_CONTROL_C_EXIT ((DWORD )0xC000013AL)
#define STATUS_FLOAT_MULTIPLE_FAULTS ((DWORD )0xC00002B4L)
#define STATUS_FLOAT_MULTIPLE_TRAPS ((DWORD )0xC00002B5L)
#define STATUS_ILLEGAL_VLM_REFERENCE ((DWORD )0xC00002C0L)
#endif
ExceptionNames ExceptionMap[] =
{
{0x40010005, _T("a Control-C")},
{0x40010008, _T("a Control-Break")},
{0x80000002, _T("a Datatype Misalignment")},
{0x80000003, _T("a Breakpoint")},
{0xc0000005, _T("an Access Violation")},
{0xc0000006, _T("an In Page Error")},
{0xc0000017, _T("a No Memory")},
{0xc000001d, _T("an Illegal Instruction")},
{0xc0000025, _T("a Noncontinuable Exception")},
{0xc0000026, _T("an Invalid Disposition")},
{0xc000008c, _T("a Array Bounds Exceeded")},
{0xc000008d, _T("a Float Denormal Operand")},
{0xc000008e, _T("a Float Divide by Zero")},
{0xc000008f, _T("a Float Inexact Result")},
{0xc0000090, _T("a Float Invalid Operation")},
{0xc0000091, _T("a Float Overflow")},
{0xc0000092, _T("a Float Stack Check")},
{0xc0000093, _T("a Float Underflow")},
{0xc0000094, _T("an Integer Divide by Zero")},
{0xc0000095, _T("an Integer Overflow")},
{0xc0000096, _T("a Privileged Instruction")},
{0xc00000fD, _T("a Stack Overflow")},
{0xc0000142, _T("a DLL Initialization Failed")},
{0xe06d7363, _T("a Microsoft C++ Exception")},
};
for (int i = 0; i < sizeof(ExceptionMap) / sizeof(ExceptionMap[0]); i++)
if (ExceptionCode == ExceptionMap[i].ExceptionCode)
return ExceptionMap[i].ExceptionName;
return _T("an Unknown exception type");
}
void echo(const char * format, ...)
{
std::string s = FormatOutputString("logs", "CrashLog", true);
FILE * m_file = fopen(s.c_str(), "a");
if(!m_file) return;
va_list ap;
va_start(ap, format);
vfprintf(m_file, format, ap);
fclose(m_file);
va_end(ap);
}
void PrintCrashInformation(PEXCEPTION_POINTERS except)
{
SYSTEM_INFO si;
GetSystemInfo(&si);
TCHAR username[200];
DWORD usize = 198;
if(!GetUserName(username, &usize))
strcpy(username, "Unknown");
TCHAR winver[200];
OSVERSIONINFO ver;
ver.dwOSVersionInfoSize = sizeof(ver);
if(GetVersionEx(&ver) == 0)
{
ver.dwBuildNumber = 0;
ver.dwMajorVersion = 5;
ver.dwMinorVersion = 1;
}
MEMORYSTATUS mi;
mi.dwLength = sizeof(mi);
GlobalMemoryStatus(&mi);
if(ver.dwMajorVersion == 5 && ver.dwMinorVersion == 0)
strcpy(winver, "Windows 2000");
else if(ver.dwMajorVersion == 5 && ver.dwMinorVersion == 1)
strcpy(winver, "Windows XP");
else if(ver.dwMajorVersion == 5 && ver.dwMajorVersion >= 2)
strcpy(winver, "Windows 2003");
else if(ver.dwMajorVersion >= 5)
strcpy(winver, "Windows Vista");
else
strcpy(winver, "Unknown Windows");
echo("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
echo("Server has crashed. Reason was:\n");
echo(" %s at 0x%08X\n", GetExceptionDescription(except->ExceptionRecord->ExceptionCode),
(unsigned long)except->ExceptionRecord->ExceptionAddress);
echo("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
echo("System Information:\n");
echo(" Running as: %s on %s Build %u\n", username, winver, ver.dwBuildNumber);
echo(" Running on %u processors (type %u)\n", si.dwNumberOfProcessors, si.dwProcessorType);
echo("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
echo("Call Stack: \n");
CStackWalker sw;
sw.ShowCallstack();
echo("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
}
void CStackWalker::OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName)
{
}
void CStackWalker::OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion)
{
}
void CStackWalker::OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr)
{
}
void CStackWalker::OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry)
{
CHAR buffer[STACKWALK_MAX_NAMELEN];
if ( (eType != lastEntry) && (entry.offset != 0) )
{
if (entry.name[0] == 0)
strcpy(entry.name, "(function-name not available)");
if (entry.undName[0] != 0)
strcpy(entry.name, entry.undName);
if (entry.undFullName[0] != 0)
strcpy(entry.name, entry.undFullName);
/* if(!stricmp(entry.symTypeString, "-exported-"))
strcpy(entry.symTypeString, "dll");
for(uint32 i = 0; i < strlen(entry.symTypeString); ++i)
entry.symTypeString[i] = tolower(entry.symTypeString);*/
char * p = strrchr(entry.loadedImageName, '\\');
if(!p)
p = entry.loadedImageName;
else
++p;
if (entry.lineFileName[0] == 0)
{
//strcpy(entry.lineFileName, "(filename not available)");
//if (entry.moduleName[0] == 0)
//strcpy(entry.moduleName, "(module-name not available)");
//sprintf(buffer, "%s): %s: %s\n", (LPVOID) entry.offset, entry.moduleName, entry.lineFileName, entry.name);
//sprintf(buffer, "%s.
if(entry.name[0] == 0)
sprintf(entry.name, "%p", entry.offset);
sprintf(buffer, "%s!%s Line %u\n", p, entry.name, entry.lineNumber );
}
else
sprintf(buffer, "%s!%s Line %u\n", p, entry.name, entry.lineNumber);
//OnOutput(buffer);
/*if(p)
OnOutput(p);
else*/
OnOutput(buffer);
}
}
void CStackWalker::OnOutput(LPCSTR szText)
{
std::string s = FormatOutputString("logs", "CrashLog", true);
FILE * m_file = fopen(s.c_str(), "a");
if(!m_file) return;
printf(" %s", szText);
fprintf(m_file, " %s\n", szText);
fclose(m_file);
}
int __cdecl HandleCrash(PEXCEPTION_POINTERS pExceptPtrs)
{
if(pExceptPtrs == 0)
{
// Raise an exception :P
__try
{
RaiseException(EXCEPTION_BREAKPOINT, 0, 0, 0);
}
__except(HandleCrash(GetExceptionInformation()), EXCEPTION_CONTINUE_EXECUTION)
{
}
}
// Create the date/time string
time_t curtime = time(NULL);
tm * pTime = localtime(&curtime);
char filename[MAX_PATH];
TCHAR modname[MAX_PATH*2];
ZeroMemory(modname, sizeof(modname));
if(GetModuleFileName(0, modname, MAX_PATH*2-2) <= 0)
strcpy(modname, "UNKNOWN");
char * mname = strrchr(modname, '\\');
(void*)mname++; // Remove the last \
sprintf(filename, "CrashDumps\\dump-%s-%u-%u-%u-%u-%u-%u-%u.dmp",
mname, pTime->tm_year+1900, pTime->tm_mon, pTime->tm_mday,
pTime->tm_hour, pTime->tm_min, pTime->tm_sec, GetCurrentThreadId());
HANDLE hDump = CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, 0);
if(hDump == INVALID_HANDLE_VALUE)
{
// Create the directory first
CreateDirectory("CrashDumps", 0);
hDump = CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, 0);
}
PrintCrashInformation(pExceptPtrs);
// beep
//printf("\x7");
printf("\nCreating crash dump file %s\n", filename);
if(hDump == INVALID_HANDLE_VALUE)
{
MessageBox(0, "Could not open crash dump file.", "Crash dump error.", MB_OK);
}
else
{
MINIDUMP_EXCEPTION_INFORMATION info;
info.ClientPointers = FALSE;
info.ExceptionPointers = pExceptPtrs;
info.ThreadId = GetCurrentThreadId();
MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),
hDump, MiniDumpWithIndirectlyReferencedMemory, &info, 0, 0);
CloseHandle(hDump);
}
OnCrash(!ON_CRASH_BREAK_DEBUGGER);
return EXCEPTION_CONTINUE_SEARCH;
}
#endif