AzerothCore 3.3.5a
OpenSource WoW Emulator
Loading...
Searching...
No Matches
WheatyExceptionReport Class Reference

#include "WheatyExceptionReport.h"

Public Member Functions

 WheatyExceptionReport ()
 
 ~WheatyExceptionReport ()
 

Static Public Member Functions

static LONG WINAPI WheatyUnhandledExceptionFilter (PEXCEPTION_POINTERS pExceptionInfo)
 
static void __cdecl WheatyCrtHandler (wchar_t const *expression, wchar_t const *function, wchar_t const *file, unsigned int line, uintptr_t pReserved)
 
static void printTracesForAllThreads (bool)
 

Private Types

typedef NTSTATUS(NTAPI * pRtlGetVersion) (PRTL_OSVERSIONINFOW lpVersionInformation)
 

Static Private Member Functions

static void GenerateExceptionReport (PEXCEPTION_POINTERS pExceptionInfo)
 
static void PrintSystemInfo ()
 
static BOOL _GetWindowsVersion (TCHAR *szVersion, DWORD cntMax)
 
static BOOL _GetProcessorName (TCHAR *sProcessorName, DWORD maxcount)
 
static LPTSTR GetExceptionString (DWORD dwCode)
 
static BOOL GetLogicalAddress (PVOID addr, PTSTR szModule, DWORD len, DWORD &section, DWORD_PTR &offset)
 
static void WriteStackDetails (PCONTEXT pContext, bool bWriteVariables, HANDLE pThreadHandle)
 
static BOOL CALLBACK EnumerateSymbolsCallback (PSYMBOL_INFO, ULONG, PVOID)
 
static bool FormatSymbolValue (PSYMBOL_INFO, STACKFRAME64 *)
 
static void DumpTypeIndex (DWORD64, DWORD, DWORD_PTR, bool &, char const *, char *, bool, bool)
 
static void FormatOutputValue (char *pszCurrBuffer, BasicType basicType, DWORD64 length, PVOID pAddress, size_t bufferSize, size_t countOverride=0)
 
static BasicType GetBasicType (DWORD typeIndex, DWORD64 modBase)
 
static DWORD_PTR DereferenceUnsafePointer (DWORD_PTR address)
 
static int __cdecl Log (const TCHAR *format,...)
 
static int __cdecl StackLog (const TCHAR *format, va_list argptr)
 
static int __cdecl HeapLog (const TCHAR *format, va_list argptr)
 
static bool StoreSymbol (DWORD type, DWORD_PTR offset)
 
static void ClearSymbols ()
 
static void PushSymbolDetail ()
 
static void PopSymbolDetail ()
 
static void PrintSymbolDetail ()
 

Static Private Attributes

static TCHAR m_szLogFileName [MAX_PATH]
 
static TCHAR m_szDumpFileName [MAX_PATH]
 
static LPTOP_LEVEL_EXCEPTION_FILTER m_previousFilter
 
static _invalid_parameter_handler m_previousCrtHandler
 
static HANDLE m_hReportFile
 
static HANDLE m_hDumpFile
 
static HANDLE m_hProcess
 
static SymbolPairs symbols
 
static std::stack< SymbolDetailsymbolDetails
 
static bool stackOverflowException
 
static bool alreadyCrashed
 
static std::mutex alreadyCrashedLock
 
static pRtlGetVersion RtlGetVersion
 

Detailed Description

Member Typedef Documentation

◆ pRtlGetVersion

typedef NTSTATUS(NTAPI * WheatyExceptionReport::pRtlGetVersion) (PRTL_OSVERSIONINFOW lpVersionInformation)
private

Constructor & Destructor Documentation

◆ WheatyExceptionReport()

WheatyExceptionReport::WheatyExceptionReport ( )
80{
81 // Install the unhandled exception filter function
82 m_previousFilter = SetUnhandledExceptionFilter(WheatyUnhandledExceptionFilter);
83 m_previousCrtHandler = _set_invalid_parameter_handler(WheatyCrtHandler);
84 m_hProcess = GetCurrentProcess();
86 alreadyCrashed = false;
87 RtlGetVersion = (pRtlGetVersion)GetProcAddress(GetModuleHandle(_T("ntdll.dll")), "RtlGetVersion");
88 if (!IsDebuggerPresent())
89 {
90 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
91 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
92 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
93 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
94 }
95}
static bool stackOverflowException
Definition: WheatyExceptionReport.h:189
static bool alreadyCrashed
Definition: WheatyExceptionReport.h:190
static LPTOP_LEVEL_EXCEPTION_FILTER m_previousFilter
Definition: WheatyExceptionReport.h:182
static _invalid_parameter_handler m_previousCrtHandler
Definition: WheatyExceptionReport.h:183
static pRtlGetVersion RtlGetVersion
Definition: WheatyExceptionReport.h:193
static HANDLE m_hProcess
Definition: WheatyExceptionReport.h:186
NTSTATUS(NTAPI * pRtlGetVersion)(PRTL_OSVERSIONINFOW lpVersionInformation)
Definition: WheatyExceptionReport.h:192
static LONG WINAPI WheatyUnhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo)
Definition: WheatyExceptionReport.cpp:116
static void __cdecl WheatyCrtHandler(wchar_t const *expression, wchar_t const *function, wchar_t const *file, unsigned int line, uintptr_t pReserved)
Definition: WheatyExceptionReport.cpp:223

References alreadyCrashed, m_hProcess, m_previousCrtHandler, m_previousFilter, RtlGetVersion, stackOverflowException, WheatyCrtHandler(), and WheatyUnhandledExceptionFilter().

◆ ~WheatyExceptionReport()

WheatyExceptionReport::~WheatyExceptionReport ( )
101{
103 {
104 SetUnhandledExceptionFilter(m_previousFilter);
105 }
107 {
108 _set_invalid_parameter_handler(m_previousCrtHandler);
109 }
110 ClearSymbols();
111}
static void ClearSymbols()
Definition: WheatyExceptionReport.cpp:1639

References ClearSymbols(), m_previousCrtHandler, and m_previousFilter.

Member Function Documentation

◆ _GetProcessorName()

BOOL WheatyExceptionReport::_GetProcessorName ( TCHAR *  sProcessorName,
DWORD  maxcount 
)
staticprivate
229{
230 if (!sProcessorName)
231 {
232 return FALSE;
233 }
234
235 HKEY hKey;
236 LONG lRet;
237 lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"),
238 0, KEY_QUERY_VALUE, &hKey);
239 if (lRet != ERROR_SUCCESS)
240 {
241 return FALSE;
242 }
243 TCHAR szTmp[2048];
244 DWORD cntBytes = sizeof(szTmp);
245 lRet = ::RegQueryValueEx(hKey, _T("ProcessorNameString"), nullptr, nullptr,
246 (LPBYTE)szTmp, &cntBytes);
247 if (lRet != ERROR_SUCCESS)
248 {
249 return FALSE;
250 }
251 ::RegCloseKey(hKey);
252 sProcessorName[0] = '\0';
253 // Skip spaces
254 TCHAR* psz = szTmp;
255 while (iswspace(*psz))
256 {
257 ++psz;
258 }
259 _tcsncpy(sProcessorName, psz, maxcount);
260 return TRUE;
261}

Referenced by PrintSystemInfo().

◆ _GetWindowsVersion()

