Mercurial > projects > ddmd
view dlib/CrashHandler.d @ 0:10317f0c89a5
Initial commit
author | korDen |
---|---|
date | Sat, 24 Oct 2009 08:42:06 +0400 |
parents | |
children | 2cc604139636 |
line wrap: on
line source
/** A simple runtime crash handler which collects various informations about the crash such as registers, stack traces, and loaded modules. TODO: * Threading support * Stack dumps Authors: Jeremie Pelletier License: Public Domain */ module dlib.CrashHandler; debug = CrashHandler; import std.c.stdio; import dbg.Debug; import dbg.ui.CrashWindow; version(X86) {} else static assert(0, "Unsupported architecture."); version(DigitalMars) { //import dlib.dmd.ErrorHandling; } else static assert(0, "Unsupported compiler."); version(Windows) { import win32.windows; import win32.psapi; //import sys.windows.Memory; //import dlib.Module; import dbg.image.PE; import dbg.symbol.CodeView; } else version(Posix) { import sys.posix.ucontext; import std.c.signal; import std.c.stdlib : free, exit, EXIT_FAILURE; } else static assert(0, "Unsupported platform."); /** Register the crash handler */ void CrashHandlerInit() { version(Windows) { //SetErrorMode(SetErrorMode(0) | SEM_FAILCRITICALERRORS); SetErrorMode(0); SetUnhandledExceptionFilter(&UnhandledExceptionHandler); } else version(Posix) { sigaction_t sa; sa.sa_handler = cast(sighandler_t)&SignalHandler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART | SA_SIGINFO; sigaction(SIGILL, &sa, null); sigaction(SIGFPE, &sa, null); sigaction(SIGSEGV, &sa, null); } else static assert(0); } /** Information collected by the crash handler */ struct CrashInfo { struct Registers { version(X86) { uint EAX, EBX, ECX, EDX; uint EDI, ESI; uint EBP, ESP; } else static assert(0); } struct Module { string fileName; ushort[4] fileVersion; } Throwable error; string moduleName; Registers registers; size_t[] backtrace; Module[] modules; void Dump() { // TODO: support more dump methods ShowCrashWindow(&this); } /** Formats the crash info as plain-text */ string toString() { string text; char[255] buffer = void; uint len = void; text ~= error.toString(); text ~= "\r\n\r\n"; version(X86) { with(registers) len = snprintf(buffer.ptr, buffer.length, " Registers:\r\n" ~ "========================================\r\n" ~ " EAX=0x%08X EBX=0x%08X ECX=0x%08X EDX=0x%08X\r\n" ~ " EDI=0x%08X ESI=0x%08X EBP=0x%08X ESP=0x%08X\r\n", EAX, EBX, ECX, EDX, EDI, ESI, EBP, ESP ); text ~= buffer[0 .. len] ~ "\r\n"; } else static assert(0); text ~= " Stack Trace:\r\n" ~ "========================================\r\n"; scope auto frames = new StackFrameInfo[backtrace.length]; ResolveStackFrames(frames); foreach(ref frame; frames) { //len = snprintf(buffer.ptr, buffer.length, "%p", frame.va); //text ~= " " ~ buffer[0 .. len] ~ ": " ~ frame.moduleName ~ "\r\n"; //with(frame.symbol) if(name.length) { // len = snprintf(buffer.ptr, buffer.length, "%X", offset); // text ~= " " ~ name ~ " @ 0x" ~ buffer[0 .. len] ~ "\r\n"; //} with(frame.fileLine) if(line) { len = snprintf(buffer.ptr, buffer.length, "%u", line); text ~= " " ~ file ~ ":" ~ buffer[0 .. len] ~ "\r\n"; } //text ~= "\r\n"; } /+ text ~= " Loaded Modules:\r\n" ~ "========================================\r\n"; foreach(mod; modules) { len = snprintf(buffer.ptr, buffer.length, "%hu.%hu.%hu.%hu", mod.fileVersion[0], mod.fileVersion[1], mod.fileVersion[2], mod.fileVersion[3]); text ~= " " ~ mod.fileName ~ "\r\n " ~ buffer[0 .. len] ~ "\r\n"; } +/ text ~= '\0'; return text; } private: struct StackFrameInfo { size_t va; string moduleName; SymbolInfo symbol; FileLineInfo fileLine; } struct DebugImage { DebugImage* next; string moduleName; size_t baseAddress; uint rvaOffset; IExecutableImage exeModule; ISymbolicDebugInfo debugInfo; } version(X86) { void ResolveStackFrames(StackFrameInfo[] frames) const { StackFrameInfo* frame = void; DebugImage* imageList, image = void; char[255] buffer = void; uint len = void; uint rva = void; version(Windows) MEMORY_BASIC_INFORMATION mbi = void; foreach(i, va; backtrace) { frame = &frames[i]; frame.va = va; version(Windows) { // mbi.Allocation base is the handle to stack frame's module VirtualQuery(cast(void*)va, &mbi, MEMORY_BASIC_INFORMATION.sizeof); if(!mbi.AllocationBase) break; image = imageList; while(image) { if(image.baseAddress == cast(size_t)mbi.AllocationBase) break; image = image.next; } if(!image) { image = new DebugImage; with(*image) { next = imageList; imageList = image; baseAddress = cast(size_t)mbi.AllocationBase; len = GetModuleFileNameA(cast(HMODULE)baseAddress, buffer.ptr, buffer.length); moduleName = buffer[0 .. len].idup; exeModule = new PEImage(moduleName); rvaOffset = baseAddress + exeModule.codeOffset; debugInfo = exeModule.debugInfo; } } } else static assert(0); frame.moduleName = image.moduleName; if(!image.debugInfo) continue; rva = va - image.rvaOffset; with(image.debugInfo) { frame.symbol = ResolveSymbol(rva); frame.fileLine = ResolveFileLine(rva); } } while(imageList) { image = imageList.next; delete imageList.debugInfo; delete imageList.exeModule; delete imageList; imageList = image; } } } // version(X86) else static assert(0); } // ---------------------------------------------------------------------------- // W i n d o w s C r a s h H a n d l e r // ---------------------------------------------------------------------------- version(Windows) { extern(C) Throwable _d_translate_se_to_d_exception(EXCEPTION_RECORD* exception_record); /** D exceptions are built on top of Windows' SEH, a simple registered callback will catch any exceptions unwinding past a thread's entry point. */ extern(Windows) int UnhandledExceptionHandler(EXCEPTION_POINTERS* e) { CrashInfo crashInfo = void; char[256] buffer = void; uint len = void; size_t ip = void, bp = void; try { with(crashInfo) { version(DigitalMars) error = _d_translate_se_to_d_exception(e.ExceptionRecord); else static assert(0); len = GetModuleFileNameA(GetModuleHandle(null), buffer.ptr, buffer.length); moduleName = buffer[0 .. len].idup; version(X86) with(*e.ContextRecord) { with(registers) { EAX = Eax, EBX = Ebx, ECX = Ecx, EDX = Edx; EDI = Edi, ESI = Esi; EBP = Ebp, ESP = Esp; } ip = Eip; bp = Ebp; } else static assert(0); backtrace = null; while(ip) { backtrace ~= ip; ip = cast(size_t)*(cast(void**)bp + 1); bp = cast(size_t)*cast(void**)bp; } HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 0, GetCurrentProcessId()); if(process == INVALID_HANDLE_VALUE) SystemException(); scope(exit) if(!CloseHandle(process)) SystemException(); scope HMODULE[] hmodules = new HMODULE[64]; uint size = HMODULE.sizeof * hmodules.length; uint sizeNeeded = void; uint nModules = void; GetModules: if(!EnumProcessModules(process, hmodules.ptr, size, &sizeNeeded)) SystemException(); nModules = sizeNeeded / HMODULE.sizeof; if(sizeNeeded > size) { hmodules.length = nModules; size = sizeNeeded; goto GetModules; } Module mod = void; char[] versionInfo; VS_FIXEDFILEINFO* fixedVersionInfo = void; modules = null; foreach(i; 0 .. nModules) { len = GetModuleFileNameA(hmodules[i], buffer.ptr, buffer.length); mod.fileName = buffer[0 .. len].idup; sizeNeeded = GetFileVersionInfoSizeA(buffer.ptr, &size); if(sizeNeeded) { if(versionInfo.length < sizeNeeded) versionInfo.length = sizeNeeded; if(!GetFileVersionInfoA(buffer.ptr, 0, versionInfo.length, versionInfo.ptr)) SystemException(); if(!VerQueryValueA(versionInfo.ptr, cast(char*)"\\".ptr, cast(void**)&fixedVersionInfo, &size)) SystemException(); with(*fixedVersionInfo) with(mod) { fileVersion[0] = HIWORD(dwProductVersionMS); fileVersion[1] = LOWORD(dwProductVersionMS); fileVersion[2] = HIWORD(dwProductVersionLS); fileVersion[3] = LOWORD(dwProductVersionLS); } } else { mod.fileVersion[] = 0; } modules ~= mod; } } // with(crashInfo) crashInfo.Dump(); } catch(Throwable e) { debug MessageBoxA(HWND.init, (e.toString ~ '\0').ptr, "Exception Handler Error!", MB_ICONERROR | MB_OK); } return EXCEPTION_EXECUTE_HANDLER; } } // version(Windows) // ---------------------------------------------------------------------------- // P o s i x C r a s h H a n d l e r // ---------------------------------------------------------------------------- else version(Posix) { /** This handler catches system signals and throws the appropriate D exception. The exception will unwind down to the thread's entry point where it is catched and sent to UnhandledExceptionHandler(). */ extern(C) void SignalHandler(int signum, siginfo_t* siginfo, ucontext_t* ucontext) { string msg = void; switch(signum) { case SIGILL: msg = "Illegal instruction"; break; case SIGFPE: msg = "Floating-point exception"; break; case SIGSEGV: msg = "Segmentation fault"; break; default: msg = "Unknown signal"; } SystemException e = new SystemException(msg); e._context = ucontext; e.GetBackTrace(); // The kernel fixed the stack frame to make us believe we called this // routine ourselves, with the nasty side effect of losing the faulty // routine's address. The undocumented parameter ucontext contains our // lost EIP. version(X86) { // It should be the 3rd frame: // SignalHandler() -> GetBackTrace() -> backtrace() if(e._backtrace.length > 2) e._backtrace[2] = cast(void*)ucontext.uc_mcontext.gregs[REG_EIP]; } else static assert(0); throw e; } /** This handler is called when an exception unwinds down to the thread's entry point, which should catch it and manually call this routine. */ void UnhandledExceptionHandler(Throwable e) { ErrorReport crashInfo = void; with(crashInfo) { assert(0); /+error = e; // Get the module filename // TODO // Dump the general purpose registers if(e._context) { gregset_t gregs = e._context.uc_mcontext.gregs; version(X86) { registers.Eax = gregs[REG_EAX]; registers.Ebx = gregs[REG_EBX]; registers.Ecx = gregs[REG_ECX]; registers.Edx = gregs[REG_EDX]; registers.Edi = gregs[REG_EDI]; registers.Esi = gregs[REG_ESI]; registers.Ebp = gregs[REG_EBP]; registers.Esp = gregs[REG_ESP]; } else static assert(0); } // Dump stack backtrace addresses = e._backtrace; // Dump the loaded modules // TODO+/ } // with(crashInfo) crashInfo.Dump(); } } // version(Posix) else static assert(0);