comparison dlib/CrashHandler.d @ 0:10317f0c89a5

Initial commit
author korDen
date Sat, 24 Oct 2009 08:42:06 +0400
parents
children 2cc604139636
comparison
equal deleted inserted replaced
-1:000000000000 0:10317f0c89a5
1 /**
2 A simple runtime crash handler which collects various informations about
3 the crash such as registers, stack traces, and loaded modules.
4
5 TODO:
6 * Threading support
7 * Stack dumps
8
9 Authors:
10 Jeremie Pelletier
11
12 License:
13 Public Domain
14 */
15 module dlib.CrashHandler;
16
17 debug = CrashHandler;
18
19 import std.c.stdio;
20 import dbg.Debug;
21 import dbg.ui.CrashWindow;
22
23 version(X86) {}
24 else static assert(0, "Unsupported architecture.");
25
26 version(DigitalMars) {
27 //import dlib.dmd.ErrorHandling;
28 }
29 else static assert(0, "Unsupported compiler.");
30
31 version(Windows) {
32 import win32.windows;
33 import win32.psapi;
34 //import sys.windows.Memory;
35 //import dlib.Module;
36 import dbg.image.PE;
37 import dbg.symbol.CodeView;
38 }
39 else version(Posix) {
40 import sys.posix.ucontext;
41 import std.c.signal;
42 import std.c.stdlib : free, exit, EXIT_FAILURE;
43 }
44 else static assert(0, "Unsupported platform.");
45
46 /**
47 Register the crash handler
48 */
49 void CrashHandlerInit() {
50 version(Windows) {
51 //SetErrorMode(SetErrorMode(0) | SEM_FAILCRITICALERRORS);
52 SetErrorMode(0);
53 SetUnhandledExceptionFilter(&UnhandledExceptionHandler);
54 }
55 else version(Posix) {
56 sigaction_t sa;
57 sa.sa_handler = cast(sighandler_t)&SignalHandler;
58 sigemptyset(&sa.sa_mask);
59 sa.sa_flags = SA_RESTART | SA_SIGINFO;
60
61 sigaction(SIGILL, &sa, null);
62 sigaction(SIGFPE, &sa, null);
63 sigaction(SIGSEGV, &sa, null);
64 }
65 else static assert(0);
66 }
67
68 /**
69 Information collected by the crash handler
70 */
71 struct CrashInfo {
72 struct Registers {
73 version(X86) {
74 uint EAX, EBX, ECX, EDX;
75 uint EDI, ESI;
76 uint EBP, ESP;
77 }
78 else static assert(0);
79 }
80
81 struct Module {
82 string fileName;
83 ushort[4] fileVersion;
84 }
85
86 Throwable error;
87 string moduleName;
88 Registers registers;
89 size_t[] backtrace;
90 Module[] modules;
91
92 void Dump() {
93 // TODO: support more dump methods
94 ShowCrashWindow(&this);
95 }
96
97 /**
98 Formats the crash info as plain-text
99 */
100 string toString() {
101 string text;
102 char[255] buffer = void;
103 uint len = void;
104
105 text ~= error.toString();
106 text ~= "\r\n\r\n";
107
108 version(X86) {
109 with(registers) len = snprintf(buffer.ptr, buffer.length,
110 " Registers:\r\n" ~
111 "========================================\r\n" ~
112 " EAX=0x%08X EBX=0x%08X ECX=0x%08X EDX=0x%08X\r\n" ~
113 " EDI=0x%08X ESI=0x%08X EBP=0x%08X ESP=0x%08X\r\n",
114 EAX, EBX, ECX, EDX, EDI, ESI, EBP, ESP
115 );
116 text ~= buffer[0 .. len] ~ "\r\n";
117 }
118 else static assert(0);
119
120 text ~= " Stack Trace:\r\n" ~
121 "========================================\r\n";
122
123 scope auto frames = new StackFrameInfo[backtrace.length];
124 ResolveStackFrames(frames);
125
126 foreach(ref frame; frames) {
127 //len = snprintf(buffer.ptr, buffer.length, "%p", frame.va);
128 //text ~= " " ~ buffer[0 .. len] ~ ": " ~ frame.moduleName ~ "\r\n";
129
130 //with(frame.symbol) if(name.length) {
131 // len = snprintf(buffer.ptr, buffer.length, "%X", offset);
132 // text ~= " " ~ name ~ " @ 0x" ~ buffer[0 .. len] ~ "\r\n";
133 //}
134
135 with(frame.fileLine) if(line) {
136 len = snprintf(buffer.ptr, buffer.length, "%u", line);
137 text ~= " " ~ file ~ ":" ~ buffer[0 .. len] ~ "\r\n";
138 }
139
140 //text ~= "\r\n";
141 }
142 /+
143 text ~= " Loaded Modules:\r\n" ~
144 "========================================\r\n";
145
146 foreach(mod; modules) {
147 len = snprintf(buffer.ptr, buffer.length, "%hu.%hu.%hu.%hu",
148 mod.fileVersion[0], mod.fileVersion[1],
149 mod.fileVersion[2], mod.fileVersion[3]);
150
151 text ~= " " ~ mod.fileName ~ "\r\n " ~ buffer[0 .. len] ~ "\r\n";
152 }
153 +/
154 text ~= '\0';
155
156 return text;
157 }
158
159 private:
160
161 struct StackFrameInfo {
162 size_t va;
163 string moduleName;
164 SymbolInfo symbol;
165 FileLineInfo fileLine;
166 }
167
168 struct DebugImage {
169 DebugImage* next;
170 string moduleName;
171 size_t baseAddress;
172 uint rvaOffset;
173 IExecutableImage exeModule;
174 ISymbolicDebugInfo debugInfo;
175 }
176
177 version(X86) {
178 void ResolveStackFrames(StackFrameInfo[] frames) const {
179 StackFrameInfo* frame = void;
180 DebugImage* imageList, image = void;
181 char[255] buffer = void;
182 uint len = void;
183 uint rva = void;
184
185 version(Windows) MEMORY_BASIC_INFORMATION mbi = void;
186
187 foreach(i, va; backtrace) {
188 frame = &frames[i];
189 frame.va = va;
190
191 version(Windows) {
192 // mbi.Allocation base is the handle to stack frame's module
193 VirtualQuery(cast(void*)va, &mbi, MEMORY_BASIC_INFORMATION.sizeof);
194 if(!mbi.AllocationBase) break;
195
196 image = imageList;
197 while(image) {
198 if(image.baseAddress == cast(size_t)mbi.AllocationBase) break;
199 image = image.next;
200 }
201
202 if(!image) {
203 image = new DebugImage;
204
205 with(*image) {
206 next = imageList;
207 imageList = image;
208 baseAddress = cast(size_t)mbi.AllocationBase;
209
210 len = GetModuleFileNameA(cast(HMODULE)baseAddress, buffer.ptr, buffer.length);
211 moduleName = buffer[0 .. len].idup;
212
213 exeModule = new PEImage(moduleName);
214 rvaOffset = baseAddress + exeModule.codeOffset;
215 debugInfo = exeModule.debugInfo;
216 }
217 }
218 }
219 else static assert(0);
220
221 frame.moduleName = image.moduleName;
222
223 if(!image.debugInfo) continue;
224
225 rva = va - image.rvaOffset;
226
227 with(image.debugInfo) {
228 frame.symbol = ResolveSymbol(rva);
229 frame.fileLine = ResolveFileLine(rva);
230 }
231 }
232
233 while(imageList) {
234 image = imageList.next;
235 delete imageList.debugInfo;
236 delete imageList.exeModule;
237 delete imageList;
238 imageList = image;
239 }
240 }
241 } // version(X86)
242 else static assert(0);
243 }
244
245 // ----------------------------------------------------------------------------
246 // W i n d o w s C r a s h H a n d l e r
247 // ----------------------------------------------------------------------------
248
249 version(Windows) {
250
251 extern(C) Throwable _d_translate_se_to_d_exception(EXCEPTION_RECORD* exception_record);
252
253 /**
254 D exceptions are built on top of Windows' SEH, a simple registered callback
255 will catch any exceptions unwinding past a thread's entry point.
256 */
257 extern(Windows)
258 int UnhandledExceptionHandler(EXCEPTION_POINTERS* e) {
259 CrashInfo crashInfo = void;
260 char[256] buffer = void;
261 uint len = void;
262 size_t ip = void, bp = void;
263
264 try {
265 with(crashInfo) {
266
267 version(DigitalMars)
268 error = _d_translate_se_to_d_exception(e.ExceptionRecord);
269 else
270 static assert(0);
271
272 len = GetModuleFileNameA(GetModuleHandle(null), buffer.ptr, buffer.length);
273 moduleName = buffer[0 .. len].idup;
274
275 version(X86) with(*e.ContextRecord) {
276 with(registers) {
277 EAX = Eax, EBX = Ebx, ECX = Ecx, EDX = Edx;
278 EDI = Edi, ESI = Esi;
279 EBP = Ebp, ESP = Esp;
280 }
281
282 ip = Eip;
283 bp = Ebp;
284 }
285 else static assert(0);
286
287 backtrace = null;
288 while(ip) {
289 backtrace ~= ip;
290
291 ip = cast(size_t)*(cast(void**)bp + 1);
292 bp = cast(size_t)*cast(void**)bp;
293 }
294
295 HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
296 0, GetCurrentProcessId());
297
298 if(process == INVALID_HANDLE_VALUE) SystemException();
299 scope(exit) if(!CloseHandle(process)) SystemException();
300
301 scope HMODULE[] hmodules = new HMODULE[64];
302 uint size = HMODULE.sizeof * hmodules.length;
303 uint sizeNeeded = void;
304 uint nModules = void;
305
306 GetModules:
307 if(!EnumProcessModules(process, hmodules.ptr, size, &sizeNeeded))
308 SystemException();
309
310 nModules = sizeNeeded / HMODULE.sizeof;
311
312 if(sizeNeeded > size) {
313 hmodules.length = nModules;
314 size = sizeNeeded;
315 goto GetModules;
316 }
317
318 Module mod = void;
319 char[] versionInfo;
320 VS_FIXEDFILEINFO* fixedVersionInfo = void;
321
322 modules = null;
323 foreach(i; 0 .. nModules) {
324 len = GetModuleFileNameA(hmodules[i], buffer.ptr, buffer.length);
325 mod.fileName = buffer[0 .. len].idup;
326
327 sizeNeeded = GetFileVersionInfoSizeA(buffer.ptr, &size);
328
329 if(sizeNeeded) {
330 if(versionInfo.length < sizeNeeded) versionInfo.length = sizeNeeded;
331
332 if(!GetFileVersionInfoA(buffer.ptr, 0, versionInfo.length, versionInfo.ptr))
333 SystemException();
334
335 if(!VerQueryValueA(versionInfo.ptr, cast(char*)"\\".ptr, cast(void**)&fixedVersionInfo, &size))
336 SystemException();
337
338 with(*fixedVersionInfo) with(mod) {
339 fileVersion[0] = HIWORD(dwProductVersionMS);
340 fileVersion[1] = LOWORD(dwProductVersionMS);
341 fileVersion[2] = HIWORD(dwProductVersionLS);
342 fileVersion[3] = LOWORD(dwProductVersionLS);
343 }
344 }
345 else {
346 mod.fileVersion[] = 0;
347 }
348
349 modules ~= mod;
350 }
351
352 } // with(crashInfo)
353
354 crashInfo.Dump();
355 }
356 catch(Throwable e) {
357 debug MessageBoxA(HWND.init, (e.toString ~ '\0').ptr, "Exception Handler Error!", MB_ICONERROR | MB_OK);
358 }
359
360 return EXCEPTION_EXECUTE_HANDLER;
361 }
362
363 } // version(Windows)
364
365 // ----------------------------------------------------------------------------
366 // P o s i x C r a s h H a n d l e r
367 // ----------------------------------------------------------------------------
368
369 else version(Posix) {
370
371 /**
372 This handler catches system signals and throws the appropriate D exception.
373 The exception will unwind down to the thread's entry point where it is catched
374 and sent to UnhandledExceptionHandler().
375 */
376 extern(C)
377 void SignalHandler(int signum, siginfo_t* siginfo, ucontext_t* ucontext) {
378 string msg = void;
379
380 switch(signum) {
381 case SIGILL: msg = "Illegal instruction"; break;
382 case SIGFPE: msg = "Floating-point exception"; break;
383 case SIGSEGV: msg = "Segmentation fault"; break;
384 default: msg = "Unknown signal";
385 }
386
387 SystemException e = new SystemException(msg);
388
389 e._context = ucontext;
390 e.GetBackTrace();
391
392 // The kernel fixed the stack frame to make us believe we called this
393 // routine ourselves, with the nasty side effect of losing the faulty
394 // routine's address. The undocumented parameter ucontext contains our
395 // lost EIP.
396 version(X86) {
397 // It should be the 3rd frame:
398 // SignalHandler() -> GetBackTrace() -> backtrace()
399 if(e._backtrace.length > 2)
400 e._backtrace[2] = cast(void*)ucontext.uc_mcontext.gregs[REG_EIP];
401 }
402 else static assert(0);
403
404 throw e;
405 }
406
407 /**
408 This handler is called when an exception unwinds down to the thread's entry
409 point, which should catch it and manually call this routine.
410 */
411 void UnhandledExceptionHandler(Throwable e) {
412 ErrorReport crashInfo = void;
413
414 with(crashInfo) {
415 assert(0);
416 /+error = e;
417
418 // Get the module filename
419 // TODO
420
421 // Dump the general purpose registers
422 if(e._context) {
423 gregset_t gregs = e._context.uc_mcontext.gregs;
424
425 version(X86) {
426 registers.Eax = gregs[REG_EAX];
427 registers.Ebx = gregs[REG_EBX];
428 registers.Ecx = gregs[REG_ECX];
429 registers.Edx = gregs[REG_EDX];
430 registers.Edi = gregs[REG_EDI];
431 registers.Esi = gregs[REG_ESI];
432 registers.Ebp = gregs[REG_EBP];
433 registers.Esp = gregs[REG_ESP];
434 }
435 else static assert(0);
436 }
437
438 // Dump stack backtrace
439 addresses = e._backtrace;
440
441 // Dump the loaded modules
442 // TODO+/
443
444 } // with(crashInfo)
445
446 crashInfo.Dump();
447 }
448
449 } // version(Posix)
450 else static assert(0);