BOOL WheatyExceptionReport::_GetWindowsVersion ( TCHAR *  szVersion,
DWORD  cntMax 
)
staticprivate
276{
277 // Try calling GetVersionEx using the OSVERSIONINFOEX structure.
278 // If that fails, try using the OSVERSIONINFO structure.
279 RTL_OSVERSIONINFOEXW osvi = { 0 };
280 osvi.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
281 NTSTATUS bVersionEx = RtlGetVersion((PRTL_OSVERSIONINFOW)&osvi);
282 if (bVersionEx < 0)
283 {
284 osvi.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOW);
285 if (!RtlGetVersion((PRTL_OSVERSIONINFOW)&osvi))
286 {
287 return FALSE;
288 }
289 }
290 *szVersion = _T('\0');
291
292 TCHAR szCSDVersion[256];
293 ToTchar(osvi.szCSDVersion, szCSDVersion, std::is_same<TCHAR, char>::type());
294
295 TCHAR wszTmp[128];
296 switch (osvi.dwPlatformId)
297 {
298 // Windows NT product family.
299 case VER_PLATFORM_WIN32_NT:
300 {
301#if WINVER < 0x0500
302 BYTE suiteMask = osvi.wReserved[0];
303 BYTE productType = osvi.wReserved[1];
304#else
305 WORD suiteMask = osvi.wSuiteMask;
306 BYTE productType = osvi.wProductType;
307#endif // WINVER < 0x0500
308
309 // Test for the specific product family.
310 if (osvi.dwMajorVersion == 10)
311 {
312 if (productType == VER_NT_WORKSTATION)
313 {
314 _tcsncat(szVersion, _T("Windows 10 "), cntMax);
315 }
316 else
317 {
318 _tcsncat(szVersion, _T("Windows Server 2016 "), cntMax);
319 }
320 }
321 else if (osvi.dwMajorVersion == 6)
322 {
323 if (productType == VER_NT_WORKSTATION)
324 {
325 if (osvi.dwMinorVersion == 3)
326 {
327 _tcsncat(szVersion, _T("Windows 8.1 "), cntMax);
328 }
329 else if (osvi.dwMinorVersion == 2)
330 {
331 _tcsncat(szVersion, _T("Windows 8 "), cntMax);
332 }
333 else if (osvi.dwMinorVersion == 1)
334 {
335 _tcsncat(szVersion, _T("Windows 7 "), cntMax);
336 }
337 else
338 {
339 _tcsncat(szVersion, _T("Windows Vista "), cntMax);
340 }
341 }
342 else if (osvi.dwMinorVersion == 3)
343 {
344 _tcsncat(szVersion, _T("Windows Server 2012 R2 "), cntMax);
345 }
346 else if (osvi.dwMinorVersion == 2)
347 {
348 _tcsncat(szVersion, _T("Windows Server 2012 "), cntMax);
349 }
350 else if (osvi.dwMinorVersion == 1)
351 {
352 _tcsncat(szVersion, _T("Windows Server 2008 R2 "), cntMax);
353 }
354 else
355 {
356 _tcsncat(szVersion, _T("Windows Server 2008 "), cntMax);
357 }
358 }
359 else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2)
360 {
361 _tcsncat(szVersion, _T("Microsoft Windows Server 2003 "), cntMax);
362 }
363 else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1)
364 {
365 _tcsncat(szVersion, _T("Microsoft Windows XP "), cntMax);
366 }
367 else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0)
368 {
369 _tcsncat(szVersion, _T("Microsoft Windows 2000 "), cntMax);
370 }
371 else if (osvi.dwMajorVersion <= 4)
372 {
373 _tcsncat(szVersion, _T("Microsoft Windows NT "), cntMax);
374 }
375
376 // Test for specific product on Windows NT 4.0 SP6 and later.
377 if (bVersionEx >= 0)
378 {
379 // Test for the workstation type.
380 if (productType == VER_NT_WORKSTATION)
381 {
382 if (osvi.dwMajorVersion == 4)
383 {
384 _tcsncat(szVersion, _T("Workstation 4.0 "), cntMax);
385 }
386 else if (suiteMask & VER_SUITE_PERSONAL)
387 {
388 _tcsncat(szVersion, _T("Home Edition "), cntMax);
389 }
390 else if (suiteMask & VER_SUITE_EMBEDDEDNT)
391 {
392 _tcsncat(szVersion, _T("Embedded "), cntMax);
393 }
394 else
395 {
396 _tcsncat(szVersion, _T("Professional "), cntMax);
397 }
398 }
399 // Test for the server type.
400 else if (productType == VER_NT_SERVER)
401 {
402 if (osvi.dwMajorVersion == 6 || osvi.dwMajorVersion == 10)
403 {
404 if (suiteMask & VER_SUITE_SMALLBUSINESS_RESTRICTED)
405 {
406 _tcsncat(szVersion, _T("Essentials "), cntMax);
407 }
408 else if (suiteMask & VER_SUITE_DATACENTER)
409 {
410 _tcsncat(szVersion, _T("Datacenter "), cntMax);
411 }
412 else if (suiteMask & VER_SUITE_ENTERPRISE)
413 {
414 _tcsncat(szVersion, _T("Enterprise "), cntMax);
415 }
416 else
417 {
418 _tcsncat(szVersion, _T("Standard "), cntMax);
419 }
420 }
421 else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2)
422 {
423 if (suiteMask & VER_SUITE_DATACENTER)
424 {
425 _tcsncat(szVersion, _T("Datacenter Edition "), cntMax);
426 }
427 else if (suiteMask & VER_SUITE_ENTERPRISE)
428 {
429 _tcsncat(szVersion, _T("Enterprise Edition "), cntMax);
430 }
431 else if (suiteMask == VER_SUITE_BLADE)
432 {
433 _tcsncat(szVersion, _T("Web Edition "), cntMax);
434 }
435 else
436 {
437 _tcsncat(szVersion, _T("Standard Edition "), cntMax);
438 }
439 }
440 else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0)
441 {
442 if (suiteMask & VER_SUITE_DATACENTER)
443 {
444 _tcsncat(szVersion, _T("Datacenter Server "), cntMax);
445 }
446 else if (suiteMask & VER_SUITE_ENTERPRISE)
447 {
448 _tcsncat(szVersion, _T("Advanced Server "), cntMax);
449 }
450 else
451 {
452 _tcsncat(szVersion, _T("Server "), cntMax);
453 }
454 }
455 else // Windows NT 4.0
456 {
457 if (suiteMask & VER_SUITE_ENTERPRISE)
458 {
459 _tcsncat(szVersion, _T("Server 4.0, Enterprise Edition "), cntMax);
460 }
461 else
462 {
463 _tcsncat(szVersion, _T("Server 4.0 "), cntMax);
464 }
465 }
466 }
467 }
468
469 // Display service pack (if any) and build number.
470 if (osvi.dwMajorVersion == 4 && _tcsicmp(szCSDVersion, _T("Service Pack 6")) == 0)
471 {
472 HKEY hKey;
473 LONG lRet;
474
475 // Test for SP6 versus SP6a.
476 lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Hotfix\\Q246009"), 0, KEY_QUERY_VALUE, &hKey);
477 if (lRet == ERROR_SUCCESS)
478 {
479 _stprintf(wszTmp, _T("Service Pack 6a (Version %d.%d, Build %d)"),
480 osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
481 _tcsncat(szVersion, wszTmp, cntMax);
482 }
483 else // Windows NT 4.0 prior to SP6a
484 {
485 _stprintf(wszTmp, _T("%s (Version %d.%d, Build %d)"),
486 szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
487 _tcsncat(szVersion, wszTmp, cntMax);
488 }
489 ::RegCloseKey(hKey);
490 }
491 else // Windows NT 3.51 and earlier or Windows 2000 and later
492 {
493 if (!_tcslen(szCSDVersion))
494 _stprintf(wszTmp, _T("(Version %d.%d, Build %d)"),
495 osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
496 else
497 _stprintf(wszTmp, _T("%s (Version %d.%d, Build %d)"),
498 szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
499 _tcsncat(szVersion, wszTmp, cntMax);
500 }
501 break;
502 }
503 default:
504 _stprintf(wszTmp, _T("%s (Version %d.%d, Build %d)"),
505 szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
506 _tcsncat(szVersion, wszTmp, cntMax);
507 break;
508 }
509
510 return TRUE;
511}
void ToTchar(wchar_t const *src, TCHAR(&dst)[size], std::true_type)
Definition: WheatyExceptionReport.cpp:264

References RtlGetVersion, and ToTchar().

Referenced by PrintSystemInfo().

◆ ClearSymbols()

void WheatyExceptionReport::ClearSymbols ( )
staticprivate
1640{
1641 symbols.clear();
1642 while (!symbolDetails.empty())
1643 {
1644 symbolDetails.pop();
1645 }
1646}
static SymbolPairs symbols
Definition: WheatyExceptionReport.h:187
static std::stack< SymbolDetail > symbolDetails
Definition: WheatyExceptionReport.h:188

References symbolDetails, and symbols.

Referenced by EnumerateSymbolsCallback(), and ~WheatyExceptionReport().

◆ DereferenceUnsafePointer()

DWORD_PTR WheatyExceptionReport::DereferenceUnsafePointer ( DWORD_PTR  address)
staticprivate
1572{
1573 __try
1574 {
1575 return *(PDWORD_PTR)address;
1576 }
1577 __except (EXCEPTION_EXECUTE_HANDLER)
1578 {
1579 return DWORD_PTR(-1);
1580 }
1581}

Referenced by DumpTypeIndex().

◆ DumpTypeIndex()

void WheatyExceptionReport::DumpTypeIndex ( DWORD64  modBase,
DWORD  dwTypeIndex,
DWORD_PTR  offset,
bool &  bHandled,
char const *  Name,
char *  ,
bool  newSymbol,
bool  logChildren 
)
staticprivate
1068{
1069 bHandled = false;
1070
1071 if (newSymbol)
1072 {
1074 }
1075
1076 DWORD typeTag;
1077 if (!SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_SYMTAG, &typeTag))
1078 {
1079 return;
1080 }
1081
1082 // Get the name of the symbol. This will either be a Type name (if a UDT),
1083 // or the structure member name.
1084 WCHAR* pwszTypeName;
1085 if (SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_SYMNAME,
1086 &pwszTypeName))
1087 {
1088 // handle special cases
1089 if (wcscmp(pwszTypeName, L"std::basic_string<char,std::char_traits<char>,std::allocator<char> >") == 0)
1090 {
1091 LocalFree(pwszTypeName);
1092 symbolDetails.top().Type = "std::string";
1093 char buffer[50];
1094 FormatOutputValue(buffer, btStdString, 0, (PVOID)offset, sizeof(buffer));
1095 symbolDetails.top().Value = buffer;
1096 if (Name != nullptr && Name[0] != '\0')
1097 {
1098 symbolDetails.top().Name = Name;
1099 }
1100 bHandled = true;
1101 return;
1102 }
1103
1104 char buffer[WER_SMALL_BUFFER_SIZE];
1105 wcstombs(buffer, pwszTypeName, sizeof(buffer));
1106 buffer[WER_SMALL_BUFFER_SIZE - 1] = '\0';
1107 if (Name != nullptr && Name[0] != '\0')
1108 {
1109 symbolDetails.top().Type = buffer;
1110 symbolDetails.top().Name = Name;
1111 }
1112 else if (buffer[0] != '\0')
1113 {
1114 symbolDetails.top().Name = buffer;
1115 }
1116
1117 LocalFree(pwszTypeName);
1118 }
1119 else if (Name != nullptr && Name[0] != '\0')
1120 {
1121 symbolDetails.top().Name = Name;
1122 }
1123
1124 if (!StoreSymbol(dwTypeIndex, offset))
1125 {
1126 // Skip printing address and base class if it has been printed already
1127 if (typeTag == SymTagBaseClass)
1128 {
1129 bHandled = true;
1130 }
1131 return;
1132 }
1133
1134 DWORD innerTypeID;
1135 switch (typeTag)
1136 {
1137 case SymTagPointerType:
1138 if (SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_TYPEID, &innerTypeID))
1139 {
1140 if (Name != nullptr && Name[0] != '\0')
1141 {
1142 symbolDetails.top().Name = Name;
1143 }
1144
1145 BOOL isReference;
1146 SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_IS_REFERENCE, &isReference);
1147
1148 char addressStr[40];
1149 memset(addressStr, 0, sizeof(addressStr));
1150
1151 if (isReference)
1152 {
1153 symbolDetails.top().Suffix += "&";
1154 }
1155 else
1156 {
1157 symbolDetails.top().Suffix += "*";
1158 }
1159
1160 // Try to dereference the pointer in a try/except block since it might be invalid
1161 DWORD_PTR address = DereferenceUnsafePointer(offset);
1162
1163 char buffer[WER_SMALL_BUFFER_SIZE];
1164 FormatOutputValue(buffer, btVoid, sizeof(PVOID), (PVOID)offset, sizeof(buffer));
1165 symbolDetails.top().Value = buffer;
1166
1168 {
1169 logChildren = false;
1170 }
1171
1172 // no need to log any children since the address is invalid anyway
1173 if (address == NULL || address == DWORD_PTR(-1))
1174 {
1175 logChildren = false;
1176 }
1177
1178 DumpTypeIndex(modBase, innerTypeID, address, bHandled, Name, addressStr, false, logChildren);
1179
1180 if (!bHandled)
1181 {
1182 BasicType basicType = GetBasicType(dwTypeIndex, modBase);
1183 if (symbolDetails.top().Type.empty())
1184 {
1185 symbolDetails.top().Type = rgBaseType[basicType];
1186 }
1187
1188 if (address == NULL)
1189 {
1190 symbolDetails.top().Value = "NULL";
1191 }
1192 else if (address == DWORD_PTR(-1))
1193 {
1194 symbolDetails.top().Value = "<Unable to read memory>";
1195 }
1196 else
1197 {
1198 // Get the size of the child member
1199 ULONG64 length;
1200 SymGetTypeInfo(m_hProcess, modBase, innerTypeID, TI_GET_LENGTH, &length);
1201 char buffer2[50];
1202 FormatOutputValue(buffer2, basicType, length, (PVOID)address, sizeof(buffer2));
1203 symbolDetails.top().Value = buffer2;
1204 }
1205 bHandled = true;
1206 return;
1207 }
1208 else if (address == NULL)
1209 {
1210 symbolDetails.top().Value = "NULL";
1211 }
1212 else if (address == DWORD_PTR(-1))
1213 {
1214 symbolDetails.top().Value = "<Unable to read memory>";
1215 bHandled = true;
1216 return;
1217 }
1218 }
1219 break;
1220 case SymTagData:
1221 if (SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_TYPEID, &innerTypeID))
1222 {
1223 DWORD innerTypeTag;
1224 if (!SymGetTypeInfo(m_hProcess, modBase, innerTypeID, TI_GET_SYMTAG, &innerTypeTag))
1225 {
1226 break;
1227 }
1228
1229 switch (innerTypeTag)
1230 {
1231 case SymTagUDT:
1233 {
1234 logChildren = false;
1235 }
1236 DumpTypeIndex(modBase, innerTypeID,
1237 offset, bHandled, symbolDetails.top().Name.c_str(), (char*)"", false, logChildren);
1238 break;
1239 case SymTagPointerType:
1240 if (Name != nullptr && Name[0] != '\0')
1241 {
1242 symbolDetails.top().Name = Name;
1243 }
1244 DumpTypeIndex(modBase, innerTypeID,
1245 offset, bHandled, symbolDetails.top().Name.c_str(), (char*)"", false, logChildren);
1246 break;
1247 case SymTagArrayType:
1248 DumpTypeIndex(modBase, innerTypeID,
1249 offset, bHandled, symbolDetails.top().Name.c_str(), (char*)"", false, logChildren);
1250 break;
1251 default:
1252 break;
1253 }
1254 }
1255 break;
1256 case SymTagArrayType:
1257 if (SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_TYPEID, &innerTypeID))
1258 {
1259 symbolDetails.top().HasChildren = true;
1260
1261 BasicType basicType = btNoType;
1262 DumpTypeIndex(modBase, innerTypeID,
1263 offset, bHandled, Name, (char*)"", false, false);
1264
1265 // Set Value back to an empty string since the Array object itself has no value, only its elements have
1266 std::string firstElementValue = symbolDetails.top().Value;
1267 symbolDetails.top().Value.clear();
1268
1269 DWORD elementsCount;
1270 if (SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_COUNT, &elementsCount))
1271 {
1272 symbolDetails.top().Suffix += "[" + std::to_string(elementsCount) + "]";
1273 }
1274 else
1275 {
1276 symbolDetails.top().Suffix += "[<unknown count>]";
1277 }
1278
1279 if (!bHandled)
1280 {
1281 basicType = GetBasicType(dwTypeIndex, modBase);
1282 if (symbolDetails.top().Type.empty())
1283 {
1284 symbolDetails.top().Type = rgBaseType[basicType];
1285 }
1286 bHandled = true;
1287 }
1288
1289 // Get the size of the child member
1290 ULONG64 length;
1291 SymGetTypeInfo(m_hProcess, modBase, innerTypeID, TI_GET_LENGTH, &length);
1292
1293 char buffer[50];
1294 switch (basicType)
1295 {
1296 case btChar:
1297 case btStdString:
1298 FormatOutputValue(buffer, basicType, length, (PVOID)offset, sizeof(buffer), elementsCount);
1299 symbolDetails.top().Value = buffer;
1300 break;
1301 default:
1302 for (DWORD index = 0; index < elementsCount && index < WER_MAX_ARRAY_ELEMENTS_COUNT; index++)
1303 {
1304 bool elementHandled = false;
1306 if (index == 0)
1307 {
1308 if (firstElementValue.empty())
1309 {
1310 FormatOutputValue(buffer, basicType, length, (PVOID)(offset + length * index), sizeof(buffer));
1311 firstElementValue = buffer;
1312 }
1313 symbolDetails.top().Value = firstElementValue;
1314 }
1315 else
1316 {
1317 DumpTypeIndex(modBase, innerTypeID, offset + length * index, elementHandled, "", (char*)"", false, false);
1318 if (!elementHandled)
1319 {
1320 FormatOutputValue(buffer, basicType, length, (PVOID)(offset + length * index), sizeof(buffer));
1321 symbolDetails.top().Value = buffer;
1322 }
1323 }
1324 symbolDetails.top().Prefix.clear();
1325 symbolDetails.top().Type.clear();
1326 symbolDetails.top().Suffix = "[" + std::to_string(index) + "]";
1327 symbolDetails.top().Name.clear();
1329 }
1330 break;
1331 }
1332
1333 return;
1334 }
1335 break;
1336 case SymTagBaseType:
1337 break;
1338 case SymTagEnum:
1339 return;
1340 default:
1341 break;
1342 }
1343
1344 // Determine how many children this type has.
1345 DWORD dwChildrenCount = 0;
1346 SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_CHILDRENCOUNT, &dwChildrenCount);
1347
1348 if (!dwChildrenCount) // If no children, we're done
1349 {
1350 return;
1351 }
1352
1353 // Prepare to get an array of "TypeIds", representing each of the children.
1354 // SymGetTypeInfo(TI_FINDCHILDREN) expects more memory than just a
1355 // TI_FINDCHILDREN_PARAMS struct has. Use derivation to accomplish this.
1356 struct FINDCHILDREN : TI_FINDCHILDREN_PARAMS
1357 {
1358 ULONG MoreChildIds[1024 * 2];
1359 FINDCHILDREN() {Count = sizeof(MoreChildIds) / sizeof(MoreChildIds[0]);}
1360 } children;
1361
1362 children.Count = dwChildrenCount;
1363 children.Start = 0;
1364
1365 // Get the array of TypeIds, one for each child type
1366 if (!SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_FINDCHILDREN,
1367 &children))
1368 {
1369 return;
1370 }
1371
1372 // Iterate through each of the children
1373 for (unsigned i = 0; i < dwChildrenCount; i++)
1374 {
1375 DWORD symTag;
1376 SymGetTypeInfo(m_hProcess, modBase, children.ChildId[i], TI_GET_SYMTAG, &symTag);
1377
1378 if (symTag == SymTagFunction ||
1379 symTag == SymTagEnum ||
1380 symTag == SymTagTypedef ||
1381 symTag == SymTagVTable)
1382 {
1383 continue;
1384 }
1385
1386 // Ignore static fields
1387 DWORD dataKind;
1388 SymGetTypeInfo(m_hProcess, modBase, children.ChildId[i], TI_GET_DATAKIND, &dataKind);
1389 if (dataKind == DataIsStaticLocal ||
1390 dataKind == DataIsGlobal ||
1391 dataKind == DataIsStaticMember)
1392 {
1393 continue;
1394 }
1395
1396 symbolDetails.top().HasChildren = true;
1397 if (!logChildren)
1398 {
1399 bHandled = false;
1400 return;
1401 }
1402
1403 // Recurse for each of the child types
1404 bool bHandled2;
1405 BasicType basicType = GetBasicType(children.ChildId[i], modBase);
1406
1407 // Get the offset of the child member, relative to its parent
1408 DWORD dwMemberOffset;
1409 SymGetTypeInfo(m_hProcess, modBase, children.ChildId[i],
1410 TI_GET_OFFSET, &dwMemberOffset);
1411
1412 // Calculate the address of the member
1413 DWORD_PTR dwFinalOffset = offset + dwMemberOffset;
1414
1415 DumpTypeIndex(modBase,
1416 children.ChildId[i],
1417 dwFinalOffset, bHandled2, ""/*Name */, (char*)"", true, true);
1418
1419 // If the child wasn't a UDT, format it appropriately
1420 if (!bHandled2)
1421 {
1422 if (symbolDetails.top().Type.empty())
1423 {
1424 symbolDetails.top().Type = rgBaseType[basicType];
1425 }
1426
1427 // Get the real "TypeId" of the child. We need this for the
1428 // SymGetTypeInfo(TI_GET_TYPEID) call below.
1429 DWORD typeId;
1430 SymGetTypeInfo(m_hProcess, modBase, children.ChildId[i],
1431 TI_GET_TYPEID, &typeId);
1432
1433 // Get the size of the child member
1434 ULONG64 length;
1435 SymGetTypeInfo(m_hProcess, modBase, typeId, TI_GET_LENGTH, &length);
1436
1437 char buffer[50];
1438 FormatOutputValue(buffer, basicType, length, (PVOID)dwFinalOffset, sizeof(buffer));
1439 symbolDetails.top().Value = buffer;
1440 }
1441
1443 }
1444
1445 bHandled = true;
1446 return;
1447}
char const *const rgBaseType[]
Definition: WheatyExceptionReport.h:59
#define WER_SMALL_BUFFER_SIZE
Definition: WheatyExceptionReport.h:17
#define WER_MAX_NESTING_LEVEL
Definition: WheatyExceptionReport.h:16
#define WER_MAX_ARRAY_ELEMENTS_COUNT
Definition: WheatyExceptionReport.h:15
@ DataIsStaticMember
Definition: WheatyExceptionReport.h:55
@ DataIsGlobal
Definition: WheatyExceptionReport.h:53
@ DataIsStaticLocal
Definition: WheatyExceptionReport.h:49
BasicType
Definition: WheatyExceptionReport.h:21
@ btVoid
Definition: WheatyExceptionReport.h:23
@ btStdString
Definition: WheatyExceptionReport.h:42
@ btChar
Definition: WheatyExceptionReport.h:24
@ btNoType
Definition: WheatyExceptionReport.h:22
size_t Count(const ContainerMapList< SPECIFIC_TYPE > &elements, SPECIFIC_TYPE *)
Definition: TypeContainerFunctions.h:162
static void FormatOutputValue(char *pszCurrBuffer, BasicType basicType, DWORD64 length, PVOID pAddress, size_t bufferSize, size_t countOverride=0)
Definition: WheatyExceptionReport.cpp:1449
static BasicType GetBasicType(DWORD typeIndex, DWORD64 modBase)
Definition: WheatyExceptionReport.cpp:1547
static DWORD_PTR DereferenceUnsafePointer(DWORD_PTR address)
Definition: WheatyExceptionReport.cpp:1571
static void PushSymbolDetail()
Definition: WheatyExceptionReport.cpp:1648
static void PopSymbolDetail()
Definition: WheatyExceptionReport.cpp:1655
static bool StoreSymbol(DWORD type, DWORD_PTR offset)
Definition: WheatyExceptionReport.cpp:1634
static void DumpTypeIndex(DWORD64, DWORD, DWORD_PTR, bool &, char const *, char *, bool, bool)
Definition: WheatyExceptionReport.cpp:1059

