|
1
|
1 #include "shared.h"
|
|
|
2 #include <imagehlp.h>
|
|
|
3 #include <forward_list>
|
|
|
4
|
|
|
5 #if SIZE_MAX == UINT32_MAX
|
|
|
6 #define STACKSPEC "%08X"
|
|
|
7 #define PTRSPEC "%08Xh"
|
|
|
8 #define OFFSETSPEC "%Xh"
|
|
|
9 #elif SIZE_MAX == UINT64_MAX
|
|
|
10 #define STACKSPEC "%016llX"
|
|
|
11 #define PTRSPEC "%016llXh"
|
|
|
12 #define OFFSETSPEC "%llXh"
|
|
|
13 #else
|
|
|
14 #error WTF?
|
|
|
15 #endif
|
|
|
16
|
|
|
17
|
|
|
18 static volatile bool g_didSuppress = false;
|
|
|
19 static critical_section g_panicHandlersSync;
|
|
|
20 static std::forward_list<fb2k::panicHandler*> g_panicHandlers;
|
|
|
21
|
|
|
22 static void callPanicHandlers() {
|
|
|
23 insync(g_panicHandlersSync);
|
|
|
24 for( auto i = g_panicHandlers.begin(); i != g_panicHandlers.end(); ++ i ) {
|
|
|
25 try {
|
|
|
26 (*i)->onPanic();
|
|
|
27 } catch(...) {}
|
|
|
28 }
|
|
|
29 }
|
|
|
30
|
|
|
31 void SHARED_EXPORT uAddPanicHandler(fb2k::panicHandler* p) {
|
|
|
32 insync(g_panicHandlersSync);
|
|
|
33 g_panicHandlers.push_front(p);
|
|
|
34 }
|
|
|
35
|
|
|
36 void SHARED_EXPORT uRemovePanicHandler(fb2k::panicHandler* p) {
|
|
|
37 insync(g_panicHandlersSync);
|
|
|
38 g_panicHandlers.remove(p);
|
|
|
39 }
|
|
|
40
|
|
|
41
|
|
|
42 enum { EXCEPTION_BUG_CHECK = 0xaa67913c };
|
|
|
43
|
|
|
44 #if FB2K_SUPPORT_CRASH_LOGS
|
|
|
45
|
|
|
46 static const unsigned char utf8_header[3] = {0xEF,0xBB,0xBF};
|
|
|
47
|
|
|
48 static __declspec(thread) char g_thread_call_stack[1024];
|
|
|
49 static __declspec(thread) t_size g_thread_call_stack_length;
|
|
|
50
|
|
|
51 static critical_section g_lastEventsSync;
|
|
|
52 static pfc::chain_list_v2_t<pfc::string8> g_lastEvents;
|
|
|
53 static constexpr t_size KLastEventCount = 200;
|
|
|
54
|
|
|
55 static pfc::string8 version_string;
|
|
|
56
|
|
|
57 static pfc::array_t<TCHAR> DumpPathBuffer;
|
|
|
58
|
|
|
59 static long crash_no = 0;
|
|
|
60
|
|
|
61 // Debug timer: GetTickCount() diff since app startup.
|
|
|
62 // Intentionally kept as dumb as possible (originally meant to format system time nicely).
|
|
|
63 // Do not want to do expensive preformat of every event that in >99% of scenarios isn't logged.
|
|
|
64 // Cannot do formatting & system calls in crash handler.
|
|
|
65 // So we just write amount of MS since app startup.
|
|
|
66 static const uint64_t debugTimerInit = GetTickCount64();
|
|
|
67 static pfc::format_int_t queryDebugTimer() { return pfc::format_int( GetTickCount64() - debugTimerInit ); }
|
|
|
68
|
|
|
69 static pfc::string8 g_components;
|
|
|
70
|
|
|
71 static void WriteFileString_internal(HANDLE hFile,const char * ptr,t_size len)
|
|
|
72 {
|
|
|
73 DWORD bah;
|
|
|
74 WriteFile(hFile,ptr,(DWORD)len,&bah,0);
|
|
|
75 }
|
|
|
76
|
|
|
77 static HANDLE create_failure_log()
|
|
|
78 {
|
|
|
79 bool rv = false;
|
|
|
80 if (DumpPathBuffer.get_size() == 0) return INVALID_HANDLE_VALUE;
|
|
|
81
|
|
|
82 TCHAR * path = DumpPathBuffer.get_ptr();
|
|
|
83
|
|
|
84 t_size lenWalk = _tcslen(path);
|
|
|
85
|
|
|
86 if (lenWalk == 0 || path[lenWalk-1] != '\\') {path[lenWalk++] = '\\';}
|
|
|
87
|
|
|
88 _tcscpy(path + lenWalk, _T("crash reports"));
|
|
|
89 lenWalk += _tcslen(path + lenWalk);
|
|
|
90
|
|
|
91 SetLastError(NO_ERROR);
|
|
|
92 if (!CreateDirectory(path, NULL)) {
|
|
|
93 if (GetLastError() != ERROR_ALREADY_EXISTS) return INVALID_HANDLE_VALUE;
|
|
|
94 }
|
|
|
95 path[lenWalk++] = '\\';
|
|
|
96
|
|
|
97 TCHAR * fn_out = path + lenWalk;
|
|
|
98 HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
|
99 unsigned attempts = 0;
|
|
|
100 for(;;) {
|
|
|
101 wsprintf(fn_out,TEXT("failure_%08u.txt"),++attempts);
|
|
|
102 hFile = CreateFile(path,GENERIC_WRITE,0,0,CREATE_NEW,0,0);
|
|
|
103 if (hFile!=INVALID_HANDLE_VALUE) break;
|
|
|
104 if (attempts > 1000) break;
|
|
|
105 }
|
|
|
106 if (hFile!=INVALID_HANDLE_VALUE) WriteFileString_internal(hFile, (const char*)utf8_header, sizeof(utf8_header));
|
|
|
107 return hFile;
|
|
|
108 }
|
|
|
109
|
|
|
110
|
|
|
111 static void WriteFileString(HANDLE hFile,const char * str)
|
|
|
112 {
|
|
|
113 const char * ptr = str;
|
|
|
114 for(;;)
|
|
|
115 {
|
|
|
116 const char * start = ptr;
|
|
|
117 ptr = strchr(ptr,'\n');
|
|
|
118 if (ptr)
|
|
|
119 {
|
|
|
120 if (ptr>start) {
|
|
|
121 t_size len = ptr-start;
|
|
|
122 if (ptr[-1] == '\r') --len;
|
|
|
123 WriteFileString_internal(hFile,start,len);
|
|
|
124 }
|
|
|
125 WriteFileString_internal(hFile,"\r\n",2);
|
|
|
126 ptr++;
|
|
|
127 }
|
|
|
128 else
|
|
|
129 {
|
|
|
130 WriteFileString_internal(hFile,start,strlen(start));
|
|
|
131 break;
|
|
|
132 }
|
|
|
133 }
|
|
|
134 }
|
|
|
135
|
|
|
136 static void WriteEvent(HANDLE hFile,const char * str) {
|
|
|
137 bool haveText = false;
|
|
|
138 const char * ptr = str;
|
|
|
139 bool isLineBreak = false;
|
|
|
140 while(*ptr) {
|
|
|
141 const char * base = ptr;
|
|
|
142 while(*ptr && *ptr != '\r' && *ptr != '\n') ++ptr;
|
|
|
143 if (ptr > base) {
|
|
|
144 if (isLineBreak) WriteFileString_internal(hFile,"\r\n",2);
|
|
|
145 WriteFileString_internal(hFile,base,ptr-base);
|
|
|
146 isLineBreak = false; haveText = true;
|
|
|
147 }
|
|
|
148 for(;;) {
|
|
|
149 if (*ptr == '\n') isLineBreak = haveText;
|
|
|
150 else if (*ptr != '\r') break;
|
|
|
151 ++ptr;
|
|
|
152 }
|
|
|
153 }
|
|
|
154 if (haveText) WriteFileString_internal(hFile,"\r\n",2);
|
|
|
155 }
|
|
|
156
|
|
|
157 static bool read_int(t_size src, t_size* out)
|
|
|
158 {
|
|
|
159 __try
|
|
|
160 {
|
|
|
161 *out = *(t_size*)src;
|
|
|
162 return true;
|
|
|
163 } __except (1) { return false; }
|
|
|
164 }
|
|
|
165
|
|
|
166 static bool hexdump8(char * out,size_t address,const char * msg,int from,int to)
|
|
|
167 {
|
|
|
168 unsigned max = (to-from)*16;
|
|
|
169 if (IsBadReadPtr((const void*)(address+(from*16)),max)) return false;
|
|
|
170 out += sprintf(out,"\n%s (" PTRSPEC "):",msg,address);
|
|
|
171 unsigned n;
|
|
|
172 const unsigned char * src = (const unsigned char*)(address)+(from*16);
|
|
|
173
|
|
|
174 for(n=0;n<max;n++)
|
|
|
175 {
|
|
|
176 if (n%16==0)
|
|
|
177 {
|
|
|
178 out += sprintf(out,"\n" PTRSPEC ": ",(size_t)src);
|
|
|
179 }
|
|
|
180
|
|
|
181 out += sprintf(out," %02X",*(src++));
|
|
|
182 }
|
|
|
183 *(out++) = '\n';
|
|
|
184 *out=0;
|
|
|
185 return true;
|
|
|
186 }
|
|
|
187
|
|
|
188 static bool hexdump_stack(char * out,size_t address,const char * msg,int from,int to)
|
|
|
189 {
|
|
|
190 constexpr unsigned lineWords = 4;
|
|
|
191 constexpr unsigned wordBytes = sizeof(size_t);
|
|
|
192 constexpr unsigned lineBytes = lineWords * wordBytes;
|
|
|
193 const unsigned max = (to-from)*lineBytes;
|
|
|
194 if (IsBadReadPtr((const void*)(address+(from*lineBytes)),max)) return false;
|
|
|
195 out += sprintf(out,"\n%s (" PTRSPEC "):",msg,address);
|
|
|
196 unsigned n;
|
|
|
197 const unsigned char * src = (const unsigned char*)(address)+(from*16);
|
|
|
198
|
|
|
199 for(n=0;n<max;n+=4)
|
|
|
200 {
|
|
|
201 if (n % lineBytes == 0)
|
|
|
202 {
|
|
|
203 out += sprintf(out,"\n" PTRSPEC ": ",(size_t)src);
|
|
|
204 }
|
|
|
205
|
|
|
206 out += sprintf(out," " STACKSPEC,*(size_t*)src);
|
|
|
207 src += wordBytes;
|
|
|
208 }
|
|
|
209 *(out++) = '\n';
|
|
|
210 *out=0;
|
|
|
211 return true;
|
|
|
212 }
|
|
|
213
|
|
|
214 static void call_stack_parse(size_t address,HANDLE hFile,char * temp,HANDLE hProcess)
|
|
|
215 {
|
|
|
216 bool inited = false;
|
|
|
217 t_size data;
|
|
|
218 t_size count_done = 0;
|
|
|
219 while(count_done<1024 && read_int(address, &data))
|
|
|
220 {
|
|
|
221 if (!IsBadCodePtr((FARPROC)data))
|
|
|
222 {
|
|
|
223 bool found = false;
|
|
|
224 {
|
|
|
225 IMAGEHLP_MODULE mod = {};
|
|
|
226 mod.SizeOfStruct = sizeof(mod);
|
|
|
227 if (SymGetModuleInfo(hProcess,data,&mod))
|
|
|
228 {
|
|
|
229 if (!inited)
|
|
|
230 {
|
|
|
231 WriteFileString(hFile,"\nStack dump analysis:\n");
|
|
|
232 inited = true;
|
|
|
233 }
|
|
|
234 sprintf(temp, "Address: " PTRSPEC " (%s+" OFFSETSPEC ")", data, mod.ModuleName, data - mod.BaseOfImage);
|
|
|
235 //sprintf(temp,"Address: %08Xh, location: \"%s\", loaded at %08Xh - %08Xh\n",data,mod.ModuleName,mod.BaseOfImage,mod.BaseOfImage+mod.ImageSize);
|
|
|
236 WriteFileString(hFile,temp);
|
|
|
237 found = true;
|
|
|
238 }
|
|
|
239 }
|
|
|
240
|
|
|
241
|
|
|
242 if (found)
|
|
|
243 {
|
|
|
244 union
|
|
|
245 {
|
|
|
246 char buffer[128];
|
|
|
247 IMAGEHLP_SYMBOL symbol;
|
|
|
248 };
|
|
|
249 memset(buffer,0,sizeof(buffer));
|
|
|
250 symbol.SizeOfStruct = sizeof(symbol);
|
|
|
251 symbol.MaxNameLength = (DWORD)(buffer + sizeof(buffer) - symbol.Name);
|
|
|
252 DWORD_PTR offset = 0;
|
|
|
253 if (SymGetSymFromAddr(hProcess,data,&offset,&symbol))
|
|
|
254 {
|
|
|
255 buffer[PFC_TABSIZE(buffer)-1]=0;
|
|
|
256 if (symbol.Name[0])
|
|
|
257 {
|
|
|
258 sprintf(temp,", symbol: \"%s\" (+" OFFSETSPEC ")",symbol.Name,offset);
|
|
|
259 WriteFileString(hFile,temp);
|
|
|
260 }
|
|
|
261 }
|
|
|
262 WriteFileString(hFile, "\n");
|
|
|
263 }
|
|
|
264 }
|
|
|
265 address += sizeof(size_t);
|
|
|
266 count_done++;
|
|
|
267 }
|
|
|
268 }
|
|
|
269
|
|
|
270 static BOOL CALLBACK EnumModulesCallback(PCSTR ModuleName,ULONG_PTR BaseOfDll,PVOID UserContext)
|
|
|
271 {
|
|
|
272 IMAGEHLP_MODULE mod = {};
|
|
|
273 mod.SizeOfStruct = sizeof(mod);
|
|
|
274 if (SymGetModuleInfo(GetCurrentProcess(),BaseOfDll,&mod))
|
|
|
275 {
|
|
|
276 char temp[1024];
|
|
|
277 char temp2[PFC_TABSIZE(mod.ModuleName)+1];
|
|
|
278 strcpy(temp2,mod.ModuleName);
|
|
|
279
|
|
|
280 {
|
|
|
281 t_size ptr;
|
|
|
282 for(ptr=strlen(temp2);ptr<PFC_TABSIZE(temp2)-1;ptr++)
|
|
|
283 temp2[ptr]=' ';
|
|
|
284 temp2[ptr]=0;
|
|
|
285 }
|
|
|
286
|
|
|
287 sprintf(temp,"%s loaded at " PTRSPEC " - " PTRSPEC "\n",temp2,mod.BaseOfImage,mod.BaseOfImage+mod.ImageSize);
|
|
|
288 WriteFileString((HANDLE)UserContext,temp);
|
|
|
289 }
|
|
|
290 return TRUE;
|
|
|
291 }
|
|
|
292
|
|
|
293 static bool SalvageString(t_size rptr, char* temp) {
|
|
|
294 __try {
|
|
|
295 const char* ptr = reinterpret_cast<const char*>(rptr);
|
|
|
296 t_size walk = 0;
|
|
|
297 for (; walk < 255; ++walk) {
|
|
|
298 char c = ptr[walk];
|
|
|
299 if (c == 0) break;
|
|
|
300 if (c < 0x20) c = ' ';
|
|
|
301 temp[walk] = c;
|
|
|
302 }
|
|
|
303 temp[walk] = 0;
|
|
|
304 return true;
|
|
|
305 } __except (1) { return false; }
|
|
|
306 }
|
|
|
307
|
|
|
308 static void DumpCPPExceptionData(HANDLE hFile, const ULONG_PTR* params, char* temp) {
|
|
|
309 t_size strPtr;
|
|
|
310 if (read_int(params[1] + sizeof(void*), &strPtr)) {
|
|
|
311 if (SalvageString(strPtr, temp)) {
|
|
|
312 WriteFileString(hFile, "Message: ");
|
|
|
313 WriteFileString(hFile, temp);
|
|
|
314 WriteFileString(hFile, "\n");
|
|
|
315 }
|
|
|
316 }
|
|
|
317 }
|
|
|
318
|
|
|
319 static void writeFailureTxt(LPEXCEPTION_POINTERS param, HANDLE hFile, DWORD lastError) {
|
|
|
320 char temp[2048];
|
|
|
321 {
|
|
|
322 t_size address = (t_size)param->ExceptionRecord->ExceptionAddress;
|
|
|
323 sprintf(temp, "Illegal operation:\nCode: %08Xh, flags: %08Xh, address: " PTRSPEC "\n", param->ExceptionRecord->ExceptionCode, param->ExceptionRecord->ExceptionFlags, address);
|
|
|
324 WriteFileString(hFile, temp);
|
|
|
325
|
|
|
326 if (param->ExceptionRecord->ExceptionCode == EXCEPTION_BUG_CHECK) {
|
|
|
327 WriteFileString(hFile, "Bug check\n");
|
|
|
328 } else if (param->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && param->ExceptionRecord->NumberParameters >= 2) {
|
|
|
329 sprintf(temp, "Access violation, operation: %s, address: " PTRSPEC "\n", param->ExceptionRecord->ExceptionInformation[0] ? "write" : "read", param->ExceptionRecord->ExceptionInformation[1]);
|
|
|
330 WriteFileString(hFile, temp);
|
|
|
331 } else if (param->ExceptionRecord->NumberParameters > 0) {
|
|
|
332 WriteFileString(hFile, "Additional parameters:");
|
|
|
333 for (DWORD walk = 0; walk < param->ExceptionRecord->NumberParameters; ++walk) {
|
|
|
334 sprintf(temp, " " PTRSPEC, param->ExceptionRecord->ExceptionInformation[walk]);
|
|
|
335 WriteFileString(hFile, temp);
|
|
|
336 }
|
|
|
337 WriteFileString(hFile, "\n");
|
|
|
338 }
|
|
|
339
|
|
|
340 if (param->ExceptionRecord->ExceptionCode == 0xE06D7363 && param->ExceptionRecord->NumberParameters >= 3) { //C++ exception
|
|
|
341 DumpCPPExceptionData(hFile, param->ExceptionRecord->ExceptionInformation, temp);
|
|
|
342 }
|
|
|
343
|
|
|
344 if (lastError) {
|
|
|
345 sprintf(temp, "Last win32 error: %u\n", lastError);
|
|
|
346 WriteFileString(hFile, temp);
|
|
|
347 }
|
|
|
348
|
|
|
349 if (g_thread_call_stack[0] != 0) {
|
|
|
350 WriteFileString(hFile, "\nCall path:\n");
|
|
|
351 WriteFileString(hFile, g_thread_call_stack);
|
|
|
352 WriteFileString(hFile, "\n");
|
|
|
353 } else {
|
|
|
354 WriteFileString(hFile, "\nCall path not available.\n");
|
|
|
355 }
|
|
|
356
|
|
|
357
|
|
|
358 if (hexdump8(temp, address, "Code bytes", -4, +4)) WriteFileString(hFile, temp);
|
|
|
359 #ifdef _M_IX86
|
|
|
360 if (hexdump_stack(temp, param->ContextRecord->Esp, "Stack", -2, +18)) WriteFileString(hFile, temp);
|
|
|
361 sprintf(temp, "\nRegisters:\nEAX: %08X, EBX: %08X, ECX: %08X, EDX: %08X\nESI: %08X, EDI: %08X, EBP: %08X, ESP: %08X\n", param->ContextRecord->Eax, param->ContextRecord->Ebx, param->ContextRecord->Ecx, param->ContextRecord->Edx, param->ContextRecord->Esi, param->ContextRecord->Edi, param->ContextRecord->Ebp, param->ContextRecord->Esp);
|
|
|
362 WriteFileString(hFile, temp);
|
|
|
363 #endif
|
|
|
364
|
|
|
365 #ifdef _M_X64
|
|
|
366 if (hexdump_stack(temp, param->ContextRecord->Rsp, "Stack", -2, +18)) WriteFileString(hFile, temp);
|
|
|
367 sprintf(temp, "\nRegisters:\nRAX: %016llX, RBX: %016llX, RCX: %016llX, RDX: %016llX\nRSI: %016llX, RDI: %016llX, RBP: %016llX, RSP: %016llX\n", param->ContextRecord->Rax, param->ContextRecord->Rbx, param->ContextRecord->Rcx, param->ContextRecord->Rdx, param->ContextRecord->Rsi, param->ContextRecord->Rdi, param->ContextRecord->Rbp, param->ContextRecord->Rsp);
|
|
|
368 WriteFileString(hFile, temp);
|
|
|
369 #endif
|
|
|
370
|
|
|
371 #ifdef _M_ARM64
|
|
|
372 if (hexdump_stack(temp, param->ContextRecord->Sp, "Stack", -2, +18)) WriteFileString(hFile, temp);
|
|
|
373 #endif
|
|
|
374
|
|
|
375 WriteFileString(hFile, "\nTimestamp:\n");
|
|
|
376 WriteFileString(hFile, queryDebugTimer() );
|
|
|
377 WriteFileString(hFile, "ms\n");
|
|
|
378
|
|
|
379 {
|
|
|
380 const HANDLE hProcess = GetCurrentProcess();
|
|
|
381 if (SymInitialize(hProcess, NULL, TRUE))
|
|
|
382 {
|
|
|
383 {
|
|
|
384 IMAGEHLP_MODULE mod = {};
|
|
|
385 mod.SizeOfStruct = sizeof(mod);
|
|
|
386 if (!IsBadCodePtr((FARPROC)address) && SymGetModuleInfo(hProcess, address, &mod))
|
|
|
387 {
|
|
|
388 sprintf(temp, "\nCrash location:\nModule: %s\nOffset: " OFFSETSPEC "\n", mod.ModuleName, address - mod.BaseOfImage);
|
|
|
389 WriteFileString(hFile, temp);
|
|
|
390 } else
|
|
|
391 {
|
|
|
392 sprintf(temp, "\nUnable to identify crash location!\n");
|
|
|
393 WriteFileString(hFile, temp);
|
|
|
394 }
|
|
|
395 }
|
|
|
396
|
|
|
397 {
|
|
|
398 union
|
|
|
399 {
|
|
|
400 char buffer[128];
|
|
|
401 IMAGEHLP_SYMBOL symbol;
|
|
|
402 };
|
|
|
403 memset(buffer, 0, sizeof(buffer));
|
|
|
404 symbol.SizeOfStruct = sizeof(symbol);
|
|
|
405 symbol.MaxNameLength = (DWORD)(buffer + sizeof(buffer) - symbol.Name);
|
|
|
406 DWORD_PTR offset = 0;
|
|
|
407 if (SymGetSymFromAddr(hProcess, address, &offset, &symbol))
|
|
|
408 {
|
|
|
409 buffer[PFC_TABSIZE(buffer) - 1] = 0;
|
|
|
410 if (symbol.Name[0])
|
|
|
411 {
|
|
|
412 sprintf(temp, "Symbol: \"%s\" (+" OFFSETSPEC ")\n", symbol.Name, offset);
|
|
|
413 WriteFileString(hFile, temp);
|
|
|
414 }
|
|
|
415 }
|
|
|
416 }
|
|
|
417
|
|
|
418 WriteFileString(hFile, "\nLoaded modules:\n");
|
|
|
419 SymEnumerateModules(hProcess, EnumModulesCallback, hFile);
|
|
|
420
|
|
|
421 #ifdef _M_IX86
|
|
|
422 call_stack_parse(param->ContextRecord->Esp, hFile, temp, hProcess);
|
|
|
423 #endif
|
|
|
424
|
|
|
425 #ifdef _M_X64
|
|
|
426 call_stack_parse(param->ContextRecord->Rsp, hFile, temp, hProcess);
|
|
|
427 #endif
|
|
|
428
|
|
|
429 SymCleanup(hProcess);
|
|
|
430 } else {
|
|
|
431 WriteFileString(hFile, "\nFailed to get module/symbol info.\n");
|
|
|
432 }
|
|
|
433 }
|
|
|
434
|
|
|
435 WriteFileString(hFile, "\nEnvironment:\n");
|
|
|
436 WriteFileString(hFile, version_string);
|
|
|
437
|
|
|
438 WriteFileString(hFile, "\n");
|
|
|
439
|
|
|
440 if (!g_components.is_empty()) {
|
|
|
441 WriteFileString(hFile, "\nComponents:\n");
|
|
|
442 WriteFileString(hFile, g_components);
|
|
|
443 }
|
|
|
444
|
|
|
445 {
|
|
|
446 insync(g_lastEventsSync);
|
|
|
447 if (g_lastEvents.get_count() > 0) {
|
|
|
448 WriteFileString(hFile, "\nRecent events:\n");
|
|
|
449 for( auto & walk : g_lastEvents) WriteEvent(hFile, walk);
|
|
|
450 }
|
|
|
451 }
|
|
|
452 }
|
|
|
453 }
|
|
|
454
|
|
|
455 static bool GrabOSVersion(char * out) {
|
|
|
456 OSVERSIONINFO ver = {}; ver.dwOSVersionInfoSize = sizeof(ver);
|
|
|
457 if (!GetVersionEx(&ver)) return false;
|
|
|
458 *out = 0;
|
|
|
459 char temp[16];
|
|
|
460 strcat(out,"Windows ");
|
|
|
461 _itoa(ver.dwMajorVersion,temp,10); strcat(out,temp);
|
|
|
462 strcat(out,".");
|
|
|
463 _itoa(ver.dwMinorVersion,temp,10); strcat(out,temp);
|
|
|
464 return true;
|
|
|
465 }
|
|
|
466
|
|
|
467 static void OnLogFileWritten() {
|
|
|
468 TCHAR exePath[MAX_PATH + 1] = {};
|
|
|
469 TCHAR params[1024];
|
|
|
470 GetModuleFileName(NULL, exePath, MAX_PATH);
|
|
|
471 exePath[MAX_PATH] = 0;
|
|
|
472 //unsafe...
|
|
|
473 wsprintf(params, _T("/crashed \"%s\""), DumpPathBuffer.get_ptr());
|
|
|
474 ShellExecute(NULL, NULL, exePath, params, NULL, SW_SHOW);
|
|
|
475 }
|
|
|
476
|
|
|
477 BOOL WriteMiniDumpHelper(HANDLE, LPEXCEPTION_POINTERS); //minidump.cpp
|
|
|
478
|
|
|
479
|
|
|
480 void SHARED_EXPORT uDumpCrashInfo(LPEXCEPTION_POINTERS param)
|
|
|
481 {
|
|
|
482 if (g_didSuppress) return;
|
|
|
483 const DWORD lastError = GetLastError();
|
|
|
484 if (InterlockedIncrement(&crash_no) > 1) {Sleep(10000);return;}
|
|
|
485 HANDLE hFile = create_failure_log();
|
|
|
486 if (hFile == INVALID_HANDLE_VALUE) return;
|
|
|
487
|
|
|
488 {
|
|
|
489 _tcscpy(_tcsrchr(DumpPathBuffer.get_ptr(), '.'), _T(".dmp"));
|
|
|
490 HANDLE hDump = CreateFile(DumpPathBuffer.get_ptr(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
|
|
|
491 if (hDump != INVALID_HANDLE_VALUE) {
|
|
|
492 const BOOL written = WriteMiniDumpHelper(hDump, param);
|
|
|
493 CloseHandle(hDump);
|
|
|
494 if (!written) { //don't bother proceeding if we don't have a valid minidump
|
|
|
495 DeleteFile(DumpPathBuffer.get_ptr());
|
|
|
496 CloseHandle(hFile);
|
|
|
497 _tcscpy(_tcsrchr(DumpPathBuffer.get_ptr(), '.'), _T(".txt"));
|
|
|
498 DeleteFile(DumpPathBuffer.get_ptr());
|
|
|
499 return;
|
|
|
500 }
|
|
|
501 }
|
|
|
502 _tcscpy(_tcsrchr(DumpPathBuffer.get_ptr(), '.'), _T(".txt"));
|
|
|
503 }
|
|
|
504
|
|
|
505 writeFailureTxt(param, hFile, lastError);
|
|
|
506
|
|
|
507 CloseHandle(hFile);
|
|
|
508
|
|
|
509 OnLogFileWritten();
|
|
|
510 }
|
|
|
511
|
|
|
512 //No longer used.
|
|
|
513 size_t SHARED_EXPORT uPrintCrashInfo(LPEXCEPTION_POINTERS param,const char * extra_info,char * outbase) {
|
|
|
514 *outbase = 0;
|
|
|
515 return 0;
|
|
|
516 }
|
|
|
517
|
|
|
518
|
|
|
519 LONG SHARED_EXPORT uExceptFilterProc(LPEXCEPTION_POINTERS param) {
|
|
|
520 if (g_didSuppress) return UnhandledExceptionFilter(param);
|
|
|
521 callPanicHandlers();
|
|
|
522 if (IsDebuggerPresent()) {
|
|
|
523 return UnhandledExceptionFilter(param);
|
|
|
524 } else {
|
|
|
525 if ( param->ExceptionRecord->ExceptionCode == STATUS_STACK_OVERFLOW ) {
|
|
|
526 pfc::thread2 trd;
|
|
|
527 trd.startHere( [param] {
|
|
|
528 uDumpCrashInfo(param);
|
|
|
529 } );
|
|
|
530 trd.waitTillDone();
|
|
|
531 } else {
|
|
|
532 uDumpCrashInfo(param);
|
|
|
533 }
|
|
|
534 TerminateProcess(GetCurrentProcess(), 0);
|
|
|
535 return 0;// never reached
|
|
|
536 }
|
|
|
537 }
|
|
|
538
|
|
|
539 void SHARED_EXPORT uPrintCrashInfo_Init(const char * name)//called only by exe on startup
|
|
|
540 {
|
|
|
541 version_string = pfc::format( "App: ", name, "\nArch: ", pfc::cpuArch());
|
|
|
542
|
|
|
543 SetUnhandledExceptionFilter(uExceptFilterProc);
|
|
|
544 }
|
|
|
545 void SHARED_EXPORT uPrintCrashInfo_Suppress() {
|
|
|
546 g_didSuppress = true;
|
|
|
547 }
|
|
|
548
|
|
|
549 void SHARED_EXPORT uPrintCrashInfo_AddEnvironmentInfo(const char * p_info) {
|
|
|
550 version_string << "\n" << p_info;
|
|
|
551 }
|
|
|
552
|
|
|
553 void SHARED_EXPORT uPrintCrashInfo_SetComponentList(const char * p_info) {
|
|
|
554 g_components = p_info;
|
|
|
555 }
|
|
|
556
|
|
|
557 void SHARED_EXPORT uPrintCrashInfo_SetDumpPath(const char * p_path) {
|
|
|
558 pfc::stringcvt::string_os_from_utf8 temp(p_path);
|
|
|
559 DumpPathBuffer.set_size(temp.length() + 256);
|
|
|
560 _tcscpy(DumpPathBuffer.get_ptr(), temp.get_ptr());
|
|
|
561 }
|
|
|
562
|
|
|
563 static HANDLE hLogFile = INVALID_HANDLE_VALUE;
|
|
|
564
|
|
|
565 static void logEvent(const char* message) {
|
|
|
566 if ( hLogFile != INVALID_HANDLE_VALUE ) {
|
|
|
567 DWORD wrote = 0;
|
|
|
568 WriteFile(hLogFile, message, (DWORD) strlen(message), &wrote, NULL );
|
|
|
569 WriteFile(hLogFile, "\r\n", 2, &wrote, NULL);
|
|
|
570 }
|
|
|
571 }
|
|
|
572
|
|
|
573 void SHARED_EXPORT uPrintCrashInfo_StartLogging(const char * path) {
|
|
|
574 insync(g_lastEventsSync);
|
|
|
575 PFC_ASSERT(hLogFile == INVALID_HANDLE_VALUE);
|
|
|
576 hLogFile = CreateFile( pfc::stringcvt::string_wide_from_utf8(path), GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);
|
|
|
577 PFC_ASSERT(hLogFile != INVALID_HANDLE_VALUE);
|
|
|
578
|
|
|
579 for( auto & walk : g_lastEvents ) {
|
|
|
580 logEvent( walk.c_str() );
|
|
|
581 }
|
|
|
582 }
|
|
|
583
|
|
|
584 void SHARED_EXPORT uPrintCrashInfo_OnEvent(const char * message, t_size length) {
|
|
|
585
|
|
|
586 pfc::string8 msg = pfc::format("[", queryDebugTimer(), "ms] ");
|
|
|
587 msg.add_string( message, length );
|
|
|
588 uOutputDebugString(msg + "\n");
|
|
|
589
|
|
|
590 insync(g_lastEventsSync);
|
|
|
591 logEvent(msg);
|
|
|
592 while(g_lastEvents.get_count() >= KLastEventCount) g_lastEvents.remove(g_lastEvents.first());
|
|
|
593 g_lastEvents.insert_last( std::move(msg) );
|
|
|
594 }
|
|
|
595
|
|
|
596 static void callstack_add(const char * param)
|
|
|
597 {
|
|
|
598 enum { MAX = PFC_TABSIZE(g_thread_call_stack) - 1} ;
|
|
|
599 t_size len = strlen(param);
|
|
|
600 if (g_thread_call_stack_length + len > MAX) len = MAX - g_thread_call_stack_length;
|
|
|
601 if (len>0)
|
|
|
602 {
|
|
|
603 memcpy(g_thread_call_stack+g_thread_call_stack_length,param,len);
|
|
|
604 g_thread_call_stack_length += len;
|
|
|
605 g_thread_call_stack[g_thread_call_stack_length]=0;
|
|
|
606 }
|
|
|
607 }
|
|
|
608
|
|
|
609 uCallStackTracker::uCallStackTracker(const char * name)
|
|
|
610 {
|
|
|
611 param = g_thread_call_stack_length;
|
|
|
612 if (g_thread_call_stack_length>0) callstack_add("=>");
|
|
|
613 callstack_add(name);
|
|
|
614 }
|
|
|
615
|
|
|
616 uCallStackTracker::~uCallStackTracker()
|
|
|
617 {
|
|
|
618 g_thread_call_stack_length = param;
|
|
|
619 g_thread_call_stack[param]=0;
|
|
|
620
|
|
|
621 }
|
|
|
622
|
|
|
623 extern "C" {LPCSTR SHARED_EXPORT uGetCallStackPath() {return g_thread_call_stack;} }
|
|
|
624
|
|
|
625 #ifdef _DEBUG
|
|
|
626 extern "C" {
|
|
|
627 void SHARED_EXPORT fb2kDebugSelfTest() {
|
|
|
628 auto ptr = SetUnhandledExceptionFilter(NULL);
|
|
|
629 PFC_ASSERT( ptr == uExceptFilterProc );
|
|
|
630 SetUnhandledExceptionFilter(ptr);
|
|
|
631 }
|
|
|
632 }
|
|
|
633 #endif
|
|
|
634
|
|
|
635 #else
|
|
|
636
|
|
|
637 void SHARED_EXPORT uDumpCrashInfo(LPEXCEPTION_POINTERS param) {}
|
|
|
638 void SHARED_EXPORT uPrintCrashInfo_OnEvent(const char * message, t_size length) {}
|
|
|
639 LONG SHARED_EXPORT uExceptFilterProc(LPEXCEPTION_POINTERS param) {
|
|
|
640 return UnhandledExceptionFilter(param);
|
|
|
641 }
|
|
|
642 uCallStackTracker::uCallStackTracker(const char * name) {}
|
|
|
643 uCallStackTracker::~uCallStackTracker() {}
|
|
|
644 extern "C" {
|
|
|
645 LPCSTR SHARED_EXPORT uGetCallStackPath() { return ""; }
|
|
|
646 void SHARED_EXPORT fb2kDebugSelfTest() {}
|
|
|
647 }
|
|
|
648 #endif
|
|
|
649
|
|
|
650 PFC_NORETURN void SHARED_EXPORT uBugCheck() {
|
|
|
651 fb2k_instacrash_scope(RaiseException(EXCEPTION_BUG_CHECK, EXCEPTION_NONCONTINUABLE, 0, NULL); );
|
|
|
652 }
|