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:1644

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
1645{
1646 symbols.clear();
1647 while (!symbolDetails.empty())
1648 {
1649 symbolDetails.pop();
1650 }
1651}
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
1577{
1578 __try
1579 {
1580 return *(PDWORD_PTR)address;
1581 }
1582 __except (EXCEPTION_EXECUTE_HANDLER)
1583 {
1584 return DWORD_PTR(-1);
1585 }
1586}

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

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
961{
962 __try
963 {
964 ClearSymbols();
965 FormatSymbolValue(pSymInfo, (STACKFRAME64*)UserContext);
966 }
967 __except (EXCEPTION_EXECUTE_HANDLER)
968 {
969 Log(_T("punting on symbol %s, partial output:\r\n"), pSymInfo->Name);
970 }
971
972 return TRUE;
973}
static bool FormatSymbolValue(PSYMBOL_INFO, STACKFRAME64 *)
Definition: WheatyExceptionReport.cpp:980
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
1460{
1461 __try
1462 {
1463 switch (basicType)
1464 {
1465 case btChar:
1466 {
1467 // Special case handling for char[] type
1468 if (countOverride != 0)
1469 {
1470 length = countOverride;
1471 }
1472 else
1473 {
1474 length = strlen((char*)pAddress);
1475 }
1476 if (length > bufferSize - 6)
1477 {
1478 pszCurrBuffer += sprintf(pszCurrBuffer, "\"%.*s...\"", (DWORD)(bufferSize - 6), (char*)pAddress);
1479 }
1480 else
1481 {
1482 pszCurrBuffer += sprintf(pszCurrBuffer, "\"%.*s\"", (DWORD)length, (char*)pAddress);
1483 }
1484 break;
1485 }
1486 case btStdString:
1487 {
1488 std::string* value = static_cast<std::string*>(pAddress);
1489 if (value->length() > bufferSize - 6)
1490 {
1491 pszCurrBuffer += sprintf(pszCurrBuffer, "\"%.*s...\"", (DWORD)(bufferSize - 6), value->c_str());
1492 }
1493 else
1494 {
1495 pszCurrBuffer += sprintf(pszCurrBuffer, "\"%s\"", value->c_str());
1496 }
1497 break;
1498 }
1499 default:
1500 // Format appropriately (assuming it's a 1, 2, or 4 bytes (!!!)
1501 if (length == 1)
1502 {
1503 pszCurrBuffer += sprintf(pszCurrBuffer, "0x%X", *(PBYTE)pAddress);
1504 }
1505 else if (length == 2)
1506 {
1507 pszCurrBuffer += sprintf(pszCurrBuffer, "0x%X", *(PWORD)pAddress);
1508 }
1509 else if (length == 4)
1510 {
1511 if (basicType == btFloat)
1512 {
1513 pszCurrBuffer += sprintf(pszCurrBuffer, "%f", *(PFLOAT)pAddress);
1514 }
1515 else
1516 {
1517 pszCurrBuffer += sprintf(pszCurrBuffer, "0x%X", *(PDWORD)pAddress);
1518 }
1519 }
1520 else if (length == 8)
1521 {
1522 if (basicType == btFloat)
1523 {
1524 pszCurrBuffer += sprintf(pszCurrBuffer, "%f",
1525 *(double*)pAddress);
1526 }
1527 else
1528 pszCurrBuffer += sprintf(pszCurrBuffer, "0x%I64X",
1529 *(DWORD64*)pAddress);
1530 }
1531 else
1532 {
1533#if _WIN64
1534 pszCurrBuffer += sprintf(pszCurrBuffer, "0x%I64X", (DWORD64)pAddress);
1535#else
1536 pszCurrBuffer += sprintf(pszCurrBuffer, "0x%X", (DWORD)pAddress);
1537#endif
1538 }
1539 break;
1540 }
1541 }
1542 __except (EXCEPTION_EXECUTE_HANDLER)
1543 {
1544#if _WIN64
1545 pszCurrBuffer += sprintf(pszCurrBuffer, "0x%I64X <Unable to read memory>", (DWORD64)pAddress);
1546#else
1547 pszCurrBuffer += sprintf(pszCurrBuffer, "0x%X <Unable to read memory>", (DWORD)pAddress);
1548#endif
1549 }
1550}
@ 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
983{
984 // If it's a function, don't do anything.
985 if (pSym->Tag == SymTagFunction) // SymTagFunction from CVCONST.H from the DIA SDK
986 {
987 return false;
988 }
989
990 DWORD_PTR pVariable = 0; // Will point to the variable's data in memory
991
992 if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGRELATIVE)
993 {
994 // if (pSym->Register == 8) // EBP is the value 8 (in DBGHELP 5.1)
995 {
996 // This may change!!!
997#ifdef _M_IX86
998 pVariable = sf->AddrFrame.Offset;
999#elif _M_X64
1000 pVariable = sf->AddrStack.Offset;
1001#endif
1002 pVariable += (DWORD_PTR)pSym->Address;
1003 }
1004 // else
1005 // return false;
1006 }
1007 else if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGISTER)
1008 {
1009 return false; // Don't try to report register variable
1010 }
1011 else
1012 {
1013 pVariable = (DWORD_PTR)pSym->Address; // It must be a global variable
1014 }
1015
1017
1018 // Indicate if the variable is a local or parameter
1019 if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_PARAMETER)
1020 {
1021 symbolDetails.top().Prefix = "Parameter ";
1022 }
1023 else if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_LOCAL)
1024 {
1025 symbolDetails.top().Prefix = "Local ";
1026 }
1027
1028 // Determine if the variable is a user defined type (UDT). IF so, bHandled
1029 // will return true.
1030 bool bHandled;
1031 DumpTypeIndex(pSym->ModBase, pSym->TypeIndex, pVariable, bHandled, pSym->Name, (char*)"", false, true);
1032
1033 if (!bHandled)
1034 {
1035 // The symbol wasn't a UDT, so do basic, stupid formatting of the
1036 // variable. Based on the size, we're assuming it's a char, WORD, or
1037 // DWORD.
1038 BasicType basicType = GetBasicType(pSym->TypeIndex, pSym->ModBase);
1039 if (symbolDetails.top().Type.empty())
1040 {
1041 symbolDetails.top().Type = rgBaseType[basicType];
1042 }
1043
1044 // Emit the variable name
1045 if (pSym->Name[0] != '\0')
1046 {
1047 symbolDetails.top().Name = pSym->Name;
1048 }
1049
1050 char buffer[50];
1051 FormatOutputValue(buffer, basicType, pSym->Size, (PVOID)pVariable, sizeof(buffer));
1052 symbolDetails.top().Value = buffer;
1053 }
1054
1056 return true;
1057}

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("\r\n"));
678 Log(_T("----\r\n"));
679 Log(_T("SYMBOL HANDLER ERROR (THIS IS NOT THE CRASH ERROR)\r\n\r\n"));
680 Log(_T("Couldn't initialize symbol handler for process when generating crash report\r\n"));
681 Log(_T("Error: %s\r\n"), ErrorMessage(GetLastError()));
682 Log(_T("THE BELOW CALL STACKS MIGHT HAVE MISSING OR INACCURATE FILE/FUNCTION NAMES\r\n\r\n"));
683 Log(_T("----\r\n"));
684 }
685
686 CONTEXT trashableContext = *pCtx;
687
688 WriteStackDetails(&trashableContext, false, nullptr);
690
691 // #ifdef _M_IX86 // X86 Only!
692
693 Log(_T("========================\r\n"));
694 Log(_T("Local Variables And Parameters\r\n"));
695
696 trashableContext = *pCtx;
697 WriteStackDetails(&trashableContext, true, nullptr);
699
700 SymCleanup(GetCurrentProcess());
701
702 Log(_T("\r\n"));
703 }
704 __except (EXCEPTION_EXECUTE_HANDLER)
705 {
706 Log(_T("Error writing the crash log\r\n"));
707 }
708}
#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:82
static BOOL GetLogicalAddress(PVOID addr, PTSTR szModule, DWORD len, DWORD &section, DWORD_PTR &offset)
Definition: WheatyExceptionReport.cpp:763
static void PrintSystemInfo()
Definition: WheatyExceptionReport.cpp:513
static void WriteStackDetails(PCONTEXT pContext, bool bWriteVariables, HANDLE pThreadHandle)
Definition: WheatyExceptionReport.cpp:834
static LPTSTR GetExceptionString(DWORD dwCode)
Definition: WheatyExceptionReport.cpp:714
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
1553{
1554 BasicType basicType;
1555 if (SymGetTypeInfo(m_hProcess, modBase, typeIndex,
1556 TI_GET_BASETYPE, &basicType))
1557 {
1558 return basicType;
1559 }
1560
1561 // Get the real "TypeId" of the child. We need this for the
1562 // SymGetTypeInfo(TI_GET_TYPEID) call below.
1563 DWORD typeId;
1564 if (SymGetTypeInfo(m_hProcess, modBase, typeIndex, TI_GET_TYPEID, &typeId))
1565 {
1566 if (SymGetTypeInfo(m_hProcess, modBase, typeId, TI_GET_BASETYPE,
1567 &basicType))
1568 {
1569 return basicType;
1570 }
1571 }
1572
1573 return btNoType;
1574}