References btChar, btNoType, btStdString, btVoid, DataIsGlobal, DataIsStaticLocal, DataIsStaticMember, DereferenceUnsafePointer(), DumpTypeIndex(), FormatOutputValue(), GetBasicType(), m_hProcess, PopSymbolDetail(), PushSymbolDetail(), rgBaseType, StoreSymbol(), symbolDetails, WER_MAX_ARRAY_ELEMENTS_COUNT, WER_MAX_NESTING_LEVEL, and WER_SMALL_BUFFER_SIZE.

Referenced by DumpTypeIndex(), and FormatSymbolValue().

◆ EnumerateSymbolsCallback()

BOOL CALLBACK WheatyExceptionReport::EnumerateSymbolsCallback ( PSYMBOL_INFO  pSymInfo,
ULONG  ,
PVOID  UserContext 
)
staticprivate
956{
957 __try
958 {
959 ClearSymbols();
960 FormatSymbolValue(pSymInfo, (STACKFRAME64*)UserContext);
961 }
962 __except (EXCEPTION_EXECUTE_HANDLER)
963 {
964 Log(_T("punting on symbol %s, partial output:\r\n"), pSymInfo->Name);
965 }
966
967 return TRUE;
968}
static bool FormatSymbolValue(PSYMBOL_INFO, STACKFRAME64 *)
Definition: WheatyExceptionReport.cpp:975
Definition: Log.h:49

