TrinityCore
WheatyExceptionReport.cpp
Go to the documentation of this file.
1//==========================================
2// Matt Pietrek
3// MSDN Magazine, 2002
4// FILE: WheatyExceptionReport.CPP
5//==========================================
7#include "Errors.h"
8#include "GitRevision.h"
9#include <algorithm>
10
11#ifdef __clang__
12// clang-cl doesn't have these hardcoded types available, correct ehdata_forceinclude.h that relies on it
13#define _ThrowInfo ThrowInfo
14#endif
15
16#include <ehdata.h>
17#include <rttidata.h>
18#include <tlhelp32.h>
19#include <tchar.h>
20
21#include <comdef.h>
22#include <WbemIdl.h>
23
24#define CrashFolder _T("Crashes")
25#pragma comment(linker, "/DEFAULTLIB:dbghelp.lib")
26#pragma comment(linker, "/DEFAULTLIB:wbemuuid.lib")
27
28inline LPTSTR ErrorMessage(DWORD dw)
29{
30 LPVOID lpMsgBuf;
31 DWORD formatResult = FormatMessage(
32 FORMAT_MESSAGE_ALLOCATE_BUFFER |
33 FORMAT_MESSAGE_FROM_SYSTEM,
34 nullptr,
35 dw,
36 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
37 (LPTSTR) &lpMsgBuf,
38 0, nullptr);
39 if (formatResult != 0)
40 return (LPTSTR)lpMsgBuf;
41 else
42 {
43 LPTSTR msgBuf = (LPTSTR)LocalAlloc(LPTR, 30);
44 snprintf(msgBuf, 30, "Unknown error: %u", dw);
45 return msgBuf;
46 }
47
48}
49
50//============================== Global Variables =============================
51
52//
53// Declare the static variables of the WheatyExceptionReport class and force their initialization before any other static in the program
54//
55#pragma warning(push)
56#pragma warning(disable: 4073) // C4073: initializers put in library initialization area
57#pragma init_seg(lib)
60LPTOP_LEVEL_EXCEPTION_FILTER WheatyExceptionReport::m_previousFilter;
61_invalid_parameter_handler WheatyExceptionReport::m_previousCrtHandler;
66std::stack<SymbolDetail> WheatyExceptionReport::symbolDetails;
70
71// Declare global instance of class
73#pragma warning(pop)
74
75//============================== Class Methods =============================
76
78{
79 // Install the unhandled exception filter function
80 m_previousFilter = SetUnhandledExceptionFilter(WheatyUnhandledExceptionFilter);
81 m_previousCrtHandler = _set_invalid_parameter_handler(WheatyCrtHandler);
82 m_hProcess = GetCurrentProcess();
83 alreadyCrashed = false;
84 RtlGetVersion = (pRtlGetVersion)GetProcAddress(GetModuleHandle(_T("ntdll.dll")), "RtlGetVersion");
85 if (!IsDebuggerPresent())
86 {
87 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
88 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
89 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
90 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
91 }
92}
93
94//============
95// Destructor
96//============
98{
100 SetUnhandledExceptionFilter(m_previousFilter);
102 _set_invalid_parameter_handler(m_previousCrtHandler);
103 ClearSymbols();
104}
105
106//===========================================================
107// Entry point where control comes on an unhandled exception
108//===========================================================
110PEXCEPTION_POINTERS pExceptionInfo)
111{
112 std::unique_lock<std::mutex> guard(alreadyCrashedLock);
113 // Handle only 1 exception in the whole process lifetime
114 if (alreadyCrashed)
115 return EXCEPTION_EXECUTE_HANDLER;
116
117 alreadyCrashed = true;
118
119 TCHAR module_folder_name[MAX_PATH];
120 GetModuleFileName(nullptr, module_folder_name, MAX_PATH);
121 TCHAR* pos = _tcsrchr(module_folder_name, '\\');
122 if (!pos)
123 return 0;
124 pos[0] = '\0';
125 ++pos;
126
127 TCHAR crash_folder_path[MAX_PATH];
128 _stprintf_s(crash_folder_path, "%s\\%s", module_folder_name, CrashFolder);
129 if (!CreateDirectory(crash_folder_path, nullptr))
130 {
131 if (GetLastError() != ERROR_ALREADY_EXISTS)
132 return 0;
133 }
134
135 SYSTEMTIME systime;
136 GetLocalTime(&systime);
137 _stprintf_s(m_szDumpFileName, "%s\\%s_%s_[%u-%u_%u-%u-%u].dmp",
138 crash_folder_path, GitRevision::GetHash(), pos, systime.wDay, systime.wMonth, systime.wHour, systime.wMinute, systime.wSecond);
139
140 _stprintf_s(m_szLogFileName, _T("%s\\%s_%s_[%u-%u_%u-%u-%u].txt"),
141 crash_folder_path, GitRevision::GetHash(), pos, systime.wDay, systime.wMonth, systime.wHour, systime.wMinute, systime.wSecond);
142
143 m_hDumpFile = CreateFile(m_szDumpFileName,
144 GENERIC_WRITE,
145 0,
146 nullptr,
147 OPEN_ALWAYS,
148 FILE_FLAG_WRITE_THROUGH,
149 nullptr);
150
151 if (m_hDumpFile)
152 {
153 MINIDUMP_EXCEPTION_INFORMATION info;
154 info.ClientPointers = FALSE;
155 info.ExceptionPointers = pExceptionInfo;
156 info.ThreadId = GetCurrentThreadId();
157
158 MINIDUMP_USER_STREAM additionalStream = {};
159 MINIDUMP_USER_STREAM_INFORMATION additionalStreamInfo = {};
160
161 if (pExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_ASSERTION_FAILURE && pExceptionInfo->ExceptionRecord->NumberParameters > 0)
162 {
163 additionalStream.Type = CommentStreamA;
164 additionalStream.Buffer = reinterpret_cast<PVOID>(pExceptionInfo->ExceptionRecord->ExceptionInformation[0]);
165 additionalStream.BufferSize = strlen(reinterpret_cast<char const*>(pExceptionInfo->ExceptionRecord->ExceptionInformation[0])) + 1;
166
167 additionalStreamInfo.UserStreamArray = &additionalStream;
168 additionalStreamInfo.UserStreamCount = 1;
169 }
170
171 MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),
172 m_hDumpFile, MiniDumpWithIndirectlyReferencedMemory, &info, &additionalStreamInfo, nullptr);
173
174 CloseHandle(m_hDumpFile);
175 }
176
177 m_hReportFile = _tfopen(m_szLogFileName, _T("wb"));
178
179 if (m_hReportFile)
180 {
181 GenerateExceptionReport(pExceptionInfo);
182
183 fclose(m_hReportFile);
184 m_hReportFile = nullptr;
185 }
186
188 return m_previousFilter(pExceptionInfo);
189 else
190 return EXCEPTION_EXECUTE_HANDLER/*EXCEPTION_CONTINUE_SEARCH*/;
191}
192
193void __cdecl WheatyExceptionReport::WheatyCrtHandler(wchar_t const* /*expression*/, wchar_t const* /*function*/, wchar_t const* /*file*/, unsigned int /*line*/, uintptr_t /*pReserved*/)
194{
195 RaiseException(EXCEPTION_ACCESS_VIOLATION, 0, 0, nullptr);
196}
197
198BOOL WheatyExceptionReport::_GetProcessorName(TCHAR* sProcessorName, DWORD maxcount)
199{
200 if (!sProcessorName)
201 return FALSE;
202
203 HKEY hKey;
204 LONG lRet;
205 lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"),
206 0, KEY_QUERY_VALUE, &hKey);
207 if (lRet != ERROR_SUCCESS)
208 return FALSE;
209 TCHAR szTmp[2048];
210 DWORD cntBytes = sizeof(szTmp);
211 lRet = ::RegQueryValueEx(hKey, _T("ProcessorNameString"), nullptr, nullptr,
212 (LPBYTE)szTmp, &cntBytes);
213 if (lRet != ERROR_SUCCESS)
214 return FALSE;
215 ::RegCloseKey(hKey);
216 sProcessorName[0] = '\0';
217 // Skip spaces
218 TCHAR* psz = szTmp;
219 while (iswspace(*psz))
220 ++psz;
221 _tcsncpy(sProcessorName, psz, maxcount);
222 return TRUE;
223}
224
225template<size_t size>
226void ToTchar(wchar_t const* src, TCHAR (&dst)[size])
227{
228 if constexpr (std::is_same_v<TCHAR, char>)
229 ::wcstombs_s(nullptr, dst, src, size);
230 else
231 ::wcscpy_s(dst, size, src);
232}
233
234BOOL WheatyExceptionReport::_GetWindowsVersion(TCHAR* szVersion, DWORD cntMax)
235{
236 *szVersion = _T('\0');
237
238 if (_GetWindowsVersionFromWMI(szVersion, cntMax))
239 return TRUE;
240
241 // Try calling GetVersionEx using the OSVERSIONINFOEX structure.
242 // If that fails, try using the OSVERSIONINFO structure.
243 RTL_OSVERSIONINFOEXW osvi = { };
244 osvi.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
245 NTSTATUS bVersionEx = RtlGetVersion((PRTL_OSVERSIONINFOW)&osvi);
246 if (FAILED(bVersionEx))
247 {
248 osvi.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOW);
249 if (!RtlGetVersion((PRTL_OSVERSIONINFOW)&osvi))
250 return FALSE;
251 }
252
253 TCHAR szCSDVersion[256];
254 ToTchar(osvi.szCSDVersion, szCSDVersion);
255
256 TCHAR wszTmp[128];
257 switch (osvi.dwPlatformId)
258 {
259 // Windows NT product family.
260 case VER_PLATFORM_WIN32_NT:
261 {
262 #if WINVER < 0x0500
263 BYTE suiteMask = osvi.wReserved[0];
264 BYTE productType = osvi.wReserved[1];
265 #else
266 WORD suiteMask = osvi.wSuiteMask;
267 BYTE productType = osvi.wProductType;
268 #endif // WINVER < 0x0500
269
270 // Test for the specific product family.
271 if (osvi.dwMajorVersion == 10)
272 {
273 if (productType == VER_NT_WORKSTATION)
274 _tcsncat(szVersion, _T("Windows 10 "), cntMax);
275 else
276 _tcsncat(szVersion, _T("Windows Server 2016 "), cntMax);
277 }
278 else if (osvi.dwMajorVersion == 6)
279 {
280 if (productType == VER_NT_WORKSTATION)
281 {
282 if (osvi.dwMinorVersion == 3)
283 _tcsncat(szVersion, _T("Windows 8.1 "), cntMax);
284 else if (osvi.dwMinorVersion == 2)
285 _tcsncat(szVersion, _T("Windows 8 "), cntMax);
286 else if (osvi.dwMinorVersion == 1)
287 _tcsncat(szVersion, _T("Windows 7 "), cntMax);
288 else
289 _tcsncat(szVersion, _T("Windows Vista "), cntMax);
290 }
291 else if (osvi.dwMinorVersion == 3)
292 _tcsncat(szVersion, _T("Windows Server 2012 R2 "), cntMax);
293 else if (osvi.dwMinorVersion == 2)
294 _tcsncat(szVersion, _T("Windows Server 2012 "), cntMax);
295 else if (osvi.dwMinorVersion == 1)
296 _tcsncat(szVersion, _T("Windows Server 2008 R2 "), cntMax);
297 else
298 _tcsncat(szVersion, _T("Windows Server 2008 "), cntMax);
299 }
300 else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2)
301 _tcsncat(szVersion, _T("Microsoft Windows Server 2003 "), cntMax);
302 else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1)
303 _tcsncat(szVersion, _T("Microsoft Windows XP "), cntMax);
304 else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0)
305 _tcsncat(szVersion, _T("Microsoft Windows 2000 "), cntMax);
306 else if (osvi.dwMajorVersion <= 4)
307 _tcsncat(szVersion, _T("Microsoft Windows NT "), cntMax);
308
309 // Test for specific product on Windows NT 4.0 SP6 and later.
310 if (bVersionEx >= 0)
311 {
312 // Test for the workstation type.
313 if (productType == VER_NT_WORKSTATION)
314 {
315 if (osvi.dwMajorVersion == 4)
316 _tcsncat(szVersion, _T("Workstation 4.0 "), cntMax);
317 else if (suiteMask & VER_SUITE_PERSONAL)
318 _tcsncat(szVersion, _T("Home Edition "), cntMax);
319 else if (suiteMask & VER_SUITE_EMBEDDEDNT)
320 _tcsncat(szVersion, _T("Embedded "), cntMax);
321 else
322 _tcsncat(szVersion, _T("Professional "), cntMax);
323 }
324 // Test for the server type.
325 else if (productType == VER_NT_SERVER)
326 {
327 if (osvi.dwMajorVersion == 6 || osvi.dwMajorVersion == 10)
328 {
329 if (suiteMask & VER_SUITE_SMALLBUSINESS_RESTRICTED)
330 _tcsncat(szVersion, _T("Essentials "), cntMax);
331 else if (suiteMask & VER_SUITE_DATACENTER)
332 _tcsncat(szVersion, _T("Datacenter "), cntMax);
333 else if (suiteMask & VER_SUITE_ENTERPRISE)
334 _tcsncat(szVersion, _T("Enterprise "), cntMax);
335 else
336 _tcsncat(szVersion, _T("Standard "), cntMax);
337 }
338 else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2)
339 {
340 if (suiteMask & VER_SUITE_DATACENTER)
341 _tcsncat(szVersion, _T("Datacenter Edition "), cntMax);
342 else if (suiteMask & VER_SUITE_ENTERPRISE)
343 _tcsncat(szVersion, _T("Enterprise Edition "), cntMax);
344 else if (suiteMask == VER_SUITE_BLADE)
345 _tcsncat(szVersion, _T("Web Edition "), cntMax);
346 else
347 _tcsncat(szVersion, _T("Standard Edition "), cntMax);
348 }
349 else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0)
350 {
351 if (suiteMask & VER_SUITE_DATACENTER)
352 _tcsncat(szVersion, _T("Datacenter Server "), cntMax);
353 else if (suiteMask & VER_SUITE_ENTERPRISE)
354 _tcsncat(szVersion, _T("Advanced Server "), cntMax);
355 else
356 _tcsncat(szVersion, _T("Server "), cntMax);
357 }
358 else // Windows NT 4.0
359 {
360 if (suiteMask & VER_SUITE_ENTERPRISE)
361 _tcsncat(szVersion, _T("Server 4.0, Enterprise Edition "), cntMax);
362 else
363 _tcsncat(szVersion, _T("Server 4.0 "), cntMax);
364 }
365 }
366 }
367
368 // Display service pack (if any) and build number.
369 if (osvi.dwMajorVersion == 4 && _tcsicmp(szCSDVersion, _T("Service Pack 6")) == 0)
370 {
371 HKEY hKey;
372 LONG lRet;
373
374 // Test for SP6 versus SP6a.
375 lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Hotfix\\Q246009"), 0, KEY_QUERY_VALUE, &hKey);
376 if (lRet == ERROR_SUCCESS)
377 {
378 _stprintf_s(wszTmp, _T("Service Pack 6a (Version %d.%d, Build %d)"),
379 osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
380 _tcsncat(szVersion, wszTmp, cntMax);
381 }
382 else // Windows NT 4.0 prior to SP6a
383 {
384 _stprintf_s(wszTmp, _T("%s (Version %d.%d, Build %d)"),
385 szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
386 _tcsncat(szVersion, wszTmp, cntMax);
387 }
388 ::RegCloseKey(hKey);
389 }
390 else // Windows NT 3.51 and earlier or Windows 2000 and later
391 {
392 if (!_tcslen(szCSDVersion))
393 _stprintf_s(wszTmp, _T("(Version %d.%d, Build %d)"),
394 osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
395 else
396 _stprintf_s(wszTmp, _T("%s (Version %d.%d, Build %d)"),
397 szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
398 _tcsncat(szVersion, wszTmp, cntMax);
399 }
400 break;
401 }
402 default:
403 _stprintf_s(wszTmp, _T("%s (Version %d.%d, Build %d)"),
404 szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
405 _tcsncat(szVersion, wszTmp, cntMax);
406 break;
407 }
408
409 return TRUE;
410}
411
412BOOL WheatyExceptionReport::_GetWindowsVersionFromWMI(TCHAR* szVersion, DWORD cntMax)
413{
414 // Step 1: --------------------------------------------------
415 // Initialize COM. ------------------------------------------
416 HRESULT hres = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
417 if (FAILED(hres))
418 return FALSE;
419
420 std::shared_ptr<void> com(nullptr, [](void*)
421 {
422 CoUninitialize();
423 });
424
425 // Step 2: --------------------------------------------------
426 // Set general COM security levels --------------------------
427 hres = CoInitializeSecurity(
428 nullptr,
429 -1, // COM authentication
430 nullptr, // Authentication services
431 nullptr, // Reserved
432 RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication
433 RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation
434 nullptr, // Authentication info
435 EOAC_NONE, // Additional capabilities
436 nullptr // Reserved
437 );
438
439 if (FAILED(hres))
440 return FALSE;
441
442 // Step 3: ---------------------------------------------------
443 // Obtain the initial locator to WMI -------------------------
444 std::shared_ptr<IWbemLocator> loc = []() -> std::shared_ptr<IWbemLocator>
445 {
446 IWbemLocator* tmp = nullptr;
447 HRESULT hres = CoCreateInstance(
448 CLSID_WbemLocator,
449 nullptr,
450 CLSCTX_INPROC_SERVER,
451 IID_IWbemLocator,
452 reinterpret_cast<LPVOID*>(&tmp));
453
454 if (FAILED(hres))
455 return nullptr;
456
457 return { tmp, [](IWbemLocator* ptr) { if (ptr) ptr->Release(); } };
458 }();
459
460 if (!loc)
461 return FALSE;
462
463 // Step 4: -----------------------------------------------------
464 // Connect to the root\cimv2 namespace with
465 // the current user and obtain pointer pSvc
466 // to make IWbemServices calls.
467 std::shared_ptr<IWbemServices> svc = [loc]() ->std::shared_ptr<IWbemServices>
468 {
469 IWbemServices* tmp = nullptr;
470 HRESULT hres = loc->ConnectServer(
471 bstr_t(L"ROOT\\CIMV2"), // Object path of WMI namespace
472 nullptr, // User name. NULL = current user
473 nullptr, // User password. NULL = current
474 nullptr, // Locale. NULL indicates current
475 WBEM_FLAG_CONNECT_USE_MAX_WAIT, // Security flags.
476 nullptr, // Authority (for example, Kerberos)
477 nullptr, // Context object
478 &tmp // pointer to IWbemServices proxy
479 );
480
481 if (FAILED(hres))
482 return nullptr;
483
484 return { tmp, [](IWbemServices* ptr) { if (ptr) ptr->Release(); } };
485 }();
486
487 if (!svc)
488 return FALSE;
489
490 // Step 5: --------------------------------------------------
491 // Set security levels on the proxy -------------------------
492 hres = CoSetProxyBlanket(
493 svc.get(), // Indicates the proxy to set
494 RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
495 RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
496 nullptr, // Server principal name
497 RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx
498 RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
499 nullptr, // client identity
500 EOAC_NONE // proxy capabilities
501 );
502
503 if (FAILED(hres))
504 return FALSE;
505
506 // Step 6: --------------------------------------------------
507 // Use the IWbemServices pointer to make requests of WMI ----
508
509 // For example, get the name of the operating system
510 std::shared_ptr<IEnumWbemClassObject> queryResult = [svc]() -> std::shared_ptr<IEnumWbemClassObject>
511 {
512 IEnumWbemClassObject* tmp = nullptr;
513 HRESULT hres = svc->ExecQuery(
514 bstr_t("WQL"),
515 bstr_t("SELECT Caption, CSDVersion FROM Win32_OperatingSystem"),
516 WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
517 nullptr,
518 &tmp);
519
520 if (FAILED(hres))
521 return nullptr;
522
523 return { tmp, [](IEnumWbemClassObject* ptr) { if (ptr) ptr->Release(); } };
524 }();
525
526 BOOL result = FALSE;
527 // Step 7: -------------------------------------------------
528 // Get the data from the query in step 6 -------------------
529 if (queryResult)
530 {
531 do
532 {
533 IWbemClassObject* fields = nullptr;
534
535 ULONG rows = 0;
536 queryResult->Next(WBEM_INFINITE, 1, &fields, &rows);
537 if (!rows)
538 break;
539
540 VARIANT field;
541 VariantInit(&field);
542 fields->Get(L"Caption", 0, &field, nullptr, nullptr);
543 TCHAR buf[256] = { };
544 ToTchar(field.bstrVal, buf);
545 _tcsncat(szVersion, buf, cntMax);
546 VariantClear(&field);
547
548 fields->Get(L"CSDVersion", 0, &field, nullptr, nullptr);
549 if (field.vt == VT_BSTR)
550 {
551 _tcsncat(szVersion, _T(" "), cntMax);
552 memset(buf, 0, sizeof(buf));
553 ToTchar(field.bstrVal, buf);
554 if (strlen(buf))
555 _tcsncat(szVersion, buf, cntMax);
556 }
557 VariantClear(&field);
558
559 fields->Release();
560
561 result = TRUE;
562 } while (true);
563 }
564
565 return result;
566}
567
569{
570 SYSTEM_INFO SystemInfo;
571 ::GetSystemInfo(&SystemInfo);
572
573 MEMORYSTATUS MemoryStatus;
574 MemoryStatus.dwLength = sizeof (MEMORYSTATUS);
575 ::GlobalMemoryStatus(&MemoryStatus);
576 TCHAR sString[1024];
577 Log(_T("//=====================================================\r\n"));
578 if (_GetProcessorName(sString, std::size(sString)))
579 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"),
580 sString, SystemInfo.dwNumberOfProcessors, MemoryStatus.dwTotalPhys/0x400, MemoryStatus.dwAvailPhys/0x400, MemoryStatus.dwTotalPageFile/0x400);
581 else
582 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"),
583 SystemInfo.dwNumberOfProcessors, MemoryStatus.dwTotalPhys/0x400, MemoryStatus.dwAvailPhys/0x400, MemoryStatus.dwTotalPageFile/0x400);
584
585 if (_GetWindowsVersion(sString, std::size(sString)))
586 Log(_T("\r\n*** Operation System ***\r\n%s\r\n"), sString);
587 else
588 Log(_T("\r\n*** Operation System:\r\n<unknown>\r\n"));
589}
590
591//===========================================================================
593{
594 THREADENTRY32 te32;
595
596 DWORD dwOwnerPID = GetCurrentProcessId();
597 DWORD dwCurrentTID = GetCurrentThreadId();
598 m_hProcess = GetCurrentProcess();
599 // Take a snapshot of all running threads
600 HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
601 if (hThreadSnap == INVALID_HANDLE_VALUE)
602 return;
603
604 // Fill in the size of the structure before using it.
605 te32.dwSize = sizeof(THREADENTRY32);
606
607 // Retrieve information about the first thread,
608 // and exit if unsuccessful
609 if (!Thread32First(hThreadSnap, &te32))
610 {
611 CloseHandle(hThreadSnap); // Must clean up the
612 // snapshot object!
613 return;
614 }
615
616 // Now walk the thread list of the system,
617 // and display information about each thread
618 // associated with the specified process
619 do
620 {
621 if (te32.th32OwnerProcessID == dwOwnerPID && te32.th32ThreadID != dwCurrentTID)
622 {
623 CONTEXT context;
624 context.ContextFlags = 0xffffffff;
625 HANDLE threadHandle = OpenThread(THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION, false, te32.th32ThreadID);
626 if (threadHandle)
627 {
628 if (GetThreadContext(threadHandle, &context))
629 WriteStackDetails(&context, bWriteVariables, threadHandle);
630 CloseHandle(threadHandle);
631 }
632 }
633 } while (Thread32Next(hThreadSnap, &te32));
634
635// Don't forget to clean up the snapshot object.
636 CloseHandle(hThreadSnap);
637}
638
639//===========================================================================
640// Open the report file, and write the desired information to it. Called by
641// WheatyUnhandledExceptionFilter
642//===========================================================================
644PEXCEPTION_POINTERS pExceptionInfo)
645{
646 __try
647 {
648 SYSTEMTIME systime;
649 GetLocalTime(&systime);
650
651 // Start out with a banner
652 Log(_T("Revision: %s\r\n"), GitRevision::GetFullVersion());
653 Log(_T("Date %u:%u:%u. Time %u:%u \r\n"), systime.wDay, systime.wMonth, systime.wYear, systime.wHour, systime.wMinute);
654 PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord;
655
657 // First print information about the type of fault
658 Log(_T("\r\n//=====================================================\r\n"));
659 Log(_T("Exception code: %08X %s\r\n"),
660 pExceptionRecord->ExceptionCode,
661 GetExceptionString(pExceptionRecord->ExceptionCode));
662 if (pExceptionRecord->ExceptionCode == EXCEPTION_ASSERTION_FAILURE && pExceptionRecord->NumberParameters >= 2)
663 {
664 pExceptionRecord->ExceptionAddress = reinterpret_cast<PVOID>(pExceptionRecord->ExceptionInformation[1]);
665 Log(_T("Assertion message: %s\r\n"), pExceptionRecord->ExceptionInformation[0]);
666 }
667
668 // Now print information about where the fault occured
669 TCHAR szFaultingModule[MAX_PATH];
670 DWORD section;
671 DWORD_PTR offset;
672 GetLogicalAddress(pExceptionRecord->ExceptionAddress,
673 szFaultingModule,
674 sizeof(szFaultingModule),
675 section, offset);
676
677#if defined(_M_IX86) || defined(_M_ARM)
678 Log(_T("Fault address: %08X %02X:%08X %s\r\n"),
679 pExceptionRecord->ExceptionAddress,
680 section, offset, szFaultingModule);
681#endif
682#if defined(_M_X64) || defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined (_M_ARM64EC)
683 Log(_T("Fault address: %016I64X %02X:%016I64X %s\r\n"),
684 pExceptionRecord->ExceptionAddress,
685 section, offset, szFaultingModule);
686#endif
687
688 PCONTEXT pCtx = pExceptionInfo->ContextRecord;
689
690 // Show the registers
691#ifdef _M_IX86 // X86 Only!
692 Log(_T("\r\nRegisters:\r\n"));
693
694 Log(_T("EAX:%08X\r\nEBX:%08X\r\nECX:%08X\r\nEDX:%08X\r\nESI:%08X\r\nEDI:%08X\r\n")
695 , pCtx->Eax, pCtx->Ebx, pCtx->Ecx, pCtx->Edx,
696 pCtx->Esi, pCtx->Edi);
697
698 Log(_T("CS:EIP:%04X:%08X\r\n"), pCtx->SegCs, pCtx->Eip);
699 Log(_T("SS:ESP:%04X:%08X EBP:%08X\r\n"),
700 pCtx->SegSs, pCtx->Esp, pCtx->Ebp);
701 Log(_T("DS:%04X ES:%04X FS:%04X GS:%04X\r\n"),
702 pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs);
703 Log(_T("Flags:%08X\r\n"), pCtx->EFlags);
704#endif
705
706#ifdef _M_X64
707 Log(_T("\r\nRegisters:\r\n"));
708 Log(_T("RAX:%016I64X\r\nRBX:%016I64X\r\nRCX:%016I64X\r\nRDX:%016I64X\r\nRSI:%016I64X\r\nRDI:%016I64X\r\n")
709 _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")
710 , pCtx->Rax, pCtx->Rbx, pCtx->Rcx, pCtx->Rdx,
711 pCtx->Rsi, pCtx->Rdi, pCtx->R8, pCtx->R9, pCtx->R10, pCtx->R11, pCtx->R12, pCtx->R13, pCtx->R14, pCtx->R15);
712 Log(_T("CS:RIP:%04X:%016I64X\r\n"), pCtx->SegCs, pCtx->Rip);
713 Log(_T("SS:RSP:%04X:%016I64X RBP:%08X\r\n"),
714 pCtx->SegSs, pCtx->Rsp, pCtx->Rbp);
715 Log(_T("DS:%04X ES:%04X FS:%04X GS:%04X\r\n"),
716 pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs);
717 Log(_T("Flags:%08X\r\n"), pCtx->EFlags);
718#endif
719
720#ifdef _M_ARM64
721 Log(_T("\r\nRegisters:\r\n"));
722 Log(_T("X0:%016I64X\r\nX1:%016I64X\r\nX2:%016I64X\r\nX3:%016I64X\r\nX4:%016I64X\r\nX5:%016I64X\r\n"),
723 _T("X6:%016I64X\r\nX7:%016I64X\r\nX8:%016I64X\r\nX9:%016I64X\r\nX10:%016I64X\r\nX11:%016I64X\r\nX12:%016I64X\r\nX13:%016I64X\r\n"),
724 _T("X14:%016I64X\r\nX15:%016I64X\r\nX16:%016I64X\r\nX17:%016I64X\r\nX18:%016I64X\r\nX19:%016I64X\r\nX20:%016I64X\r\nX21:%016I64X\r\n"),
725 _T("X22:%016I64X\r\nX23:%016I64X\r\nX24:%016I64X\r\nX25:%016I64X\r\nX26:%016I64X\r\nX27:%016I64X\r\nX28:%016I64X\r\n"),
726 pCtx->X0, pCtx->X1, pCtx->X2, pCtx->X3, pCtx->X4, pCtx->X5,
727 pCtx->X6, pCtx->X7, pCtx->X8, pCtx->X9, pCtx->X10, pCtx->X11, pCtx->X12, pCtx->X13,
728 pCtx->X14, pCtx->X15, pCtx->X16, pCtx->X17, pCtx->X18, pCtx->X19, pCtx->X20, pCtx->X21,
729 pCtx->X22, pCtx->X23, pCtx->X24, pCtx->X25, pCtx->X26, pCtx->X27, pCtx->X28);
730 Log(_T("LR:%016I64X\r\n"), pCtx->Lr);
731 Log(_T("PC:%016I64X\r\n"), pCtx->Pc);
732 Log(_T("SP:%016I64X FP:%016I64X\r\n"), pCtx->Sp, pCtx->Fp);
733 Log(_T("Flags:%08X\r\n"), pCtx->Cpsr);
734#endif
735
736 SymSetOptions(SYMOPT_DEFERRED_LOADS);
737
738 // Initialize DbgHelp
739 if (!SymInitialize(GetCurrentProcess(), nullptr, TRUE))
740 {
741 Log(_T("\r\n"));
742 Log(_T("----\r\n"));
743 Log(_T("SYMBOL HANDLER ERROR (THIS IS NOT THE CRASH ERROR)\r\n\r\n"));
744 Log(_T("Couldn't initialize symbol handler for process when generating crash report\r\n"));
745 Log(_T("Error: %s\r\n"), ErrorMessage(GetLastError()));
746 Log(_T("THE BELOW CALL STACKS MIGHT HAVE MISSING OR INACCURATE FILE/FUNCTION NAMES\r\n\r\n"));
747 Log(_T("----\r\n"));
748 }
749
750 if (pExceptionRecord->ExceptionCode == 0xE06D7363 && pExceptionRecord->NumberParameters >= 2)
751 {
752 PVOID exceptionObject = reinterpret_cast<PVOID>(pExceptionRecord->ExceptionInformation[1]);
753 ThrowInfo const* throwInfo = reinterpret_cast<ThrowInfo const*>(pExceptionRecord->ExceptionInformation[2]);
754#if _EH_RELATIVE_TYPEINFO
755 // When _EH_RELATIVE_TYPEINFO is defined, the pointers need to be retrieved with some pointer math
756 auto resolveExceptionRVA = [pExceptionRecord](int32 rva) -> DWORD_PTR
757 {
758 return rva + (pExceptionRecord->NumberParameters >= 4 ? pExceptionRecord->ExceptionInformation[3] : 0);
759 };
760#else
761 // Otherwise the pointers are already there in the API types
762 auto resolveExceptionRVA = [](void const* input) -> void const* { return input; };
763#endif
764
765 CatchableTypeArray const* catchables = reinterpret_cast<CatchableTypeArray const*>(resolveExceptionRVA(throwInfo->pCatchableTypeArray));
766 CatchableType const* catchable = catchables->nCatchableTypes ? reinterpret_cast<CatchableType const*>(resolveExceptionRVA(catchables->arrayOfCatchableTypes[0])) : nullptr;
767 TypeDescriptor const* exceptionTypeinfo = catchable ? reinterpret_cast<TypeDescriptor const*>(resolveExceptionRVA(catchable->pType)) : nullptr;
768
769 if (exceptionTypeinfo)
770 {
771 void* stdExceptionTypeInfo = []() -> void*
772 {
773 try
774 {
775 std::exception fake;
776 return __RTtypeid(&fake);
777 }
778 catch (...)
779 {
780 return nullptr;
781 }
782 }();
783 std::exception const* exceptionPtr = [](void* object, TypeDescriptor const* typeInfo, void* stdExceptionTypeInfo) -> std::exception const*
784 {
785 try
786 {
787 // real_type descriptor is obtained by parsing throwinfo
788 // equivalent to expression like this
789 // std::exception* e = object;
790 // real_type* r = dynamic_cast<real_type*>(e);
791 // return r;
792 return reinterpret_cast<std::exception const*>(__RTDynamicCast(object, 0, stdExceptionTypeInfo, (void*)typeInfo, false));
793 }
794 catch (...)
795 {
796 return nullptr;
797 }
798 }(exceptionObject, exceptionTypeinfo, stdExceptionTypeInfo);
799
800 // dynamic_cast<type>(variable_that_already_has_that_type) is optimized away by compiler and attempting to call __RTDynamicCast fails for it
801 if (!exceptionPtr && exceptionTypeinfo == stdExceptionTypeInfo)
802 exceptionPtr = reinterpret_cast<std::exception*>(exceptionObject);
803
804 Log(_T("\r\nUncaught C++ exception info:"));
805 if (exceptionPtr)
806 Log(_T(" %s"), exceptionPtr->what());
807
808 Log(_T("\r\n"));
809
810 char undName[MAX_SYM_NAME] = { };
811 if (UnDecorateSymbolName(&exceptionTypeinfo->name[1], &undName[0], MAX_SYM_NAME, UNDNAME_32_BIT_DECODE | UNDNAME_NAME_ONLY | UNDNAME_NO_ARGUMENTS))
812 {
813 char buf[MAX_SYM_NAME + sizeof(SYMBOL_INFO)] = { };
814 PSYMBOL_INFO sym = (PSYMBOL_INFO)&buf[0];
815 sym->SizeOfStruct = sizeof(SYMBOL_INFO);
816 sym->MaxNameLen = MAX_SYM_NAME;
817 if (SymGetTypeFromName(m_hProcess, (ULONG64)GetModuleHandle(nullptr), undName, sym))
818 {
819 sym->Address = pExceptionRecord->ExceptionInformation[1];
820 sym->Flags = 0;
821 char const* variableName = "uncaught_exception";
822 memset(sym->Name, 0, MAX_SYM_NAME);
823 memcpy(sym->Name, variableName, strlen(variableName));
824 FormatSymbolValue(sym, nullptr);
825 }
826 }
827 }
828 }
829
830 CONTEXT trashableContext = *pCtx;
831
832 WriteStackDetails(&trashableContext, false, nullptr);
834
835 // #ifdef _M_IX86 // X86 Only!
836
837 Log(_T("========================\r\n"));
838 Log(_T("Local Variables And Parameters\r\n"));
839
840 trashableContext = *pCtx;
841 WriteStackDetails(&trashableContext, true, nullptr);
843
844 SymCleanup(GetCurrentProcess());
845
846 Log(_T("\r\n"));
847 }
848 __except (EXCEPTION_EXECUTE_HANDLER)
849 {
850 Log(_T("Error writing the crash log\r\n"));
851 }
852}
853
854//======================================================================
855// Given an exception code, returns a pointer to a static string with a
856// description of the exception
857//======================================================================
859{
860 #define EXCEPTION(x) case EXCEPTION_##x: return _T(#x);
861
862 switch (dwCode)
863 {
864 EXCEPTION(ACCESS_VIOLATION)
865 EXCEPTION(DATATYPE_MISALIGNMENT)
866 EXCEPTION(BREAKPOINT)
867 EXCEPTION(SINGLE_STEP)
868 EXCEPTION(ARRAY_BOUNDS_EXCEEDED)
869 EXCEPTION(FLT_DENORMAL_OPERAND)
870 EXCEPTION(FLT_DIVIDE_BY_ZERO)
871 EXCEPTION(FLT_INEXACT_RESULT)
872 EXCEPTION(FLT_INVALID_OPERATION)
873 EXCEPTION(FLT_OVERFLOW)
874 EXCEPTION(FLT_STACK_CHECK)
875 EXCEPTION(FLT_UNDERFLOW)
876 EXCEPTION(INT_DIVIDE_BY_ZERO)
877 EXCEPTION(INT_OVERFLOW)
878 EXCEPTION(PRIV_INSTRUCTION)
879 EXCEPTION(IN_PAGE_ERROR)
880 EXCEPTION(ILLEGAL_INSTRUCTION)
881 EXCEPTION(NONCONTINUABLE_EXCEPTION)
882 EXCEPTION(STACK_OVERFLOW)
883 EXCEPTION(INVALID_DISPOSITION)
884 EXCEPTION(GUARD_PAGE)
885 EXCEPTION(INVALID_HANDLE)
886 case 0xE06D7363: return _T("Unhandled C++ exception");
887 }
888
889 // If not one of the "known" exceptions, try to get the string
890 // from NTDLL.DLL's message table.
891
892 static TCHAR szBuffer[512] = { 0 };
893
894 FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE,
895 GetModuleHandle(_T("NTDLL.DLL")),
896 dwCode, 0, szBuffer, sizeof(szBuffer), nullptr);
897
898 return szBuffer;
899}
900
901//=============================================================================
902// Given a linear address, locates the module, section, and offset containing
903// that address.
904//
905// Note: the szModule paramater buffer is an output buffer of length specified
906// by the len parameter (in characters!)
907//=============================================================================
909PVOID addr, PTSTR szModule, DWORD len, DWORD& section, DWORD_PTR& offset)
910{
911 MEMORY_BASIC_INFORMATION mbi;
912
913 if (!VirtualQuery(addr, &mbi, sizeof(mbi)))
914 return FALSE;
915
916 DWORD_PTR hMod = (DWORD_PTR)mbi.AllocationBase;
917
918 if (!hMod)
919 return FALSE;
920
921 if (!GetModuleFileName((HMODULE)hMod, szModule, len))
922 return FALSE;
923
924 // Point to the DOS header in memory
925 PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)hMod;
926
927 // From the DOS header, find the NT (PE) header
928 PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)(hMod + DWORD_PTR(pDosHdr->e_lfanew));
929
930 PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNtHdr);
931
932 DWORD_PTR rva = (DWORD_PTR)addr - hMod; // RVA is offset from module load address
933
934 // Iterate through the section table, looking for the one that encompasses
935 // the linear address.
936 for (unsigned i = 0;
937 i < pNtHdr->FileHeader.NumberOfSections;
938 i++, pSection++)
939 {
940 DWORD_PTR sectionStart = pSection->VirtualAddress;
941 DWORD_PTR sectionEnd = sectionStart
942 + DWORD_PTR(std::max(pSection->SizeOfRawData, pSection->Misc.VirtualSize));
943
944 // Is the address in this section???
945 if ((rva >= sectionStart) && (rva <= sectionEnd))
946 {
947 // Yes, address is in the section. Calculate section and offset,
948 // and store in the "section" & "offset" params, which were
949 // passed by reference.
950 section = i+1;
951 offset = rva - sectionStart;
952 return TRUE;
953 }
954 }
955
956 return FALSE; // Should never get here!
957}
958
959// It contains SYMBOL_INFO structure plus additional
960// space for the name of the symbol
961struct CSymbolInfoPackage : public SYMBOL_INFO_PACKAGE
962{
964 {
965 si.SizeOfStruct = sizeof(SYMBOL_INFO);
966 si.MaxNameLen = sizeof(name);
967 }
968};
969
970//============================================================
971// Walks the stack, and writes the results to the report file
972//============================================================
974PCONTEXT pContext,
975bool bWriteVariables, HANDLE pThreadHandle) // true if local/params should be output
976{
977 Log(_T("\r\nCall stack:\r\n"));
978
979 Log(_T("Address Frame Function SourceFile\r\n"));
980
981 DWORD dwMachineType = 0;
982 // Could use SymSetOptions here to add the SYMOPT_DEFERRED_LOADS flag
983
984 STACKFRAME64 sf;
985 memset(&sf, 0, sizeof(sf));
986
987 // Initialize the STACKFRAME structure for the first call.
988 sf.AddrPC.Mode = AddrModeFlat;
989 sf.AddrStack.Mode = AddrModeFlat;
990 sf.AddrFrame.Mode = AddrModeFlat;
991
992#ifdef _M_IX86
993 sf.AddrPC.Offset = pContext->Eip;
994 sf.AddrStack.Offset = pContext->Esp;
995 sf.AddrFrame.Offset = pContext->Ebp;
996 dwMachineType = IMAGE_FILE_MACHINE_I386;
997#elif defined(_M_X64)
998 sf.AddrPC.Offset = pContext->Rip;
999 sf.AddrStack.Offset = pContext->Rsp;
1000 sf.AddrFrame.Offset = pContext->Rbp;
1001 dwMachineType = IMAGE_FILE_MACHINE_AMD64;
1002#elif defined(_M_ARM64)
1003 sf.AddrPC.Offset = pContext->Pc;
1004 sf.AddrStack.Offset = pContext->Sp;
1005 sf.AddrFrame.Offset = pContext->Fp;
1006 dwMachineType = IMAGE_FILE_MACHINE_ARM64;
1007#endif
1008
1009 for (;;)
1010 {
1011 // Get the next stack frame
1012 if (! StackWalk64(dwMachineType,
1013 m_hProcess,
1014 pThreadHandle != nullptr ? pThreadHandle : GetCurrentThread(),
1015 &sf,
1016 pContext,
1017 nullptr,
1018 SymFunctionTableAccess64,
1019 SymGetModuleBase64,
1020 nullptr))
1021 break;
1022 if (0 == sf.AddrFrame.Offset) // Basic sanity check to make sure
1023 break; // the frame is OK. Bail if not.
1024#ifdef _M_IX86
1025 Log(_T("%08X %08X "), sf.AddrPC.Offset, sf.AddrFrame.Offset);
1026#elif defined(_M_X64) || defined(_M_ARM64)
1027 Log(_T("%016I64X %016I64X "), sf.AddrPC.Offset, sf.AddrFrame.Offset);
1028#endif
1029
1030 DWORD64 symDisplacement = 0; // Displacement of the input address,
1031 // relative to the start of the symbol
1032
1033 // Get the name of the function for this stack frame entry
1035 if (SymFromAddr(
1036 m_hProcess, // Process handle of the current process
1037 sf.AddrPC.Offset, // Symbol address
1038 &symDisplacement, // Address of the variable that will receive the displacement
1039 &sip.si)) // Address of the SYMBOL_INFO structure (inside "sip" object)
1040 {
1041 Log(_T("%hs+%I64X"), sip.si.Name, symDisplacement);
1042
1043 }
1044 else // No symbol found. Print out the logical address instead.
1045 {
1046 TCHAR szModule[MAX_PATH] = _T("");
1047 DWORD section = 0;
1048 DWORD_PTR offset = 0;
1049
1050 GetLogicalAddress((PVOID)sf.AddrPC.Offset,
1051 szModule, sizeof(szModule), section, offset);
1052#ifdef _M_IX86
1053 Log(_T("%04X:%08X %s"), section, offset, szModule);
1054#elif defined(_M_X64) || defined(_M_ARM64)
1055 Log(_T("%04X:%016I64X %s"), section, offset, szModule);
1056#endif
1057 }
1058
1059 // Get the source line for this stack frame entry
1060 IMAGEHLP_LINE64 lineInfo = { sizeof(IMAGEHLP_LINE64) };
1061 DWORD dwLineDisplacement;
1062 if (SymGetLineFromAddr64(m_hProcess, sf.AddrPC.Offset,
1063 &dwLineDisplacement, &lineInfo))
1064 {
1065 Log(_T(" %s line %u"), lineInfo.FileName, lineInfo.LineNumber);
1066 }
1067
1068 Log(_T("\r\n"));
1069
1070 // Write out the variables, if desired
1071 if (bWriteVariables)
1072 {
1073 // Use SymSetContext to get just the locals/params for this frame
1074 IMAGEHLP_STACK_FRAME imagehlpStackFrame;
1075 imagehlpStackFrame.InstructionOffset = sf.AddrPC.Offset;
1076 SymSetContext(m_hProcess, &imagehlpStackFrame, nullptr);
1077
1078 // Enumerate the locals/parameters
1080 ctx.sf = &sf;
1081 ctx.context = pContext;
1082 SymEnumSymbols(m_hProcess, 0, nullptr, EnumerateSymbolsCallback, &ctx);
1083
1084 Log(_T("\r\n"));
1085 }
1086 }
1087
1088}
1089
1091// The function invoked by SymEnumSymbols
1093
1094BOOL CALLBACK
1096PSYMBOL_INFO pSymInfo,
1097ULONG /*SymbolSize*/,
1098PVOID UserContext)
1099{
1100 __try
1101 {
1102 ClearSymbols();
1103 FormatSymbolValue(pSymInfo, (EnumerateSymbolsCallbackContext*)UserContext);
1104
1105 }
1106 __except (EXCEPTION_EXECUTE_HANDLER)
1107 {
1108 Log(_T("punting on symbol %s, partial output:\r\n"), pSymInfo->Name);
1109 }
1110
1111 return TRUE;
1112}
1113
1115{
1116#define REG_L(x) ((BYTE)(((DWORD_PTR)(x)) & 0xff))
1117#define REG_H(x) ((BYTE)((((DWORD_PTR)(x)) >> 8) & 0xff))
1118#define REG_X(x) ((WORD)(((DWORD_PTR)(x)) & 0xffff))
1119#define REG_E(x) ((DWORD)(((DWORD_PTR)(x)) & 0xffffffff))
1120#define REG_R(x) ((DWORD64)(((DWORD_PTR)(x)) & 0xffffffffffffffff))
1121#define CPU_REG(reg, field, part) case reg: return part(context->field);
1122 switch (registerId)
1123 {
1124#ifdef _M_IX86
1125 CPU_REG(CV_REG_AL, Eax, REG_L);
1126 CPU_REG(CV_REG_CL, Ecx, REG_L);
1127 CPU_REG(CV_REG_DL, Edx, REG_L);
1128 CPU_REG(CV_REG_BL, Ebx, REG_L);
1129 CPU_REG(CV_REG_AH, Eax, REG_H);
1130 CPU_REG(CV_REG_CH, Ecx, REG_H);
1131 CPU_REG(CV_REG_DH, Edx, REG_H);
1132 CPU_REG(CV_REG_BH, Ebx, REG_H);
1133 CPU_REG(CV_REG_AX, Eax, REG_X);
1134 CPU_REG(CV_REG_CX, Ecx, REG_X);
1135 CPU_REG(CV_REG_DX, Edx, REG_X);
1136 CPU_REG(CV_REG_BX, Ebx, REG_X);
1137 CPU_REG(CV_REG_SP, Esp, REG_X);
1138 CPU_REG(CV_REG_BP, Ebp, REG_X);
1139 CPU_REG(CV_REG_SI, Esi, REG_X);
1140 CPU_REG(CV_REG_DI, Edi, REG_X);
1141 CPU_REG(CV_REG_EAX, Eax, REG_E);
1142 CPU_REG(CV_REG_ECX, Ecx, REG_E);
1143 CPU_REG(CV_REG_EDX, Edx, REG_E);
1144 CPU_REG(CV_REG_EBX, Ebx, REG_E);
1145 CPU_REG(CV_REG_ESP, Esp, REG_E);
1146 CPU_REG(CV_REG_EBP, Ebp, REG_E);
1147 CPU_REG(CV_REG_ESI, Esi, REG_E);
1148 CPU_REG(CV_REG_EDI, Edi, REG_E);
1149 CPU_REG(CV_REG_EIP, Eip, REG_E);
1150#elif defined (_M_X64)
1151 CPU_REG(CV_AMD64_AL, Rax, REG_L);
1152 CPU_REG(CV_AMD64_CL, Rcx, REG_L);
1153 CPU_REG(CV_AMD64_DL, Rdx, REG_L);
1154 CPU_REG(CV_AMD64_BL, Rbx, REG_L);
1155 CPU_REG(CV_AMD64_SIL, Rsi, REG_L);
1156 CPU_REG(CV_AMD64_DIL, Rdi, REG_L);
1157 CPU_REG(CV_AMD64_BPL, Rbp, REG_L);
1158 CPU_REG(CV_AMD64_SPL, Rsp, REG_L);
1167 CPU_REG(CV_AMD64_AH, Rax, REG_H);
1168 CPU_REG(CV_AMD64_CH, Rcx, REG_H);
1169 CPU_REG(CV_AMD64_DH, Rdx, REG_H);
1170 CPU_REG(CV_AMD64_BH, Rbx, REG_H);
1171 CPU_REG(CV_AMD64_AX, Rax, REG_X);
1172 CPU_REG(CV_AMD64_CX, Rcx, REG_X);
1173 CPU_REG(CV_AMD64_DX, Rdx, REG_X);
1174 CPU_REG(CV_AMD64_BX, Rbx, REG_X);
1175 CPU_REG(CV_AMD64_SP, Rsp, REG_X);
1176 CPU_REG(CV_AMD64_BP, Rbp, REG_X);
1177 CPU_REG(CV_AMD64_SI, Rsi, REG_X);
1178 CPU_REG(CV_AMD64_DI, Rdi, REG_X);
1187 CPU_REG(CV_AMD64_EAX, Rax, REG_E);
1188 CPU_REG(CV_AMD64_ECX, Rcx, REG_E);
1189 CPU_REG(CV_AMD64_EDX, Rdx, REG_E);
1190 CPU_REG(CV_AMD64_EBX, Rbx, REG_E);
1191 CPU_REG(CV_AMD64_ESP, Rsp, REG_E);
1192 CPU_REG(CV_AMD64_EBP, Rbp, REG_E);
1193 CPU_REG(CV_AMD64_ESI, Rsi, REG_E);
1194 CPU_REG(CV_AMD64_EDI, Rdi, REG_E);
1203 CPU_REG(CV_AMD64_RIP, Rip, REG_R);
1204 CPU_REG(CV_AMD64_RAX, Rax, REG_R);
1205 CPU_REG(CV_AMD64_RBX, Rbx, REG_R);
1206 CPU_REG(CV_AMD64_RCX, Rcx, REG_R);
1207 CPU_REG(CV_AMD64_RDX, Rdx, REG_R);
1208 CPU_REG(CV_AMD64_RSI, Rsi, REG_R);
1209 CPU_REG(CV_AMD64_RDI, Rdi, REG_R);
1210 CPU_REG(CV_AMD64_RBP, Rbp, REG_R);
1211 CPU_REG(CV_AMD64_RSP, Rsp, REG_R);
1214 CPU_REG(CV_AMD64_R10, R10, REG_R);
1215 CPU_REG(CV_AMD64_R11, R11, REG_R);
1216 CPU_REG(CV_AMD64_R12, R12, REG_R);
1217 CPU_REG(CV_AMD64_R13, R13, REG_R);
1218 CPU_REG(CV_AMD64_R14, R14, REG_R);
1219 CPU_REG(CV_AMD64_R15, R15, REG_R);
1220#elif defined(_M_ARM64)
1231 CPU_REG(CV_ARM64_W10, X10, REG_E);
1232 CPU_REG(CV_ARM64_W11, X11, REG_E);
1233 CPU_REG(CV_ARM64_W12, X12, REG_E);
1234 CPU_REG(CV_ARM64_W13, X13, REG_E);
1235 CPU_REG(CV_ARM64_W14, X14, REG_E);
1236 CPU_REG(CV_ARM64_W15, X15, REG_E);
1237 CPU_REG(CV_ARM64_W16, X16, REG_E);
1238 CPU_REG(CV_ARM64_W17, X17, REG_E);
1239 CPU_REG(CV_ARM64_W18, X18, REG_E);
1240 CPU_REG(CV_ARM64_W19, X19, REG_E);
1241 CPU_REG(CV_ARM64_W20, X20, REG_E);
1242 CPU_REG(CV_ARM64_W21, X21, REG_E);
1243 CPU_REG(CV_ARM64_W22, X22, REG_E);
1244 CPU_REG(CV_ARM64_W23, X23, REG_E);
1245 CPU_REG(CV_ARM64_W24, X24, REG_E);
1246 CPU_REG(CV_ARM64_W25, X25, REG_E);
1247 CPU_REG(CV_ARM64_W26, X26, REG_E);
1248 CPU_REG(CV_ARM64_W27, X27, REG_E);
1249 CPU_REG(CV_ARM64_W28, X28, REG_E);
1252 case CV_ARM64_WZR: return 0;
1263 CPU_REG(CV_ARM64_X10, X10, REG_R);
1264 CPU_REG(CV_ARM64_X11, X11, REG_R);
1265 CPU_REG(CV_ARM64_X12, X12, REG_R);
1266 CPU_REG(CV_ARM64_X13, X13, REG_R);
1267 CPU_REG(CV_ARM64_X14, X14, REG_R);
1268 CPU_REG(CV_ARM64_X15, X15, REG_R);
1269 CPU_REG(CV_ARM64_IP0, X16, REG_R);
1270 CPU_REG(CV_ARM64_IP1, X17, REG_R);
1271 CPU_REG(CV_ARM64_X18, X18, REG_R);
1272 CPU_REG(CV_ARM64_X19, X19, REG_R);
1273 CPU_REG(CV_ARM64_X20, X20, REG_R);
1274 CPU_REG(CV_ARM64_X21, X21, REG_R);
1275 CPU_REG(CV_ARM64_X22, X22, REG_R);
1276 CPU_REG(CV_ARM64_X23, X23, REG_R);
1277 CPU_REG(CV_ARM64_X24, X24, REG_R);
1278 CPU_REG(CV_ARM64_X25, X25, REG_R);
1279 CPU_REG(CV_ARM64_X26, X26, REG_R);
1280 CPU_REG(CV_ARM64_X27, X27, REG_R);
1281 CPU_REG(CV_ARM64_X28, X28, REG_R);
1285 case CV_ARM64_ZR: return 0;
1286#endif
1287 default:
1288 break;
1289 }
1290 return {};
1291#undef CPU_REG
1292#undef REG_R
1293#undef REG_E
1294#undef REG_X
1295#undef REG_H
1296#undef REG_L
1297}
1298
1300// Given a SYMBOL_INFO representing a particular variable, displays its
1301// contents. If it's a user defined type, display the members and their
1302// values.
1305PSYMBOL_INFO pSym,
1307{
1308 // If it's a function, don't do anything.
1309 if (pSym->Tag == SymTagFunction) // SymTagFunction from CVCONST.H from the DIA SDK
1310 return false;
1311
1312 DWORD_PTR pVariable = pSym->Address; // Will point to the variable's data in memory
1313 Optional<DWORD_PTR> registerVariableStorage;
1314
1315 if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_FRAMERELATIVE
1316 || (pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGRELATIVE && pSym->Register == CV_ALLREG_VFRAME))
1317 {
1318 pVariable += pCtx->sf->AddrFrame.Offset;
1319 }
1320 else if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGRELATIVE)
1321 {
1322 Optional<DWORD_PTR> registerValue = GetIntegerRegisterValue(pCtx->context, pSym->Register);
1323 if (!registerValue)
1324 return false;
1325
1326 pVariable += *registerValue;
1327 }
1328 else if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGISTER)
1329 {
1330 registerVariableStorage = GetIntegerRegisterValue(pCtx->context, pSym->Register);
1331 if (!registerVariableStorage)
1332 return false; // Don't try to report non-integer register variable
1333
1334 pVariable = reinterpret_cast<DWORD_PTR>(&*registerVariableStorage);
1335 }
1336 else
1337 {
1338 // It must be a global variable
1339 }
1340
1342
1343 // Indicate if the variable is a local or parameter
1344 if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_PARAMETER)
1345 symbolDetails.top().Prefix = "Parameter ";
1346 else if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_LOCAL)
1347 symbolDetails.top().Prefix = "Local ";
1348
1349 // Determine if the variable is a user defined type (UDT). IF so, bHandled
1350 // will return true.
1351 bool bHandled;
1352 DumpTypeIndex(pSym->ModBase, pSym->TypeIndex, pVariable, bHandled, pSym->Name, "", false, true);
1353
1354 if (!bHandled)
1355 {
1356 // The symbol wasn't a UDT, so do basic, stupid formatting of the
1357 // variable. Based on the size, we're assuming it's a char, WORD, or
1358 // DWORD.
1359 BasicType basicType = GetBasicType(pSym->TypeIndex, pSym->ModBase);
1360 if (symbolDetails.top().Type.empty())
1361 symbolDetails.top().Type = rgBaseType[basicType];
1362
1363 // Emit the variable name
1364 if (pSym->Name[0] != '\0')
1365 symbolDetails.top().Name = pSym->Name;
1366
1367 char buffer[50];
1368 FormatOutputValue(buffer, basicType, pSym->Size, (PVOID)pVariable, sizeof(buffer));
1369 symbolDetails.top().Value = buffer;
1370 }
1371
1373 return true;
1374}
1375
1377// If it's a user defined type (UDT), recurse through its members until we're
1378// at fundamental types. When he hit fundamental types, return
1379// bHandled = false, so that FormatSymbolValue() will format them.
1382DWORD64 modBase,
1383DWORD dwTypeIndex,
1384DWORD_PTR offset,
1385bool & bHandled,
1386char const* Name,
1387char const* /*suffix*/,
1388bool newSymbol,
1389bool logChildren)
1390{
1391 bHandled = false;
1392
1393 if (newSymbol)
1395
1396 DWORD typeTag;
1397 if (!SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_SYMTAG, &typeTag))
1398 return;
1399
1400 // Get the name of the symbol. This will either be a Type name (if a UDT),
1401 // or the structure member name.
1402 WCHAR * pwszTypeName;
1403 if (SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_SYMNAME,
1404 &pwszTypeName))
1405 {
1406 // handle special cases
1407 if (wcscmp(pwszTypeName, L"std::basic_string<char,std::char_traits<char>,std::allocator<char> >") == 0)
1408 {
1409 LocalFree(pwszTypeName);
1410 symbolDetails.top().Type = "std::string";
1411 char buffer[50];
1412 FormatOutputValue(buffer, btStdString, 0, (PVOID)offset, sizeof(buffer));
1413 symbolDetails.top().Value = buffer;
1414 if (Name != nullptr && Name[0] != '\0')
1415 symbolDetails.top().Name = Name;
1416 bHandled = true;
1417 return;
1418 }
1419
1420 char buffer[WER_SMALL_BUFFER_SIZE];
1421 wcstombs(buffer, pwszTypeName, sizeof(buffer));
1422 buffer[WER_SMALL_BUFFER_SIZE - 1] = '\0';
1423 if (Name != nullptr && Name[0] != '\0')
1424 {
1425 symbolDetails.top().Type = buffer;
1426 symbolDetails.top().Name = Name;
1427 }
1428 else if (buffer[0] != '\0')
1429 symbolDetails.top().Name = buffer;
1430
1431 LocalFree(pwszTypeName);
1432 }
1433 else if (Name != nullptr && Name[0] != '\0')
1434 symbolDetails.top().Name = Name;
1435
1436 if (!StoreSymbol(dwTypeIndex, offset))
1437 {
1438 // Skip printing address and base class if it has been printed already
1439 if (typeTag == SymTagBaseClass)
1440 bHandled = true;
1441 return;
1442 }
1443
1444 DWORD innerTypeID;
1445 switch (typeTag)
1446 {
1447 case SymTagPointerType:
1448 if (SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_TYPEID, &innerTypeID))
1449 {
1450 if (Name != nullptr && Name[0] != '\0')
1451 symbolDetails.top().Name = Name;
1452
1453 BOOL isReference;
1454 SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_IS_REFERENCE, &isReference);
1455
1456 char addressStr[40];
1457 memset(addressStr, 0, sizeof(addressStr));
1458
1459 if (isReference)
1460 symbolDetails.top().Suffix += "&";
1461 else
1462 symbolDetails.top().Suffix += "*";
1463
1464 // Try to dereference the pointer in a try/except block since it might be invalid
1465 DWORD_PTR address = DereferenceUnsafePointer(offset);
1466
1467 char buffer[WER_SMALL_BUFFER_SIZE];
1468 FormatOutputValue(buffer, btVoid, sizeof(PVOID), (PVOID)offset, sizeof(buffer));
1469 symbolDetails.top().Value = buffer;
1470
1472 logChildren = false;
1473
1474 // no need to log any children since the address is invalid anyway
1475 if (address == 0 || address == DWORD_PTR(-1))
1476 logChildren = false;
1477
1478 DumpTypeIndex(modBase, innerTypeID, address, bHandled, Name, addressStr, false, logChildren);
1479
1480 if (!bHandled)
1481 {
1482 BasicType basicType = GetBasicType(dwTypeIndex, modBase);
1483 if (symbolDetails.top().Type.empty())
1484 symbolDetails.top().Type = rgBaseType[basicType];
1485
1486 if (address == 0)
1487 symbolDetails.top().Value = "NULL";
1488 else if (address == DWORD_PTR(-1))
1489 symbolDetails.top().Value = "<Unable to read memory>";
1490 else
1491 {
1492 // Get the size of the child member
1493 ULONG64 length;
1494 SymGetTypeInfo(m_hProcess, modBase, innerTypeID, TI_GET_LENGTH, &length);
1495 char buffer2[50];
1496 FormatOutputValue(buffer2, basicType, length, (PVOID)address, sizeof(buffer2));
1497 symbolDetails.top().Value = buffer2;
1498 }
1499 bHandled = true;
1500 return;
1501 }
1502 else if (address == 0)
1503 symbolDetails.top().Value = "NULL";
1504 else if (address == DWORD_PTR(-1))
1505 {
1506 symbolDetails.top().Value = "<Unable to read memory>";
1507 bHandled = true;
1508 return;
1509 }
1510 }
1511 break;
1512 case SymTagData:
1513 if (SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_TYPEID, &innerTypeID))
1514 {
1515 DWORD innerTypeTag;
1516 if (!SymGetTypeInfo(m_hProcess, modBase, innerTypeID, TI_GET_SYMTAG, &innerTypeTag))
1517 break;
1518
1519 switch (innerTypeTag)
1520 {
1521 case SymTagUDT:
1523 logChildren = false;
1524 DumpTypeIndex(modBase, innerTypeID,
1525 offset, bHandled, symbolDetails.top().Name.c_str(), "", false, logChildren);
1526 break;
1527 case SymTagPointerType:
1528 if (Name != nullptr && Name[0] != '\0')
1529 symbolDetails.top().Name = Name;
1530 DumpTypeIndex(modBase, innerTypeID,
1531 offset, bHandled, symbolDetails.top().Name.c_str(), "", false, logChildren);
1532 break;
1533 case SymTagArrayType:
1534 DumpTypeIndex(modBase, innerTypeID,
1535 offset, bHandled, symbolDetails.top().Name.c_str(), "", false, logChildren);
1536 break;
1537 default:
1538 break;
1539 }
1540 }
1541 break;
1542 case SymTagArrayType:
1543 if (SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_TYPEID, &innerTypeID))
1544 {
1545 symbolDetails.top().HasChildren = true;
1546
1547 BasicType basicType = btNoType;
1548 DumpTypeIndex(modBase, innerTypeID,
1549 offset, bHandled, Name, "", false, false);
1550
1551 // Set Value back to an empty string since the Array object itself has no value, only its elements have
1552 std::string firstElementValue = symbolDetails.top().Value;
1553 symbolDetails.top().Value.clear();
1554
1555 DWORD elementsCount;
1556 if (SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_COUNT, &elementsCount))
1557 symbolDetails.top().Suffix += "[" + std::to_string(elementsCount) + "]";
1558 else
1559 symbolDetails.top().Suffix += "[<unknown count>]";
1560
1561 if (!bHandled)
1562 {
1563 basicType = GetBasicType(dwTypeIndex, modBase);
1564 if (symbolDetails.top().Type.empty())
1565 symbolDetails.top().Type = rgBaseType[basicType];
1566 bHandled = true;
1567 }
1568
1569 // Get the size of the child member
1570 ULONG64 length;
1571 SymGetTypeInfo(m_hProcess, modBase, innerTypeID, TI_GET_LENGTH, &length);
1572
1573 char buffer[50];
1574 switch (basicType)
1575 {
1576 case btChar:
1577 case btStdString:
1578 FormatOutputValue(buffer, basicType, length, (PVOID)offset, sizeof(buffer), elementsCount);
1579 symbolDetails.top().Value = buffer;
1580 break;
1581 default:
1582 for (DWORD index = 0; index < elementsCount && index < WER_MAX_ARRAY_ELEMENTS_COUNT; index++)
1583 {
1584 bool elementHandled = false;
1586 if (index == 0)
1587 {
1588 if (firstElementValue.empty())
1589 {
1590 FormatOutputValue(buffer, basicType, length, (PVOID)(offset + length * index), sizeof(buffer));
1591 firstElementValue = buffer;
1592 }
1593 symbolDetails.top().Value = firstElementValue;
1594 }
1595 else
1596 {
1597 DumpTypeIndex(modBase, innerTypeID, offset + length * index, elementHandled, "", "", false, false);
1598 if (!elementHandled)
1599 {
1600 FormatOutputValue(buffer, basicType, length, (PVOID)(offset + length * index), sizeof(buffer));
1601 symbolDetails.top().Value = buffer;
1602 }
1603 }
1604 symbolDetails.top().Prefix.clear();
1605 symbolDetails.top().Type.clear();
1606 symbolDetails.top().Suffix = "[" + std::to_string(index) + "]";
1607 symbolDetails.top().Name.clear();
1609 }
1610 break;
1611 }
1612
1613 return;
1614 }
1615 break;
1616 case SymTagBaseType:
1617 break;
1618 case SymTagEnum:
1619 return;
1620 default:
1621 break;
1622 }
1623
1624 // Determine how many children this type has.
1625 DWORD dwChildrenCount = 0;
1626 SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_CHILDRENCOUNT, &dwChildrenCount);
1627
1628 if (!dwChildrenCount) // If no children, we're done
1629 return;
1630
1631 // Prepare to get an array of "TypeIds", representing each of the children.
1632 // SymGetTypeInfo(TI_FINDCHILDREN) expects more memory than just a
1633 // TI_FINDCHILDREN_PARAMS struct has. Use derivation to accomplish this.
1634 struct FINDCHILDREN : TI_FINDCHILDREN_PARAMS
1635 {
1636 ULONG MoreChildIds[1024*2];
1637 FINDCHILDREN(){Count = sizeof(MoreChildIds) / sizeof(MoreChildIds[0]);}
1638 } children;
1639
1640 children.Count = dwChildrenCount;
1641 children.Start= 0;
1642
1643 // Get the array of TypeIds, one for each child type
1644 if (!SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_FINDCHILDREN,
1645 &children))
1646 {
1647 return;
1648 }
1649
1650 // Iterate through each of the children
1651 for (unsigned i = 0; i < dwChildrenCount; i++)
1652 {
1653 DWORD symTag;
1654 SymGetTypeInfo(m_hProcess, modBase, children.ChildId[i], TI_GET_SYMTAG, &symTag);
1655
1656 if (symTag == SymTagFunction ||
1657 symTag == SymTagEnum ||
1658 symTag == SymTagTypedef ||
1659 symTag == SymTagVTable)
1660 continue;
1661
1662 // Ignore static fields
1663 DWORD dataKind;
1664 SymGetTypeInfo(m_hProcess, modBase, children.ChildId[i], TI_GET_DATAKIND, &dataKind);
1665 if (dataKind == DataIsStaticLocal ||
1666 dataKind == DataIsGlobal ||
1667 dataKind == DataIsStaticMember)
1668 continue;
1669
1670 symbolDetails.top().HasChildren = true;
1671 if (!logChildren)
1672 {
1673 bHandled = false;
1674 return;
1675 }
1676
1677 // Recurse for each of the child types
1678 bool bHandled2;
1679 BasicType basicType = GetBasicType(children.ChildId[i], modBase);
1680
1681 // Get the offset of the child member, relative to its parent
1682 DWORD dwMemberOffset;
1683 SymGetTypeInfo(m_hProcess, modBase, children.ChildId[i],
1684 TI_GET_OFFSET, &dwMemberOffset);
1685
1686 // Calculate the address of the member
1687 DWORD_PTR dwFinalOffset = offset + dwMemberOffset;
1688
1689 DumpTypeIndex(modBase,
1690 children.ChildId[i],
1691 dwFinalOffset, bHandled2, ""/*Name */, "", true, true);
1692
1693 // If the child wasn't a UDT, format it appropriately
1694 if (!bHandled2)
1695 {
1696 if (symbolDetails.top().Type.empty())
1697 symbolDetails.top().Type = rgBaseType[basicType];
1698
1699 // Get the real "TypeId" of the child. We need this for the
1700 // SymGetTypeInfo(TI_GET_TYPEID) call below.
1701 DWORD typeId;
1702 SymGetTypeInfo(m_hProcess, modBase, children.ChildId[i],
1703 TI_GET_TYPEID, &typeId);
1704
1705 // Get the size of the child member
1706 ULONG64 length;
1707 SymGetTypeInfo(m_hProcess, modBase, typeId, TI_GET_LENGTH, &length);
1708
1709 char buffer[50];
1710 FormatOutputValue(buffer, basicType, length, (PVOID)dwFinalOffset, sizeof(buffer));
1711 symbolDetails.top().Value = buffer;
1712 }
1713
1715 }
1716
1717 bHandled = true;
1718}
1719
1721BasicType basicType,
1722DWORD64 length,
1723PVOID pAddress,
1724size_t bufferSize,
1725size_t countOverride)
1726{
1727 __try
1728 {
1729 switch (basicType)
1730 {
1731 case btChar:
1732 {
1733 // Special case handling for char[] type
1734 if (countOverride != 0)
1735 length = countOverride;
1736 else
1737 length = strlen((char*)pAddress);
1738 if (length > bufferSize - 6)
1739 pszCurrBuffer += snprintf(pszCurrBuffer, bufferSize, "\"%.*s...\"", (int)(bufferSize - 6), (char*)pAddress);
1740 else
1741 pszCurrBuffer += snprintf(pszCurrBuffer, bufferSize, "\"%.*s\"", (int)length, (char*)pAddress);
1742 break;
1743 }
1744 case btStdString:
1745 {
1746 std::string* value = static_cast<std::string*>(pAddress);
1747 if (value->length() > bufferSize - 6)
1748 pszCurrBuffer += snprintf(pszCurrBuffer, bufferSize, "\"%.*s...\"", (int)(bufferSize - 6), value->c_str());
1749 else
1750 pszCurrBuffer += snprintf(pszCurrBuffer, bufferSize, "\"%s\"", value->c_str());
1751 break;
1752 }
1753 default:
1754 // Format appropriately (assuming it's a 1, 2, or 4 bytes (!!!)
1755 if (length == 1)
1756 pszCurrBuffer += snprintf(pszCurrBuffer, bufferSize, "0x%X", *(PBYTE)pAddress);
1757 else if (length == 2)
1758 pszCurrBuffer += snprintf(pszCurrBuffer, bufferSize, "0x%X", *(PWORD)pAddress);
1759 else if (length == 4)
1760 {
1761 if (basicType == btFloat)
1762 pszCurrBuffer += snprintf(pszCurrBuffer, bufferSize, "%f", *(PFLOAT)pAddress);
1763 else
1764 pszCurrBuffer += snprintf(pszCurrBuffer, bufferSize, "0x%X", *(PDWORD)pAddress);
1765 }
1766 else if (length == 8)
1767 {
1768 if (basicType == btFloat)
1769 {
1770 pszCurrBuffer += snprintf(pszCurrBuffer, bufferSize, "%f",
1771 *(double *)pAddress);
1772 }
1773 else
1774 pszCurrBuffer += snprintf(pszCurrBuffer, bufferSize, "0x%I64X",
1775 *(DWORD64*)pAddress);
1776 }
1777 else
1778 {
1779 #if _WIN64
1780 pszCurrBuffer += snprintf(pszCurrBuffer, bufferSize, "0x%I64X", (DWORD64)pAddress);
1781 #else
1782 pszCurrBuffer += snprintf(pszCurrBuffer, bufferSize, "0x%X", (DWORD)pAddress);
1783 #endif
1784 }
1785 break;
1786 }
1787 }
1788 __except (EXCEPTION_EXECUTE_HANDLER)
1789 {
1790#if _WIN64
1791 pszCurrBuffer += snprintf(pszCurrBuffer, bufferSize, "0x%I64X <Unable to read memory>", (DWORD64)pAddress);
1792#else
1793 pszCurrBuffer += snprintf(pszCurrBuffer, bufferSize, "0x%X <Unable to read memory>", (DWORD)pAddress);
1794#endif
1795 }
1796}
1797
1799WheatyExceptionReport::GetBasicType(DWORD typeIndex, DWORD64 modBase)
1800{
1801 BasicType basicType;
1802 if (SymGetTypeInfo(m_hProcess, modBase, typeIndex,
1803 TI_GET_BASETYPE, &basicType))
1804 {
1805 return basicType;
1806 }
1807
1808 // Get the real "TypeId" of the child. We need this for the
1809 // SymGetTypeInfo(TI_GET_TYPEID) call below.
1810 DWORD typeId;
1811 if (SymGetTypeInfo(m_hProcess, modBase, typeIndex, TI_GET_TYPEID, &typeId))
1812 {
1813 if (SymGetTypeInfo(m_hProcess, modBase, typeId, TI_GET_BASETYPE,
1814 &basicType))
1815 {
1816 return basicType;
1817 }
1818 }
1819
1820 return btNoType;
1821}
1822
1824{
1825 __try
1826 {
1827 return *(PDWORD_PTR)address;
1828 }
1829 __except (EXCEPTION_EXECUTE_HANDLER)
1830 {
1831 return DWORD_PTR(-1);
1832 }
1833}
1834
1835//============================================================================
1836// Helper function that writes to the report file, and allows the user to use
1837// printf style formating
1838//============================================================================
1839int __cdecl WheatyExceptionReport::Log(const TCHAR * format, ...)
1840{
1841 va_list argptr;
1842 va_start(argptr, format);
1843 int retValue = _vftprintf(m_hReportFile, format, argptr);
1844 va_end(argptr);
1845 return retValue;
1846}
1847
1848bool WheatyExceptionReport::StoreSymbol(DWORD type, DWORD_PTR offset)
1849{
1850 return symbols.insert(SymbolPair(type, offset)).second;
1851}
1852
1854{
1855 symbols.clear();
1856 while (!symbolDetails.empty())
1857 symbolDetails.pop();
1858}
1859
1861{
1862 // Log current symbol and then add another to the stack to keep the hierarchy format
1864 symbolDetails.emplace();
1865}
1866
1868{
1870 symbolDetails.pop();
1871}
1872
1874{
1875 if (symbolDetails.empty())
1876 return;
1877
1878 // Don't log anything if has been logged already or if it's empty
1879 if (symbolDetails.top().Logged || symbolDetails.top().empty())
1880 return;
1881
1882 // Add appropriate indentation level (since this routine is recursive)
1883 for (size_t i = 0; i < symbolDetails.size(); i++)
1884 Log(_T("\t"));
1885
1886 Log(_T("%s\r\n"), symbolDetails.top().ToString().c_str());
1887}
1888
1890{
1891 Logged = true;
1892 std::string formatted = Prefix + Type + Suffix;
1893 if (!Name.empty())
1894 {
1895 if (!formatted.empty())
1896 formatted += " ";
1897 formatted += Name;
1898 }
1899 if (!Value.empty())
1900 {
1901 if (Name == "passwd" || Name == "password")
1902 Value = "<sensitive data>";
1903 formatted += " = " + Value;
1904 }
1905 return formatted;
1906}
int32_t int32
Definition: Define.h:138
#define EXCEPTION_ASSERTION_FAILURE
Definition: Errors.h:53
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition: Optional.h:25
const size_t bufferSize
Definition: RASession.h:27
if(posix_memalign(&__mallocedMemory, __align, __size)) return NULL
#define EXCEPTION(x)
#define REG_E(x)
#define CPU_REG(reg, field, part)
void ToTchar(wchar_t const *src, TCHAR(&dst)[size])
#define CrashFolder
#define REG_X(x)
LPTSTR ErrorMessage(DWORD dw)
#define REG_L(x)
WheatyExceptionReport g_WheatyExceptionReport
#define REG_H(x)
#define REG_R(x)
char const *const rgBaseType[]
#define WER_SMALL_BUFFER_SIZE
#define WER_MAX_NESTING_LEVEL
#define WER_MAX_ARRAY_ELEMENTS_COUNT
std::set< SymbolPair > SymbolPairs
@ CV_ARM64_W29
@ CV_ARM64_W14
@ CV_ARM64_WZR
@ CV_AMD64_DIL
@ CV_ARM64_W15
@ CV_ARM64_X10
@ CV_ARM64_X15
@ CV_AMD64_RSI
@ CV_ARM64_X25
@ CV_AMD64_SPL
@ CV_AMD64_RIP
@ CV_AMD64_EAX
@ CV_AMD64_R13W
@ CV_AMD64_R14D
@ CV_ARM64_X14
@ CV_AMD64_R14W
@ CV_ARM64_IP1
@ CV_AMD64_R10B
@ CV_AMD64_R10D
@ CV_ARM64_X20
@ CV_ARM64_W23
@ CV_REG_EBX
@ CV_AMD64_R15W
@ CV_AMD64_R13
@ CV_ARM64_W21
@ CV_ARM64_W24
@ CV_ARM64_W17
@ CV_AMD64_RBX
@ CV_AMD64_RDI
@ CV_ARM64_W30
@ CV_REG_ESI
@ CV_REG_EIP
@ CV_ARM64_X23
@ CV_AMD64_EBX
@ CV_ARM64_W12
@ CV_AMD64_ESP
@ CV_REG_ECX
@ CV_ARM64_W27
@ CV_AMD64_R14B
@ CV_ARM64_W20
@ CV_ARM64_W19
@ CV_AMD64_ESI
@ CV_REG_EBP
@ CV_REG_EAX
@ CV_AMD64_R13B
@ CV_AMD64_EDX
@ CV_AMD64_R11B
@ CV_AMD64_R15B
@ CV_AMD64_R15
@ CV_AMD64_R14
@ CV_AMD64_R10W
@ CV_AMD64_ECX
@ CV_ARM64_W16
@ CV_ARM64_X13
@ CV_ARM64_X22
@ CV_ARM64_X12
@ CV_ARM64_X26
@ CV_AMD64_R11D
@ CV_AMD64_BPL
@ CV_ARM64_X28
@ CV_AMD64_R12
@ CV_AMD64_R12W
@ CV_ARM64_X24
@ CV_ARM64_X19
@ CV_AMD64_RDX
@ CV_ARM64_X27
@ CV_AMD64_R11
@ CV_REG_EDI
@ CV_ARM64_W28
@ CV_ARM64_W11
@ CV_AMD64_R13D
@ CV_REG_ESP
@ CV_AMD64_R8W
@ CV_ARM64_W22
@ CV_AMD64_RBP
@ CV_AMD64_RAX
@ CV_AMD64_RCX
@ CV_ARM64_W13
@ CV_AMD64_R12D
@ CV_ARM64_W10
@ CV_AMD64_RSP
@ CV_ARM64_X11
@ CV_ARM64_W25
@ CV_AMD64_R9D
@ CV_AMD64_R15D
@ CV_AMD64_EBP
@ CV_ALLREG_VFRAME
@ CV_AMD64_SIL
@ CV_ARM64_W18
@ CV_AMD64_R8D
@ CV_ARM64_W26
@ CV_AMD64_R11W
@ CV_ARM64_X18
@ CV_AMD64_R9B
@ CV_ARM64_X21
@ CV_REG_EDX
@ CV_AMD64_R10
@ CV_ARM64_IP0
@ CV_AMD64_R8B
@ CV_AMD64_R9W
@ CV_AMD64_EDI
@ CV_AMD64_AL
@ CV_AMD64_R12B
@ DataIsStaticMember
@ DataIsGlobal
@ DataIsStaticLocal
@ btStdString
static LPCTSTR GetExceptionString(DWORD dwCode)
static BOOL GetLogicalAddress(PVOID addr, PTSTR szModule, DWORD len, DWORD &section, DWORD_PTR &offset)
static int __cdecl Log(const TCHAR *format,...)
static BOOL CALLBACK EnumerateSymbolsCallback(PSYMBOL_INFO, ULONG, PVOID)
static BOOL _GetProcessorName(TCHAR *sProcessorName, DWORD maxcount)
static bool FormatSymbolValue(PSYMBOL_INFO, EnumerateSymbolsCallbackContext *)
static SymbolPairs symbols
static void FormatOutputValue(char *pszCurrBuffer, BasicType basicType, DWORD64 length, PVOID pAddress, size_t bufferSize, size_t countOverride=0)
static BOOL _GetWindowsVersionFromWMI(TCHAR *szVersion, DWORD cntMax)
static void DumpTypeIndex(DWORD64, DWORD, DWORD_PTR, bool &, char const *, char const *, bool, bool)
static LPTOP_LEVEL_EXCEPTION_FILTER m_previousFilter
static _invalid_parameter_handler m_previousCrtHandler
static BasicType GetBasicType(DWORD typeIndex, DWORD64 modBase)
static DWORD_PTR DereferenceUnsafePointer(DWORD_PTR address)
static std::mutex alreadyCrashedLock
static TCHAR m_szDumpFileName[MAX_PATH]
static pRtlGetVersion RtlGetVersion
static BOOL _GetWindowsVersion(TCHAR *szVersion, DWORD cntMax)
static void WriteStackDetails(PCONTEXT pContext, bool bWriteVariables, HANDLE pThreadHandle)
NTSTATUS(NTAPI * pRtlGetVersion)(PRTL_OSVERSIONINFOW lpVersionInformation)
static bool StoreSymbol(DWORD type, DWORD_PTR offset)
static Optional< DWORD_PTR > GetIntegerRegisterValue(PCONTEXT context, ULONG registerId)
static void GenerateExceptionReport(PEXCEPTION_POINTERS pExceptionInfo)
static void printTracesForAllThreads(bool)
static std::stack< SymbolDetail > symbolDetails
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 TCHAR m_szLogFileName[MAX_PATH]
TC_COMMON_API char const * GetFullVersion()
Definition: GitRevision.cpp:96
TC_COMMON_API char const * GetHash()
Definition: GitRevision.cpp:21
constexpr std::size_t size()
Definition: UpdateField.h:796