comparison foosdk/sdk/foobar2000/shared/crash_info.cpp @ 1:20d02a178406 default tip

*: check in everything else yay
author Paper <paper@tflc.us>
date Mon, 05 Jan 2026 02:15:46 -0500
parents
children
comparison
equal deleted inserted replaced
0:e9bb126753e7 1:20d02a178406
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 }