References ClearSymbols(), and FormatSymbolValue().

Referenced by WriteStackDetails().

◆ FormatOutputValue()

void WheatyExceptionReport::FormatOutputValue ( char *  pszCurrBuffer,
BasicType  basicType,
DWORD64  length,
PVOID  pAddress,
size_t  bufferSize,
size_t  countOverride = 0 
)
staticprivate
1455{
1456 __try
1457 {
1458 switch (basicType)
1459 {
1460 case btChar:
1461 {
1462 // Special case handling for char[] type
1463 if (countOverride != 0)
1464 {
1465 length = countOverride;
1466 }
1467 else
1468 {
1469 length = strlen((char*)pAddress);
1470 }
1471 if (length > bufferSize - 6)
1472 {
1473 pszCurrBuffer += sprintf(pszCurrBuffer, "\"%.*s...\"", (DWORD)(bufferSize - 6), (char*)pAddress);
1474 }
1475 else
1476 {
1477 pszCurrBuffer += sprintf(pszCurrBuffer, "\"%.*s\"", (DWORD)length, (char*)pAddress);
1478 }
1479 break;
1480 }
1481 case btStdString:
1482 {
1483 std::string* value = static_cast<std::string*>(pAddress);
1484 if (value->length() > bufferSize - 6)
1485 {
1486 pszCurrBuffer += sprintf(pszCurrBuffer, "\"%.*s...\"", (DWORD)(bufferSize - 6), value->c_str());
1487 }
1488 else
1489 {
1490 pszCurrBuffer += sprintf(pszCurrBuffer, "\"%s\"", value->c_str());
1491 }
1492 break;
1493 }
1494 default:
1495 // Format appropriately (assuming it's a 1, 2, or 4 bytes (!!!)
1496 if (length == 1)
1497 {
1498 pszCurrBuffer += sprintf(pszCurrBuffer, "0x%X", *(PBYTE)pAddress);
1499 }
1500 else if (length == 2)
1501 {
1502 pszCurrBuffer += sprintf(pszCurrBuffer, "0x%X", *(PWORD)pAddress);
1503 }
1504 else if (length == 4)
1505 {
1506 if (basicType == btFloat)
1507 {
1508 pszCurrBuffer += sprintf(pszCurrBuffer, "%f", *(PFLOAT)pAddress);
1509 }
1510 else
1511 {
1512 pszCurrBuffer += sprintf(pszCurrBuffer, "0x%X", *(PDWORD)pAddress);
1513 }
1514 }
1515 else if (length == 8)
1516 {
1517 if (basicType == btFloat)
1518 {
1519 pszCurrBuffer += sprintf(pszCurrBuffer, "%f",
1520 *(double*)pAddress);
1521 }
1522 else
1523 pszCurrBuffer += sprintf(pszCurrBuffer, "0x%I64X",
1524 *(DWORD64*)pAddress);
1525 }
1526 else
1527 {
1528#if _WIN64
1529 pszCurrBuffer += sprintf(pszCurrBuffer, "0x%I64X", (DWORD64)pAddress);
1530#else
1531 pszCurrBuffer += sprintf(pszCurrBuffer, "0x%X", (DWORD)pAddress);
1532#endif
1533 }
1534 break;
1535 }
1536 }
1537 __except (EXCEPTION_EXECUTE_HANDLER)
1538 {
1539#if _WIN64
1540 pszCurrBuffer += sprintf(pszCurrBuffer, "0x%I64X <Unable to read memory>", (DWORD64)pAddress);
1541#else
1542 pszCurrBuffer += sprintf(pszCurrBuffer, "0x%X <Unable to read memory>", (DWORD)pAddress);
1543#endif
1544 }
1545}
@ btFloat
Definition: WheatyExceptionReport.h:28
const size_t bufferSize
Definition: RASession.h:29