References btNoType, and m_hProcess.

Referenced by DumpTypeIndex(), and FormatSymbolValue().

◆ GetExceptionString()

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

References EXCEPTION.

Referenced by GenerateExceptionReport().

◆ GetLogicalAddress()

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

Referenced by GenerateExceptionReport(), and WriteStackDetails().

◆ HeapLog()

int __cdecl WheatyExceptionReport::HeapLog ( const TCHAR *  format,
va_list  argptr 
)
staticprivate
1625{
1626 int retValue = 0;
1627 DWORD cbWritten;
1628 TCHAR* szBuff = (TCHAR*)malloc(sizeof(TCHAR) * WER_LARGE_BUFFER_SIZE);
1629 if (szBuff != nullptr)
1630 {
1631 retValue = vsprintf(szBuff, format, argptr);
1632 WriteFile(m_hReportFile, szBuff, retValue * sizeof(TCHAR), &cbWritten, 0);
1633 free(szBuff);
1634 }
1635
1636 return retValue;
1637}
#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
1593{
1594 int retValue;
1595 va_list argptr;
1596 va_start(argptr, format);
1597
1599 {
1600 retValue = HeapLog(format, argptr);
1601 }
1602 else
1603 {
1604 retValue = StackLog(format, argptr);
1605 }
1606
1607 va_end(argptr);
1608
1609 return retValue;
1610}
static int __cdecl StackLog(const TCHAR *format, va_list argptr)
Definition: WheatyExceptionReport.cpp:1612
static int __cdecl HeapLog(const TCHAR *format, va_list argptr)
Definition: WheatyExceptionReport.cpp:1624

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

