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