References btChar, btFloat, btStdString, and bufferSize.

Referenced by DumpTypeIndex(), and FormatSymbolValue().

◆ FormatSymbolValue()

bool WheatyExceptionReport::FormatSymbolValue ( PSYMBOL_INFO  pSym,
STACKFRAME64 *  sf 
)
staticprivate
978{
979 // If it's a function, don't do anything.
980 if (pSym->Tag == SymTagFunction) // SymTagFunction from CVCONST.H from the DIA SDK
981 {
982 return false;
983 }
984
985 DWORD_PTR pVariable = 0; // Will point to the variable's data in memory
986
987 if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGRELATIVE)
988 {
989 // if (pSym->Register == 8) // EBP is the value 8 (in DBGHELP 5.1)
990 {
991 // This may change!!!
992#ifdef _M_IX86
993 pVariable = sf->AddrFrame.Offset;
994#elif _M_X64
995 pVariable = sf->AddrStack.Offset;
996#endif
997 pVariable += (DWORD_PTR)pSym->Address;
998 }
999 // else
1000 // return false;
1001 }
1002 else if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGISTER)
1003 {
1004 return false; // Don't try to report register variable
1005 }
1006 else
1007 {
1008 pVariable = (DWORD_PTR)pSym->Address; // It must be a global variable
1009 }
1010
1012
1013 // Indicate if the variable is a local or parameter
1014 if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_PARAMETER)
1015 {
1016 symbolDetails.top().Prefix = "Parameter ";
1017 }
1018 else if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_LOCAL)
1019 {
1020 symbolDetails.top().Prefix = "Local ";
1021 }
1022
1023 // Determine if the variable is a user defined type (UDT). IF so, bHandled
1024 // will return true.
1025 bool bHandled;
1026 DumpTypeIndex(pSym->ModBase, pSym->TypeIndex, pVariable, bHandled, pSym->Name, (char*)"", false, true);
1027
1028 if (!bHandled)
1029 {
1030 // The symbol wasn't a UDT, so do basic, stupid formatting of the
1031 // variable. Based on the size, we're assuming it's a char, WORD, or
1032 // DWORD.
1033 BasicType basicType = GetBasicType(pSym->TypeIndex, pSym->ModBase);
1034 if (symbolDetails.top().Type.empty())
1035 {
1036 symbolDetails.top().Type = rgBaseType[basicType];
1037 }
1038
1039 // Emit the variable name
1040 if (pSym->Name[0] != '\0')
1041 {
1042 symbolDetails.top().Name = pSym->Name;
1043 }
1044
1045 char buffer[50];
1046 FormatOutputValue(buffer, basicType, pSym->Size, (PVOID)pVariable, sizeof(buffer));
1047 symbolDetails.top().Value = buffer;
1048 }
1049
1051 return true;
1052}

References DumpTypeIndex(), FormatOutputValue(), GetBasicType(), PopSymbolDetail(), PushSymbolDetail(), rgBaseType, and symbolDetails.

Referenced by EnumerateSymbolsCallback().

◆ GenerateExceptionReport()

void WheatyExceptionReport::GenerateExceptionReport ( PEXCEPTION_POINTERS  pExceptionInfo)
staticprivate
597{
598 __try
599 {
600 SYSTEMTIME systime;
601 GetLocalTime(&systime);
602
603 // Start out with a banner
604 Log(_T("Revision: %s\r\n"), GitRevision::GetFullVersion());
605 Log(_T("Date %u:%u:%u. Time %u:%u \r\n"), systime.wDay, systime.wMonth, systime.wYear, systime.wHour, systime.wMinute);
606 PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord;
607
609 // First print information about the type of fault
610 Log(_T("\r\n//=====================================================\r\n"));
611 Log(_T("Exception code: %08X %s\r\n"),
612 pExceptionRecord->ExceptionCode,
613 GetExceptionString(pExceptionRecord->ExceptionCode));
614 if (pExceptionRecord->ExceptionCode == EXCEPTION_ASSERTION_FAILURE && pExceptionRecord->NumberParameters >= 2)
615 {
616 pExceptionRecord->ExceptionAddress = reinterpret_cast<PVOID>(pExceptionRecord->ExceptionInformation[1]);
617 Log(_T("Assertion message: %s\r\n"), pExceptionRecord->ExceptionInformation[0]);
618 }
619
620 // Now print information about where the fault occured
621 TCHAR szFaultingModule[MAX_PATH];
622 DWORD section;
623 DWORD_PTR offset;
624 GetLogicalAddress(pExceptionRecord->ExceptionAddress,
625 szFaultingModule,
626 sizeof(szFaultingModule),
627 section, offset);
628
629#ifdef _M_IX86
630 Log(_T("Fault address: %08X %02X:%08X %s\r\n"),
631 pExceptionRecord->ExceptionAddress,
632 section, offset, szFaultingModule);
633#endif
634#ifdef _M_X64
635 Log(_T("Fault address: %016I64X %02X:%016I64X %s\r\n"),
636 pExceptionRecord->ExceptionAddress,
637 section, offset, szFaultingModule);
638#endif
639
640 PCONTEXT pCtx = pExceptionInfo->ContextRecord;
641
642 // Show the registers
643#ifdef _M_IX86 // X86 Only!
644 Log(_T("\r\nRegisters:\r\n"));
645
646 Log(_T("EAX:%08X\r\nEBX:%08X\r\nECX:%08X\r\nEDX:%08X\r\nESI:%08X\r\nEDI:%08X\r\n")
647 , pCtx->Eax, pCtx->Ebx, pCtx->Ecx, pCtx->Edx,
648 pCtx->Esi, pCtx->Edi);
649
650 Log(_T("CS:EIP:%04X:%08X\r\n"), pCtx->SegCs, pCtx->Eip);
651 Log(_T("SS:ESP:%04X:%08X EBP:%08X\r\n"),
652 pCtx->SegSs, pCtx->Esp, pCtx->Ebp);
653 Log(_T("DS:%04X ES:%04X FS:%04X GS:%04X\r\n"),
654 pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs);
655 Log(_T("Flags:%08X\r\n"), pCtx->EFlags);
656#endif
657
658#ifdef _M_X64
659 Log(_T("\r\nRegisters:\r\n"));
660 Log(_T("RAX:%016I64X\r\nRBX:%016I64X\r\nRCX:%016I64X\r\nRDX:%016I64X\r\nRSI:%016I64X\r\nRDI:%016I64X\r\n")
661 _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")
662 , pCtx->Rax, pCtx->Rbx, pCtx->Rcx, pCtx->Rdx,
663 pCtx->Rsi, pCtx->Rdi, pCtx->R9, pCtx->R10, pCtx->R11, pCtx->R12, pCtx->R13, pCtx->R14, pCtx->R15);
664 Log(_T("CS:RIP:%04X:%016I64X\r\n"), pCtx->SegCs, pCtx->Rip);
665 Log(_T("SS:RSP:%04X:%016X RBP:%08X\r\n"),
666 pCtx->SegSs, pCtx->Rsp, pCtx->Rbp);
667 Log(_T("DS:%04X ES:%04X FS:%04X GS:%04X\r\n"),
668 pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs);
669 Log(_T("Flags:%08X\r\n"), pCtx->EFlags);
670#endif
671
672 SymSetOptions(SYMOPT_DEFERRED_LOADS);
673
674 // Initialize DbgHelp
675 if (!SymInitialize(GetCurrentProcess(), 0, TRUE))
676 {
677 Log(_T("\n\rCRITICAL ERROR.\n\r Couldn't initialize the symbol handler for process.\n\rError [%s].\n\r\n\r"),
678 ErrorMessage(GetLastError()));
679 }
680
681 CONTEXT trashableContext = *pCtx;
682
683 WriteStackDetails(&trashableContext, false, nullptr);
685
686 // #ifdef _M_IX86 // X86 Only!
687
688 Log(_T("========================\r\n"));
689 Log(_T("Local Variables And Parameters\r\n"));
690
691 trashableContext = *pCtx;
692 WriteStackDetails(&trashableContext, true, nullptr);
694
695 SymCleanup(GetCurrentProcess());
696
697 Log(_T("\r\n"));
698 }
699 __except (EXCEPTION_EXECUTE_HANDLER)
700 {
701 Log(_T("Error writing the crash log\r\n"));
702 }
703}
#define EXCEPTION_ASSERTION_FAILURE
Definition: Errors.h:73
LPTSTR ErrorMessage(DWORD dw)
Definition: WheatyExceptionReport.cpp:32
AC_COMMON_API char const * GetFullVersion()
Definition: GitRevision.cpp:70
static BOOL GetLogicalAddress(PVOID addr, PTSTR szModule, DWORD len, DWORD &section, DWORD_PTR &offset)
Definition: WheatyExceptionReport.cpp:758
static void PrintSystemInfo()
Definition: WheatyExceptionReport.cpp:513
static void WriteStackDetails(PCONTEXT pContext, bool bWriteVariables, HANDLE pThreadHandle)
Definition: WheatyExceptionReport.cpp:829
static LPTSTR GetExceptionString(DWORD dwCode)
Definition: WheatyExceptionReport.cpp:709
static void printTracesForAllThreads(bool)
Definition: WheatyExceptionReport.cpp:541