◆ PopSymbolDetail()

void WheatyExceptionReport::PopSymbolDetail ( )
staticprivate
1661{
1663 symbolDetails.pop();
1664}
static void PrintSymbolDetail()
Definition: WheatyExceptionReport.cpp:1666

References PrintSymbolDetail(), and symbolDetails.

Referenced by DumpTypeIndex(), and FormatSymbolValue().

◆ PrintSymbolDetail()

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

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
1654{
1655 // Log current symbol and then add another to the stack to keep the hierarchy format
1657 symbolDetails.emplace();
1658}

References PrintSymbolDetail(), and symbolDetails.

Referenced by DumpTypeIndex(), and FormatSymbolValue().

◆ StackLog()

int __cdecl WheatyExceptionReport::StackLog ( const TCHAR *  format,
va_list  argptr 
)
staticprivate
1613{
1614 int retValue;
1615 DWORD cbWritten;
1616
1617 TCHAR szBuff[WER_LARGE_BUFFER_SIZE];
1618 retValue = vsprintf(szBuff, format, argptr);
1619 WriteFile(m_hReportFile, szBuff, retValue * sizeof(TCHAR), &cbWritten, 0);
1620
1621 return retValue;
1622}

References m_hReportFile, and WER_LARGE_BUFFER_SIZE.

Referenced by Log().

◆ StoreSymbol()

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

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().