PageRenderTime 28ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/src/server/shared/Debugging/WheatyExceptionReport.cpp

https://github.com/chucho/FaceCore
C++ | 1045 lines | 767 code | 139 blank | 139 comment | 156 complexity | 043468d7c57187eca0a62d9c0432e670 MD5 | raw file
  1. //==========================================
  2. // Matt Pietrek
  3. // MSDN Magazine, 2002
  4. // FILE: WheatyExceptionReport.CPP
  5. //==========================================
  6. #if PLATFORM == PLATFORM_WINDOWS
  7. #define WIN32_LEAN_AND_MEAN
  8. #pragma warning(disable:4996)
  9. #pragma warning(disable:4312)
  10. #pragma warning(disable:4311)
  11. #include <windows.h>
  12. #include <tlhelp32.h>
  13. #include <stdio.h>
  14. #include <tchar.h>
  15. #define _NO_CVCONST_H
  16. #include <dbghelp.h>
  17. #include "WheatyExceptionReport.h"
  18. #include "Common.h"
  19. #include "SystemConfig.h"
  20. #include "revision.h"
  21. #define CrashFolder _T("Crashes")
  22. #pragma comment(linker, "/DEFAULTLIB:dbghelp.lib")
  23. inline LPTSTR ErrorMessage(DWORD dw)
  24. {
  25. LPVOID lpMsgBuf;
  26. FormatMessage(
  27. FORMAT_MESSAGE_ALLOCATE_BUFFER |
  28. FORMAT_MESSAGE_FROM_SYSTEM,
  29. NULL,
  30. dw,
  31. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  32. (LPTSTR) &lpMsgBuf,
  33. 0, NULL);
  34. return (LPTSTR)lpMsgBuf;
  35. }
  36. //============================== Global Variables =============================
  37. //
  38. // Declare the static variables of the WheatyExceptionReport class
  39. //
  40. TCHAR WheatyExceptionReport::m_szLogFileName[MAX_PATH];
  41. TCHAR WheatyExceptionReport::m_szDumpFileName[MAX_PATH];
  42. LPTOP_LEVEL_EXCEPTION_FILTER WheatyExceptionReport::m_previousFilter;
  43. HANDLE WheatyExceptionReport::m_hReportFile;
  44. HANDLE WheatyExceptionReport::m_hDumpFile;
  45. HANDLE WheatyExceptionReport::m_hProcess;
  46. // Declare global instance of class
  47. WheatyExceptionReport g_WheatyExceptionReport;
  48. //============================== Class Methods =============================
  49. WheatyExceptionReport::WheatyExceptionReport() // Constructor
  50. {
  51. // Install the unhandled exception filter function
  52. m_previousFilter = SetUnhandledExceptionFilter(WheatyUnhandledExceptionFilter);
  53. m_hProcess = GetCurrentProcess();
  54. }
  55. //============
  56. // Destructor
  57. //============
  58. WheatyExceptionReport::~WheatyExceptionReport()
  59. {
  60. if (m_previousFilter)
  61. SetUnhandledExceptionFilter(m_previousFilter);
  62. }
  63. //===========================================================
  64. // Entry point where control comes on an unhandled exception
  65. //===========================================================
  66. LONG WINAPI WheatyExceptionReport::WheatyUnhandledExceptionFilter(
  67. PEXCEPTION_POINTERS pExceptionInfo)
  68. {
  69. TCHAR module_folder_name[MAX_PATH];
  70. GetModuleFileName(0, module_folder_name, MAX_PATH);
  71. TCHAR* pos = _tcsrchr(module_folder_name, '\\');
  72. if (!pos)
  73. return 0;
  74. pos[0] = '\0';
  75. ++pos;
  76. TCHAR crash_folder_path[MAX_PATH];
  77. sprintf(crash_folder_path, "%s\\%s", module_folder_name, CrashFolder);
  78. if (!CreateDirectory(crash_folder_path, NULL))
  79. {
  80. if (GetLastError() != ERROR_ALREADY_EXISTS)
  81. return 0;
  82. }
  83. SYSTEMTIME systime;
  84. GetLocalTime(&systime);
  85. sprintf(m_szDumpFileName, "%s\\%s_%s_[%u-%u_%u-%u-%u].dmp",
  86. crash_folder_path, _HASH, pos, systime.wDay, systime.wMonth, systime.wHour, systime.wMinute, systime.wSecond);
  87. sprintf(m_szLogFileName, "%s\\%s_%s_[%u-%u_%u-%u-%u].txt",
  88. crash_folder_path, _HASH, pos, systime.wDay, systime.wMonth, systime.wHour, systime.wMinute, systime.wSecond);
  89. m_hDumpFile = CreateFile(m_szDumpFileName,
  90. GENERIC_WRITE,
  91. 0,
  92. 0,
  93. OPEN_ALWAYS,
  94. FILE_FLAG_WRITE_THROUGH,
  95. 0);
  96. m_hReportFile = CreateFile(m_szLogFileName,
  97. GENERIC_WRITE,
  98. 0,
  99. 0,
  100. OPEN_ALWAYS,
  101. FILE_FLAG_WRITE_THROUGH,
  102. 0);
  103. if (m_hDumpFile)
  104. {
  105. MINIDUMP_EXCEPTION_INFORMATION info;
  106. info.ClientPointers = FALSE;
  107. info.ExceptionPointers = pExceptionInfo;
  108. info.ThreadId = GetCurrentThreadId();
  109. MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),
  110. m_hDumpFile, MiniDumpWithIndirectlyReferencedMemory, &info, 0, 0);
  111. CloseHandle(m_hDumpFile);
  112. }
  113. if (m_hReportFile)
  114. {
  115. SetFilePointer(m_hReportFile, 0, 0, FILE_END);
  116. GenerateExceptionReport(pExceptionInfo);
  117. CloseHandle(m_hReportFile);
  118. m_hReportFile = 0;
  119. }
  120. if (m_previousFilter)
  121. return m_previousFilter(pExceptionInfo);
  122. else
  123. return EXCEPTION_EXECUTE_HANDLER/*EXCEPTION_CONTINUE_SEARCH*/;
  124. }
  125. BOOL WheatyExceptionReport::_GetProcessorName(TCHAR* sProcessorName, DWORD maxcount)
  126. {
  127. if (!sProcessorName)
  128. return FALSE;
  129. HKEY hKey;
  130. LONG lRet;
  131. lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"),
  132. 0, KEY_QUERY_VALUE, &hKey);
  133. if (lRet != ERROR_SUCCESS)
  134. return FALSE;
  135. TCHAR szTmp[2048];
  136. DWORD cntBytes = sizeof(szTmp);
  137. lRet = ::RegQueryValueEx(hKey, _T("ProcessorNameString"), NULL, NULL,
  138. (LPBYTE)szTmp, &cntBytes);
  139. if (lRet != ERROR_SUCCESS)
  140. return FALSE;
  141. ::RegCloseKey(hKey);
  142. sProcessorName[0] = '\0';
  143. // Skip spaces
  144. TCHAR* psz = szTmp;
  145. while (iswspace(*psz))
  146. ++psz;
  147. _tcsncpy(sProcessorName, psz, maxcount);
  148. return TRUE;
  149. }
  150. BOOL WheatyExceptionReport::_GetWindowsVersion(TCHAR* szVersion, DWORD cntMax)
  151. {
  152. // Try calling GetVersionEx using the OSVERSIONINFOEX structure.
  153. // If that fails, try using the OSVERSIONINFO structure.
  154. OSVERSIONINFOEX osvi = { 0 };
  155. osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
  156. BOOL bOsVersionInfoEx;
  157. bOsVersionInfoEx = ::GetVersionEx((LPOSVERSIONINFO)(&osvi));
  158. if (!bOsVersionInfoEx)
  159. {
  160. osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  161. if (!::GetVersionEx((OSVERSIONINFO*)&osvi))
  162. return FALSE;
  163. }
  164. *szVersion = _T('\0');
  165. TCHAR wszTmp[128];
  166. switch (osvi.dwPlatformId)
  167. {
  168. // Windows NT product family.
  169. case VER_PLATFORM_WIN32_NT:
  170. // Test for the specific product family.
  171. if (osvi.dwMajorVersion == 6)
  172. _tcsncat(szVersion, _T("Windows Vista or Windows Server 2008 "), cntMax);
  173. if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2)
  174. _tcsncat(szVersion, _T("Microsoft Windows Server 2003 "), cntMax);
  175. if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1)
  176. _tcsncat(szVersion, _T("Microsoft Windows XP "), cntMax);
  177. if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0)
  178. _tcsncat(szVersion, _T("Microsoft Windows 2000 "), cntMax);
  179. if (osvi.dwMajorVersion <= 4)
  180. _tcsncat(szVersion, _T("Microsoft Windows NT "), cntMax);
  181. // Test for specific product on Windows NT 4.0 SP6 and later.
  182. if (bOsVersionInfoEx)
  183. {
  184. // Test for the workstation type.
  185. #if WINVER < 0x0500
  186. if (osvi.wReserved[1] == VER_NT_WORKSTATION)
  187. #else
  188. if (osvi.wProductType == VER_NT_WORKSTATION)
  189. #endif // WINVER < 0x0500
  190. {
  191. if (osvi.dwMajorVersion == 4)
  192. _tcsncat(szVersion, _T("Workstation 4.0 "), cntMax);
  193. #if WINVER < 0x0500
  194. else if (osvi.wReserved[0] & VER_SUITE_PERSONAL)
  195. #else
  196. else if (osvi.wSuiteMask & VER_SUITE_PERSONAL)
  197. #endif // WINVER < 0x0500
  198. _tcsncat(szVersion, _T("Home Edition "), cntMax);
  199. #if WINVER < 0x0500
  200. else if (osvi.wReserved[0] & VER_SUITE_EMBEDDEDNT)
  201. #else
  202. else if (osvi.wSuiteMask & VER_SUITE_EMBEDDEDNT)
  203. #endif // WINVER < 0x0500
  204. _tcsncat(szVersion, _T("Embedded "), cntMax);
  205. else
  206. _tcsncat(szVersion, _T("Professional "), cntMax);
  207. }
  208. // Test for the server type.
  209. #if WINVER < 0x0500
  210. else if (osvi.wReserved[1] == VER_NT_SERVER)
  211. #else
  212. else if (osvi.wProductType == VER_NT_SERVER)
  213. #endif // WINVER < 0x0500
  214. {
  215. if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2)
  216. {
  217. #if WINVER < 0x0500
  218. if (osvi.wReserved[0] & VER_SUITE_DATACENTER)
  219. #else
  220. if (osvi.wSuiteMask & VER_SUITE_DATACENTER)
  221. #endif // WINVER < 0x0500
  222. _tcsncat(szVersion, _T("Datacenter Edition "), cntMax);
  223. #if WINVER < 0x0500
  224. else if (osvi.wReserved[0] & VER_SUITE_ENTERPRISE)
  225. #else
  226. else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE)
  227. #endif // WINVER < 0x0500
  228. _tcsncat(szVersion, _T("Enterprise Edition "), cntMax);
  229. #if WINVER < 0x0500
  230. else if (osvi.wReserved[0] == VER_SUITE_BLADE)
  231. #else
  232. else if (osvi.wSuiteMask == VER_SUITE_BLADE)
  233. #endif // WINVER < 0x0500
  234. _tcsncat(szVersion, _T("Web Edition "), cntMax);
  235. else
  236. _tcsncat(szVersion, _T("Standard Edition "), cntMax);
  237. }
  238. else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0)
  239. {
  240. #if WINVER < 0x0500
  241. if (osvi.wReserved[0] & VER_SUITE_DATACENTER)
  242. #else
  243. if (osvi.wSuiteMask & VER_SUITE_DATACENTER)
  244. #endif // WINVER < 0x0500
  245. _tcsncat(szVersion, _T("Datacenter Server "), cntMax);
  246. #if WINVER < 0x0500
  247. else if (osvi.wReserved[0] & VER_SUITE_ENTERPRISE)
  248. #else
  249. else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE)
  250. #endif // WINVER < 0x0500
  251. _tcsncat(szVersion, _T("Advanced Server "), cntMax);
  252. else
  253. _tcsncat(szVersion, _T("Server "), cntMax);
  254. }
  255. else // Windows NT 4.0
  256. {
  257. #if WINVER < 0x0500
  258. if (osvi.wReserved[0] & VER_SUITE_ENTERPRISE)
  259. #else
  260. if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE)
  261. #endif // WINVER < 0x0500
  262. _tcsncat(szVersion, _T("Server 4.0, Enterprise Edition "), cntMax);
  263. else
  264. _tcsncat(szVersion, _T("Server 4.0 "), cntMax);
  265. }
  266. }
  267. }
  268. // Display service pack (if any) and build number.
  269. if (osvi.dwMajorVersion == 4 && _tcsicmp(osvi.szCSDVersion, _T("Service Pack 6")) == 0)
  270. {
  271. HKEY hKey;
  272. LONG lRet;
  273. // Test for SP6 versus SP6a.
  274. lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Hotfix\\Q246009"), 0, KEY_QUERY_VALUE, &hKey);
  275. if (lRet == ERROR_SUCCESS)
  276. {
  277. _stprintf(wszTmp, _T("Service Pack 6a (Version %d.%d, Build %d)"),
  278. osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
  279. _tcsncat(szVersion, wszTmp, cntMax);
  280. }
  281. else // Windows NT 4.0 prior to SP6a
  282. {
  283. _stprintf(wszTmp, _T("%s (Version %d.%d, Build %d)"),
  284. osvi.szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
  285. _tcsncat(szVersion, wszTmp, cntMax);
  286. }
  287. ::RegCloseKey(hKey);
  288. }
  289. else // Windows NT 3.51 and earlier or Windows 2000 and later
  290. {
  291. if (!_tcslen(osvi.szCSDVersion))
  292. _stprintf(wszTmp, _T("(Version %d.%d, Build %d)"),
  293. osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
  294. else
  295. _stprintf(wszTmp, _T("%s (Version %d.%d, Build %d)"),
  296. osvi.szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
  297. _tcsncat(szVersion, wszTmp, cntMax);
  298. }
  299. break;
  300. default:
  301. _stprintf(wszTmp, _T("%s (Version %d.%d, Build %d)"),
  302. osvi.szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
  303. _tcsncat(szVersion, wszTmp, cntMax);
  304. break;
  305. }
  306. return TRUE;
  307. }
  308. void WheatyExceptionReport::PrintSystemInfo()
  309. {
  310. SYSTEM_INFO SystemInfo;
  311. ::GetSystemInfo(&SystemInfo);
  312. MEMORYSTATUS MemoryStatus;
  313. MemoryStatus.dwLength = sizeof (MEMORYSTATUS);
  314. ::GlobalMemoryStatus(&MemoryStatus);
  315. TCHAR sString[1024];
  316. _tprintf(_T("//=====================================================\r\n"));
  317. if (_GetProcessorName(sString, countof(sString)))
  318. _tprintf(_T("*** Hardware ***\r\nProcessor: %s\r\nNumber Of Processors: %d\r\nPhysical Memory: %d KB (Available: %d KB)\r\nCommit Charge Limit: %d KB\r\n"),
  319. sString, SystemInfo.dwNumberOfProcessors, MemoryStatus.dwTotalPhys/0x400, MemoryStatus.dwAvailPhys/0x400, MemoryStatus.dwTotalPageFile/0x400);
  320. else
  321. _tprintf(_T("*** Hardware ***\r\nProcessor: <unknown>\r\nNumber Of Processors: %d\r\nPhysical Memory: %d KB (Available: %d KB)\r\nCommit Charge Limit: %d KB\r\n"),
  322. SystemInfo.dwNumberOfProcessors, MemoryStatus.dwTotalPhys/0x400, MemoryStatus.dwAvailPhys/0x400, MemoryStatus.dwTotalPageFile/0x400);
  323. if (_GetWindowsVersion(sString, countof(sString)))
  324. _tprintf(_T("\r\n*** Operation System ***\r\n%s\r\n"), sString);
  325. else
  326. _tprintf(_T("\r\n*** Operation System:\r\n<unknown>\r\n"));
  327. }
  328. //===========================================================================
  329. void WheatyExceptionReport::printTracesForAllThreads()
  330. {
  331. HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
  332. THREADENTRY32 te32;
  333. DWORD dwOwnerPID = GetCurrentProcessId();
  334. m_hProcess = GetCurrentProcess();
  335. // Take a snapshot of all running threads
  336. hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
  337. if (hThreadSnap == INVALID_HANDLE_VALUE)
  338. return;
  339. // Fill in the size of the structure before using it.
  340. te32.dwSize = sizeof(THREADENTRY32);
  341. // Retrieve information about the first thread,
  342. // and exit if unsuccessful
  343. if (!Thread32First(hThreadSnap, &te32))
  344. {
  345. CloseHandle(hThreadSnap); // Must clean up the
  346. // snapshot object!
  347. return;
  348. }
  349. // Now walk the thread list of the system,
  350. // and display information about each thread
  351. // associated with the specified process
  352. do
  353. {
  354. if (te32.th32OwnerProcessID == dwOwnerPID)
  355. {
  356. CONTEXT context;
  357. context.ContextFlags = 0xffffffff;
  358. HANDLE threadHandle = OpenThread(THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION, false, te32.th32ThreadID);
  359. if (threadHandle && GetThreadContext(threadHandle, &context))
  360. {
  361. WriteStackDetails(&context, false, threadHandle);
  362. }
  363. CloseHandle(threadHandle);
  364. }
  365. } while(Thread32Next(hThreadSnap, &te32));
  366. // Don't forget to clean up the snapshot object.
  367. CloseHandle(hThreadSnap);
  368. }
  369. //===========================================================================
  370. // Open the report file, and write the desired information to it. Called by
  371. // WheatyUnhandledExceptionFilter
  372. //===========================================================================
  373. void WheatyExceptionReport::GenerateExceptionReport(
  374. PEXCEPTION_POINTERS pExceptionInfo)
  375. {
  376. SYSTEMTIME systime;
  377. GetLocalTime(&systime);
  378. // Start out with a banner
  379. _tprintf(_T("Revision: %s\r\n"), _FULLVERSION);
  380. _tprintf(_T("Date %u:%u:%u. Time %u:%u \r\n"), systime.wDay, systime.wMonth, systime.wYear, systime.wHour, systime.wMinute);
  381. PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord;
  382. PrintSystemInfo();
  383. // First print information about the type of fault
  384. _tprintf(_T("\r\n//=====================================================\r\n"));
  385. _tprintf(_T("Exception code: %08X %s\r\n"),
  386. pExceptionRecord->ExceptionCode,
  387. GetExceptionString(pExceptionRecord->ExceptionCode));
  388. // Now print information about where the fault occured
  389. TCHAR szFaultingModule[MAX_PATH];
  390. DWORD section;
  391. DWORD_PTR offset;
  392. GetLogicalAddress(pExceptionRecord->ExceptionAddress,
  393. szFaultingModule,
  394. sizeof(szFaultingModule),
  395. section, offset);
  396. #ifdef _M_IX86
  397. _tprintf(_T("Fault address: %08X %02X:%08X %s\r\n"),
  398. pExceptionRecord->ExceptionAddress,
  399. section, offset, szFaultingModule);
  400. #endif
  401. #ifdef _M_X64
  402. _tprintf(_T("Fault address: %016I64X %02X:%016I64X %s\r\n"),
  403. pExceptionRecord->ExceptionAddress,
  404. section, offset, szFaultingModule);
  405. #endif
  406. PCONTEXT pCtx = pExceptionInfo->ContextRecord;
  407. // Show the registers
  408. #ifdef _M_IX86 // X86 Only!
  409. _tprintf(_T("\r\nRegisters:\r\n"));
  410. _tprintf(_T("EAX:%08X\r\nEBX:%08X\r\nECX:%08X\r\nEDX:%08X\r\nESI:%08X\r\nEDI:%08X\r\n")
  411. , pCtx->Eax, pCtx->Ebx, pCtx->Ecx, pCtx->Edx,
  412. pCtx->Esi, pCtx->Edi);
  413. _tprintf(_T("CS:EIP:%04X:%08X\r\n"), pCtx->SegCs, pCtx->Eip);
  414. _tprintf(_T("SS:ESP:%04X:%08X EBP:%08X\r\n"),
  415. pCtx->SegSs, pCtx->Esp, pCtx->Ebp);
  416. _tprintf(_T("DS:%04X ES:%04X FS:%04X GS:%04X\r\n"),
  417. pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs);
  418. _tprintf(_T("Flags:%08X\r\n"), pCtx->EFlags);
  419. #endif
  420. #ifdef _M_X64
  421. _tprintf(_T("\r\nRegisters:\r\n"));
  422. _tprintf(_T("RAX:%016I64X\r\nRBX:%016I64X\r\nRCX:%016I64X\r\nRDX:%016I64X\r\nRSI:%016I64X\r\nRDI:%016I64X\r\n")
  423. _T("R8: %016I64X\r\nR9: %016I64X\r\nR10:%016I64X\r\nR11:%016I64X\r\nR12:%016I64X\r\nR13:%016I64X\r\nR14:%016I64X\r\nR15:%016I64X\r\n")
  424. , pCtx->Rax, pCtx->Rbx, pCtx->Rcx, pCtx->Rdx,
  425. pCtx->Rsi, pCtx->Rdi , pCtx->R9, pCtx->R10, pCtx->R11, pCtx->R12, pCtx->R13, pCtx->R14, pCtx->R15);
  426. _tprintf(_T("CS:RIP:%04X:%016I64X\r\n"), pCtx->SegCs, pCtx->Rip);
  427. _tprintf(_T("SS:RSP:%04X:%016X RBP:%08X\r\n"),
  428. pCtx->SegSs, pCtx->Rsp, pCtx->Rbp);
  429. _tprintf(_T("DS:%04X ES:%04X FS:%04X GS:%04X\r\n"),
  430. pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs);
  431. _tprintf(_T("Flags:%08X\r\n"), pCtx->EFlags);
  432. #endif
  433. SymSetOptions(SYMOPT_DEFERRED_LOADS);
  434. // Initialize DbgHelp
  435. if (!SymInitialize(GetCurrentProcess(), 0, TRUE))
  436. {
  437. _tprintf(_T("\n\rCRITICAL ERROR.\n\r Couldn't initialize the symbol handler for process.\n\rError [%s].\n\r\n\r"),
  438. ErrorMessage(GetLastError()));
  439. }
  440. CONTEXT trashableContext = *pCtx;
  441. WriteStackDetails(&trashableContext, false, NULL);
  442. printTracesForAllThreads();
  443. // #ifdef _M_IX86 // X86 Only!
  444. _tprintf(_T("========================\r\n"));
  445. _tprintf(_T("Local Variables And Parameters\r\n"));
  446. trashableContext = *pCtx;
  447. WriteStackDetails(&trashableContext, true, NULL);
  448. _tprintf(_T("========================\r\n"));
  449. _tprintf(_T("Global Variables\r\n"));
  450. SymEnumSymbols(GetCurrentProcess(),
  451. (DWORD64)GetModuleHandle(szFaultingModule),
  452. 0, EnumerateSymbolsCallback, 0);
  453. // #endif // X86 Only!
  454. SymCleanup(GetCurrentProcess());
  455. _tprintf(_T("\r\n"));
  456. }
  457. //======================================================================
  458. // Given an exception code, returns a pointer to a static string with a
  459. // description of the exception
  460. //======================================================================
  461. LPTSTR WheatyExceptionReport::GetExceptionString(DWORD dwCode)
  462. {
  463. #define EXCEPTION(x) case EXCEPTION_##x: return _T(#x);
  464. switch (dwCode)
  465. {
  466. EXCEPTION(ACCESS_VIOLATION)
  467. EXCEPTION(DATATYPE_MISALIGNMENT)
  468. EXCEPTION(BREAKPOINT)
  469. EXCEPTION(SINGLE_STEP)
  470. EXCEPTION(ARRAY_BOUNDS_EXCEEDED)
  471. EXCEPTION(FLT_DENORMAL_OPERAND)
  472. EXCEPTION(FLT_DIVIDE_BY_ZERO)
  473. EXCEPTION(FLT_INEXACT_RESULT)
  474. EXCEPTION(FLT_INVALID_OPERATION)
  475. EXCEPTION(FLT_OVERFLOW)
  476. EXCEPTION(FLT_STACK_CHECK)
  477. EXCEPTION(FLT_UNDERFLOW)
  478. EXCEPTION(INT_DIVIDE_BY_ZERO)
  479. EXCEPTION(INT_OVERFLOW)
  480. EXCEPTION(PRIV_INSTRUCTION)
  481. EXCEPTION(IN_PAGE_ERROR)
  482. EXCEPTION(ILLEGAL_INSTRUCTION)
  483. EXCEPTION(NONCONTINUABLE_EXCEPTION)
  484. EXCEPTION(STACK_OVERFLOW)
  485. EXCEPTION(INVALID_DISPOSITION)
  486. EXCEPTION(GUARD_PAGE)
  487. EXCEPTION(INVALID_HANDLE)
  488. }
  489. // If not one of the "known" exceptions, try to get the string
  490. // from NTDLL.DLL's message table.
  491. static TCHAR szBuffer[512] = { 0 };
  492. FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE,
  493. GetModuleHandle(_T("NTDLL.DLL")),
  494. dwCode, 0, szBuffer, sizeof(szBuffer), 0);
  495. return szBuffer;
  496. }
  497. //=============================================================================
  498. // Given a linear address, locates the module, section, and offset containing
  499. // that address.
  500. //
  501. // Note: the szModule paramater buffer is an output buffer of length specified
  502. // by the len parameter (in characters!)
  503. //=============================================================================
  504. BOOL WheatyExceptionReport::GetLogicalAddress(
  505. PVOID addr, PTSTR szModule, DWORD len, DWORD& section, DWORD_PTR& offset)
  506. {
  507. MEMORY_BASIC_INFORMATION mbi;
  508. if (!VirtualQuery(addr, &mbi, sizeof(mbi)))
  509. return FALSE;
  510. DWORD_PTR hMod = (DWORD_PTR)mbi.AllocationBase;
  511. if (!GetModuleFileName((HMODULE)hMod, szModule, len))
  512. return FALSE;
  513. // Point to the DOS header in memory
  514. PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)hMod;
  515. // From the DOS header, find the NT (PE) header
  516. PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)(hMod + DWORD_PTR(pDosHdr->e_lfanew));
  517. PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNtHdr);
  518. DWORD_PTR rva = (DWORD_PTR)addr - hMod; // RVA is offset from module load address
  519. // Iterate through the section table, looking for the one that encompasses
  520. // the linear address.
  521. for (unsigned i = 0;
  522. i < pNtHdr->FileHeader.NumberOfSections;
  523. i++, pSection++)
  524. {
  525. DWORD_PTR sectionStart = pSection->VirtualAddress;
  526. DWORD_PTR sectionEnd = sectionStart
  527. + DWORD_PTR(std::max(pSection->SizeOfRawData, pSection->Misc.VirtualSize));
  528. // Is the address in this section???
  529. if ((rva >= sectionStart) && (rva <= sectionEnd))
  530. {
  531. // Yes, address is in the section. Calculate section and offset,
  532. // and store in the "section" & "offset" params, which were
  533. // passed by reference.
  534. section = i+1;
  535. offset = rva - sectionStart;
  536. return TRUE;
  537. }
  538. }
  539. return FALSE; // Should never get here!
  540. }
  541. // It contains SYMBOL_INFO structure plus additional
  542. // space for the name of the symbol
  543. struct CSymbolInfoPackage : public SYMBOL_INFO_PACKAGE
  544. {
  545. CSymbolInfoPackage()
  546. {
  547. si.SizeOfStruct = sizeof(SYMBOL_INFO);
  548. si.MaxNameLen = sizeof(name);
  549. }
  550. };
  551. //============================================================
  552. // Walks the stack, and writes the results to the report file
  553. //============================================================
  554. void WheatyExceptionReport::WriteStackDetails(
  555. PCONTEXT pContext,
  556. bool bWriteVariables, HANDLE pThreadHandle) // true if local/params should be output
  557. {
  558. _tprintf(_T("\r\nCall stack:\r\n"));
  559. _tprintf(_T("Address Frame Function SourceFile\r\n"));
  560. DWORD dwMachineType = 0;
  561. // Could use SymSetOptions here to add the SYMOPT_DEFERRED_LOADS flag
  562. STACKFRAME64 sf;
  563. memset(&sf, 0, sizeof(sf));
  564. #ifdef _M_IX86
  565. // Initialize the STACKFRAME structure for the first call. This is only
  566. // necessary for Intel CPUs, and isn't mentioned in the documentation.
  567. sf.AddrPC.Offset = pContext->Eip;
  568. sf.AddrPC.Mode = AddrModeFlat;
  569. sf.AddrStack.Offset = pContext->Esp;
  570. sf.AddrStack.Mode = AddrModeFlat;
  571. sf.AddrFrame.Offset = pContext->Ebp;
  572. sf.AddrFrame.Mode = AddrModeFlat;
  573. dwMachineType = IMAGE_FILE_MACHINE_I386;
  574. #endif
  575. #ifdef _M_X64
  576. sf.AddrPC.Offset = pContext->Rip;
  577. sf.AddrPC.Mode = AddrModeFlat;
  578. sf.AddrStack.Offset = pContext->Rsp;
  579. sf.AddrStack.Mode = AddrModeFlat;
  580. sf.AddrFrame.Offset = pContext->Rbp;
  581. sf.AddrFrame.Mode = AddrModeFlat;
  582. dwMachineType = IMAGE_FILE_MACHINE_AMD64;
  583. #endif
  584. while (1)
  585. {
  586. // Get the next stack frame
  587. if (! StackWalk64(dwMachineType,
  588. m_hProcess,
  589. pThreadHandle != NULL ? pThreadHandle : GetCurrentThread(),
  590. &sf,
  591. pContext,
  592. 0,
  593. SymFunctionTableAccess64,
  594. SymGetModuleBase64,
  595. 0))
  596. break;
  597. if (0 == sf.AddrFrame.Offset) // Basic sanity check to make sure
  598. break; // the frame is OK. Bail if not.
  599. #ifdef _M_IX86
  600. _tprintf(_T("%08X %08X "), sf.AddrPC.Offset, sf.AddrFrame.Offset);
  601. #endif
  602. #ifdef _M_X64
  603. _tprintf(_T("%016I64X %016I64X "), sf.AddrPC.Offset, sf.AddrFrame.Offset);
  604. #endif
  605. DWORD64 symDisplacement = 0; // Displacement of the input address,
  606. // relative to the start of the symbol
  607. // Get the name of the function for this stack frame entry
  608. CSymbolInfoPackage sip;
  609. if (SymFromAddr(
  610. m_hProcess, // Process handle of the current process
  611. sf.AddrPC.Offset, // Symbol address
  612. &symDisplacement, // Address of the variable that will receive the displacement
  613. &sip.si)) // Address of the SYMBOL_INFO structure (inside "sip" object)
  614. {
  615. _tprintf(_T("%hs+%I64X"), sip.si.Name, symDisplacement);
  616. }
  617. else // No symbol found. Print out the logical address instead.
  618. {
  619. TCHAR szModule[MAX_PATH] = _T("");
  620. DWORD section = 0;
  621. DWORD_PTR offset = 0;
  622. GetLogicalAddress((PVOID)sf.AddrPC.Offset,
  623. szModule, sizeof(szModule), section, offset);
  624. #ifdef _M_IX86
  625. _tprintf(_T("%04X:%08X %s"), section, offset, szModule);
  626. #endif
  627. #ifdef _M_X64
  628. _tprintf(_T("%04X:%016I64X %s"), section, offset, szModule);
  629. #endif
  630. }
  631. // Get the source line for this stack frame entry
  632. IMAGEHLP_LINE64 lineInfo = { sizeof(IMAGEHLP_LINE) };
  633. DWORD dwLineDisplacement;
  634. if (SymGetLineFromAddr64(m_hProcess, sf.AddrPC.Offset,
  635. &dwLineDisplacement, &lineInfo))
  636. {
  637. _tprintf(_T(" %s line %u"), lineInfo.FileName, lineInfo.LineNumber);
  638. }
  639. _tprintf(_T("\r\n"));
  640. // Write out the variables, if desired
  641. if (bWriteVariables)
  642. {
  643. // Use SymSetContext to get just the locals/params for this frame
  644. IMAGEHLP_STACK_FRAME imagehlpStackFrame;
  645. imagehlpStackFrame.InstructionOffset = sf.AddrPC.Offset;
  646. SymSetContext(m_hProcess, &imagehlpStackFrame, 0);
  647. // Enumerate the locals/parameters
  648. SymEnumSymbols(m_hProcess, 0, 0, EnumerateSymbolsCallback, &sf);
  649. _tprintf(_T("\r\n"));
  650. }
  651. }
  652. }
  653. //////////////////////////////////////////////////////////////////////////////
  654. // The function invoked by SymEnumSymbols
  655. //////////////////////////////////////////////////////////////////////////////
  656. BOOL CALLBACK
  657. WheatyExceptionReport::EnumerateSymbolsCallback(
  658. PSYMBOL_INFO pSymInfo,
  659. ULONG /*SymbolSize*/,
  660. PVOID UserContext)
  661. {
  662. char szBuffer[2048];
  663. __try
  664. {
  665. if (FormatSymbolValue(pSymInfo, (STACKFRAME*)UserContext,
  666. szBuffer, sizeof(szBuffer)))
  667. _tprintf(_T("\t%s\r\n"), szBuffer);
  668. }
  669. __except(1)
  670. {
  671. _tprintf(_T("punting on symbol %s\r\n"), pSymInfo->Name);
  672. }
  673. return TRUE;
  674. }
  675. //////////////////////////////////////////////////////////////////////////////
  676. // Given a SYMBOL_INFO representing a particular variable, displays its
  677. // contents. If it's a user defined type, display the members and their
  678. // values.
  679. //////////////////////////////////////////////////////////////////////////////
  680. bool WheatyExceptionReport::FormatSymbolValue(
  681. PSYMBOL_INFO pSym,
  682. STACKFRAME * sf,
  683. char * pszBuffer,
  684. unsigned /*cbBuffer*/)
  685. {
  686. char * pszCurrBuffer = pszBuffer;
  687. // Indicate if the variable is a local or parameter
  688. if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_PARAMETER)
  689. pszCurrBuffer += sprintf(pszCurrBuffer, "Parameter ");
  690. else if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_LOCAL)
  691. pszCurrBuffer += sprintf(pszCurrBuffer, "Local ");
  692. // If it's a function, don't do anything.
  693. if (pSym->Tag == 5) // SymTagFunction from CVCONST.H from the DIA SDK
  694. return false;
  695. DWORD_PTR pVariable = 0; // Will point to the variable's data in memory
  696. if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGRELATIVE)
  697. {
  698. // if (pSym->Register == 8) // EBP is the value 8 (in DBGHELP 5.1)
  699. { // This may change!!!
  700. pVariable = sf->AddrFrame.Offset;
  701. pVariable += (DWORD_PTR)pSym->Address;
  702. }
  703. // else
  704. // return false;
  705. }
  706. else if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGISTER)
  707. {
  708. return false; // Don't try to report register variable
  709. }
  710. else
  711. {
  712. pVariable = (DWORD_PTR)pSym->Address; // It must be a global variable
  713. }
  714. // Determine if the variable is a user defined type (UDT). IF so, bHandled
  715. // will return true.
  716. bool bHandled;
  717. pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, pSym->ModBase, pSym->TypeIndex,
  718. 0, pVariable, bHandled, pSym->Name);
  719. if (!bHandled)
  720. {
  721. // The symbol wasn't a UDT, so do basic, stupid formatting of the
  722. // variable. Based on the size, we're assuming it's a char, WORD, or
  723. // DWORD.
  724. BasicType basicType = GetBasicType(pSym->TypeIndex, pSym->ModBase);
  725. pszCurrBuffer += sprintf(pszCurrBuffer, rgBaseType[basicType]);
  726. // Emit the variable name
  727. pszCurrBuffer += sprintf(pszCurrBuffer, "\'%s\'", pSym->Name);
  728. pszCurrBuffer = FormatOutputValue(pszCurrBuffer, basicType, pSym->Size,
  729. (PVOID)pVariable);
  730. }
  731. return true;
  732. }
  733. //////////////////////////////////////////////////////////////////////////////
  734. // If it's a user defined type (UDT), recurse through its members until we're
  735. // at fundamental types. When he hit fundamental types, return
  736. // bHandled = false, so that FormatSymbolValue() will format them.
  737. //////////////////////////////////////////////////////////////////////////////
  738. char * WheatyExceptionReport::DumpTypeIndex(
  739. char * pszCurrBuffer,
  740. DWORD64 modBase,
  741. DWORD dwTypeIndex,
  742. unsigned nestingLevel,
  743. DWORD_PTR offset,
  744. bool & bHandled,
  745. char* /*Name*/)
  746. {
  747. bHandled = false;
  748. // Get the name of the symbol. This will either be a Type name (if a UDT),
  749. // or the structure member name.
  750. WCHAR * pwszTypeName;
  751. if (SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_SYMNAME,
  752. &pwszTypeName))
  753. {
  754. pszCurrBuffer += sprintf(pszCurrBuffer, " %ls", pwszTypeName);
  755. LocalFree(pwszTypeName);
  756. }
  757. // Determine how many children this type has.
  758. DWORD dwChildrenCount = 0;
  759. SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_CHILDRENCOUNT,
  760. &dwChildrenCount);
  761. if (!dwChildrenCount) // If no children, we're done
  762. return pszCurrBuffer;
  763. // Prepare to get an array of "TypeIds", representing each of the children.
  764. // SymGetTypeInfo(TI_FINDCHILDREN) expects more memory than just a
  765. // TI_FINDCHILDREN_PARAMS struct has. Use derivation to accomplish this.
  766. struct FINDCHILDREN : TI_FINDCHILDREN_PARAMS
  767. {
  768. ULONG MoreChildIds[1024];
  769. FINDCHILDREN(){Count = sizeof(MoreChildIds) / sizeof(MoreChildIds[0]);}
  770. } children;
  771. children.Count = dwChildrenCount;
  772. children.Start= 0;
  773. // Get the array of TypeIds, one for each child type
  774. if (!SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_FINDCHILDREN,
  775. &children))
  776. {
  777. return pszCurrBuffer;
  778. }
  779. // Append a line feed
  780. pszCurrBuffer += sprintf(pszCurrBuffer, "\r\n");
  781. // Iterate through each of the children
  782. for (unsigned i = 0; i < dwChildrenCount; i++)
  783. {
  784. // Add appropriate indentation level (since this routine is recursive)
  785. for (unsigned j = 0; j <= nestingLevel+1; j++)
  786. pszCurrBuffer += sprintf(pszCurrBuffer, "\t");
  787. // Recurse for each of the child types
  788. bool bHandled2;
  789. BasicType basicType = GetBasicType(children.ChildId[i], modBase);
  790. pszCurrBuffer += sprintf(pszCurrBuffer, rgBaseType[basicType]);
  791. pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase,
  792. children.ChildId[i], nestingLevel+1,
  793. offset, bHandled2, ""/*Name */);
  794. // If the child wasn't a UDT, format it appropriately
  795. if (!bHandled2)
  796. {
  797. // Get the offset of the child member, relative to its parent
  798. DWORD dwMemberOffset;
  799. SymGetTypeInfo(m_hProcess, modBase, children.ChildId[i],
  800. TI_GET_OFFSET, &dwMemberOffset);
  801. // Get the real "TypeId" of the child. We need this for the
  802. // SymGetTypeInfo(TI_GET_TYPEID) call below.
  803. DWORD typeId;
  804. SymGetTypeInfo(m_hProcess, modBase, children.ChildId[i],
  805. TI_GET_TYPEID, &typeId);
  806. // Get the size of the child member
  807. ULONG64 length;
  808. SymGetTypeInfo(m_hProcess, modBase, typeId, TI_GET_LENGTH, &length);
  809. // Calculate the address of the member
  810. DWORD_PTR dwFinalOffset = offset + dwMemberOffset;
  811. // BasicType basicType = GetBasicType(children.ChildId[i], modBase);
  812. //
  813. // pszCurrBuffer += sprintf(pszCurrBuffer, rgBaseType[basicType]);
  814. //
  815. // Emit the variable name
  816. // pszCurrBuffer += sprintf(pszCurrBuffer, "\'%s\'", Name);
  817. pszCurrBuffer = FormatOutputValue(pszCurrBuffer, basicType,
  818. length, (PVOID)dwFinalOffset);
  819. pszCurrBuffer += sprintf(pszCurrBuffer, "\r\n");
  820. }
  821. }
  822. bHandled = true;
  823. return pszCurrBuffer;
  824. }
  825. char * WheatyExceptionReport::FormatOutputValue(char * pszCurrBuffer,
  826. BasicType basicType,
  827. DWORD64 length,
  828. PVOID pAddress)
  829. {
  830. // Format appropriately (assuming it's a 1, 2, or 4 bytes (!!!)
  831. if (length == 1)
  832. pszCurrBuffer += sprintf(pszCurrBuffer, " = %X", *(PBYTE)pAddress);
  833. else if (length == 2)
  834. pszCurrBuffer += sprintf(pszCurrBuffer, " = %X", *(PWORD)pAddress);
  835. else if (length == 4)
  836. {
  837. if (basicType == btFloat)
  838. {
  839. pszCurrBuffer += sprintf(pszCurrBuffer, " = %f", *(PFLOAT)pAddress);
  840. }
  841. else if (basicType == btChar)
  842. {
  843. if (!IsBadStringPtr(*(PSTR*)pAddress, 32))
  844. {
  845. pszCurrBuffer += sprintf(pszCurrBuffer, " = \"%.31s\"",
  846. *(PDWORD)pAddress);
  847. }
  848. else
  849. pszCurrBuffer += sprintf(pszCurrBuffer, " = %X",
  850. *(PDWORD)pAddress);
  851. }
  852. else
  853. pszCurrBuffer += sprintf(pszCurrBuffer, " = %X", *(PDWORD)pAddress);
  854. }
  855. else if (length == 8)
  856. {
  857. if (basicType == btFloat)
  858. {
  859. pszCurrBuffer += sprintf(pszCurrBuffer, " = %lf",
  860. *(double *)pAddress);
  861. }
  862. else
  863. pszCurrBuffer += sprintf(pszCurrBuffer, " = %I64X",
  864. *(DWORD64*)pAddress);
  865. }
  866. return pszCurrBuffer;
  867. }
  868. BasicType
  869. WheatyExceptionReport::GetBasicType(DWORD typeIndex, DWORD64 modBase)
  870. {
  871. BasicType basicType;
  872. if (SymGetTypeInfo(m_hProcess, modBase, typeIndex,
  873. TI_GET_BASETYPE, &basicType))
  874. {
  875. return basicType;
  876. }
  877. // Get the real "TypeId" of the child. We need this for the
  878. // SymGetTypeInfo(TI_GET_TYPEID) call below.
  879. DWORD typeId;
  880. if (SymGetTypeInfo(m_hProcess, modBase, typeIndex, TI_GET_TYPEID, &typeId))
  881. {
  882. if (SymGetTypeInfo(m_hProcess, modBase, typeId, TI_GET_BASETYPE,
  883. &basicType))
  884. {
  885. return basicType;
  886. }
  887. }
  888. return btNoType;
  889. }
  890. //============================================================================
  891. // Helper function that writes to the report file, and allows the user to use
  892. // printf style formating
  893. //============================================================================
  894. int __cdecl WheatyExceptionReport::_tprintf(const TCHAR * format, ...)
  895. {
  896. TCHAR szBuff[1024];
  897. int retValue;
  898. DWORD cbWritten;
  899. va_list argptr;
  900. va_start(argptr, format);
  901. retValue = vsprintf(szBuff, format, argptr);
  902. va_end(argptr);
  903. WriteFile(m_hReportFile, szBuff, retValue * sizeof(TCHAR), &cbWritten, 0);
  904. return retValue;
  905. }
  906. #endif // _WIN32