70
|
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 dbg.CallStackInfo;
|
|
16
|
|
17 import dbg.Debug;
|
|
18 import dbg.image.PE;
|
|
19
|
|
20 import core.stdc.stdio;
|
|
21 import core.sys.windows.windows;
|
|
22
|
|
23 class CallStackInfo
|
|
24 {
|
|
25 this(EXCEPTION_POINTERS* e = null)
|
|
26 {
|
|
27 size_t[16] buff;
|
|
28 size_t[] backtrace = buff[];
|
|
29 size_t numTraces = 0;
|
|
30
|
|
31 bool skipFirst = false;
|
|
32
|
|
33 size_t ip = void, bp = void;
|
|
34 if (e !is null) {
|
|
35 ip = e.ContextRecord.Eip;
|
|
36 bp = e.ContextRecord.Ebp;
|
|
37
|
|
38 error = _d_translate_se_to_d_exception(e.ExceptionRecord);
|
|
39 append(backtrace, numTraces, ip);
|
|
40 } else {
|
|
41 asm {
|
|
42 mov bp, EBP;
|
|
43 }
|
|
44 }
|
|
45
|
|
46 while (true) {
|
|
47 ip = cast(size_t)*(cast(void**)bp + 1);
|
|
48 if (ip == 0) break;
|
|
49
|
|
50 append(backtrace, numTraces, ip);
|
|
51
|
|
52 bp = cast(size_t)*cast(void**)bp;
|
|
53 }
|
|
54
|
|
55 frames = new StackFrameInfo[numTraces];
|
|
56 ResolveStackFrames(backtrace[0..numTraces], frames);
|
|
57 }
|
|
58
|
|
59 Throwable error;
|
|
60 StackFrameInfo[] frames;
|
|
61
|
|
62 string toString() {
|
|
63 string text;
|
|
64
|
|
65 if (error !is null) {
|
|
66 text ~= error.toString() ~ "\n";
|
|
67 }
|
|
68
|
|
69 text ~= "Stack trace:\n------------------\n";
|
|
70 char buffer[128];
|
|
71 foreach(ref frame; frames) {
|
|
72 with(frame.fileLine) if(line) {
|
|
73 auto len = snprintf(buffer.ptr, buffer.length, "%u", line);
|
|
74 text ~= file ~ ":" ~ buffer[0 .. len] ~ "\r\n";
|
|
75 }
|
|
76 }
|
|
77
|
|
78 text ~= '\0';
|
|
79
|
|
80 return text;
|
|
81 }
|
|
82
|
|
83 void dump() {
|
|
84 if (error !is null) {
|
|
85 printf("%.*s\n", error.toString());
|
|
86 }
|
|
87
|
|
88 printf("Stack trace:\n------------------\n");
|
|
89 foreach(ref frame; frames) {
|
|
90 with(frame.fileLine) if (line) {
|
|
91 printf("%.*s:%d\r\n", file, line);
|
|
92 }
|
|
93 }
|
|
94 }
|
|
95
|
|
96 private:
|
|
97
|
|
98 struct StackFrameInfo {
|
|
99 size_t va;
|
|
100 string moduleName;
|
|
101 SymbolInfo symbol;
|
|
102 FileLineInfo fileLine;
|
|
103 }
|
|
104
|
|
105 struct DebugImage {
|
|
106 DebugImage* next;
|
|
107 string moduleName;
|
|
108 size_t baseAddress;
|
|
109 uint rvaOffset;
|
|
110 IExecutableImage exeModule;
|
|
111 ISymbolicDebugInfo debugInfo;
|
|
112 }
|
|
113
|
|
114 void ResolveStackFrames(size_t[] backtrace, StackFrameInfo[] frames) const {
|
|
115 StackFrameInfo* frame = void;
|
|
116 DebugImage* imageList, image = void;
|
|
117 char[255] buffer = void;
|
|
118 uint len = void;
|
|
119 uint rva = void;
|
|
120
|
|
121 version(Windows) MEMORY_BASIC_INFORMATION mbi = void;
|
|
122
|
|
123 foreach(i, va; backtrace) {
|
|
124 frame = &frames[i];
|
|
125 frame.va = va;
|
|
126
|
|
127 // mbi.Allocation base is the handle to stack frame's module
|
|
128 VirtualQuery(cast(void*)va, &mbi, MEMORY_BASIC_INFORMATION.sizeof);
|
|
129 if(!mbi.AllocationBase) break;
|
|
130
|
|
131 image = imageList;
|
|
132 while(image) {
|
|
133 if(image.baseAddress == cast(size_t)mbi.AllocationBase) break;
|
|
134 image = image.next;
|
|
135 }
|
|
136
|
|
137 if(!image) {
|
|
138 image = new DebugImage;
|
|
139
|
|
140 with(*image) {
|
|
141 next = imageList;
|
|
142 imageList = image;
|
|
143 baseAddress = cast(size_t)mbi.AllocationBase;
|
|
144
|
|
145 len = GetModuleFileNameA(cast(HMODULE)baseAddress, buffer.ptr, buffer.length);
|
|
146 moduleName = buffer[0 .. len].idup;
|
|
147 if (len != 0) {
|
|
148 exeModule = new PEImage(moduleName);
|
|
149 rvaOffset = baseAddress + exeModule.codeOffset;
|
|
150 debugInfo = exeModule.debugInfo;
|
|
151 }
|
|
152 }
|
|
153 }
|
|
154
|
|
155 frame.moduleName = image.moduleName;
|
|
156
|
|
157 if(!image.debugInfo) continue;
|
|
158
|
|
159 rva = va - image.rvaOffset;
|
|
160
|
|
161 with(image.debugInfo) {
|
|
162 frame.symbol = ResolveSymbol(rva);
|
|
163 frame.fileLine = ResolveFileLine(rva);
|
|
164 }
|
|
165 }
|
|
166
|
|
167 while(imageList) {
|
|
168 image = imageList.next;
|
|
169 delete imageList.debugInfo;
|
|
170 delete imageList.exeModule;
|
|
171 delete imageList;
|
|
172 imageList = image;
|
|
173 }
|
|
174 }
|
|
175 }
|
|
176
|
|
177 void CrashHandlerInit() {
|
|
178 //SetErrorMode(SetErrorMode(0) | SEM_FAILCRITICALERRORS);
|
|
179 SetErrorMode(0);
|
|
180 SetUnhandledExceptionFilter(&UnhandledExceptionHandler);
|
|
181 }
|
|
182
|
|
183 enum EXCEPTION_EXECUTE_HANDLER = 1;
|
|
184
|
|
185 extern(Windows) int UnhandledExceptionHandler(EXCEPTION_POINTERS* e) {
|
|
186 scope CallStackInfo info = new CallStackInfo(e);
|
|
187 info.dump();
|
|
188
|
|
189 return EXCEPTION_EXECUTE_HANDLER;
|
|
190 }
|
|
191
|
|
192 extern (Windows) extern UINT SetErrorMode(UINT);
|
|
193 alias LONG function(EXCEPTION_POINTERS*) PTOP_LEVEL_EXCEPTION_FILTER;
|
|
194 extern (Windows) PTOP_LEVEL_EXCEPTION_FILTER SetUnhandledExceptionFilter(PTOP_LEVEL_EXCEPTION_FILTER);
|
|
195
|
|
196 void append(T)(T[] array, ref size_t index, T value)
|
|
197 {
|
|
198 size_t capacity = array.length;
|
|
199 assert(capacity >= index);
|
|
200 if (capacity == index) {
|
|
201 if (capacity < 8) {
|
|
202 capacity = 8;
|
|
203 } else {
|
|
204 array.length = capacity * 2;
|
|
205 }
|
|
206 }
|
|
207
|
|
208 array[index++] = value;
|
|
209 }
|
|
210
|
|
211 struct EXCEPTION_POINTERS {
|
|
212 EXCEPTION_RECORD* ExceptionRecord;
|
|
213 CONTEXT* ContextRecord;
|
|
214 }
|
|
215
|
|
216 const size_t EXCEPTION_MAXIMUM_PARAMETERS = 15;
|
|
217
|
|
218 struct EXCEPTION_RECORD {
|
|
219 DWORD ExceptionCode;
|
|
220 DWORD ExceptionFlags;
|
|
221 EXCEPTION_RECORD* ExceptionRecord;
|
|
222 PVOID ExceptionAddress;
|
|
223 DWORD NumberParameters;
|
|
224 DWORD[EXCEPTION_MAXIMUM_PARAMETERS] ExceptionInformation;
|
|
225 }
|
|
226
|
|
227 const MAXIMUM_SUPPORTED_EXTENSION = 512;
|
|
228
|
|
229 struct CONTEXT {
|
|
230 DWORD ContextFlags;
|
|
231 DWORD Dr0;
|
|
232 DWORD Dr1;
|
|
233 DWORD Dr2;
|
|
234 DWORD Dr3;
|
|
235 DWORD Dr6;
|
|
236 DWORD Dr7;
|
|
237 FLOATING_SAVE_AREA FloatSave;
|
|
238 DWORD SegGs;
|
|
239 DWORD SegFs;
|
|
240 DWORD SegEs;
|
|
241 DWORD SegDs;
|
|
242 DWORD Edi;
|
|
243 DWORD Esi;
|
|
244 DWORD Ebx;
|
|
245 DWORD Edx;
|
|
246 DWORD Ecx;
|
|
247 DWORD Eax;
|
|
248 DWORD Ebp;
|
|
249 DWORD Eip;
|
|
250 DWORD SegCs;
|
|
251 DWORD EFlags;
|
|
252 DWORD Esp;
|
|
253 DWORD SegSs;
|
|
254 BYTE[MAXIMUM_SUPPORTED_EXTENSION] ExtendedRegisters;
|
|
255 }
|
|
256
|
|
257 extern(C) Throwable _d_translate_se_to_d_exception(EXCEPTION_RECORD* exception_record); |