unrealircd

- supernets unrealircd source & configuration
git clone git://git.acid.vegas/unrealircd.git
Log | Files | Refs | Archive | README | LICENSE

windebug.c (11196B)

      1 /************************************************************************
      2  *   IRC - Internet Relay Chat, windows/windebug.c
      3  *   Copyright (C) 2002-2004 Dominick Meglio (codemastr)
      4  *   
      5  *   This program is free software; you can redistribute it and/or modify
      6  *   it under the terms of the GNU General Public License as published by
      7  *   the Free Software Foundation; either version 1, or (at your option)
      8  *   any later version.
      9  *
     10  *   This program is distributed in the hope that it will be useful,
     11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  *   GNU General Public License for more details.
     14  *
     15  *   You should have received a copy of the GNU General Public License
     16  *   along with this program; if not, write to the Free Software
     17  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     18  */
     19 
     20 #include "unrealircd.h"
     21 #include <dbghelp.h>
     22 
     23 #ifndef IRCDTOTALVERSION
     24 #define IRCDTOTALVERSION BASE_VERSION "-" PATCH1 PATCH2 PATCH3 PATCH4 PATCH5 PATCH6 PATCH7 PATCH8 PATCH9
     25 #endif
     26 
     27 extern OSVERSIONINFO VerInfo;
     28 extern char OSName[256];
     29 extern char backupbuf[8192];
     30 extern char *buildid;
     31 extern char *extraflags;
     32 
     33 /* crappy, but safe :p */
     34 typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType,
     35 										CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
     36 										CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
     37 										CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam
     38 										);
     39 
     40 
     41 /* Runs a stack trace 
     42  * Parameters:
     43  *  e - The exception information
     44  * Returns:
     45  *  The stack trace with function and line number information
     46  */
     47 __inline char *StackTrace(EXCEPTION_POINTERS *e) 
     48 {
     49 	static char buffer[5000];
     50 	char curmodule[256];
     51 	DWORD symOptions;
     52 	DWORD64 dwDisp;
     53 	DWORD dwDisp32;
     54 	int frame;
     55 	HANDLE hProcess = GetCurrentProcess();
     56 	IMAGEHLP_SYMBOL64 *pSym = safe_alloc(sizeof(IMAGEHLP_SYMBOL64)+500);
     57 	IMAGEHLP_LINE64 pLine;
     58 	IMAGEHLP_MODULE64 pMod;
     59 	STACKFRAME64 Stack;
     60 	CONTEXT context;
     61 
     62 	memcpy(&context, e->ContextRecord, sizeof(CONTEXT));
     63 
     64 	/* Load the stack information */
     65 	memset(&Stack, 0, sizeof(Stack));
     66 	Stack.AddrPC.Offset = e->ContextRecord->Rip;
     67 	Stack.AddrPC.Mode = AddrModeFlat;
     68 	Stack.AddrFrame.Offset = e->ContextRecord->Rbp;
     69 	Stack.AddrFrame.Mode = AddrModeFlat;
     70 	Stack.AddrStack.Offset = e->ContextRecord->Rsp;
     71 	Stack.AddrStack.Mode = AddrModeFlat;
     72 	hProcess = GetCurrentProcess();
     73 
     74 	/* Initialize symbol retrieval system */
     75 	SymInitialize(hProcess, NULL, TRUE);
     76 	SymSetOptions(SYMOPT_LOAD_LINES|SYMOPT_UNDNAME);
     77 	pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
     78 	pSym->MaxNameLength = 500;
     79 
     80 	/* Retrieve the first module name */
     81 	memset(&pMod, 0, sizeof(pMod));
     82 	pMod.SizeOfStruct = sizeof(IMAGEHLP_MODULE64);
     83 	SymGetModuleInfo64(hProcess, Stack.AddrPC.Offset, &pMod);
     84 	strcpy(curmodule, pMod.ModuleName);
     85 	sprintf(buffer, "\tModule: %s\n", pMod.ModuleName);
     86 
     87 	/* Walk through the stack */
     88 	for (frame = 0; ; frame++) 
     89 	{
     90 		char buf[500];
     91 		if (!StackWalk64(IMAGE_FILE_MACHINE_AMD64, GetCurrentProcess(), GetCurrentThread(),
     92 			&Stack, &context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
     93 			break;
     94 
     95 		memset(&pMod, 0, sizeof(pMod));
     96 		pMod.SizeOfStruct = sizeof(IMAGEHLP_MODULE64);
     97 		SymGetModuleInfo64(hProcess, Stack.AddrPC.Offset, &pMod);
     98 		if (strcmp(curmodule, pMod.ModuleName)) 
     99 		{
    100 			strcpy(curmodule, pMod.ModuleName);
    101 			sprintf(buf, "\tModule: %s\n", pMod.ModuleName);
    102 			strcat(buffer, buf);
    103 		}
    104 
    105 		memset(&pLine, 0, sizeof(pLine));
    106 		pLine.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
    107 		SymGetLineFromAddr64(hProcess, Stack.AddrPC.Offset, &dwDisp32, &pLine);
    108 		SymGetSymFromAddr64(hProcess, Stack.AddrPC.Offset, &dwDisp, pSym);
    109 		sprintf(buf, "\t\t#%d %s:%d: %s\n", frame, pLine.FileName, pLine.LineNumber, 
    110 		        pSym->Name);
    111 		strcat(buffer, buf);
    112 	}
    113 	strcat(buffer, "End of Stack trace\n");
    114 	return buffer;
    115 
    116 }
    117 
    118 /* Retrieves the values of several registers
    119  * Parameters:
    120  *  context - The CPU context
    121  * Returns:
    122  *  The values of the registers as a string.
    123  */
    124 __inline char *GetRegisters(CONTEXT *context) 
    125 {
    126 	static char buffer[1024];
    127 
    128 	sprintf(buffer,
    129 		"\tRAX=%p"
    130 		"\tRBX=%p"
    131 		"\tRCX=%p"
    132 		"\tRDX=%p\n"
    133 		"\tRSI=%p"
    134 		"\tRDI=%p"
    135 		"\tRBP=%p"
    136 		"\tRSP=%p\n"
    137 		"\tR8=%p"
    138 		"\tR9=%p"
    139 		"\tR10=%p"
    140 		"\tR11=%p\n"
    141 		"\tR12=%p"
    142 		"\tR13=%p"
    143 		"\tR14=%p"
    144 		"\tR15=%p\n"
    145 		"\tRIP=%p\n",
    146 		(void *)context->Rax,
    147 		(void *)context->Rbx,
    148 		(void *)context->Rcx,
    149 		(void *)context->Rdx,
    150 		(void *)context->Rsi,
    151 		(void *)context->Rdi,
    152 		(void *)context->Rbp,
    153 		(void *)context->Rsp,
    154 		(void *)context->R8,
    155 		(void *)context->R9,
    156 		(void *)context->R10,
    157 		(void *)context->R11,
    158 		(void *)context->R12,
    159 		(void *)context->R13,
    160 		(void *)context->R14,
    161 		(void *)context->R15,
    162 		(void *)context->Rip);
    163 
    164 	return buffer;
    165 }
    166 
    167 /* Convert the exception code to a human readable string
    168  * Parameters:
    169  *  code - The exception code to convert
    170  * Returns:
    171  *  The exception code represented as a string
    172  */
    173 __inline char *GetException(DWORD code) 
    174 {
    175 	switch (code) 
    176 	{
    177 		case EXCEPTION_ACCESS_VIOLATION:
    178 			return "Access Violation";
    179 		case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
    180 			return "Array Bounds Exceeded";
    181 		case EXCEPTION_BREAKPOINT:
    182 			return "Breakpoint";
    183 		case EXCEPTION_DATATYPE_MISALIGNMENT:
    184 			return "Datatype Misalignment";
    185 		case EXCEPTION_FLT_DENORMAL_OPERAND:
    186 			return "Floating Point Denormal Operand";
    187 		case EXCEPTION_FLT_DIVIDE_BY_ZERO:
    188 			return "Floating Point Division By Zero";
    189 		case EXCEPTION_FLT_INEXACT_RESULT:
    190 			return "Floating Point Inexact Result";
    191 		case EXCEPTION_FLT_INVALID_OPERATION:
    192 			return "Floating Point Invalid Operation";
    193 		case EXCEPTION_FLT_OVERFLOW:
    194 			return "Floating Point Overflow";
    195 		case EXCEPTION_FLT_STACK_CHECK:
    196 			return "Floating Point Stack Overflow";
    197 		case EXCEPTION_FLT_UNDERFLOW:
    198 			return "Floating Point Underflow";
    199 		case EXCEPTION_ILLEGAL_INSTRUCTION:
    200 			return "Illegal Instruction";
    201 		case EXCEPTION_IN_PAGE_ERROR:
    202 			return "In Page Error";
    203 		case EXCEPTION_INT_DIVIDE_BY_ZERO:
    204 			return "Integer Division By Zero";
    205 		case EXCEPTION_INT_OVERFLOW:
    206 			return "Integer Overflow";
    207 		case EXCEPTION_INVALID_DISPOSITION:
    208 			return "Invalid Disposition";
    209 		case EXCEPTION_NONCONTINUABLE_EXCEPTION:
    210 			return "Noncontinuable Exception";
    211 		case EXCEPTION_PRIV_INSTRUCTION:
    212 			return "Unallowed Instruction";
    213 		case EXCEPTION_SINGLE_STEP:
    214 			return "Single Step";
    215 		case EXCEPTION_STACK_OVERFLOW:
    216 			return "Stack Overflow";
    217 		default:
    218 			return "Unknown Exception";
    219 	}
    220 }
    221 
    222 void StartCrashReporter(void)
    223 {
    224 	char fname[MAX_PATH], fnamewarg[MAX_PATH+32];
    225 	PROCESS_INFORMATION pi;
    226 	STARTUPINFO si;
    227 	
    228 	memset(&pi, 0, sizeof(pi));
    229 	memset(&si, 0, sizeof(si));
    230 	
    231 	GetModuleFileName(GetModuleHandle(NULL), fname, MAX_PATH);
    232 	
    233 	snprintf(fnamewarg, sizeof(fnamewarg), "\"%s\" %s", fname, "-R");
    234 	CreateProcess(fname, fnamewarg, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
    235 }
    236 
    237 void StartUnrealAgain(void)
    238 {
    239 	char fname[MAX_PATH], fnamewarg[MAX_PATH+32];
    240 	PROCESS_INFORMATION pi;
    241 	STARTUPINFO si;
    242 	
    243 	memset(&pi, 0, sizeof(pi));
    244 	memset(&si, 0, sizeof(si));
    245 	
    246 	GetModuleFileName(GetModuleHandle(NULL), fname, MAX_PATH);
    247 	
    248 	snprintf(fnamewarg, sizeof(fnamewarg), "\"%s\"", fname);
    249 	CreateProcess(fname, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
    250 }
    251 
    252 /* Callback for the exception handler
    253  * Parameters:
    254  *  e - The exception information
    255  * Returns:
    256  *  EXCEPTION_EXECUTE_HANDLER to terminate the process
    257  * Side Effects:
    258  *  unrealircd.PID.core is created
    259  *  If not running in service mode, a message box is displayed, 
    260  *   else output is written to service.log
    261  */
    262 LONG __stdcall ExceptionFilter(EXCEPTION_POINTERS *e) 
    263 {
    264 	MEMORYSTATUSEX memStats;
    265 	char file[512], text[1024], minidumpf[512];
    266 	FILE *fd;
    267 	time_t timet = time(NULL);
    268 #ifndef NOMINIDUMP
    269 	HANDLE hDump;
    270 	HMODULE hDll = NULL;
    271 #endif
    272 
    273 	sprintf(file, "unrealircd.%d.core", getpid());
    274 	fd = fopen(file, "w");
    275 	GlobalMemoryStatusEx(&memStats);
    276 	fprintf(fd, "Generated at %s\nOS: %s\n%s[%s%s%s] (%s) on %s\n"
    277 		    "-----------------\nMemory Information:\n"
    278 		    "\tPhysical: (Available:%lluMB/Total:%lluMB)\n"
    279 		    "\tVirtual: (Available:%lluMB/Total:%lluMB)\n"
    280 		    "-----------------\nException:\n\t%s\n-----------------\n"
    281 		    "Backup Buffer:\n\t%s\n-----------------\nRegisters:\n"
    282 		    "%s-----------------\nStack Trace:\n%s",
    283 		     asctime(gmtime(&timet)), OSName,
    284 			 IRCDTOTALVERSION,
    285 		     serveropts, extraflags ? extraflags : "", tainted ? "3" : "",
    286 		     buildid, me.name, memStats.ullAvailPhys/1048576, memStats.ullTotalPhys/1048576,
    287 		     memStats.ullAvailVirtual/1048576, memStats.ullTotalVirtual/1048576,
    288 		     GetException(e->ExceptionRecord->ExceptionCode), backupbuf,
    289 		     GetRegisters(e->ContextRecord), StackTrace(e));
    290 
    291 	sprintf(text, "UnrealIRCd has encountered a fatal error. Debugging information has been dumped to %s.", file);
    292 	fclose(fd);
    293 
    294 #ifndef NOMINIDUMP
    295 	hDll = LoadLibrary("DBGHELP.DLL");
    296 	if (hDll)
    297 	{
    298 		MINIDUMPWRITEDUMP pDump = (MINIDUMPWRITEDUMP)GetProcAddress(hDll, "MiniDumpWriteDump");
    299 		if (pDump)
    300 		{
    301 			MINIDUMP_EXCEPTION_INFORMATION ExInfo;
    302 			sprintf(minidumpf, "unrealircd.%d.mdmp", getpid());
    303 			hDump = CreateFile(minidumpf, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    304 			if (hDump != INVALID_HANDLE_VALUE)
    305 			{
    306 				ExInfo.ThreadId = GetCurrentThreadId();
    307 				ExInfo.ExceptionPointers = e;
    308 				ExInfo.ClientPointers = 0;
    309 
    310 				if (pDump(GetCurrentProcess(), GetCurrentProcessId(), hDump, MiniDumpWithIndirectlyReferencedMemory, &ExInfo, NULL, NULL))
    311 				{
    312 					sprintf(text, "UnrealIRCd has encountered a fatal error. Debugging information has been dumped to %s and %s.", file, minidumpf);
    313 				}
    314 				CloseHandle(hDump);
    315 			}
    316 			sprintf(minidumpf, "unrealircd.%d.full.mdmp", getpid());
    317 			hDump = CreateFile(minidumpf, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    318 			if (hDump != INVALID_HANDLE_VALUE)
    319 			{
    320 				ExInfo.ThreadId = GetCurrentThreadId();
    321 				ExInfo.ExceptionPointers = e;
    322 				ExInfo.ClientPointers = 0;
    323 
    324 				pDump(GetCurrentProcess(), GetCurrentProcessId(), hDump, MiniDumpWithPrivateReadWriteMemory, &ExInfo, NULL, NULL);
    325 				CloseHandle(hDump);
    326 			}
    327 		}
    328 	}
    329 #endif
    330 	
    331 	if (!IsService)
    332 	{
    333 		MessageBox(NULL, text, "Fatal Error", MB_OK);
    334 		StartCrashReporter();
    335 	}
    336 	else 
    337 	{
    338 		FILE *fd = fopen("logs\\service.log", "a");
    339 
    340 		if (fd)
    341 		{
    342 			fprintf(fd, "UnrealIRCd has encountered a fatal error. Debugging information "
    343 					"has been dumped to unrealircd.%d.core, please file a bug and upload "
    344 					"this file to https://bugs.unrealircd.org/.", getpid());
    345 			fclose(fd);
    346 		}
    347 	}
    348 	CleanUp();
    349 	return EXCEPTION_EXECUTE_HANDLER;
    350 }
    351 
    352 void GotSigAbort(int signal)
    353 {
    354 	/* I just want to call ExceptionFilter() but it requires an argument which we don't have...
    355 	 * So just crash, which is rather silly but produces at least a crash report.
    356 	 * Feel free to improve this!
    357 	 */
    358 	char *crash = NULL;
    359 	*crash = 'X';
    360 }
    361 
    362 /* Initializes the exception handler */
    363 void InitDebug(void) 
    364 {
    365 	SetUnhandledExceptionFilter(&ExceptionFilter);
    366 	_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
    367 	signal(SIGABRT, GotSigAbort);
    368 }
    369 
    370