CrashCatcher.cpp 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237
  1. #include "CrashCatcher.h"
  2. #include "../util/Dictionary.h"
  3. #include <commdlg.h>
  4. #include <time.h>
  5. #include <shellapi.h>
  6. USING_NS_BF;
  7. #pragma comment(lib, "version.lib")
  8. #pragma comment(lib, "comdlg32.lib")
  9. #pragma warning(disable:4091)
  10. #pragma warning(disable:4996)
  11. #include <imagehlp.h>
  12. #ifdef BF64
  13. typedef BOOL(__stdcall * SYMINITIALIZEPROC)(HANDLE, LPSTR, BOOL);
  14. typedef DWORD(__stdcall *SYMSETOPTIONSPROC)(DWORD);
  15. typedef BOOL(__stdcall *SYMCLEANUPPROC)(HANDLE);
  16. typedef LPCSTR(__stdcall *UNDECORATESYMBOLNAMEPROC)(LPCSTR, LPSTR, DWORD, DWORD);
  17. typedef BOOL(__stdcall * STACKWALKPROC)
  18. (DWORD, HANDLE, HANDLE, LPSTACKFRAME, LPVOID,
  19. PREAD_PROCESS_MEMORY_ROUTINE, PFUNCTION_TABLE_ACCESS_ROUTINE,
  20. PGET_MODULE_BASE_ROUTINE, PTRANSLATE_ADDRESS_ROUTINE);
  21. typedef LPVOID(__stdcall *SYMFUNCTIONTABLEACCESSPROC)(HANDLE, DWORD64);
  22. typedef DWORD64 (__stdcall *SYMGETMODULEBASEPROC)(HANDLE, DWORD64);
  23. typedef BOOL(__stdcall *SYMGETSYMFROMADDRPROC)(HANDLE, DWORD64, PDWORD64, PIMAGEHLP_SYMBOL64);
  24. typedef BOOL(__stdcall *SYMGETLINEFROMADDR)(HANDLE hProcess, DWORD64 qwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINE64 Line64);
  25. #else
  26. typedef BOOL(__stdcall * SYMINITIALIZEPROC)(HANDLE, LPSTR, BOOL);
  27. typedef DWORD(__stdcall *SYMSETOPTIONSPROC)(DWORD);
  28. typedef BOOL(__stdcall *SYMCLEANUPPROC)(HANDLE);
  29. typedef LPCSTR(__stdcall *UNDECORATESYMBOLNAMEPROC)(LPCSTR, LPSTR, DWORD, DWORD);
  30. typedef BOOL(__stdcall * STACKWALKPROC)
  31. (DWORD, HANDLE, HANDLE, LPSTACKFRAME, LPVOID,
  32. PREAD_PROCESS_MEMORY_ROUTINE, PFUNCTION_TABLE_ACCESS_ROUTINE,
  33. PGET_MODULE_BASE_ROUTINE, PTRANSLATE_ADDRESS_ROUTINE);
  34. typedef LPVOID(__stdcall *SYMFUNCTIONTABLEACCESSPROC)(HANDLE, DWORD);
  35. typedef DWORD(__stdcall *SYMGETMODULEBASEPROC)(HANDLE, DWORD);
  36. typedef BOOL(__stdcall *SYMGETSYMFROMADDRPROC)(HANDLE, DWORD, PDWORD, PIMAGEHLP_SYMBOL);
  37. typedef BOOL(__stdcall *SYMGETLINEFROMADDR)(HANDLE hProcess, DWORD qwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINE Line64);
  38. #endif
  39. static HMODULE gImageHelpLib = NULL;
  40. static SYMINITIALIZEPROC gSymInitialize = NULL;
  41. static SYMSETOPTIONSPROC gSymSetOptions = NULL;
  42. static UNDECORATESYMBOLNAMEPROC gUnDecorateSymbolName = NULL;
  43. static SYMCLEANUPPROC gSymCleanup = NULL;
  44. static STACKWALKPROC gStackWalk = NULL;
  45. static SYMFUNCTIONTABLEACCESSPROC gSymFunctionTableAccess = NULL;
  46. static SYMGETMODULEBASEPROC gSymGetModuleBase = NULL;
  47. static SYMGETSYMFROMADDRPROC gSymGetSymFromAddr = NULL;
  48. static SYMGETLINEFROMADDR gSymGetLineFromAddr = NULL;
  49. static bool CreateMiniDump(EXCEPTION_POINTERS* pep, const StringImpl& filePath);
  50. static bool LoadImageHelp()
  51. {
  52. gImageHelpLib = LoadLibraryA("IMAGEHLP.DLL");
  53. if (!gImageHelpLib)
  54. return false;
  55. gSymInitialize = (SYMINITIALIZEPROC)GetProcAddress(gImageHelpLib, "SymInitialize");
  56. if (!gSymInitialize)
  57. return false;
  58. gSymSetOptions = (SYMSETOPTIONSPROC)GetProcAddress(gImageHelpLib, "SymSetOptions");
  59. if (!gSymSetOptions)
  60. return false;
  61. gSymCleanup = (SYMCLEANUPPROC)GetProcAddress(gImageHelpLib, "SymCleanup");
  62. if (!gSymCleanup)
  63. return false;
  64. gUnDecorateSymbolName = (UNDECORATESYMBOLNAMEPROC)GetProcAddress(gImageHelpLib, "UnDecorateSymbolName");
  65. if (!gUnDecorateSymbolName)
  66. return false;
  67. gStackWalk = (STACKWALKPROC)GetProcAddress(gImageHelpLib, "StackWalk");
  68. if (!gStackWalk)
  69. return false;
  70. gSymFunctionTableAccess = (SYMFUNCTIONTABLEACCESSPROC)GetProcAddress(gImageHelpLib, "SymFunctionTableAccess");
  71. if (!gSymFunctionTableAccess)
  72. return false;
  73. gSymGetModuleBase = (SYMGETMODULEBASEPROC)GetProcAddress(gImageHelpLib, "SymGetModuleBase");
  74. if (!gSymGetModuleBase)
  75. return false;
  76. gSymGetSymFromAddr = (SYMGETSYMFROMADDRPROC)GetProcAddress(gImageHelpLib, "SymGetSymFromAddr");
  77. if (!gSymGetSymFromAddr)
  78. return false;
  79. gSymGetLineFromAddr = (SYMGETLINEFROMADDR)GetProcAddress(gImageHelpLib, "SymGetLineFromAddr64");
  80. if (!gSymGetLineFromAddr)
  81. return false;
  82. gSymSetOptions(SYMOPT_DEFERRED_LOADS);
  83. // Get image filename of the main executable
  84. char filepath[MAX_PATH], *lastdir, *pPath;
  85. DWORD filepathlen = GetModuleFileNameA(NULL, filepath, sizeof(filepath));
  86. lastdir = strrchr(filepath, '/');
  87. if (lastdir == NULL) lastdir = strrchr(filepath, '\\');
  88. if (lastdir != NULL) lastdir[0] = '\0';
  89. // Initialize the symbol table routines, supplying a pointer to the path
  90. pPath = filepath;
  91. if (filepath[0] == 0) pPath = NULL;
  92. if (!gSymInitialize(GetCurrentProcess(), pPath, TRUE))
  93. return false;
  94. return true;
  95. }
  96. struct
  97. {
  98. DWORD dwExceptionCode;
  99. char *szMessage;
  100. } gMsgTable[] = {
  101. { STATUS_SEGMENT_NOTIFICATION, "Segment Notification" },
  102. { STATUS_BREAKPOINT, "Breakpoint" },
  103. { STATUS_SINGLE_STEP, "Single step" },
  104. { STATUS_WAIT_0, "Wait 0" },
  105. { STATUS_ABANDONED_WAIT_0, "Abandoned Wait 0" },
  106. { STATUS_USER_APC, "User APC" },
  107. { STATUS_TIMEOUT, "Timeout" },
  108. { STATUS_PENDING, "Pending" },
  109. { STATUS_GUARD_PAGE_VIOLATION, "Guard Page Violation" },
  110. { STATUS_DATATYPE_MISALIGNMENT, "Data Type Misalignment" },
  111. { STATUS_ACCESS_VIOLATION, "Access Violation" },
  112. { STATUS_IN_PAGE_ERROR, "In Page Error" },
  113. { STATUS_NO_MEMORY, "No Memory" },
  114. { STATUS_ILLEGAL_INSTRUCTION, "Illegal Instruction" },
  115. { STATUS_NONCONTINUABLE_EXCEPTION, "Noncontinuable Exception" },
  116. { STATUS_INVALID_DISPOSITION, "Invalid Disposition" },
  117. { STATUS_ARRAY_BOUNDS_EXCEEDED, "Array Bounds Exceeded" },
  118. { STATUS_FLOAT_DENORMAL_OPERAND, "Float Denormal Operand" },
  119. { STATUS_FLOAT_DIVIDE_BY_ZERO, "Divide by Zero" },
  120. { STATUS_FLOAT_INEXACT_RESULT, "Float Inexact Result" },
  121. { STATUS_FLOAT_INVALID_OPERATION, "Float Invalid Operation" },
  122. { STATUS_FLOAT_OVERFLOW, "Float Overflow" },
  123. { STATUS_FLOAT_STACK_CHECK, "Float Stack Check" },
  124. { STATUS_FLOAT_UNDERFLOW, "Float Underflow" },
  125. { STATUS_INTEGER_DIVIDE_BY_ZERO, "Integer Divide by Zero" },
  126. { STATUS_INTEGER_OVERFLOW, "Integer Overflow" },
  127. { STATUS_PRIVILEGED_INSTRUCTION, "Privileged Instruction" },
  128. { STATUS_STACK_OVERFLOW, "Stack Overflow" },
  129. { STATUS_CONTROL_C_EXIT, "Ctrl+C Exit" },
  130. { 0xFFFFFFFF, "" }
  131. };
  132. static HFONT gDialogFont;
  133. static HFONT gBoldFont;
  134. static String gErrorTitle;
  135. static String gErrorText;
  136. static HWND gDebugButtonWindow = NULL;
  137. static HWND gYesButtonWindow = NULL;
  138. static HWND gNoButtonWindow = NULL;
  139. static bool gExiting = false;
  140. static LRESULT CALLBACK SEHWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  141. {
  142. switch (uMsg)
  143. {
  144. case WM_COMMAND:
  145. {
  146. HWND hwndCtl = (HWND)lParam;
  147. if (hwndCtl == gYesButtonWindow)
  148. {
  149. WCHAR fileName[MAX_PATH];
  150. fileName[0] = 0;
  151. OPENFILENAMEW openFileName = { 0 };
  152. openFileName.hInstance = ::GetModuleHandle(NULL);
  153. openFileName.hwndOwner = hWnd;
  154. openFileName.lStructSize = sizeof(OPENFILENAMEW);
  155. openFileName.lpstrDefExt = L".dmp";
  156. openFileName.lpstrFilter = L"Crash Dump (*.dmp)\0*.dmp\0All files (*.*)\0*.*\0\0";
  157. openFileName.lpstrFile = fileName;
  158. openFileName.nMaxFile = MAX_PATH;
  159. openFileName.Flags = OFN_EXPLORER | OFN_ENABLESIZING;
  160. openFileName.lpstrTitle = L"Save Crash Dump";
  161. if (::GetSaveFileNameW(&openFileName))
  162. {
  163. CreateMiniDump(CrashCatcher::Get()->mExceptionPointers, UTF8Encode(fileName));
  164. }
  165. }
  166. else if (hwndCtl == gNoButtonWindow)
  167. {
  168. if (!CrashCatcher::Get()->mRelaunchCmd.IsEmpty())
  169. {
  170. SHELLEXECUTEINFOW shellExecuteInfo = { 0 };
  171. shellExecuteInfo.cbSize = sizeof(SHELLEXECUTEINFOW);
  172. shellExecuteInfo.nShow = SW_SHOWNORMAL;
  173. String cmd = CrashCatcher::Get()->mRelaunchCmd;
  174. String file;
  175. bool nameQuoted = cmd[0] == '\"';
  176. int i;
  177. for (i = (nameQuoted ? 1 : 0); cmd[i] != 0; i++)
  178. {
  179. wchar_t c = cmd[i];
  180. if (((nameQuoted) && (c == '"')) ||
  181. ((!nameQuoted) && (c == ' ')))
  182. {
  183. i++;
  184. break;
  185. }
  186. file += cmd[i];
  187. }
  188. const char* useParamsPtr = cmd.c_str();
  189. useParamsPtr += i;
  190. while (*useParamsPtr == L' ')
  191. useParamsPtr++;
  192. auto fileW = UTF8Decode(file);
  193. shellExecuteInfo.lpFile = fileW.c_str();
  194. auto paramsW = UTF8Decode(useParamsPtr);
  195. shellExecuteInfo.lpParameters = paramsW.c_str();
  196. BOOL success = ::ShellExecuteExW(&shellExecuteInfo);
  197. }
  198. CrashCatcher::Get()->mCloseRequested = true;
  199. gExiting = true;
  200. }
  201. else if (hwndCtl == gDebugButtonWindow)
  202. {
  203. CrashCatcher::Get()->mDebugError = true;
  204. gExiting = true;
  205. }
  206. }
  207. break;
  208. case WM_CLOSE:
  209. CrashCatcher::Get()->mCloseRequested = true;
  210. gExiting = true;
  211. return 0;
  212. }
  213. return DefWindowProc(hWnd, uMsg, wParam, lParam);
  214. }
  215. static void ShowErrorDialog(const StringImpl& errorTitle, const StringImpl& errorText, const StringImpl& relaunchCmd)
  216. {
  217. bool gUseDefaultFonts;
  218. HINSTANCE gHInstance = ::GetModuleHandle(NULL);
  219. OSVERSIONINFO aVersionInfo;
  220. aVersionInfo.dwOSVersionInfoSize = sizeof(aVersionInfo);
  221. GetVersionEx(&aVersionInfo);
  222. // Setting fonts on 98 causes weirdo crash things in GDI upon the second crash.
  223. // That's no good.
  224. gUseDefaultFonts = aVersionInfo.dwPlatformId != VER_PLATFORM_WIN32_NT;
  225. int aHeight = -MulDiv(8, 96, 72);
  226. gDialogFont = ::CreateFontA(aHeight, 0, 0, 0, FW_NORMAL, FALSE, FALSE,
  227. false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
  228. DEFAULT_PITCH | FF_DONTCARE, "Tahoma");
  229. aHeight = -MulDiv(10, 96, 72);
  230. gBoldFont = ::CreateFontA(aHeight, 0, 0, 0, FW_BOLD, FALSE, FALSE,
  231. false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
  232. DEFAULT_PITCH | FF_DONTCARE, "Tahoma");
  233. ::SetCursor(::LoadCursor(NULL, IDC_ARROW));
  234. gErrorTitle = errorTitle;
  235. gErrorText = errorText;
  236. WNDCLASSW wc;
  237. wc.style = 0;
  238. wc.cbClsExtra = 0;
  239. wc.cbWndExtra = 0;
  240. wc.hbrBackground = ::GetSysColorBrush(COLOR_BTNFACE);
  241. wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
  242. wc.hIcon = ::LoadIcon(NULL, IDI_ERROR);
  243. wc.hInstance = gHInstance;
  244. wc.lpfnWndProc = SEHWindowProc;
  245. wc.lpszClassName = L"SEHWindow";
  246. wc.lpszMenuName = NULL;
  247. RegisterClassW(&wc);
  248. RECT aRect;
  249. aRect.left = 0;
  250. aRect.top = 0;
  251. aRect.right = 500;
  252. aRect.bottom = 400;
  253. DWORD aWindowStyle = WS_CLIPCHILDREN | WS_POPUP | WS_BORDER | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
  254. RECT windowRect = aRect;
  255. BOOL worked = AdjustWindowRect(&windowRect, aWindowStyle, FALSE);
  256. HWND aHWnd = ::CreateWindowExW(0, L"SEHWindow", L"Fatal Error!",
  257. aWindowStyle,
  258. 64, 64,
  259. windowRect.right - windowRect.left,
  260. windowRect.bottom - windowRect.top,
  261. NULL,
  262. NULL,
  263. gHInstance,
  264. 0);
  265. int textHeight = 30;
  266. HWND aLabelWindow = ::CreateWindowW(L"EDIT",
  267. L"An unexpected error has occured!",
  268. WS_VISIBLE | WS_CHILD | ES_MULTILINE | ES_READONLY,
  269. 8, 8,
  270. aRect.right - 8 - 8,
  271. textHeight,
  272. aHWnd,
  273. NULL,
  274. gHInstance,
  275. 0);
  276. int aFontHeight = -MulDiv(9, 96, 72);
  277. HFONT aBoldArialFont = CreateFontA(aFontHeight, 0, 0, 0, FW_BOLD, 0, 0,
  278. false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
  279. DEFAULT_PITCH | FF_DONTCARE, "Arial");
  280. if (!gUseDefaultFonts)
  281. SendMessage(aLabelWindow, WM_SETFONT, (WPARAM)aBoldArialFont, 0);
  282. HWND anEditWindow = CreateWindowA("EDIT", errorText.c_str(),
  283. WS_VISIBLE | WS_CHILD | ES_MULTILINE | WS_BORDER | WS_HSCROLL | WS_VSCROLL | ES_READONLY,
  284. 8, textHeight + 8,
  285. aRect.right - 8 - 8,
  286. aRect.bottom - textHeight - 24 - 8 - 8 - 8,
  287. aHWnd,
  288. NULL,
  289. gHInstance,
  290. 0);
  291. aFontHeight = -MulDiv(8, 96, 72);
  292. HFONT aCourierNewFont = CreateFontA(aFontHeight, 0, 0, 0, FW_NORMAL, 0, 0,
  293. false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
  294. DEFAULT_PITCH | FF_DONTCARE, "Courier New");
  295. if (!gUseDefaultFonts)
  296. SendMessage(anEditWindow, WM_SETFONT, (WPARAM)aCourierNewFont, 0);
  297. aWindowStyle = WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON | BS_PUSHBUTTON;
  298. //if (mApp == NULL)
  299. //aWindowStyle |= WS_DISABLED;
  300. //#ifdef _DEBUG
  301. bool doDebugButton = true;
  302. // #else
  303. // bool doDebugButton = false;
  304. // #endif
  305. //bool canSubmit = mAllowSubmit && !mSubmitHost.empty();
  306. bool canSubmit = true;
  307. int aNumButtons = 1 + (doDebugButton ? 1 : 0) + (canSubmit ? 1 : 0);
  308. int aButtonWidth = (aRect.right - 8 - 8 - (aNumButtons - 1) * 8) / aNumButtons;
  309. int aCurX = 8;
  310. if (canSubmit)
  311. {
  312. gYesButtonWindow = CreateWindowA("BUTTON", "Save Crash Dump...",
  313. aWindowStyle,
  314. aCurX, aRect.bottom - 24 - 8,
  315. aButtonWidth,
  316. 24,
  317. aHWnd,
  318. NULL,
  319. gHInstance,
  320. 0);
  321. if (!gUseDefaultFonts)
  322. SendMessage(gYesButtonWindow, WM_SETFONT, (WPARAM)aBoldArialFont, 0);
  323. aCurX += aButtonWidth + 8;
  324. }
  325. if (doDebugButton)
  326. {
  327. gDebugButtonWindow = CreateWindowA("BUTTON", "Debug",
  328. aWindowStyle,
  329. aCurX, aRect.bottom - 24 - 8,
  330. aButtonWidth,
  331. 24,
  332. aHWnd,
  333. NULL,
  334. gHInstance,
  335. 0);
  336. if (!gUseDefaultFonts)
  337. SendMessage(gDebugButtonWindow, WM_SETFONT, (WPARAM)aBoldArialFont, 0);
  338. aCurX += aButtonWidth + 8;
  339. }
  340. gNoButtonWindow = CreateWindowA("BUTTON", relaunchCmd.IsEmpty() ? "Close Now" : "Relaunch",
  341. WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON | BS_PUSHBUTTON,
  342. aCurX, aRect.bottom - 24 - 8,
  343. aButtonWidth,
  344. 24,
  345. aHWnd,
  346. NULL,
  347. gHInstance,
  348. 0);
  349. if (!gUseDefaultFonts)
  350. SendMessage(gNoButtonWindow, WM_SETFONT, (WPARAM)aBoldArialFont, 0);
  351. ShowWindow(aHWnd, SW_NORMAL);
  352. MSG msg;
  353. while ((GetMessage(&msg, NULL, 0, 0) > 0) && (!gExiting))
  354. {
  355. TranslateMessage(&msg);
  356. DispatchMessage(&msg);
  357. }
  358. DestroyWindow(aHWnd);
  359. DeleteObject(gDialogFont);
  360. DeleteObject(gBoldFont);
  361. DeleteObject(aBoldArialFont);
  362. DeleteObject(aCourierNewFont);
  363. }
  364. static bool GetLogicalAddress(void* addr, char* szModule, DWORD len, uintptr& section, uintptr& offset)
  365. {
  366. MEMORY_BASIC_INFORMATION mbi;
  367. if (!VirtualQuery(addr, &mbi, sizeof(mbi)))
  368. return false;
  369. uintptr hMod = (uintptr)mbi.AllocationBase;
  370. if (hMod == NULL)
  371. {
  372. szModule[0] = 0;
  373. section = 0;
  374. offset = 0;
  375. return false;
  376. }
  377. if (!GetModuleFileNameA((HMODULE)hMod, szModule, len))
  378. return false;
  379. // Point to the DOS header in memory
  380. PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)hMod;
  381. // From the DOS header, find the NT (PE) header
  382. PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)((uint8*)hMod + pDosHdr->e_lfanew);
  383. PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNtHdr);
  384. uintptr rva = (uintptr)addr - hMod; // RVA is offset from module load address
  385. // Iterate through the section table, looking for the one that encompasses
  386. // the linear address.
  387. for (unsigned i = 0; i < pNtHdr->FileHeader.NumberOfSections; i++, pSection++)
  388. {
  389. uintptr sectionStart = pSection->VirtualAddress;
  390. uintptr sectionEnd = sectionStart + BF_MAX(pSection->SizeOfRawData, pSection->Misc.VirtualSize);
  391. // Is the address in this section???
  392. if ((rva >= sectionStart) && (rva <= sectionEnd))
  393. {
  394. // Yes, address is in the section. Calculate section and offset,
  395. // and store in the "section" & "offset" params, which were
  396. // passed by reference.
  397. section = i + 1;
  398. offset = rva - sectionStart;
  399. return true;
  400. }
  401. }
  402. return false; // Should never get here!
  403. }
  404. static BOOL CALLBACK MyMiniDumpCallback(
  405. PVOID pParam,
  406. const PMINIDUMP_CALLBACK_INPUT pInput,
  407. PMINIDUMP_CALLBACK_OUTPUT pOutput
  408. )
  409. {
  410. BOOL bRet = FALSE;
  411. // Check parameters
  412. if (pInput == 0)
  413. return FALSE;
  414. if (pOutput == 0)
  415. return FALSE;
  416. // Process the callbacks
  417. switch (pInput->CallbackType)
  418. {
  419. case IncludeModuleCallback:
  420. {
  421. // Include the module into the dump
  422. bRet = TRUE;
  423. }
  424. break;
  425. case IncludeThreadCallback:
  426. {
  427. // Include the thread into the dump
  428. bRet = TRUE;
  429. }
  430. break;
  431. case ModuleCallback:
  432. {
  433. // Does the module have ModuleReferencedByMemory flag set ?
  434. if (!(pOutput->ModuleWriteFlags & ModuleReferencedByMemory))
  435. {
  436. // No, it does not - exclude it
  437. //wprintf(L"Excluding module: %s \n", pInput->Module.FullPath);
  438. pOutput->ModuleWriteFlags &= (~ModuleWriteModule);
  439. }
  440. bRet = TRUE;
  441. }
  442. break;
  443. case ThreadCallback:
  444. {
  445. // Include all thread information into the minidump
  446. bRet = TRUE;
  447. }
  448. break;
  449. case ThreadExCallback:
  450. {
  451. // Include this information
  452. bRet = TRUE;
  453. }
  454. break;
  455. case MemoryCallback:
  456. {
  457. // We do not include any information here -> return FALSE
  458. bRet = FALSE;
  459. }
  460. break;
  461. case CancelCallback:
  462. break;
  463. }
  464. return bRet;
  465. }
  466. static bool CreateMiniDump(EXCEPTION_POINTERS* pep, const StringImpl& filePath)
  467. {
  468. // Open the file
  469. typedef BOOL(*PDUMPFN)(
  470. HANDLE hProcess,
  471. DWORD ProcessId,
  472. HANDLE hFile,
  473. MINIDUMP_TYPE DumpType,
  474. PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
  475. PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
  476. PMINIDUMP_CALLBACK_INFORMATION CallbackParam
  477. );
  478. HANDLE hFile = CreateFileW(UTF8Decode(filePath).c_str(), GENERIC_READ | GENERIC_WRITE,
  479. 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  480. HMODULE h = ::LoadLibrary(L"DbgHelp.dll");
  481. PDUMPFN pFn = (PDUMPFN)GetProcAddress(h, "MiniDumpWriteDump");
  482. if (pFn == NULL)
  483. return false;
  484. if ((hFile == NULL) || (hFile == INVALID_HANDLE_VALUE))
  485. return false;
  486. // Create the minidump
  487. MINIDUMP_EXCEPTION_INFORMATION mdei;
  488. mdei.ThreadId = GetCurrentThreadId();
  489. mdei.ExceptionPointers = pep;
  490. mdei.ClientPointers = TRUE;
  491. MINIDUMP_CALLBACK_INFORMATION mci;
  492. mci.CallbackRoutine = (MINIDUMP_CALLBACK_ROUTINE)MyMiniDumpCallback;
  493. mci.CallbackParam = 0;
  494. MINIDUMP_TYPE mdt = (MINIDUMP_TYPE)(MiniDumpWithIndirectlyReferencedMemory | MiniDumpScanMemory);
  495. CrashCatcher* crashCatcher = CrashCatcher::Get();
  496. MINIDUMP_USER_STREAM user_info_stream = {
  497. 0xBEEF00,
  498. (ULONG)crashCatcher->mCrashInfo.length(),
  499. (void*)crashCatcher->mCrashInfo.c_str()
  500. };
  501. MINIDUMP_USER_STREAM_INFORMATION user_stream_info = {
  502. 1,
  503. &user_info_stream
  504. };
  505. BOOL rv = (*pFn)(GetCurrentProcess(), GetCurrentProcessId(),
  506. hFile, mdt, (pep != 0) ? &mdei : 0, &user_stream_info, &mci);
  507. // Close the file
  508. CloseHandle(hFile);
  509. return true;
  510. }
  511. static String ImageHelpWalk(PCONTEXT theContext, int theSkipCount)
  512. {
  513. //char aBuffer[2048];
  514. String aDebugDump;
  515. STACKFRAME sf;
  516. memset(&sf, 0, sizeof(sf));
  517. // Initialize the STACKFRAME structure for the first call. This is only
  518. // necessary for Intel CPUs, and isn't mentioned in the documentation.
  519. #ifdef BF64
  520. sf.AddrPC.Offset = theContext->Rip;
  521. sf.AddrPC.Mode = AddrModeFlat;
  522. sf.AddrStack.Offset = theContext->Rsp;
  523. sf.AddrStack.Mode = AddrModeFlat;
  524. sf.AddrFrame.Offset = theContext->Rbp;
  525. sf.AddrFrame.Mode = AddrModeFlat;
  526. #else
  527. sf.AddrPC.Offset = theContext->Eip;
  528. sf.AddrPC.Mode = AddrModeFlat;
  529. sf.AddrStack.Offset = theContext->Esp;
  530. sf.AddrStack.Mode = AddrModeFlat;
  531. sf.AddrFrame.Offset = theContext->Ebp;
  532. sf.AddrFrame.Mode = AddrModeFlat;
  533. #endif
  534. int aLevelCount = 0;
  535. CONTEXT ctx = *theContext;
  536. struct ModuleInfo
  537. {
  538. };
  539. Dictionary<String, ModuleInfo> moduleInfoMap;
  540. for (;;)
  541. {
  542. #ifdef BF64
  543. DWORD machineType = IMAGE_FILE_MACHINE_AMD64;
  544. PCONTEXT ctxPtr = &ctx;
  545. #else
  546. DWORD machineType = IMAGE_FILE_MACHINE_I386;
  547. PCONTEXT ctxPtr = NULL;
  548. #endif
  549. if (!gStackWalk(machineType, GetCurrentProcess(), GetCurrentThread(),
  550. &sf, ctxPtr, NULL, gSymFunctionTableAccess, gSymGetModuleBase, 0))
  551. {
  552. //DWORD lastErr = GetLastError();
  553. //sprintf(aBuffer, "StackWalk failed (error %d)\r\n", lastErr);
  554. //aDebugDump += aBuffer;
  555. break;
  556. }
  557. if ((aLevelCount > 0) && ((sf.AddrFrame.Offset == 0) || (sf.AddrPC.Offset == 0)))
  558. break;
  559. if (theSkipCount > 0)
  560. {
  561. theSkipCount--;
  562. continue;
  563. }
  564. BYTE symbolBuffer[sizeof(IMAGEHLP_SYMBOL) + 512];
  565. PIMAGEHLP_SYMBOL pSymbol = (PIMAGEHLP_SYMBOL)symbolBuffer;
  566. pSymbol->SizeOfStruct = sizeof(symbolBuffer);
  567. pSymbol->MaxNameLength = 512;
  568. // Displacement of the input address, relative to the start of the symbol
  569. #ifdef BF64
  570. DWORD64 symDisplacement = 0;
  571. #else
  572. DWORD symDisplacement = 0;
  573. #endif
  574. HANDLE hProcess = GetCurrentProcess();
  575. char szModule[MAX_PATH];
  576. szModule[0] = 0;
  577. uintptr section = 0, offset = 0;
  578. GetLogicalAddress((PVOID)sf.AddrPC.Offset, szModule, sizeof(szModule), section, offset);
  579. bool forceFail = false;
  580. if ((gSymGetSymFromAddr(hProcess, sf.AddrPC.Offset, &symDisplacement, pSymbol)) && (!forceFail))
  581. {
  582. char aUDName[256];
  583. gUnDecorateSymbolName(pSymbol->Name, aUDName, 256,
  584. UNDNAME_NO_ALLOCATION_MODEL | UNDNAME_NO_ALLOCATION_LANGUAGE |
  585. UNDNAME_NO_MS_THISTYPE | UNDNAME_NO_ACCESS_SPECIFIERS |
  586. UNDNAME_NO_THISTYPE | UNDNAME_NO_MEMBER_TYPE |
  587. UNDNAME_NO_RETURN_UDT_MODEL | UNDNAME_NO_THROW_SIGNATURES |
  588. UNDNAME_NO_SPECIAL_SYMS);
  589. String dispName = aUDName;
  590. if (dispName.StartsWith("_bf::"))
  591. {
  592. dispName.Remove(0, 5);
  593. dispName.Replace("::", ".");
  594. }
  595. aDebugDump += StrFormat("%@ %@ %s %hs+%X\r\n",
  596. sf.AddrFrame.Offset, sf.AddrPC.Offset, GetFileName(szModule).c_str(), dispName.c_str(), symDisplacement);
  597. DWORD displacement = 0;
  598. #ifdef BF64
  599. IMAGEHLP_LINE64 lineInfo = { 0 };
  600. lineInfo.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
  601. #else
  602. IMAGEHLP_LINE lineInfo = { 0 };
  603. lineInfo.SizeOfStruct = sizeof(IMAGEHLP_LINE);
  604. #endif
  605. if (gSymGetLineFromAddr(hProcess, sf.AddrPC.Offset, &displacement, &lineInfo))
  606. {
  607. aDebugDump += StrFormat(" at %s:%d\r\n", lineInfo.FileName, lineInfo.LineNumber);
  608. }
  609. }
  610. else // No symbol found. Print out the logical address instead.
  611. {
  612. // ModuleInfo* moduleInfo = NULL;
  613. // if (moduleInfoMap.TryAdd(szModule, NULL, &moduleInfo))
  614. // {
  615. //
  616. // }
  617. aDebugDump += StrFormat("%@ %@ %04X:%@ %s\r\n", sf.AddrFrame.Offset, sf.AddrPC.Offset, section, offset, GetFileName(szModule).c_str());
  618. }
  619. aDebugDump += StrFormat(" Params: %@ %@ %@ %@\r\n", sf.Params[0], sf.Params[1], sf.Params[2], sf.Params[3]);
  620. aDebugDump += "\r\n";
  621. aLevelCount++;
  622. }
  623. return aDebugDump;
  624. }
  625. static String GetSysInfo()
  626. {
  627. return "";
  628. }
  629. static String GetVersion(const StringImpl& fileName)
  630. {
  631. String verStr = "";
  632. bool bReturn = false;
  633. DWORD dwReserved = 0;
  634. DWORD dwBufferSize = GetFileVersionInfoSizeA(fileName.c_str(), &dwReserved);
  635. struct TRANSARRAY
  636. {
  637. WORD wLanguageID;
  638. WORD wCharacterSet;
  639. };
  640. if (dwBufferSize > 0)
  641. {
  642. LPVOID pBuffer = (void*)malloc(dwBufferSize);
  643. if (pBuffer != (void*)NULL)
  644. {
  645. UINT nInfoSize = 0,
  646. nFixedLength = 0;
  647. LPSTR lpVersion = NULL;
  648. void* lpFixedPointer;
  649. TRANSARRAY* lpTransArray;
  650. GetFileVersionInfoA(fileName.c_str(),
  651. dwReserved,
  652. dwBufferSize,
  653. pBuffer);
  654. VerQueryValueA(pBuffer,
  655. "\\VarFileInfo\\Translation",
  656. &lpFixedPointer,
  657. &nFixedLength);
  658. lpTransArray = (TRANSARRAY*)lpFixedPointer;
  659. String langStr = StrFormat(
  660. "\\StringFileInfo\\%04x%04x\\FileVersion",
  661. lpTransArray[0].wLanguageID,
  662. lpTransArray[0].wCharacterSet);
  663. VerQueryValueA(pBuffer,
  664. langStr.c_str(),
  665. (void**)&lpVersion,
  666. &nInfoSize);
  667. if (nInfoSize != 0)
  668. {
  669. verStr += "File Version: ";
  670. verStr += lpVersion;
  671. verStr += "\r\n";
  672. }
  673. langStr = StrFormat(
  674. "\\StringFileInfo\\%04x%04x\\ProductVersion",
  675. lpTransArray[0].wLanguageID,
  676. lpTransArray[0].wCharacterSet);
  677. VerQueryValueA(pBuffer,
  678. langStr.c_str(),
  679. (void**)&lpVersion,
  680. &nInfoSize);
  681. if (nInfoSize != 0)
  682. {
  683. verStr += "Product Version: ";
  684. verStr += lpVersion;
  685. verStr += "\r\n";
  686. }
  687. free(pBuffer);
  688. }
  689. }
  690. return verStr;
  691. }
  692. static void DoHandleDebugEvent(LPEXCEPTION_POINTERS lpEP)
  693. {
  694. auto crashCatcher = CrashCatcher::Get();
  695. if (crashCatcher->mCrashed)
  696. return;
  697. crashCatcher->mCrashed = true;
  698. HMODULE hMod = GetModuleHandleA(NULL);
  699. PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)hMod;
  700. PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)((uint8*)hMod + pDosHdr->e_lfanew);
  701. bool isCLI = pNtHdr->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI;
  702. if (CrashCatcher::Get()->mCrashReportKind == BfpCrashReportKind_GUI)
  703. isCLI = false;
  704. else if ((CrashCatcher::Get()->mCrashReportKind == BfpCrashReportKind_Console) || (CrashCatcher::Get()->mCrashReportKind == BfpCrashReportKind_PrintOnly))
  705. isCLI = true;
  706. bool hasImageHelp = LoadImageHelp();
  707. String anErrorTitle;
  708. String aDebugDump;
  709. String& crashInfo = CrashCatcher::Get()->mCrashInfo;
  710. char aBuffer[2048];
  711. if (isCLI)
  712. aDebugDump += "**** FATAL APPLICATION ERROR ****\n";
  713. for (auto func : CrashCatcher::Get()->mCrashInfoFuncs)
  714. func();
  715. CHAR path[MAX_PATH];
  716. GetModuleFileNameA(NULL, path, MAX_PATH);
  717. crashInfo += "\nExecutable: ";
  718. crashInfo += path;
  719. crashInfo += "\r\n";
  720. crashInfo += GetVersion(path);
  721. aDebugDump.Append(crashInfo);
  722. for (int i = 0; i < (int)aDebugDump.length(); i++)
  723. {
  724. char c = aDebugDump[i];
  725. if (c == '\n')
  726. {
  727. aDebugDump.Insert(i, '\r');
  728. i++;
  729. }
  730. else if (c == '\t')
  731. {
  732. aDebugDump[i] = ' ';
  733. aDebugDump.Insert(i, ' ');
  734. }
  735. }
  736. // aDebugDump.Replace("\n", "\r\n");
  737. // aDebugDump.Replace("\t", " ");
  738. if (!aDebugDump.IsEmpty())
  739. {
  740. if (!aDebugDump.EndsWith("\n"))
  741. aDebugDump += "\r\n";
  742. aDebugDump += "\r\n";
  743. }
  744. WCHAR exeFilePathW[MAX_PATH];
  745. exeFilePathW[0] = 0;
  746. ::GetModuleFileNameW(hMod, exeFilePathW, MAX_PATH);
  747. String exeFilePath = UTF8Encode(exeFilePathW);
  748. String exeDir = GetFileDir(exeFilePath);
  749. String crashPath = exeDir + "\\CrashDumps";
  750. if (BfpDirectory_Exists(crashPath.c_str()))
  751. {
  752. crashPath += "\\" + GetFileName(exeFilePath);
  753. crashPath.RemoveToEnd((int)crashPath.length() - 4);
  754. crashPath += "_";
  755. time_t curTime = time(NULL);
  756. auto time_info = localtime(&curTime);
  757. crashPath += StrFormat("%4d%02d%02d_%02d%02d%02d",
  758. time_info->tm_year + 1900, time_info->tm_mon + 1, time_info->tm_mday,
  759. time_info->tm_hour, time_info->tm_min, time_info->tm_sec);
  760. crashPath += ".dmp";
  761. if (CreateMiniDump(lpEP, crashPath))
  762. {
  763. aDebugDump += StrFormat("Crash minidump saved as %s\n", crashPath.c_str());
  764. }
  765. }
  766. ///////////////////////////
  767. // first name the exception
  768. char *szName = NULL;
  769. for (int i = 0; gMsgTable[i].dwExceptionCode != 0xFFFFFFFF; i++)
  770. {
  771. if (gMsgTable[i].dwExceptionCode == lpEP->ExceptionRecord->ExceptionCode)
  772. {
  773. szName = gMsgTable[i].szMessage;
  774. break;
  775. }
  776. }
  777. if (szName != NULL)
  778. {
  779. aDebugDump += StrFormat("Exception: %s (code 0x%x) at address %@ in thread %X\r\n",
  780. szName, lpEP->ExceptionRecord->ExceptionCode,
  781. lpEP->ExceptionRecord->ExceptionAddress, GetCurrentThreadId());
  782. }
  783. else
  784. {
  785. aDebugDump += StrFormat("Unknown exception: (code 0x%x) at address %@ in thread %X\r\n",
  786. lpEP->ExceptionRecord->ExceptionCode,
  787. lpEP->ExceptionRecord->ExceptionAddress, GetCurrentThreadId());
  788. }
  789. ///////////////////////////////////////////////////////////
  790. // Get logical address of the module where exception occurs
  791. uintptr section, offset;
  792. GetLogicalAddress(lpEP->ExceptionRecord->ExceptionAddress, aBuffer, sizeof(aBuffer), section, offset);
  793. aDebugDump += StrFormat("Logical Address: %04X:%@\r\n", section, offset);
  794. aDebugDump += "\r\n";
  795. anErrorTitle = StrFormat("Exception at %04X:%08X", section, offset);
  796. String aWalkString;
  797. if (hasImageHelp)
  798. aWalkString = ImageHelpWalk(lpEP->ContextRecord, 0);
  799. /*if (aWalkString.length() == 0)
  800. aWalkString = IntelWalk(lpEP->ContextRecord, 0);*/
  801. aDebugDump += aWalkString;
  802. aDebugDump += "\r\n";
  803. #ifdef BF64
  804. aDebugDump += StrFormat("RAX:%@ RBX:%@ RCX:%@ RDX:%@ RSI:%@ RDI:%@\r\n",
  805. lpEP->ContextRecord->Rax, lpEP->ContextRecord->Rbx, lpEP->ContextRecord->Rcx, lpEP->ContextRecord->Rdx, lpEP->ContextRecord->Rsi, lpEP->ContextRecord->Rdi);
  806. aDebugDump += StrFormat("R8:%@ R9:%@ R10:%@ R11:%@\r\nR12:%@ R13:%@ R14:%@ R15:%@\r\n",
  807. lpEP->ContextRecord->R8, lpEP->ContextRecord->R9, lpEP->ContextRecord->R10, lpEP->ContextRecord->R11, lpEP->ContextRecord->R12, lpEP->ContextRecord->R13, lpEP->ContextRecord->R14, lpEP->ContextRecord->R15);
  808. aDebugDump += StrFormat("EIP:%@ ESP:%@ EBP:%@\r\n", lpEP->ContextRecord->Rip, lpEP->ContextRecord->Rsp, lpEP->ContextRecord->Rbp);
  809. aDebugDump += StrFormat("CS:%04X SS:%04X DS:%04X ES:%04X FS:%04X GS:%04X\r\n", lpEP->ContextRecord->SegCs, lpEP->ContextRecord->SegSs, lpEP->ContextRecord->SegDs, lpEP->ContextRecord->SegEs, lpEP->ContextRecord->SegFs, lpEP->ContextRecord->SegGs);
  810. aDebugDump += StrFormat("Flags:%@\r\n", lpEP->ContextRecord->EFlags);
  811. #else
  812. aDebugDump += StrFormat("EAX:%08X EBX:%08X ECX:%08X EDX:%08X ESI:%08X EDI:%08X\r\n",
  813. lpEP->ContextRecord->Eax, lpEP->ContextRecord->Ebx, lpEP->ContextRecord->Ecx, lpEP->ContextRecord->Edx, lpEP->ContextRecord->Esi, lpEP->ContextRecord->Edi);
  814. aDebugDump += StrFormat("EIP:%08X ESP:%08X EBP:%08X\r\n", lpEP->ContextRecord->Eip, lpEP->ContextRecord->Esp, lpEP->ContextRecord->Ebp);
  815. aDebugDump += StrFormat("CS:%04X SS:%04X DS:%04X ES:%04X FS:%04X GS:%04X\r\n", lpEP->ContextRecord->SegCs, lpEP->ContextRecord->SegSs, lpEP->ContextRecord->SegDs, lpEP->ContextRecord->SegEs, lpEP->ContextRecord->SegFs, lpEP->ContextRecord->SegGs);
  816. aDebugDump += StrFormat("Flags:%08X\r\n", lpEP->ContextRecord->EFlags);
  817. #endif
  818. aDebugDump += "\r\n";
  819. aDebugDump += GetSysInfo();
  820. /*if (mApp != NULL)
  821. {
  822. String aGameSEHInfo = mApp->GetGameSEHInfo();
  823. if (aGameSEHInfo.length() > 0)
  824. {
  825. aDebugDump += "\r\n";
  826. aDebugDump += aGameSEHInfo;
  827. }
  828. mApp->CopyToClipboard(aDebugDump);
  829. }
  830. if (hasImageHelp)
  831. GetSymbolsFromMapFile(aDebugDump);*/
  832. if (isCLI)
  833. {
  834. aDebugDump += "\n";
  835. //fwrite(aDebugDump.c_str(), 1, aDebugDump.length(), stderr);
  836. //fflush(stderr);
  837. DWORD bytesWritten;
  838. ::WriteFile(::GetStdHandle(STD_ERROR_HANDLE), aDebugDump.c_str(), (DWORD)aDebugDump.length(), &bytesWritten, NULL);
  839. }
  840. else
  841. ShowErrorDialog(anErrorTitle, aDebugDump, crashCatcher->mRelaunchCmd);
  842. }
  843. CrashCatcher::CrashCatcher()
  844. {
  845. mCrashed = false;
  846. mInitialized = false;
  847. mExceptionPointers = NULL;
  848. mPreviousFilter = NULL;
  849. mDebugError = false;
  850. mCrashReportKind = BfpCrashReportKind_Default;
  851. mCloseRequested = false;
  852. }
  853. static long __stdcall SEHFilter(LPEXCEPTION_POINTERS lpExceptPtr)
  854. {
  855. OutputDebugStrF("SEH Filter! CraskReportKind:%d\n", CrashCatcher::Get()->mCrashReportKind);
  856. if (CrashCatcher::Get()->mCrashReportKind == BfpCrashReportKind_None)
  857. {
  858. OutputDebugStrF("Silent Exiting\n");
  859. ::TerminateProcess(GetCurrentProcess(), lpExceptPtr->ExceptionRecord->ExceptionCode);
  860. }
  861. AutoCrit autoCrit(CrashCatcher::Get()->mBfpCritSect);
  862. //::ExitProcess();
  863. //quick_exit(1);
  864. if (!CrashCatcher::Get()->mCrashed)
  865. {
  866. CrashCatcher::Get()->mExceptionPointers = lpExceptPtr;
  867. //CreateMiniDump(lpExceptPtr);
  868. DoHandleDebugEvent(lpExceptPtr);
  869. }
  870. //if (!gDebugError)
  871. //SetErrorMode(SEM_NOGPFAULTERRORBOX);
  872. if (CrashCatcher::Get()->mCrashReportKind == BfpCrashReportKind_PrintOnly)
  873. {
  874. ::TerminateProcess(GetCurrentProcess(), lpExceptPtr->ExceptionRecord->ExceptionCode);
  875. }
  876. //return EXCEPTION_CONTINUE_SEARCH;
  877. return (CrashCatcher::Get()->mCloseRequested) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH;
  878. }
  879. //PVECTORED_EXCEPTION_HANDLER(
  880. static long __stdcall VectorExceptionHandler(LPEXCEPTION_POINTERS lpExceptPtr)
  881. {
  882. OutputDebugStrF("VectorExceptionHandler\n");
  883. return EXCEPTION_CONTINUE_SEARCH;
  884. }
  885. void CrashCatcher::Init()
  886. {
  887. if (mInitialized)
  888. return;
  889. mPreviousFilter = SetUnhandledExceptionFilter(SEHFilter);
  890. OutputDebugStrF("Setting SEH filter %p\n", mPreviousFilter);
  891. mInitialized = true;
  892. // OutputDebugStrF("AddVectoredExceptionHandler 2\n");
  893. // AddVectoredExceptionHandler(0, VectorExceptionHandler);
  894. }
  895. void CrashCatcher::Test()
  896. {
  897. __try
  898. {
  899. // all of code normally inside of main or WinMain here...
  900. int a = 123;
  901. int b = 0;
  902. a /= b;
  903. }
  904. __except (SEHFilter(GetExceptionInformation()))
  905. {
  906. }
  907. }
  908. void CrashCatcher::AddCrashInfoFunc(CrashInfoFunc crashInfoFunc)
  909. {
  910. AutoCrit autoCrit(mBfpCritSect);
  911. mCrashInfoFuncs.Add(crashInfoFunc);
  912. }
  913. void CrashCatcher::AddInfo(const StringImpl& str)
  914. {
  915. AutoCrit autoCrit(mBfpCritSect);
  916. mCrashInfo.Append(str);
  917. if (!str.EndsWith('\n'))
  918. mCrashInfo.Append('\n');
  919. }
  920. void CrashCatcher::Crash(const StringImpl& str)
  921. {
  922. OutputDebugStrF("CrashCatcher::Crash\n");
  923. mBfpCritSect.Lock();
  924. mCrashInfo.Append(str);
  925. mCrashInfo.Append("\n");
  926. if (mPreviousFilter == NULL)
  927. {
  928. // A little late, but install handler now so we can catch this crash
  929. Init();
  930. }
  931. OutputDebugStr(str);
  932. mBfpCritSect.Unlock();
  933. __debugbreak();
  934. // When we catch the exception information like this, it displays the dump correctly but
  935. // the minidump doesn't contain a valid callstack, so we need to rely on SetUnhandledExceptionFilter
  936. /*__try
  937. {
  938. ::MessageBoxA(NULL, "A", "B", MB_ICONERROR);
  939. __debugbreak();
  940. }
  941. __except (SEHFilter(GetExceptionInformation()))
  942. {
  943. }*/
  944. for (auto func : CrashCatcher::Get()->mCrashInfoFuncs)
  945. func();
  946. exit(1);
  947. }
  948. void CrashCatcher::SetCrashReportKind(BfpCrashReportKind crashReportKind)
  949. {
  950. mCrashReportKind = crashReportKind;
  951. }
  952. void CrashCatcher::SetRelaunchCmd(const StringImpl& relaunchCmd)
  953. {
  954. mRelaunchCmd = relaunchCmd;
  955. }
  956. struct CrashCatchMemory
  957. {
  958. public:
  959. CrashCatcher* mBpManager;
  960. int mABIVersion;
  961. };
  962. #define CRASHCATCH_ABI_VERSION 1
  963. static CrashCatcher* sCrashCatcher = NULL;
  964. CrashCatcher* CrashCatcher::Get()
  965. {
  966. if (sCrashCatcher != NULL)
  967. return sCrashCatcher;
  968. char mutexName[128];
  969. sprintf(mutexName, "BfCrashCatch_mutex_%d", GetCurrentProcessId());
  970. char memName[128];
  971. sprintf(memName, "BfCrashCatch_mem_%d", GetCurrentProcessId());
  972. auto mutex = ::CreateMutexA(NULL, TRUE, mutexName);
  973. if (mutex != NULL)
  974. {
  975. HANDLE fileMapping = ::OpenFileMappingA(FILE_MAP_ALL_ACCESS, FALSE, memName);
  976. if (fileMapping != NULL)
  977. {
  978. CrashCatchMemory* sharedMem = (CrashCatchMemory*)MapViewOfFile(fileMapping, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(CrashCatchMemory));
  979. if (sharedMem != NULL)
  980. {
  981. if (sharedMem->mABIVersion == CRASHCATCH_ABI_VERSION)
  982. sCrashCatcher = sharedMem->mBpManager;
  983. ::UnmapViewOfFile(sharedMem);
  984. }
  985. ::CloseHandle(fileMapping);
  986. }
  987. else
  988. {
  989. fileMapping = ::CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(CrashCatchMemory), memName);
  990. if (fileMapping != NULL)
  991. {
  992. CrashCatchMemory* sharedMem = (CrashCatchMemory*)MapViewOfFile(fileMapping, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(CrashCatchMemory));
  993. if (sharedMem != NULL)
  994. {
  995. sCrashCatcher = new CrashCatcher();
  996. sharedMem->mBpManager = sCrashCatcher;
  997. sharedMem->mABIVersion = CRASHCATCH_ABI_VERSION;
  998. ::UnmapViewOfFile(sharedMem);
  999. ::ReleaseMutex(mutex);
  1000. }
  1001. else
  1002. {
  1003. ::CloseHandle(fileMapping);
  1004. ::CloseHandle(mutex);
  1005. }
  1006. }
  1007. }
  1008. }
  1009. if (sCrashCatcher == NULL)
  1010. sCrashCatcher = new CrashCatcher();
  1011. return sCrashCatcher;
  1012. }