References ErrorMessage(), EXCEPTION_ASSERTION_FAILURE, GetExceptionString(), GitRevision::GetFullVersion(), GetLogicalAddress(), PrintSystemInfo(), printTracesForAllThreads(), and WriteStackDetails().

Referenced by WheatyUnhandledExceptionFilter().

◆ GetBasicType()

BasicType WheatyExceptionReport::GetBasicType ( DWORD  typeIndex,
DWORD64  modBase 
)
staticprivate
1548{
1549 BasicType basicType;
1550 if (SymGetTypeInfo(m_hProcess, modBase, typeIndex,
1551 TI_GET_BASETYPE, &basicType))
1552 {
1553 return basicType;
1554 }
1555
1556 // Get the real "TypeId" of the child. We need this for the
1557 // SymGetTypeInfo(TI_GET_TYPEID) call below.
1558 DWORD typeId;
1559 if (SymGetTypeInfo(m_hProcess, modBase, typeIndex, TI_GET_TYPEID, &typeId))
1560 {
1561 if (SymGetTypeInfo(m_hProcess, modBase, typeId, TI_GET_BASETYPE,
1562 &basicType))
1563 {
1564 return basicType;
1565 }
1566 }
1567
1568 return btNoType;
1569}

References btNoType, and m_hProcess.

Referenced by DumpTypeIndex(), and FormatSymbolValue().

◆ GetExceptionString()

LPTSTR WheatyExceptionReport::GetExceptionString ( DWORD  dwCode)
staticprivate
710{
711#define EXCEPTION(x) case EXCEPTION_##x: return LPTSTR(_T(#x));
712
713 switch (dwCode)
714 {
715 EXCEPTION(ACCESS_VIOLATION)
716 EXCEPTION(DATATYPE_MISALIGNMENT)
717 EXCEPTION(BREAKPOINT)
718 EXCEPTION(SINGLE_STEP)
719 EXCEPTION(ARRAY_BOUNDS_EXCEEDED)
720 EXCEPTION(FLT_DENORMAL_OPERAND)
721 EXCEPTION(FLT_DIVIDE_BY_ZERO)
722 EXCEPTION(FLT_INEXACT_RESULT)
723 EXCEPTION(FLT_INVALID_OPERATION)
724 EXCEPTION(FLT_OVERFLOW)
725 EXCEPTION(FLT_STACK_CHECK)
726 EXCEPTION(FLT_UNDERFLOW)
727 EXCEPTION(INT_DIVIDE_BY_ZERO)
728 EXCEPTION(INT_OVERFLOW)
729 EXCEPTION(PRIV_INSTRUCTION)
730 EXCEPTION(IN_PAGE_ERROR)
731 EXCEPTION(ILLEGAL_INSTRUCTION)
732 EXCEPTION(NONCONTINUABLE_EXCEPTION)
733 EXCEPTION(STACK_OVERFLOW)
734 EXCEPTION(INVALID_DISPOSITION)
735 EXCEPTION(GUARD_PAGE)
736 EXCEPTION(INVALID_HANDLE)
737 }
738
739 // If not one of the "known" exceptions, try to get the string
740 // from NTDLL.DLL's message table.
741
742 static TCHAR szBuffer[512] = { 0 };
743
744 FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE,
745 GetModuleHandle(_T("NTDLL.DLL")),
746 dwCode, 0, szBuffer, sizeof(szBuffer), 0);
747
748 return szBuffer;
749}
#define EXCEPTION(x)

References EXCEPTION.

Referenced by GenerateExceptionReport().

◆ GetLogicalAddress()

BOOL WheatyExceptionReport::GetLogicalAddress ( PVOID  addr,
PTSTR  szModule,
DWORD  len,
DWORD &  section,
DWORD_PTR &  offset 
)
staticprivate
760{
761 MEMORY_BASIC_INFORMATION mbi;
762
763 if (!VirtualQuery(addr, &mbi, sizeof(mbi)))
764 {
765 return FALSE;
766 }
767
768 DWORD_PTR hMod = (DWORD_PTR)mbi.AllocationBase;
769
770 if (!hMod)
771 {
772 return FALSE;
773 }
774
775 if (!GetModuleFileName((HMODULE)hMod, szModule, len))
776 {
777 return FALSE;
778 }
779
780 // Point to the DOS header in memory
781 PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)hMod;
782
783 // From the DOS header, find the NT (PE) header
784 PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)(hMod + DWORD_PTR(pDosHdr->e_lfanew));
785
786 PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNtHdr);
787
788 DWORD_PTR rva = (DWORD_PTR)addr - hMod; // RVA is offset from module load address
789
790 // Iterate through the section table, looking for the one that encompasses
791 // the linear address.
792 for (unsigned i = 0;
793 i < pNtHdr->FileHeader.NumberOfSections;
794 i++, pSection++)
795 {
796 DWORD_PTR sectionStart = pSection->VirtualAddress;
797 DWORD_PTR sectionEnd = sectionStart
798 + DWORD_PTR(std::max(pSection->SizeOfRawData, pSection->Misc.VirtualSize));
799
800 // Is the address in this section???
801 if ((rva >= sectionStart) && (rva <= sectionEnd))
802 {
803 // Yes, address is in the section. Calculate section and offset,
804 // and store in the "section" & "offset" params, which were
805 // passed by reference.
806 section = i + 1;
807 offset = rva - sectionStart;
808 return TRUE;
809 }
810 }
811
812 return FALSE; // Should never get here!
813}

Referenced by GenerateExceptionReport(), and WriteStackDetails().

◆ HeapLog()

int __cdecl WheatyExceptionReport::HeapLog ( const TCHAR *  format,
va_list  argptr 
)
staticprivate
1620{
1621 int retValue = 0;
1622 DWORD cbWritten;
1623 TCHAR* szBuff = (TCHAR*)malloc(sizeof(TCHAR) * WER_LARGE_BUFFER_SIZE);
1624 if (szBuff != nullptr)
1625 {
1626 retValue = vsprintf(szBuff, format, argptr);
1627 WriteFile(m_hReportFile, szBuff, retValue * sizeof(TCHAR), &cbWritten, 0);
1628 free(szBuff);
1629 }
1630
1631 return retValue;
1632}
#define WER_LARGE_BUFFER_SIZE
Definition: WheatyExceptionReport.h:18
static HANDLE m_hReportFile
Definition: WheatyExceptionReport.h:184

References m_hReportFile, and WER_LARGE_BUFFER_SIZE.

Referenced by Log().

◆ Log()

int __cdecl WheatyExceptionReport::Log ( const TCHAR *  format,
  ... 
)
staticprivate
1588{
1589 int retValue;
1590 va_list argptr;
1591 va_start(argptr, format);
1592
1594 {
1595 retValue = HeapLog(format, argptr);
1596 }
1597 else
1598 {
1599 retValue = StackLog(format, argptr);
1600 }
1601
1602 va_end(argptr);
1603
1604 return retValue;
1605}
static int __cdecl StackLog(const TCHAR *format, va_list argptr)
Definition: WheatyExceptionReport.cpp:1607
static int __cdecl HeapLog(const TCHAR *format, va_list argptr)
Definition: WheatyExceptionReport.cpp:1619

References HeapLog(), StackLog(), and stackOverflowException.

◆ PopSymbolDetail()

void WheatyExceptionReport::PopSymbolDetail ( )
staticprivate
1656{
1658 symbolDetails.pop();
1659}
static void PrintSymbolDetail()
Definition: WheatyExceptionReport.cpp:1661

References PrintSymbolDetail(), and symbolDetails.

