mirror of https://github.com/pissnet/pissircd.git
371 lines
11 KiB
C
371 lines
11 KiB
C
/************************************************************************
|
|
* IRC - Internet Relay Chat, windows/windebug.c
|
|
* Copyright (C) 2002-2004 Dominick Meglio (codemastr)
|
|
*
|
|
* 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 1, or (at your option)
|
|
* 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, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#include "unrealircd.h"
|
|
#include <dbghelp.h>
|
|
|
|
#ifndef IRCDTOTALVERSION
|
|
#define IRCDTOTALVERSION BASE_VERSION "-" PATCH1 PATCH2 PATCH3 PATCH4 PATCH5 PATCH6 PATCH7 PATCH8 PATCH9
|
|
#endif
|
|
|
|
extern OSVERSIONINFO VerInfo;
|
|
extern char OSName[256];
|
|
extern char backupbuf[8192];
|
|
extern char *buildid;
|
|
extern char *extraflags;
|
|
|
|
/* crappy, but safe :p */
|
|
typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType,
|
|
CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
|
|
CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
|
|
CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam
|
|
);
|
|
|
|
|
|
/* Runs a stack trace
|
|
* Parameters:
|
|
* e - The exception information
|
|
* Returns:
|
|
* The stack trace with function and line number information
|
|
*/
|
|
__inline char *StackTrace(EXCEPTION_POINTERS *e)
|
|
{
|
|
static char buffer[5000];
|
|
char curmodule[256];
|
|
DWORD symOptions;
|
|
DWORD64 dwDisp;
|
|
DWORD dwDisp32;
|
|
int frame;
|
|
HANDLE hProcess = GetCurrentProcess();
|
|
IMAGEHLP_SYMBOL64 *pSym = safe_alloc(sizeof(IMAGEHLP_SYMBOL64)+500);
|
|
IMAGEHLP_LINE64 pLine;
|
|
IMAGEHLP_MODULE64 pMod;
|
|
STACKFRAME64 Stack;
|
|
CONTEXT context;
|
|
|
|
memcpy(&context, e->ContextRecord, sizeof(CONTEXT));
|
|
|
|
/* Load the stack information */
|
|
memset(&Stack, 0, sizeof(Stack));
|
|
Stack.AddrPC.Offset = e->ContextRecord->Rip;
|
|
Stack.AddrPC.Mode = AddrModeFlat;
|
|
Stack.AddrFrame.Offset = e->ContextRecord->Rbp;
|
|
Stack.AddrFrame.Mode = AddrModeFlat;
|
|
Stack.AddrStack.Offset = e->ContextRecord->Rsp;
|
|
Stack.AddrStack.Mode = AddrModeFlat;
|
|
hProcess = GetCurrentProcess();
|
|
|
|
/* Initialize symbol retrieval system */
|
|
SymInitialize(hProcess, NULL, TRUE);
|
|
SymSetOptions(SYMOPT_LOAD_LINES|SYMOPT_UNDNAME);
|
|
pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
|
|
pSym->MaxNameLength = 500;
|
|
|
|
/* Retrieve the first module name */
|
|
memset(&pMod, 0, sizeof(pMod));
|
|
pMod.SizeOfStruct = sizeof(IMAGEHLP_MODULE64);
|
|
SymGetModuleInfo64(hProcess, Stack.AddrPC.Offset, &pMod);
|
|
strcpy(curmodule, pMod.ModuleName);
|
|
sprintf(buffer, "\tModule: %s\n", pMod.ModuleName);
|
|
|
|
/* Walk through the stack */
|
|
for (frame = 0; ; frame++)
|
|
{
|
|
char buf[500];
|
|
if (!StackWalk64(IMAGE_FILE_MACHINE_AMD64, GetCurrentProcess(), GetCurrentThread(),
|
|
&Stack, &context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
|
|
break;
|
|
|
|
memset(&pMod, 0, sizeof(pMod));
|
|
pMod.SizeOfStruct = sizeof(IMAGEHLP_MODULE64);
|
|
SymGetModuleInfo64(hProcess, Stack.AddrPC.Offset, &pMod);
|
|
if (strcmp(curmodule, pMod.ModuleName))
|
|
{
|
|
strcpy(curmodule, pMod.ModuleName);
|
|
sprintf(buf, "\tModule: %s\n", pMod.ModuleName);
|
|
strcat(buffer, buf);
|
|
}
|
|
|
|
memset(&pLine, 0, sizeof(pLine));
|
|
pLine.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
|
SymGetLineFromAddr64(hProcess, Stack.AddrPC.Offset, &dwDisp32, &pLine);
|
|
SymGetSymFromAddr64(hProcess, Stack.AddrPC.Offset, &dwDisp, pSym);
|
|
sprintf(buf, "\t\t#%d %s:%d: %s\n", frame, pLine.FileName, pLine.LineNumber,
|
|
pSym->Name);
|
|
strcat(buffer, buf);
|
|
}
|
|
strcat(buffer, "End of Stack trace\n");
|
|
return buffer;
|
|
|
|
}
|
|
|
|
/* Retrieves the values of several registers
|
|
* Parameters:
|
|
* context - The CPU context
|
|
* Returns:
|
|
* The values of the registers as a string.
|
|
*/
|
|
__inline char *GetRegisters(CONTEXT *context)
|
|
{
|
|
static char buffer[1024];
|
|
|
|
sprintf(buffer,
|
|
"\tRAX=%p"
|
|
"\tRBX=%p"
|
|
"\tRCX=%p"
|
|
"\tRDX=%p\n"
|
|
"\tRSI=%p"
|
|
"\tRDI=%p"
|
|
"\tRBP=%p"
|
|
"\tRSP=%p\n"
|
|
"\tR8=%p"
|
|
"\tR9=%p"
|
|
"\tR10=%p"
|
|
"\tR11=%p\n"
|
|
"\tR12=%p"
|
|
"\tR13=%p"
|
|
"\tR14=%p"
|
|
"\tR15=%p\n"
|
|
"\tRIP=%p\n",
|
|
(void *)context->Rax,
|
|
(void *)context->Rbx,
|
|
(void *)context->Rcx,
|
|
(void *)context->Rdx,
|
|
(void *)context->Rsi,
|
|
(void *)context->Rdi,
|
|
(void *)context->Rbp,
|
|
(void *)context->Rsp,
|
|
(void *)context->R8,
|
|
(void *)context->R9,
|
|
(void *)context->R10,
|
|
(void *)context->R11,
|
|
(void *)context->R12,
|
|
(void *)context->R13,
|
|
(void *)context->R14,
|
|
(void *)context->R15,
|
|
(void *)context->Rip);
|
|
|
|
return buffer;
|
|
}
|
|
|
|
/* Convert the exception code to a human readable string
|
|
* Parameters:
|
|
* code - The exception code to convert
|
|
* Returns:
|
|
* The exception code represented as a string
|
|
*/
|
|
__inline char *GetException(DWORD code)
|
|
{
|
|
switch (code)
|
|
{
|
|
case EXCEPTION_ACCESS_VIOLATION:
|
|
return "Access Violation";
|
|
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
|
|
return "Array Bounds Exceeded";
|
|
case EXCEPTION_BREAKPOINT:
|
|
return "Breakpoint";
|
|
case EXCEPTION_DATATYPE_MISALIGNMENT:
|
|
return "Datatype Misalignment";
|
|
case EXCEPTION_FLT_DENORMAL_OPERAND:
|
|
return "Floating Point Denormal Operand";
|
|
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
|
|
return "Floating Point Division By Zero";
|
|
case EXCEPTION_FLT_INEXACT_RESULT:
|
|
return "Floating Point Inexact Result";
|
|
case EXCEPTION_FLT_INVALID_OPERATION:
|
|
return "Floating Point Invalid Operation";
|
|
case EXCEPTION_FLT_OVERFLOW:
|
|
return "Floating Point Overflow";
|
|
case EXCEPTION_FLT_STACK_CHECK:
|
|
return "Floating Point Stack Overflow";
|
|
case EXCEPTION_FLT_UNDERFLOW:
|
|
return "Floating Point Underflow";
|
|
case EXCEPTION_ILLEGAL_INSTRUCTION:
|
|
return "Illegal Instruction";
|
|
case EXCEPTION_IN_PAGE_ERROR:
|
|
return "In Page Error";
|
|
case EXCEPTION_INT_DIVIDE_BY_ZERO:
|
|
return "Integer Division By Zero";
|
|
case EXCEPTION_INT_OVERFLOW:
|
|
return "Integer Overflow";
|
|
case EXCEPTION_INVALID_DISPOSITION:
|
|
return "Invalid Disposition";
|
|
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
|
|
return "Noncontinuable Exception";
|
|
case EXCEPTION_PRIV_INSTRUCTION:
|
|
return "Unallowed Instruction";
|
|
case EXCEPTION_SINGLE_STEP:
|
|
return "Single Step";
|
|
case EXCEPTION_STACK_OVERFLOW:
|
|
return "Stack Overflow";
|
|
default:
|
|
return "Unknown Exception";
|
|
}
|
|
}
|
|
|
|
void StartCrashReporter(void)
|
|
{
|
|
char fname[MAX_PATH], fnamewarg[MAX_PATH+32];
|
|
PROCESS_INFORMATION pi;
|
|
STARTUPINFO si;
|
|
|
|
memset(&pi, 0, sizeof(pi));
|
|
memset(&si, 0, sizeof(si));
|
|
|
|
GetModuleFileName(GetModuleHandle(NULL), fname, MAX_PATH);
|
|
|
|
snprintf(fnamewarg, sizeof(fnamewarg), "\"%s\" %s", fname, "-R");
|
|
CreateProcess(fname, fnamewarg, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
|
|
}
|
|
|
|
void StartUnrealAgain(void)
|
|
{
|
|
char fname[MAX_PATH], fnamewarg[MAX_PATH+32];
|
|
PROCESS_INFORMATION pi;
|
|
STARTUPINFO si;
|
|
|
|
memset(&pi, 0, sizeof(pi));
|
|
memset(&si, 0, sizeof(si));
|
|
|
|
GetModuleFileName(GetModuleHandle(NULL), fname, MAX_PATH);
|
|
|
|
snprintf(fnamewarg, sizeof(fnamewarg), "\"%s\"", fname);
|
|
CreateProcess(fname, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
|
|
}
|
|
|
|
/* Callback for the exception handler
|
|
* Parameters:
|
|
* e - The exception information
|
|
* Returns:
|
|
* EXCEPTION_EXECUTE_HANDLER to terminate the process
|
|
* Side Effects:
|
|
* unrealircd.PID.core is created
|
|
* If not running in service mode, a message box is displayed,
|
|
* else output is written to service.log
|
|
*/
|
|
LONG __stdcall ExceptionFilter(EXCEPTION_POINTERS *e)
|
|
{
|
|
MEMORYSTATUSEX memStats;
|
|
char file[512], text[1024], minidumpf[512];
|
|
FILE *fd;
|
|
time_t timet = time(NULL);
|
|
#ifndef NOMINIDUMP
|
|
HANDLE hDump;
|
|
HMODULE hDll = NULL;
|
|
#endif
|
|
|
|
sprintf(file, "unrealircd.%d.core", getpid());
|
|
fd = fopen(file, "w");
|
|
GlobalMemoryStatusEx(&memStats);
|
|
fprintf(fd, "Generated at %s\nOS: %s\n%s[%s%s%s] (%s) on %s\n"
|
|
"-----------------\nMemory Information:\n"
|
|
"\tPhysical: (Available:%lluMB/Total:%lluMB)\n"
|
|
"\tVirtual: (Available:%lluMB/Total:%lluMB)\n"
|
|
"-----------------\nException:\n\t%s\n-----------------\n"
|
|
"Backup Buffer:\n\t%s\n-----------------\nRegisters:\n"
|
|
"%s-----------------\nStack Trace:\n%s",
|
|
asctime(gmtime(&timet)), OSName,
|
|
IRCDTOTALVERSION,
|
|
serveropts, extraflags ? extraflags : "", tainted ? "3" : "",
|
|
buildid, me.name, memStats.ullAvailPhys/1048576, memStats.ullTotalPhys/1048576,
|
|
memStats.ullAvailVirtual/1048576, memStats.ullTotalVirtual/1048576,
|
|
GetException(e->ExceptionRecord->ExceptionCode), backupbuf,
|
|
GetRegisters(e->ContextRecord), StackTrace(e));
|
|
|
|
sprintf(text, "UnrealIRCd has encountered a fatal error. Debugging information has been dumped to %s.", file);
|
|
fclose(fd);
|
|
|
|
#ifndef NOMINIDUMP
|
|
hDll = LoadLibrary("DBGHELP.DLL");
|
|
if (hDll)
|
|
{
|
|
MINIDUMPWRITEDUMP pDump = (MINIDUMPWRITEDUMP)GetProcAddress(hDll, "MiniDumpWriteDump");
|
|
if (pDump)
|
|
{
|
|
MINIDUMP_EXCEPTION_INFORMATION ExInfo;
|
|
sprintf(minidumpf, "unrealircd.%d.mdmp", getpid());
|
|
hDump = CreateFile(minidumpf, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (hDump != INVALID_HANDLE_VALUE)
|
|
{
|
|
ExInfo.ThreadId = GetCurrentThreadId();
|
|
ExInfo.ExceptionPointers = e;
|
|
ExInfo.ClientPointers = 0;
|
|
|
|
if (pDump(GetCurrentProcess(), GetCurrentProcessId(), hDump, MiniDumpWithIndirectlyReferencedMemory, &ExInfo, NULL, NULL))
|
|
{
|
|
sprintf(text, "UnrealIRCd has encountered a fatal error. Debugging information has been dumped to %s and %s.", file, minidumpf);
|
|
}
|
|
CloseHandle(hDump);
|
|
}
|
|
sprintf(minidumpf, "unrealircd.%d.full.mdmp", getpid());
|
|
hDump = CreateFile(minidumpf, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (hDump != INVALID_HANDLE_VALUE)
|
|
{
|
|
ExInfo.ThreadId = GetCurrentThreadId();
|
|
ExInfo.ExceptionPointers = e;
|
|
ExInfo.ClientPointers = 0;
|
|
|
|
pDump(GetCurrentProcess(), GetCurrentProcessId(), hDump, MiniDumpWithPrivateReadWriteMemory, &ExInfo, NULL, NULL);
|
|
CloseHandle(hDump);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (!IsService)
|
|
{
|
|
MessageBox(NULL, text, "Fatal Error", MB_OK);
|
|
StartCrashReporter();
|
|
}
|
|
else
|
|
{
|
|
FILE *fd = fopen("logs\\service.log", "a");
|
|
|
|
if (fd)
|
|
{
|
|
fprintf(fd, "UnrealIRCd has encountered a fatal error. Debugging information "
|
|
"has been dumped to unrealircd.%d.core, please file a bug and upload "
|
|
"this file to https://bugs.unrealircd.org/.", getpid());
|
|
fclose(fd);
|
|
}
|
|
}
|
|
CleanUp();
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
}
|
|
|
|
void GotSigAbort(int signal)
|
|
{
|
|
/* I just want to call ExceptionFilter() but it requires an argument which we don't have...
|
|
* So just crash, which is rather silly but produces at least a crash report.
|
|
* Feel free to improve this!
|
|
*/
|
|
char *crash = NULL;
|
|
*crash = 'X';
|
|
}
|
|
|
|
/* Initializes the exception handler */
|
|
void InitDebug(void)
|
|
{
|
|
SetUnhandledExceptionFilter(&ExceptionFilter);
|
|
_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
|
|
signal(SIGABRT, GotSigAbort);
|
|
}
|
|
|
|
|