Mercurial > foo_out_sdl
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 } |