Referenced by DumpTypeIndex(), and FormatSymbolValue().

◆ PrintSymbolDetail()

void WheatyExceptionReport::PrintSymbolDetail ( )
staticprivate
1662{
1663 if (symbolDetails.empty())
1664 {
1665 return;
1666 }
1667
1668 // Don't log anything if has been logged already or if it's empty
1669 if (symbolDetails.top().Logged || symbolDetails.top().empty())
1670 {
1671 return;
1672 }
1673
1674 // Add appropriate indentation level (since this routine is recursive)
1675 for (size_t i = 0; i < symbolDetails.size(); i++)
1676 {
1677 Log(_T("\t"));
1678 }
1679
1680 Log(_T("%s\r\n"), symbolDetails.top().ToString().c_str());
1681
1682 return;
1683}

References symbolDetails.

Referenced by PopSymbolDetail(), and PushSymbolDetail().

◆ PrintSystemInfo()

void WheatyExceptionReport::PrintSystemInfo ( )
staticprivate
514{
515 SYSTEM_INFO SystemInfo;
516 ::GetSystemInfo(&SystemInfo);
517
518 MEMORYSTATUS MemoryStatus;
519 MemoryStatus.dwLength = sizeof (MEMORYSTATUS);
520 ::GlobalMemoryStatus(&MemoryStatus);
521 TCHAR sString[1024];
522 Log(_T("//=====================================================\r\n"));
523 if (_GetProcessorName(sString, countof(sString)))
524 Log(_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"),
525 sString, SystemInfo.dwNumberOfProcessors, MemoryStatus.dwTotalPhys / 0x400, MemoryStatus.dwAvailPhys / 0x400, MemoryStatus.dwTotalPageFile / 0x400);
526 else
527 Log(_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"),
528 SystemInfo.dwNumberOfProcessors, MemoryStatus.dwTotalPhys / 0x400, MemoryStatus.dwAvailPhys / 0x400, MemoryStatus.dwTotalPageFile / 0x400);
529
530 if (_GetWindowsVersion(sString, countof(sString)))
531 {
532 Log(_T("\r\n*** Operation System ***\r\n%s\r\n"), sString);
533 }
534 else
535 {
536 Log(_T("\r\n*** Operation System:\r\n<unknown>\r\n"));
537 }
538}
#define countof
Definition: WheatyExceptionReport.h:13
static BOOL _GetProcessorName(TCHAR *sProcessorName, DWORD maxcount)
Definition: WheatyExceptionReport.cpp:228
static BOOL _GetWindowsVersion(TCHAR *szVersion, DWORD cntMax)
Definition: WheatyExceptionReport.cpp:275

References _GetProcessorName(), _GetWindowsVersion(), and countof.

Referenced by GenerateExceptionReport().

◆ printTracesForAllThreads()

void WheatyExceptionReport::printTracesForAllThreads ( bool  bWriteVariables)
static
542{
543 THREADENTRY32 te32;
544
545 DWORD dwOwnerPID = GetCurrentProcessId();
546 m_hProcess = GetCurrentProcess();
547 // Take a snapshot of all running threads
548 HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
549 if (hThreadSnap == INVALID_HANDLE_VALUE)
550 {
551 return;
552 }
553
554 // Fill in the size of the structure before using it.
555 te32.dwSize = sizeof(THREADENTRY32);
556
557 // Retrieve information about the first thread,
558 // and exit if unsuccessful
559 if (!Thread32First(hThreadSnap, &te32))
560 {
561 CloseHandle(hThreadSnap); // Must clean up the
562 // snapshot object!
563 return;
564 }
565
566 // Now walk the thread list of the system,
567 // and display information about each thread
568 // associated with the specified process
569 do
570 {
571 if (te32.th32OwnerProcessID == dwOwnerPID)
572 {
573 CONTEXT context;
574 context.ContextFlags = 0xffffffff;
575 HANDLE threadHandle = OpenThread(THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION, false, te32.th32ThreadID);
576 if (threadHandle)
577 {
578 if (GetThreadContext(threadHandle, &context))
579 {
580 WriteStackDetails(&context, bWriteVariables, threadHandle);
581 }
582 CloseHandle(threadHandle);
583 }
584 }
585 } while (Thread32Next(hThreadSnap, &te32));
586
587 // Don't forget to clean up the snapshot object.
588 CloseHandle(hThreadSnap);
589}

References m_hProcess, and WriteStackDetails().

Referenced by GenerateExceptionReport().

◆ PushSymbolDetail()

void WheatyExceptionReport::PushSymbolDetail ( )
staticprivate
1649{
1650 // Log current symbol and then add another to the stack to keep the hierarchy format
1652 symbolDetails.emplace();
1653}

References PrintSymbolDetail(), and symbolDetails.

Referenced by DumpTypeIndex(), and FormatSymbolValue().

◆ StackLog()

int __cdecl WheatyExceptionReport::StackLog ( const TCHAR *  format,
va_list  argptr 
)
staticprivate
1608{
1609 int retValue;
1610 DWORD cbWritten;
1611
1612 TCHAR szBuff[WER_LARGE_BUFFER_SIZE];
1613 retValue = vsprintf(szBuff, format, argptr);
1614 WriteFile(m_hReportFile, szBuff, retValue * sizeof(TCHAR), &cbWritten, 0);
1615
1616 return retValue;
1617}

References m_hReportFile, and WER_LARGE_BUFFER_SIZE.

Referenced by Log().

◆ StoreSymbol()

bool WheatyExceptionReport::StoreSymbol ( DWORD  type,
DWORD_PTR  offset 
)
staticprivate
1635{
1636 return symbols.insert(SymbolPair(type, offset)).second;
1637}
Definition: WheatyExceptionReport.h:96

References symbols.

Referenced by DumpTypeIndex().

◆ WheatyCrtHandler()

void __cdecl WheatyExceptionReport::WheatyCrtHandler ( wchar_t const *  expression,
wchar_t const *  function,
wchar_t const *  file,
unsigned int  line,
uintptr_t  pReserved 
)
static
224{
225 RaiseException(EXCEPTION_ACCESS_VIOLATION, 0, 0, nullptr);
226}

Referenced by WheatyExceptionReport().

◆ WheatyUnhandledExceptionFilter()

LONG WINAPI WheatyExceptionReport::WheatyUnhandledExceptionFilter ( PEXCEPTION_POINTERS  pExceptionInfo)
static
118{
119 std::unique_lock<std::mutex> guard(alreadyCrashedLock);
120 // Handle only 1 exception in the whole process lifetime
121 if (alreadyCrashed)
122 {
123 return EXCEPTION_EXECUTE_HANDLER;
124 }
125
126 alreadyCrashed = true;
127
128 if (pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_STACK_OVERFLOW)
129 {
131 }
132
133 TCHAR module_folder_name[MAX_PATH];
134 GetModuleFileName(0, module_folder_name, MAX_PATH);
135 TCHAR* pos = _tcsrchr(module_folder_name, '\\');
136 if (!pos)
137 {
138 return 0;
139 }
140 pos[0] = '\0';
141 ++pos;
142
143 TCHAR crash_folder_path[MAX_PATH];
144 sprintf_s(crash_folder_path, "%s\\%s", module_folder_name, CrashFolder);
145 if (!CreateDirectory(crash_folder_path, nullptr))
146 {
147 if (GetLastError() != ERROR_ALREADY_EXISTS)
148 {
149 return 0;
150 }
151 }
152
153 SYSTEMTIME systime;
154 GetLocalTime(&systime);
155 sprintf(m_szDumpFileName, "%s\\%s_%s_[%u-%u_%u-%u-%u].dmp",
156 crash_folder_path, GitRevision::GetHash(), pos, systime.wDay, systime.wMonth, systime.wHour, systime.wMinute, systime.wSecond);
157
158 sprintf(m_szLogFileName, "%s\\%s_%s_[%u-%u_%u-%u-%u].txt",
159 crash_folder_path, GitRevision::GetHash(), pos, systime.wDay, systime.wMonth, systime.wHour, systime.wMinute, systime.wSecond);
160
161 m_hDumpFile = CreateFile(m_szDumpFileName,
162 GENERIC_WRITE,
163 0,
164 0,
165 OPEN_ALWAYS,
166 FILE_FLAG_WRITE_THROUGH,
167 0);
168
169 m_hReportFile = CreateFile(m_szLogFileName,
170 GENERIC_WRITE,
171 0,
172 0,
173 OPEN_ALWAYS,
174 FILE_FLAG_WRITE_THROUGH,
175 0);
176
177 if (m_hDumpFile)
178 {
179 MINIDUMP_EXCEPTION_INFORMATION info;
180 info.ClientPointers = FALSE;
181 info.ExceptionPointers = pExceptionInfo;
182 info.ThreadId = GetCurrentThreadId();
183
184 MINIDUMP_USER_STREAM additionalStream = {};
185 MINIDUMP_USER_STREAM_INFORMATION additionalStreamInfo = {};
186
187 if (pExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_ASSERTION_FAILURE && pExceptionInfo->ExceptionRecord->NumberParameters > 0)
188 {
189 additionalStream.Type = CommentStreamA;
190 additionalStream.Buffer = reinterpret_cast<PVOID>(pExceptionInfo->ExceptionRecord->ExceptionInformation[0]);
191 additionalStream.BufferSize = strlen(reinterpret_cast<char const*>(pExceptionInfo->ExceptionRecord->ExceptionInformation[0])) + 1;
192
193 additionalStreamInfo.UserStreamArray = &additionalStream;
194 additionalStreamInfo.UserStreamCount = 1;
195 }
196
197 MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),
198 m_hDumpFile, MiniDumpWithIndirectlyReferencedMemory, &info, &additionalStreamInfo, 0);
199
200 CloseHandle(m_hDumpFile);
201 }
202
203 if (m_hReportFile)
204 {
205 SetFilePointer(m_hReportFile, 0, 0, FILE_END);
206
207 GenerateExceptionReport(pExceptionInfo);
208
209 CloseHandle(m_hReportFile);
210 m_hReportFile = 0;
211 }
212
214 {
215 return m_previousFilter(pExceptionInfo);
216 }
217 else
218 {
219 return EXCEPTION_EXECUTE_HANDLER/*EXCEPTION_CONTINUE_SEARCH*/;
220 }
221}
#define CrashFolder
Definition: WheatyExceptionReport.cpp:29
AC_COMMON_API char const * GetHash()
Definition: GitRevision.cpp:9
static HANDLE m_hDumpFile
Definition: WheatyExceptionReport.h:185
static std::mutex alreadyCrashedLock
Definition: WheatyExceptionReport.h:191
static TCHAR m_szDumpFileName[MAX_PATH]
Definition: WheatyExceptionReport.h:181
static void GenerateExceptionReport(PEXCEPTION_POINTERS pExceptionInfo)
Definition: WheatyExceptionReport.cpp:595
static TCHAR m_szLogFileName[MAX_PATH]
Definition: WheatyExceptionReport.h:180

References alreadyCrashed, alreadyCrashedLock, CrashFolder, EXCEPTION_ASSERTION_FAILURE, GenerateExceptionReport(), GitRevision::GetHash(), m_hDumpFile, m_hReportFile, m_previousFilter, m_szDumpFileName, m_szLogFileName, and stackOverflowException.

Referenced by WheatyExceptionReport().

◆ WriteStackDetails()

void WheatyExceptionReport::WriteStackDetails ( PCONTEXT  pContext,
bool  bWriteVariables,
HANDLE  pThreadHandle 
)
staticprivate
832{
833 Log(_T("\r\nCall stack:\r\n"));
834
835 Log(_T("Address Frame Function SourceFile\r\n"));
836
837 DWORD dwMachineType = 0;
838 // Could use SymSetOptions here to add the SYMOPT_DEFERRED_LOADS flag
839
840 STACKFRAME64 sf;
841 memset(&sf, 0, sizeof(sf));
842
843#ifdef _M_IX86
844 // Initialize the STACKFRAME structure for the first call. This is only
845 // necessary for Intel CPUs, and isn't mentioned in the documentation.
846 sf.AddrPC.Offset = pContext->Eip;
847 sf.AddrPC.Mode = AddrModeFlat;
848 sf.AddrStack.Offset = pContext->Esp;
849 sf.AddrStack.Mode = AddrModeFlat;
850 sf.AddrFrame.Offset = pContext->Ebp;
851 sf.AddrFrame.Mode = AddrModeFlat;
852
853 dwMachineType = IMAGE_FILE_MACHINE_I386;
854#endif
855
856#ifdef _M_X64
857 sf.AddrPC.Offset = pContext->Rip;
858 sf.AddrPC.Mode = AddrModeFlat;
859 sf.AddrStack.Offset = pContext->Rsp;
860 sf.AddrStack.Mode = AddrModeFlat;
861 sf.AddrFrame.Offset = pContext->Rbp;
862 sf.AddrFrame.Mode = AddrModeFlat;
863 dwMachineType = IMAGE_FILE_MACHINE_AMD64;
864#endif
865
866 for (;;)
867 {
868 // Get the next stack frame
869 if (! StackWalk64(dwMachineType,
871 pThreadHandle != nullptr ? pThreadHandle : GetCurrentThread(),
872 &sf,
873 pContext,
874 0,
875 SymFunctionTableAccess64,
876 SymGetModuleBase64,
877 0))
878 {
879 break;
880 }
881 if (0 == sf.AddrFrame.Offset) // Basic sanity check to make sure
882 {
883 break; // the frame is OK. Bail if not.
884 }
885#ifdef _M_IX86
886 Log(_T("%08X %08X "), sf.AddrPC.Offset, sf.AddrFrame.Offset);
887#endif
888#ifdef _M_X64
889 Log(_T("%016I64X %016I64X "), sf.AddrPC.Offset, sf.AddrFrame.Offset);
890#endif
891
892 DWORD64 symDisplacement = 0; // Displacement of the input address,
893 // relative to the start of the symbol
894
895 // Get the name of the function for this stack frame entry
897 if (SymFromAddr(
898 m_hProcess, // Process handle of the current process
899 sf.AddrPC.Offset, // Symbol address
900 &symDisplacement, // Address of the variable that will receive the displacement
901 &sip.si)) // Address of the SYMBOL_INFO structure (inside "sip" object)
902 {
903 Log(_T("%hs+%I64X"), sip.si.Name, symDisplacement);
904 }
905 else // No symbol found. Print out the logical address instead.
906 {
907 TCHAR szModule[MAX_PATH] = _T("");
908 DWORD section = 0;
909 DWORD_PTR offset = 0;
910
911 GetLogicalAddress((PVOID)sf.AddrPC.Offset,
912 szModule, sizeof(szModule), section, offset);
913#ifdef _M_IX86
914 Log(_T("%04X:%08X %s"), section, offset, szModule);
915#endif
916#ifdef _M_X64
917 Log(_T("%04X:%016I64X %s"), section, offset, szModule);
918#endif
919 }
920
921 // Get the source line for this stack frame entry
922 IMAGEHLP_LINE64 lineInfo = { sizeof(IMAGEHLP_LINE64) };
923 DWORD dwLineDisplacement;
924 if (SymGetLineFromAddr64(m_hProcess, sf.AddrPC.Offset,
925 &dwLineDisplacement, &lineInfo))
926 {
927 Log(_T(" %s line %u"), lineInfo.FileName, lineInfo.LineNumber);
928 }
929
930 Log(_T("\r\n"));
931
932 // Write out the variables, if desired
933 if (bWriteVariables)
934 {
935 // Use SymSetContext to get just the locals/params for this frame
936 IMAGEHLP_STACK_FRAME imagehlpStackFrame;
937 imagehlpStackFrame.InstructionOffset = sf.AddrPC.Offset;
938 SymSetContext(m_hProcess, &imagehlpStackFrame, 0);
939
940 // Enumerate the locals/parameters
941 SymEnumSymbols(m_hProcess, 0, 0, EnumerateSymbolsCallback, &sf);
942
943 Log(_T("\r\n"));
944 }
945 }
946}
Definition: WheatyExceptionReport.cpp:818
static BOOL CALLBACK EnumerateSymbolsCallback(PSYMBOL_INFO, ULONG, PVOID)
Definition: WheatyExceptionReport.cpp:952

References EnumerateSymbolsCallback(), GetLogicalAddress(), and m_hProcess.

Referenced by GenerateExceptionReport(), and printTracesForAllThreads().

Member Data Documentation

◆ alreadyCrashed

bool WheatyExceptionReport::alreadyCrashed
staticprivate

◆ alreadyCrashedLock

std::mutex WheatyExceptionReport::alreadyCrashedLock
staticprivate

◆ m_hDumpFile

HANDLE WheatyExceptionReport::m_hDumpFile
staticprivate

◆ m_hProcess

HANDLE WheatyExceptionReport::m_hProcess
staticprivate

◆ m_hReportFile

HANDLE WheatyExceptionReport::m_hReportFile
staticprivate

◆ m_previousCrtHandler

_invalid_parameter_handler WheatyExceptionReport::m_previousCrtHandler
staticprivate

◆ m_previousFilter

LPTOP_LEVEL_EXCEPTION_FILTER WheatyExceptionReport::m_previousFilter
staticprivate

◆ m_szDumpFileName

TCHAR WheatyExceptionReport::m_szDumpFileName
staticprivate

◆ m_szLogFileName

TCHAR WheatyExceptionReport::m_szLogFileName
staticprivate

◆ RtlGetVersion

WheatyExceptionReport::pRtlGetVersion WheatyExceptionReport::RtlGetVersion
staticprivate

◆ stackOverflowException

bool WheatyExceptionReport::stackOverflowException
staticprivate

◆ symbolDetails

std::stack< SymbolDetail > WheatyExceptionReport::symbolDetails
staticprivate

◆ symbols

SymbolPairs WheatyExceptionReport::symbols
staticprivate

Referenced by ClearSymbols(), and StoreSymbol().