aboutsummaryrefslogtreecommitdiff
path: root/src/common/Debugging/WheatyExceptionReport.cpp
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2024-09-15 12:13:54 +0200
committerShauren <shauren.trinity@gmail.com>2024-09-15 12:13:54 +0200
commitfd0a7ba871060c895fdf701799dbf649ee697078 (patch)
tree88c6d64fce3df94b545b2c13b3c7178d465b3f03 /src/common/Debugging/WheatyExceptionReport.cpp
parent6a5b24cacc4ea2eb55c481c400621a97d940b905 (diff)
Core/CrashHandler: Compile WheatyExceptionReport only once and moved its global variable initializer to all projects using it
Diffstat (limited to 'src/common/Debugging/WheatyExceptionReport.cpp')
-rw-r--r--src/common/Debugging/WheatyExceptionReport.cpp1912
1 files changed, 0 insertions, 1912 deletions
diff --git a/src/common/Debugging/WheatyExceptionReport.cpp b/src/common/Debugging/WheatyExceptionReport.cpp
deleted file mode 100644
index c9e3a985543..00000000000
--- a/src/common/Debugging/WheatyExceptionReport.cpp
+++ /dev/null
@@ -1,1912 +0,0 @@
-//==========================================
-// Matt Pietrek
-// MSDN Magazine, 2002
-// FILE: WheatyExceptionReport.CPP
-//==========================================
-#include "WheatyExceptionReport.h"
-#include "Errors.h"
-#include "GitRevision.h"
-#include <algorithm>
-
-#ifdef __clang__
-// clang-cl doesn't have these hardcoded types available, correct ehdata_forceinclude.h that relies on it
-#define _ThrowInfo ThrowInfo
-#endif
-
-#include <ehdata.h>
-#include <rttidata.h>
-#include <tlhelp32.h>
-#include <tchar.h>
-
-#include <comdef.h>
-#include <WbemIdl.h>
-
-#define CrashFolder _T("Crashes")
-#pragma comment(linker, "/DEFAULTLIB:dbghelp.lib")
-#pragma comment(linker, "/DEFAULTLIB:wbemuuid.lib")
-
-inline LPTSTR ErrorMessage(DWORD dw)
-{
- LPVOID lpMsgBuf;
- DWORD formatResult = FormatMessage(
- FORMAT_MESSAGE_ALLOCATE_BUFFER |
- FORMAT_MESSAGE_FROM_SYSTEM,
- nullptr,
- dw,
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPTSTR) &lpMsgBuf,
- 0, nullptr);
- if (formatResult != 0)
- return (LPTSTR)lpMsgBuf;
- else
- {
- LPTSTR msgBuf = (LPTSTR)LocalAlloc(LPTR, 30);
- _sntprintf(msgBuf, 30, _T("Unknown error: %d"), (int)dw);
- return msgBuf;
- }
-
-}
-
-//============================== Global Variables =============================
-
-//
-// Declare the static variables of the WheatyExceptionReport class and force their initialization before any other static in the program
-//
-#pragma warning(push)
-#pragma warning(disable: 4073) // C4073: initializers put in library initialization area
-#pragma init_seg(lib)
-TCHAR WheatyExceptionReport::m_szLogFileName[MAX_PATH];
-TCHAR WheatyExceptionReport::m_szDumpFileName[MAX_PATH];
-LPTOP_LEVEL_EXCEPTION_FILTER WheatyExceptionReport::m_previousFilter;
-_invalid_parameter_handler WheatyExceptionReport::m_previousCrtHandler;
-FILE* WheatyExceptionReport::m_hReportFile;
-HANDLE WheatyExceptionReport::m_hDumpFile;
-HANDLE WheatyExceptionReport::m_hProcess;
-SymbolPairs WheatyExceptionReport::symbols;
-std::stack<SymbolDetail> WheatyExceptionReport::symbolDetails;
-bool WheatyExceptionReport::alreadyCrashed;
-std::mutex WheatyExceptionReport::alreadyCrashedLock;
-WheatyExceptionReport::pRtlGetVersion WheatyExceptionReport::RtlGetVersion;
-
-// Declare global instance of class
-WheatyExceptionReport g_WheatyExceptionReport;
-#pragma warning(pop)
-
-//============================== Class Methods =============================
-
-WheatyExceptionReport::WheatyExceptionReport() // Constructor
-{
- // Install the unhandled exception filter function
- m_previousFilter = SetUnhandledExceptionFilter(WheatyUnhandledExceptionFilter);
- m_previousCrtHandler = _set_invalid_parameter_handler(WheatyCrtHandler);
- m_hProcess = GetCurrentProcess();
- alreadyCrashed = false;
- RtlGetVersion = (pRtlGetVersion)GetProcAddress(GetModuleHandle(_T("ntdll.dll")), "RtlGetVersion");
- if (!IsDebuggerPresent())
- {
- _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
- _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
- _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
- _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
- }
-}
-
-//============
-// Destructor
-//============
-WheatyExceptionReport::~WheatyExceptionReport()
-{
- if (m_previousFilter)
- SetUnhandledExceptionFilter(m_previousFilter);
- if (m_previousCrtHandler)
- _set_invalid_parameter_handler(m_previousCrtHandler);
- ClearSymbols();
-}
-
-//===========================================================
-// Entry point where control comes on an unhandled exception
-//===========================================================
-LONG WINAPI WheatyExceptionReport::WheatyUnhandledExceptionFilter(
-PEXCEPTION_POINTERS pExceptionInfo)
-{
- std::unique_lock<std::mutex> guard(alreadyCrashedLock);
- // Handle only 1 exception in the whole process lifetime
- if (alreadyCrashed)
- return EXCEPTION_EXECUTE_HANDLER;
-
- alreadyCrashed = true;
-
- TCHAR module_folder_name[MAX_PATH];
- GetModuleFileName(nullptr, module_folder_name, MAX_PATH);
- TCHAR* pos = _tcsrchr(module_folder_name, '\\');
- if (!pos)
- return 0;
- pos[0] = '\0';
- ++pos;
-
- TCHAR crash_folder_path[MAX_PATH];
- _stprintf_s(crash_folder_path, _T("%s\\%s"), module_folder_name, CrashFolder);
- if (!CreateDirectory(crash_folder_path, nullptr))
- {
- if (GetLastError() != ERROR_ALREADY_EXISTS)
- return 0;
- }
-
-#ifdef _UNICODE
-#define PRSTRc "S"
-#else
-#define PRSTRc "s"
-#endif
-
- SYSTEMTIME systime;
- GetLocalTime(&systime);
- _stprintf_s(m_szDumpFileName, _T("%s\\%" PRSTRc "_%s_[%u-%u_%u-%u-%u].dmp"),
- crash_folder_path, GitRevision::GetHash(), pos, systime.wDay, systime.wMonth, systime.wHour, systime.wMinute, systime.wSecond);
-
- _stprintf_s(m_szLogFileName, _T("%s\\%" PRSTRc "_%s_[%u-%u_%u-%u-%u].txt"),
- crash_folder_path, GitRevision::GetHash(), pos, systime.wDay, systime.wMonth, systime.wHour, systime.wMinute, systime.wSecond);
-
- m_hDumpFile = CreateFile(m_szDumpFileName,
- GENERIC_WRITE,
- 0,
- nullptr,
- OPEN_ALWAYS,
- FILE_FLAG_WRITE_THROUGH,
- nullptr);
-
- if (m_hDumpFile)
- {
- MINIDUMP_EXCEPTION_INFORMATION info;
- info.ClientPointers = FALSE;
- info.ExceptionPointers = pExceptionInfo;
- info.ThreadId = GetCurrentThreadId();
-
- MINIDUMP_USER_STREAM additionalStream = {};
- MINIDUMP_USER_STREAM_INFORMATION additionalStreamInfo = {};
-
- if (pExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_ASSERTION_FAILURE && pExceptionInfo->ExceptionRecord->NumberParameters > 0)
- {
- additionalStream.Type = CommentStreamA;
- additionalStream.Buffer = reinterpret_cast<PVOID>(pExceptionInfo->ExceptionRecord->ExceptionInformation[0]);
- additionalStream.BufferSize = strlen(reinterpret_cast<char const*>(pExceptionInfo->ExceptionRecord->ExceptionInformation[0])) + 1;
-
- additionalStreamInfo.UserStreamArray = &additionalStream;
- additionalStreamInfo.UserStreamCount = 1;
- }
-
- MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),
- m_hDumpFile, MiniDumpWithIndirectlyReferencedMemory, &info, &additionalStreamInfo, nullptr);
-
- CloseHandle(m_hDumpFile);
- }
-
- m_hReportFile = _tfopen(m_szLogFileName, _T("wb"));
-
- if (m_hReportFile)
- {
- GenerateExceptionReport(pExceptionInfo);
-
- fclose(m_hReportFile);
- m_hReportFile = nullptr;
- }
-
- if (m_previousFilter)
- return m_previousFilter(pExceptionInfo);
- else
- return EXCEPTION_EXECUTE_HANDLER/*EXCEPTION_CONTINUE_SEARCH*/;
-}
-
-void __cdecl WheatyExceptionReport::WheatyCrtHandler(wchar_t const* /*expression*/, wchar_t const* /*function*/, wchar_t const* /*file*/, unsigned int /*line*/, uintptr_t /*pReserved*/)
-{
- RaiseException(EXCEPTION_ACCESS_VIOLATION, 0, 0, nullptr);
-}
-
-BOOL WheatyExceptionReport::_GetProcessorName(TCHAR* sProcessorName, DWORD maxcount)
-{
- if (!sProcessorName)
- return FALSE;
-
- HKEY hKey;
- LONG lRet;
- lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"),
- 0, KEY_QUERY_VALUE, &hKey);
- if (lRet != ERROR_SUCCESS)
- return FALSE;
- TCHAR szTmp[2048];
- DWORD cntBytes = sizeof(szTmp);
- lRet = ::RegQueryValueEx(hKey, _T("ProcessorNameString"), nullptr, nullptr,
- (LPBYTE)szTmp, &cntBytes);
- if (lRet != ERROR_SUCCESS)
- return FALSE;
- ::RegCloseKey(hKey);
- sProcessorName[0] = '\0';
- // Skip spaces
- TCHAR* psz = szTmp;
- while (iswspace(*psz))
- ++psz;
- _tcsncpy(sProcessorName, psz, maxcount);
- return TRUE;
-}
-
-template<size_t size>
-void ToTchar(wchar_t const* src, TCHAR (&dst)[size])
-{
- if constexpr (std::is_same_v<TCHAR, char>)
- ::wcstombs_s(nullptr, dst, src, _TRUNCATE);
- else
- ::wcscpy_s(dst, size, src);
-}
-
-BOOL WheatyExceptionReport::_GetWindowsVersion(TCHAR* szVersion, DWORD cntMax)
-{
- *szVersion = _T('\0');
-
- if (_GetWindowsVersionFromWMI(szVersion, cntMax))
- return TRUE;
-
- // Try calling GetVersionEx using the OSVERSIONINFOEX structure.
- // If that fails, try using the OSVERSIONINFO structure.
- RTL_OSVERSIONINFOEXW osvi = { };
- osvi.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
- NTSTATUS bVersionEx = RtlGetVersion((PRTL_OSVERSIONINFOW)&osvi);
- if (FAILED(bVersionEx))
- {
- osvi.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOW);
- if (!RtlGetVersion((PRTL_OSVERSIONINFOW)&osvi))
- return FALSE;
- }
-
- TCHAR szCSDVersion[256];
- ToTchar(osvi.szCSDVersion, szCSDVersion);
-
- TCHAR wszTmp[128];
- switch (osvi.dwPlatformId)
- {
- // Windows NT product family.
- case VER_PLATFORM_WIN32_NT:
- {
- #if WINVER < 0x0500
- BYTE suiteMask = osvi.wReserved[0];
- BYTE productType = osvi.wReserved[1];
- #else
- WORD suiteMask = osvi.wSuiteMask;
- BYTE productType = osvi.wProductType;
- #endif // WINVER < 0x0500
-
- // Test for the specific product family.
- if (osvi.dwMajorVersion == 10)
- {
- if (productType == VER_NT_WORKSTATION)
- _tcsncat(szVersion, _T("Windows 10 "), cntMax);
- else
- _tcsncat(szVersion, _T("Windows Server 2016 "), cntMax);
- }
- else if (osvi.dwMajorVersion == 6)
- {
- if (productType == VER_NT_WORKSTATION)
- {
- if (osvi.dwMinorVersion == 3)
- _tcsncat(szVersion, _T("Windows 8.1 "), cntMax);
- else if (osvi.dwMinorVersion == 2)
- _tcsncat(szVersion, _T("Windows 8 "), cntMax);
- else if (osvi.dwMinorVersion == 1)
- _tcsncat(szVersion, _T("Windows 7 "), cntMax);
- else
- _tcsncat(szVersion, _T("Windows Vista "), cntMax);
- }
- else if (osvi.dwMinorVersion == 3)
- _tcsncat(szVersion, _T("Windows Server 2012 R2 "), cntMax);
- else if (osvi.dwMinorVersion == 2)
- _tcsncat(szVersion, _T("Windows Server 2012 "), cntMax);
- else if (osvi.dwMinorVersion == 1)
- _tcsncat(szVersion, _T("Windows Server 2008 R2 "), cntMax);
- else
- _tcsncat(szVersion, _T("Windows Server 2008 "), cntMax);
- }
- else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2)
- _tcsncat(szVersion, _T("Microsoft Windows Server 2003 "), cntMax);
- else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1)
- _tcsncat(szVersion, _T("Microsoft Windows XP "), cntMax);
- else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0)
- _tcsncat(szVersion, _T("Microsoft Windows 2000 "), cntMax);
- else if (osvi.dwMajorVersion <= 4)
- _tcsncat(szVersion, _T("Microsoft Windows NT "), cntMax);
-
- // Test for specific product on Windows NT 4.0 SP6 and later.
- if (bVersionEx >= 0)
- {
- // Test for the workstation type.
- if (productType == VER_NT_WORKSTATION)
- {
- if (osvi.dwMajorVersion == 4)
- _tcsncat(szVersion, _T("Workstation 4.0 "), cntMax);
- else if (suiteMask & VER_SUITE_PERSONAL)
- _tcsncat(szVersion, _T("Home Edition "), cntMax);
- else if (suiteMask & VER_SUITE_EMBEDDEDNT)
- _tcsncat(szVersion, _T("Embedded "), cntMax);
- else
- _tcsncat(szVersion, _T("Professional "), cntMax);
- }
- // Test for the server type.
- else if (productType == VER_NT_SERVER)
- {
- if (osvi.dwMajorVersion == 6 || osvi.dwMajorVersion == 10)
- {
- if (suiteMask & VER_SUITE_SMALLBUSINESS_RESTRICTED)
- _tcsncat(szVersion, _T("Essentials "), cntMax);
- else if (suiteMask & VER_SUITE_DATACENTER)
- _tcsncat(szVersion, _T("Datacenter "), cntMax);
- else if (suiteMask & VER_SUITE_ENTERPRISE)
- _tcsncat(szVersion, _T("Enterprise "), cntMax);
- else
- _tcsncat(szVersion, _T("Standard "), cntMax);
- }
- else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2)
- {
- if (suiteMask & VER_SUITE_DATACENTER)
- _tcsncat(szVersion, _T("Datacenter Edition "), cntMax);
- else if (suiteMask & VER_SUITE_ENTERPRISE)
- _tcsncat(szVersion, _T("Enterprise Edition "), cntMax);
- else if (suiteMask == VER_SUITE_BLADE)
- _tcsncat(szVersion, _T("Web Edition "), cntMax);
- else
- _tcsncat(szVersion, _T("Standard Edition "), cntMax);
- }
- else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0)
- {
- if (suiteMask & VER_SUITE_DATACENTER)
- _tcsncat(szVersion, _T("Datacenter Server "), cntMax);
- else if (suiteMask & VER_SUITE_ENTERPRISE)
- _tcsncat(szVersion, _T("Advanced Server "), cntMax);
- else
- _tcsncat(szVersion, _T("Server "), cntMax);
- }
- else // Windows NT 4.0
- {
- if (suiteMask & VER_SUITE_ENTERPRISE)
- _tcsncat(szVersion, _T("Server 4.0, Enterprise Edition "), cntMax);
- else
- _tcsncat(szVersion, _T("Server 4.0 "), cntMax);
- }
- }
- }
-
- // Display service pack (if any) and build number.
- if (osvi.dwMajorVersion == 4 && _tcsicmp(szCSDVersion, _T("Service Pack 6")) == 0)
- {
- HKEY hKey;
- LONG lRet;
-
- // Test for SP6 versus SP6a.
- lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Hotfix\\Q246009"), 0, KEY_QUERY_VALUE, &hKey);
- if (lRet == ERROR_SUCCESS)
- {
- _stprintf_s(wszTmp, _T("Service Pack 6a (Version %d.%d, Build %d)"),
- osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
- _tcsncat(szVersion, wszTmp, cntMax);
- }
- else // Windows NT 4.0 prior to SP6a
- {
- _stprintf_s(wszTmp, _T("%s (Version %d.%d, Build %d)"),
- szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
- _tcsncat(szVersion, wszTmp, cntMax);
- }
- ::RegCloseKey(hKey);
- }
- else // Windows NT 3.51 and earlier or Windows 2000 and later
- {
- if (!_tcslen(szCSDVersion))
- _stprintf_s(wszTmp, _T("(Version %d.%d, Build %d)"),
- osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
- else
- _stprintf_s(wszTmp, _T("%s (Version %d.%d, Build %d)"),
- szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
- _tcsncat(szVersion, wszTmp, cntMax);
- }
- break;
- }
- default:
- _stprintf_s(wszTmp, _T("%s (Version %d.%d, Build %d)"),
- szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
- _tcsncat(szVersion, wszTmp, cntMax);
- break;
- }
-
- return TRUE;
-}
-
-BOOL WheatyExceptionReport::_GetWindowsVersionFromWMI(TCHAR* szVersion, DWORD cntMax)
-{
- // Step 1: --------------------------------------------------
- // Initialize COM. ------------------------------------------
- HRESULT hres = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
- if (FAILED(hres))
- return FALSE;
-
- std::shared_ptr<void> com(nullptr, [](void*)
- {
- CoUninitialize();
- });
-
- // Step 2: --------------------------------------------------
- // Set general COM security levels --------------------------
- hres = CoInitializeSecurity(
- nullptr,
- -1, // COM authentication
- nullptr, // Authentication services
- nullptr, // Reserved
- RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication
- RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation
- nullptr, // Authentication info
- EOAC_NONE, // Additional capabilities
- nullptr // Reserved
- );
-
- if (FAILED(hres))
- return FALSE;
-
- // Step 3: ---------------------------------------------------
- // Obtain the initial locator to WMI -------------------------
- std::shared_ptr<IWbemLocator> loc = []() -> std::shared_ptr<IWbemLocator>
- {
- IWbemLocator* tmp = nullptr;
- HRESULT hres = CoCreateInstance(
- CLSID_WbemLocator,
- nullptr,
- CLSCTX_INPROC_SERVER,
- IID_IWbemLocator,
- reinterpret_cast<LPVOID*>(&tmp));
-
- if (FAILED(hres))
- return nullptr;
-
- return { tmp, [](IWbemLocator* ptr) { if (ptr) ptr->Release(); } };
- }();
-
- if (!loc)
- return FALSE;
-
- // Step 4: -----------------------------------------------------
- // Connect to the root\cimv2 namespace with
- // the current user and obtain pointer pSvc
- // to make IWbemServices calls.
- std::shared_ptr<IWbemServices> svc = [loc]() ->std::shared_ptr<IWbemServices>
- {
- IWbemServices* tmp = nullptr;
- HRESULT hres = loc->ConnectServer(
- bstr_t(L"ROOT\\CIMV2"), // Object path of WMI namespace
- nullptr, // User name. NULL = current user
- nullptr, // User password. NULL = current
- nullptr, // Locale. NULL indicates current
- WBEM_FLAG_CONNECT_USE_MAX_WAIT, // Security flags.
- nullptr, // Authority (for example, Kerberos)
- nullptr, // Context object
- &tmp // pointer to IWbemServices proxy
- );
-
- if (FAILED(hres))
- return nullptr;
-
- return { tmp, [](IWbemServices* ptr) { if (ptr) ptr->Release(); } };
- }();
-
- if (!svc)
- return FALSE;
-
- // Step 5: --------------------------------------------------
- // Set security levels on the proxy -------------------------
- hres = CoSetProxyBlanket(
- svc.get(), // Indicates the proxy to set
- RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
- RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
- nullptr, // Server principal name
- RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx
- RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
- nullptr, // client identity
- EOAC_NONE // proxy capabilities
- );
-
- if (FAILED(hres))
- return FALSE;
-
- // Step 6: --------------------------------------------------
- // Use the IWbemServices pointer to make requests of WMI ----
-
- // For example, get the name of the operating system
- std::shared_ptr<IEnumWbemClassObject> queryResult = [svc]() -> std::shared_ptr<IEnumWbemClassObject>
- {
- IEnumWbemClassObject* tmp = nullptr;
- HRESULT hres = svc->ExecQuery(
- bstr_t("WQL"),
- bstr_t("SELECT Caption, CSDVersion FROM Win32_OperatingSystem"),
- WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
- nullptr,
- &tmp);
-
- if (FAILED(hres))
- return nullptr;
-
- return { tmp, [](IEnumWbemClassObject* ptr) { if (ptr) ptr->Release(); } };
- }();
-
- BOOL result = FALSE;
- // Step 7: -------------------------------------------------
- // Get the data from the query in step 6 -------------------
- if (queryResult)
- {
- do
- {
- IWbemClassObject* fields = nullptr;
-
- ULONG rows = 0;
- queryResult->Next(WBEM_INFINITE, 1, &fields, &rows);
- if (!rows)
- break;
-
- VARIANT field;
- VariantInit(&field);
- fields->Get(L"Caption", 0, &field, nullptr, nullptr);
- TCHAR buf[256] = { };
- ToTchar(field.bstrVal, buf);
- _tcsncat(szVersion, buf, cntMax);
- VariantClear(&field);
-
- fields->Get(L"CSDVersion", 0, &field, nullptr, nullptr);
- if (field.vt == VT_BSTR)
- {
- _tcsncat(szVersion, _T(" "), cntMax);
- memset(buf, 0, sizeof(buf));
- ToTchar(field.bstrVal, buf);
- if (_tcslen(buf))
- _tcsncat(szVersion, buf, cntMax);
- }
- VariantClear(&field);
-
- fields->Release();
-
- result = TRUE;
- } while (true);
- }
-
- return result;
-}
-
-void WheatyExceptionReport::PrintSystemInfo()
-{
- SYSTEM_INFO SystemInfo;
- ::GetSystemInfo(&SystemInfo);
-
- MEMORYSTATUS MemoryStatus;
- MemoryStatus.dwLength = sizeof (MEMORYSTATUS);
- ::GlobalMemoryStatus(&MemoryStatus);
- TCHAR sString[1024];
- Log(_T("//=====================================================\r\n"));
- if (_GetProcessorName(sString, std::size(sString)))
- 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"),
- sString, SystemInfo.dwNumberOfProcessors, MemoryStatus.dwTotalPhys/0x400, MemoryStatus.dwAvailPhys/0x400, MemoryStatus.dwTotalPageFile/0x400);
- else
- 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"),
- SystemInfo.dwNumberOfProcessors, MemoryStatus.dwTotalPhys/0x400, MemoryStatus.dwAvailPhys/0x400, MemoryStatus.dwTotalPageFile/0x400);
-
- if (_GetWindowsVersion(sString, std::size(sString)))
- Log(_T("\r\n*** Operation System ***\r\n%s\r\n"), sString);
- else
- Log(_T("\r\n*** Operation System:\r\n<unknown>\r\n"));
-}
-
-//===========================================================================
-void WheatyExceptionReport::printTracesForAllThreads(bool bWriteVariables)
-{
- THREADENTRY32 te32;
-
- DWORD dwOwnerPID = GetCurrentProcessId();
- DWORD dwCurrentTID = GetCurrentThreadId();
- m_hProcess = GetCurrentProcess();
- // Take a snapshot of all running threads
- HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
- if (hThreadSnap == INVALID_HANDLE_VALUE)
- return;
-
- // Fill in the size of the structure before using it.
- te32.dwSize = sizeof(THREADENTRY32);
-
- // Retrieve information about the first thread,
- // and exit if unsuccessful
- if (!Thread32First(hThreadSnap, &te32))
- {
- CloseHandle(hThreadSnap); // Must clean up the
- // snapshot object!
- return;
- }
-
- // Now walk the thread list of the system,
- // and display information about each thread
- // associated with the specified process
- do
- {
- if (te32.th32OwnerProcessID == dwOwnerPID && te32.th32ThreadID != dwCurrentTID)
- {
- CONTEXT context;
- context.ContextFlags = 0xffffffff;
- HANDLE threadHandle = OpenThread(THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION, false, te32.th32ThreadID);
- if (threadHandle)
- {
- if (GetThreadContext(threadHandle, &context))
- WriteStackDetails(&context, bWriteVariables, threadHandle);
- CloseHandle(threadHandle);
- }
- }
- } while (Thread32Next(hThreadSnap, &te32));
-
-// Don't forget to clean up the snapshot object.
- CloseHandle(hThreadSnap);
-}
-
-//===========================================================================
-// Open the report file, and write the desired information to it. Called by
-// WheatyUnhandledExceptionFilter
-//===========================================================================
-void WheatyExceptionReport::GenerateExceptionReport(
-PEXCEPTION_POINTERS pExceptionInfo)
-{
- __try
- {
- SYSTEMTIME systime;
- GetLocalTime(&systime);
-
- // Start out with a banner
- Log(_T("Revision: %s\r\n"), GitRevision::GetFullVersion());
- Log(_T("Date %u:%u:%u. Time %u:%u \r\n"), systime.wDay, systime.wMonth, systime.wYear, systime.wHour, systime.wMinute);
- PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord;
-
- PrintSystemInfo();
- // First print information about the type of fault
- Log(_T("\r\n//=====================================================\r\n"));
- Log(_T("Exception code: %08X %s\r\n"),
- pExceptionRecord->ExceptionCode,
- GetExceptionString(pExceptionRecord->ExceptionCode));
- if (pExceptionRecord->ExceptionCode == EXCEPTION_ASSERTION_FAILURE && pExceptionRecord->NumberParameters >= 2)
- {
- pExceptionRecord->ExceptionAddress = reinterpret_cast<PVOID>(pExceptionRecord->ExceptionInformation[1]);
- Log(_T("Assertion message: %s\r\n"), pExceptionRecord->ExceptionInformation[0]);
- }
-
- // Now print information about where the fault occured
- TCHAR szFaultingModule[MAX_PATH];
- DWORD section;
- DWORD_PTR offset;
- GetLogicalAddress(pExceptionRecord->ExceptionAddress,
- szFaultingModule,
- sizeof(szFaultingModule),
- section, offset);
-
-#if defined(_M_IX86) || defined(_M_ARM)
- Log(_T("Fault address: %08X %02X:%08X %s\r\n"),
- pExceptionRecord->ExceptionAddress,
- section, offset, szFaultingModule);
-#endif
-#if defined(_M_X64) || defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined (_M_ARM64EC)
- Log(_T("Fault address: %016I64X %02X:%016I64X %s\r\n"),
- pExceptionRecord->ExceptionAddress,
- section, offset, szFaultingModule);
-#endif
-
- PCONTEXT pCtx = pExceptionInfo->ContextRecord;
-
- // Show the registers
-#ifdef _M_IX86 // X86 Only!
- Log(_T("\r\nRegisters:\r\n"));
-
- Log(_T("EAX:%08X\r\nEBX:%08X\r\nECX:%08X\r\nEDX:%08X\r\nESI:%08X\r\nEDI:%08X\r\n")
- , pCtx->Eax, pCtx->Ebx, pCtx->Ecx, pCtx->Edx,
- pCtx->Esi, pCtx->Edi);
-
- Log(_T("CS:EIP:%04X:%08X\r\n"), pCtx->SegCs, pCtx->Eip);
- Log(_T("SS:ESP:%04X:%08X EBP:%08X\r\n"),
- pCtx->SegSs, pCtx->Esp, pCtx->Ebp);
- Log(_T("DS:%04X ES:%04X FS:%04X GS:%04X\r\n"),
- pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs);
- Log(_T("Flags:%08X\r\n"), pCtx->EFlags);
-#endif
-
-#ifdef _M_X64
- Log(_T("\r\nRegisters:\r\n"));
- Log(_T("RAX:%016I64X\r\nRBX:%016I64X\r\nRCX:%016I64X\r\nRDX:%016I64X\r\nRSI:%016I64X\r\nRDI:%016I64X\r\n")
- _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")
- , pCtx->Rax, pCtx->Rbx, pCtx->Rcx, pCtx->Rdx,
- pCtx->Rsi, pCtx->Rdi, pCtx->R8, pCtx->R9, pCtx->R10, pCtx->R11, pCtx->R12, pCtx->R13, pCtx->R14, pCtx->R15);
- Log(_T("CS:RIP:%04X:%016I64X\r\n"), pCtx->SegCs, pCtx->Rip);
- Log(_T("SS:RSP:%04X:%016I64X RBP:%08X\r\n"),
- pCtx->SegSs, pCtx->Rsp, pCtx->Rbp);
- Log(_T("DS:%04X ES:%04X FS:%04X GS:%04X\r\n"),
- pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs);
- Log(_T("Flags:%08X\r\n"), pCtx->EFlags);
-#endif
-
-#ifdef _M_ARM64
- Log(_T("\r\nRegisters:\r\n"));
- Log(_T("X0:%016I64X\r\nX1:%016I64X\r\nX2:%016I64X\r\nX3:%016I64X\r\nX4:%016I64X\r\nX5:%016I64X\r\n"),
- _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"),
- _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"),
- _T("X22:%016I64X\r\nX23:%016I64X\r\nX24:%016I64X\r\nX25:%016I64X\r\nX26:%016I64X\r\nX27:%016I64X\r\nX28:%016I64X\r\n"),
- pCtx->X0, pCtx->X1, pCtx->X2, pCtx->X3, pCtx->X4, pCtx->X5,
- pCtx->X6, pCtx->X7, pCtx->X8, pCtx->X9, pCtx->X10, pCtx->X11, pCtx->X12, pCtx->X13,
- pCtx->X14, pCtx->X15, pCtx->X16, pCtx->X17, pCtx->X18, pCtx->X19, pCtx->X20, pCtx->X21,
- pCtx->X22, pCtx->X23, pCtx->X24, pCtx->X25, pCtx->X26, pCtx->X27, pCtx->X28);
- Log(_T("LR:%016I64X\r\n"), pCtx->Lr);
- Log(_T("PC:%016I64X\r\n"), pCtx->Pc);
- Log(_T("SP:%016I64X FP:%016I64X\r\n"), pCtx->Sp, pCtx->Fp);
- Log(_T("Flags:%08X\r\n"), pCtx->Cpsr);
-#endif
-
- SymSetOptions(SYMOPT_DEFERRED_LOADS);
-
- // Initialize DbgHelp
- if (!SymInitialize(GetCurrentProcess(), nullptr, TRUE))
- {
- Log(_T("\r\n"));
- Log(_T("----\r\n"));
- Log(_T("SYMBOL HANDLER ERROR (THIS IS NOT THE CRASH ERROR)\r\n\r\n"));
- Log(_T("Couldn't initialize symbol handler for process when generating crash report\r\n"));
- Log(_T("Error: %s\r\n"), ErrorMessage(GetLastError()));
- Log(_T("THE BELOW CALL STACKS MIGHT HAVE MISSING OR INACCURATE FILE/FUNCTION NAMES\r\n\r\n"));
- Log(_T("----\r\n"));
- }
-
- if (pExceptionRecord->ExceptionCode == 0xE06D7363 && pExceptionRecord->NumberParameters >= 2)
- {
- PVOID exceptionObject = reinterpret_cast<PVOID>(pExceptionRecord->ExceptionInformation[1]);
- ThrowInfo const* throwInfo = reinterpret_cast<ThrowInfo const*>(pExceptionRecord->ExceptionInformation[2]);
-#if _EH_RELATIVE_TYPEINFO
- // When _EH_RELATIVE_TYPEINFO is defined, the pointers need to be retrieved with some pointer math
- auto resolveExceptionRVA = [pExceptionRecord](int32 rva) -> DWORD_PTR
- {
- return rva + (pExceptionRecord->NumberParameters >= 4 ? pExceptionRecord->ExceptionInformation[3] : 0);
- };
-#else
- // Otherwise the pointers are already there in the API types
- auto resolveExceptionRVA = [](void const* input) -> void const* { return input; };
-#endif
-
- CatchableTypeArray const* catchables = reinterpret_cast<CatchableTypeArray const*>(resolveExceptionRVA(throwInfo->pCatchableTypeArray));
- CatchableType const* catchable = catchables->nCatchableTypes ? reinterpret_cast<CatchableType const*>(resolveExceptionRVA(catchables->arrayOfCatchableTypes[0])) : nullptr;
- TypeDescriptor const* exceptionTypeinfo = catchable ? reinterpret_cast<TypeDescriptor const*>(resolveExceptionRVA(catchable->pType)) : nullptr;
-
- if (exceptionTypeinfo)
- {
- void* stdExceptionTypeInfo = []() -> void*
- {
- try
- {
- std::exception fake;
- return __RTtypeid(&fake);
- }
- catch (...)
- {
- return nullptr;
- }
- }();
- std::exception const* exceptionPtr = [](void* object, TypeDescriptor const* typeInfo, void* stdExceptionTypeInfo) -> std::exception const*
- {
- try
- {
- // real_type descriptor is obtained by parsing throwinfo
- // equivalent to expression like this
- // std::exception* e = object;
- // real_type* r = dynamic_cast<real_type*>(e);
- // return r;
- return reinterpret_cast<std::exception const*>(__RTDynamicCast(object, 0, stdExceptionTypeInfo, (void*)typeInfo, false));
- }
- catch (...)
- {
- return nullptr;
- }
- }(exceptionObject, exceptionTypeinfo, stdExceptionTypeInfo);
-
- // dynamic_cast<type>(variable_that_already_has_that_type) is optimized away by compiler and attempting to call __RTDynamicCast fails for it
- if (!exceptionPtr && exceptionTypeinfo == stdExceptionTypeInfo)
- exceptionPtr = reinterpret_cast<std::exception*>(exceptionObject);
-
- Log(_T("\r\nUncaught C++ exception info:"));
- if (exceptionPtr)
- Log(_T(" %s"), exceptionPtr->what());
-
- Log(_T("\r\n"));
-
- char undName[MAX_SYM_NAME] = { };
- if (UnDecorateSymbolName(&exceptionTypeinfo->name[1], &undName[0], MAX_SYM_NAME, UNDNAME_32_BIT_DECODE | UNDNAME_NAME_ONLY | UNDNAME_NO_ARGUMENTS))
- {
- char buf[MAX_SYM_NAME + sizeof(SYMBOL_INFO)] = { };
- PSYMBOL_INFO sym = (PSYMBOL_INFO)&buf[0];
- sym->SizeOfStruct = sizeof(SYMBOL_INFO);
- sym->MaxNameLen = MAX_SYM_NAME;
- if (SymGetTypeFromName(m_hProcess, (ULONG64)GetModuleHandle(nullptr), undName, sym))
- {
- sym->Address = pExceptionRecord->ExceptionInformation[1];
- sym->Flags = 0;
- char const* variableName = "uncaught_exception";
- memset(sym->Name, 0, MAX_SYM_NAME);
- memcpy(sym->Name, variableName, strlen(variableName));
- FormatSymbolValue(sym, nullptr);
- }
- }
- }
- }
-
- CONTEXT trashableContext = *pCtx;
-
- WriteStackDetails(&trashableContext, false, nullptr);
- printTracesForAllThreads(false);
-
- // #ifdef _M_IX86 // X86 Only!
-
- Log(_T("========================\r\n"));
- Log(_T("Local Variables And Parameters\r\n"));
-
- trashableContext = *pCtx;
- WriteStackDetails(&trashableContext, true, nullptr);
- printTracesForAllThreads(true);
-
- SymCleanup(GetCurrentProcess());
-
- Log(_T("\r\n"));
- }
- __except (EXCEPTION_EXECUTE_HANDLER)
- {
- Log(_T("Error writing the crash log\r\n"));
- }
-}
-
-//======================================================================
-// Given an exception code, returns a pointer to a static string with a
-// description of the exception
-//======================================================================
-LPCTSTR WheatyExceptionReport::GetExceptionString(DWORD dwCode)
-{
- #define EXCEPTION(x) case EXCEPTION_##x: return _T(#x);
-
- switch (dwCode)
- {
- EXCEPTION(ACCESS_VIOLATION)
- EXCEPTION(DATATYPE_MISALIGNMENT)
- EXCEPTION(BREAKPOINT)
- EXCEPTION(SINGLE_STEP)
- EXCEPTION(ARRAY_BOUNDS_EXCEEDED)
- EXCEPTION(FLT_DENORMAL_OPERAND)
- EXCEPTION(FLT_DIVIDE_BY_ZERO)
- EXCEPTION(FLT_INEXACT_RESULT)
- EXCEPTION(FLT_INVALID_OPERATION)
- EXCEPTION(FLT_OVERFLOW)
- EXCEPTION(FLT_STACK_CHECK)
- EXCEPTION(FLT_UNDERFLOW)
- EXCEPTION(INT_DIVIDE_BY_ZERO)
- EXCEPTION(INT_OVERFLOW)
- EXCEPTION(PRIV_INSTRUCTION)
- EXCEPTION(IN_PAGE_ERROR)
- EXCEPTION(ILLEGAL_INSTRUCTION)
- EXCEPTION(NONCONTINUABLE_EXCEPTION)
- EXCEPTION(STACK_OVERFLOW)
- EXCEPTION(INVALID_DISPOSITION)
- EXCEPTION(GUARD_PAGE)
- EXCEPTION(INVALID_HANDLE)
- case 0xE06D7363: return _T("Unhandled C++ exception");
- }
-
- // If not one of the "known" exceptions, try to get the string
- // from NTDLL.DLL's message table.
-
- static TCHAR szBuffer[512] = { 0 };
-
- FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE,
- GetModuleHandle(_T("NTDLL.DLL")),
- dwCode, 0, szBuffer, sizeof(szBuffer), nullptr);
-
- return szBuffer;
-}
-
-//=============================================================================
-// Given a linear address, locates the module, section, and offset containing
-// that address.
-//
-// Note: the szModule paramater buffer is an output buffer of length specified
-// by the len parameter (in characters!)
-//=============================================================================
-BOOL WheatyExceptionReport::GetLogicalAddress(
-PVOID addr, PTSTR szModule, DWORD len, DWORD& section, DWORD_PTR& offset)
-{
- MEMORY_BASIC_INFORMATION mbi;
-
- if (!VirtualQuery(addr, &mbi, sizeof(mbi)))
- return FALSE;
-
- DWORD_PTR hMod = (DWORD_PTR)mbi.AllocationBase;
-
- if (!hMod)
- return FALSE;
-
- if (!GetModuleFileName((HMODULE)hMod, szModule, len))
- return FALSE;
-
- // Point to the DOS header in memory
- PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)hMod;
-
- // From the DOS header, find the NT (PE) header
- PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)(hMod + DWORD_PTR(pDosHdr->e_lfanew));
-
- PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNtHdr);
-
- DWORD_PTR rva = (DWORD_PTR)addr - hMod; // RVA is offset from module load address
-
- // Iterate through the section table, looking for the one that encompasses
- // the linear address.
- for (unsigned i = 0;
- i < pNtHdr->FileHeader.NumberOfSections;
- i++, pSection++)
- {
- DWORD_PTR sectionStart = pSection->VirtualAddress;
- DWORD_PTR sectionEnd = sectionStart
- + DWORD_PTR(std::max(pSection->SizeOfRawData, pSection->Misc.VirtualSize));
-
- // Is the address in this section???
- if ((rva >= sectionStart) && (rva <= sectionEnd))
- {
- // Yes, address is in the section. Calculate section and offset,
- // and store in the "section" & "offset" params, which were
- // passed by reference.
- section = i+1;
- offset = rva - sectionStart;
- return TRUE;
- }
- }
-
- return FALSE; // Should never get here!
-}
-
-// It contains SYMBOL_INFO structure plus additional
-// space for the name of the symbol
-struct CSymbolInfoPackage : public SYMBOL_INFO_PACKAGE
-{
- CSymbolInfoPackage()
- {
- si.SizeOfStruct = sizeof(SYMBOL_INFO);
- si.MaxNameLen = sizeof(name);
- }
-};
-
-//============================================================
-// Walks the stack, and writes the results to the report file
-//============================================================
-void WheatyExceptionReport::WriteStackDetails(
-PCONTEXT pContext,
-bool bWriteVariables, HANDLE pThreadHandle) // true if local/params should be output
-{
- Log(_T("\r\nCall stack:\r\n"));
-
- Log(_T("Address Frame Function SourceFile\r\n"));
-
- DWORD dwMachineType = 0;
- // Could use SymSetOptions here to add the SYMOPT_DEFERRED_LOADS flag
-
- STACKFRAME64 sf;
- memset(&sf, 0, sizeof(sf));
-
- // Initialize the STACKFRAME structure for the first call.
- sf.AddrPC.Mode = AddrModeFlat;
- sf.AddrStack.Mode = AddrModeFlat;
- sf.AddrFrame.Mode = AddrModeFlat;
-
-#ifdef _M_IX86
- sf.AddrPC.Offset = pContext->Eip;
- sf.AddrStack.Offset = pContext->Esp;
- sf.AddrFrame.Offset = pContext->Ebp;
- dwMachineType = IMAGE_FILE_MACHINE_I386;
-#elif defined(_M_X64)
- sf.AddrPC.Offset = pContext->Rip;
- sf.AddrStack.Offset = pContext->Rsp;
- sf.AddrFrame.Offset = pContext->Rbp;
- dwMachineType = IMAGE_FILE_MACHINE_AMD64;
-#elif defined(_M_ARM64)
- sf.AddrPC.Offset = pContext->Pc;
- sf.AddrStack.Offset = pContext->Sp;
- sf.AddrFrame.Offset = pContext->Fp;
- dwMachineType = IMAGE_FILE_MACHINE_ARM64;
-#endif
-
- for (;;)
- {
- // Get the next stack frame
- if (! StackWalk64(dwMachineType,
- m_hProcess,
- pThreadHandle != nullptr ? pThreadHandle : GetCurrentThread(),
- &sf,
- pContext,
- nullptr,
- SymFunctionTableAccess64,
- SymGetModuleBase64,
- nullptr))
- break;
- if (0 == sf.AddrFrame.Offset) // Basic sanity check to make sure
- break; // the frame is OK. Bail if not.
-#ifdef _M_IX86
- Log(_T("%08X %08X "), sf.AddrPC.Offset, sf.AddrFrame.Offset);
-#elif defined(_M_X64) || defined(_M_ARM64)
- Log(_T("%016I64X %016I64X "), sf.AddrPC.Offset, sf.AddrFrame.Offset);
-#endif
-
- DWORD64 symDisplacement = 0; // Displacement of the input address,
- // relative to the start of the symbol
-
- // Get the name of the function for this stack frame entry
- CSymbolInfoPackage sip;
- if (SymFromAddr(
- m_hProcess, // Process handle of the current process
- sf.AddrPC.Offset, // Symbol address
- &symDisplacement, // Address of the variable that will receive the displacement
- &sip.si)) // Address of the SYMBOL_INFO structure (inside "sip" object)
- {
- Log(_T("%hs+%I64X"), sip.si.Name, symDisplacement);
-
- }
- else // No symbol found. Print out the logical address instead.
- {
- TCHAR szModule[MAX_PATH] = _T("");
- DWORD section = 0;
- DWORD_PTR offset = 0;
-
- GetLogicalAddress((PVOID)sf.AddrPC.Offset,
- szModule, sizeof(szModule), section, offset);
-#ifdef _M_IX86
- Log(_T("%04X:%08X %s"), section, offset, szModule);
-#elif defined(_M_X64) || defined(_M_ARM64)
- Log(_T("%04X:%016I64X %s"), section, offset, szModule);
-#endif
- }
-
- // Get the source line for this stack frame entry
- IMAGEHLP_LINE64 lineInfo = { sizeof(IMAGEHLP_LINE64) };
- DWORD dwLineDisplacement;
- if (SymGetLineFromAddr64(m_hProcess, sf.AddrPC.Offset,
- &dwLineDisplacement, &lineInfo))
- {
- Log(_T(" %s line %u"), lineInfo.FileName, lineInfo.LineNumber);
- }
-
- Log(_T("\r\n"));
-
- // Write out the variables, if desired
- if (bWriteVariables)
- {
- // Use SymSetContext to get just the locals/params for this frame
- IMAGEHLP_STACK_FRAME imagehlpStackFrame;
- imagehlpStackFrame.InstructionOffset = sf.AddrPC.Offset;
- SymSetContext(m_hProcess, &imagehlpStackFrame, nullptr);
-
- // Enumerate the locals/parameters
- EnumerateSymbolsCallbackContext ctx;
- ctx.sf = &sf;
- ctx.context = pContext;
- SymEnumSymbols(m_hProcess, 0, nullptr, EnumerateSymbolsCallback, &ctx);
-
- Log(_T("\r\n"));
- }
- }
-
-}
-
-//////////////////////////////////////////////////////////////////////////////
-// The function invoked by SymEnumSymbols
-//////////////////////////////////////////////////////////////////////////////
-
-BOOL CALLBACK
-WheatyExceptionReport::EnumerateSymbolsCallback(
-PSYMBOL_INFO pSymInfo,
-ULONG /*SymbolSize*/,
-PVOID UserContext)
-{
- __try
- {
- ClearSymbols();
- FormatSymbolValue(pSymInfo, (EnumerateSymbolsCallbackContext*)UserContext);
-
- }
- __except (EXCEPTION_EXECUTE_HANDLER)
- {
- Log(_T("punting on symbol %s, partial output:\r\n"), pSymInfo->Name);
- }
-
- return TRUE;
-}
-
-Optional<DWORD_PTR> WheatyExceptionReport::GetIntegerRegisterValue(PCONTEXT context, ULONG registerId)
-{
-#define REG_L(x) ((BYTE)(((DWORD_PTR)(x)) & 0xff))
-#define REG_H(x) ((BYTE)((((DWORD_PTR)(x)) >> 8) & 0xff))
-#define REG_X(x) ((WORD)(((DWORD_PTR)(x)) & 0xffff))
-#define REG_E(x) ((DWORD)(((DWORD_PTR)(x)) & 0xffffffff))
-#define REG_R(x) ((DWORD64)(((DWORD_PTR)(x)) & 0xffffffffffffffff))
-#define CPU_REG(reg, field, part) case reg: return part(context->field);
- switch (registerId)
- {
-#ifdef _M_IX86
- CPU_REG(CV_REG_AL, Eax, REG_L);
- CPU_REG(CV_REG_CL, Ecx, REG_L);
- CPU_REG(CV_REG_DL, Edx, REG_L);
- CPU_REG(CV_REG_BL, Ebx, REG_L);
- CPU_REG(CV_REG_AH, Eax, REG_H);
- CPU_REG(CV_REG_CH, Ecx, REG_H);
- CPU_REG(CV_REG_DH, Edx, REG_H);
- CPU_REG(CV_REG_BH, Ebx, REG_H);
- CPU_REG(CV_REG_AX, Eax, REG_X);
- CPU_REG(CV_REG_CX, Ecx, REG_X);
- CPU_REG(CV_REG_DX, Edx, REG_X);
- CPU_REG(CV_REG_BX, Ebx, REG_X);
- CPU_REG(CV_REG_SP, Esp, REG_X);
- CPU_REG(CV_REG_BP, Ebp, REG_X);
- CPU_REG(CV_REG_SI, Esi, REG_X);
- CPU_REG(CV_REG_DI, Edi, REG_X);
- CPU_REG(CV_REG_EAX, Eax, REG_E);
- CPU_REG(CV_REG_ECX, Ecx, REG_E);
- CPU_REG(CV_REG_EDX, Edx, REG_E);
- CPU_REG(CV_REG_EBX, Ebx, REG_E);
- CPU_REG(CV_REG_ESP, Esp, REG_E);
- CPU_REG(CV_REG_EBP, Ebp, REG_E);
- CPU_REG(CV_REG_ESI, Esi, REG_E);
- CPU_REG(CV_REG_EDI, Edi, REG_E);
- CPU_REG(CV_REG_EIP, Eip, REG_E);
-#elif defined (_M_X64)
- CPU_REG(CV_AMD64_AL, Rax, REG_L);
- CPU_REG(CV_AMD64_CL, Rcx, REG_L);
- CPU_REG(CV_AMD64_DL, Rdx, REG_L);
- CPU_REG(CV_AMD64_BL, Rbx, REG_L);
- CPU_REG(CV_AMD64_SIL, Rsi, REG_L);
- CPU_REG(CV_AMD64_DIL, Rdi, REG_L);
- CPU_REG(CV_AMD64_BPL, Rbp, REG_L);
- CPU_REG(CV_AMD64_SPL, Rsp, REG_L);
- CPU_REG(CV_AMD64_R8B, R8, REG_L);
- CPU_REG(CV_AMD64_R9B, R9, REG_L);
- CPU_REG(CV_AMD64_R10B, R10, REG_L);
- CPU_REG(CV_AMD64_R11B, R11, REG_L);
- CPU_REG(CV_AMD64_R12B, R12, REG_L);
- CPU_REG(CV_AMD64_R13B, R13, REG_L);
- CPU_REG(CV_AMD64_R14B, R14, REG_L);
- CPU_REG(CV_AMD64_R15B, R15, REG_L);
- CPU_REG(CV_AMD64_AH, Rax, REG_H);
- CPU_REG(CV_AMD64_CH, Rcx, REG_H);
- CPU_REG(CV_AMD64_DH, Rdx, REG_H);
- CPU_REG(CV_AMD64_BH, Rbx, REG_H);
- CPU_REG(CV_AMD64_AX, Rax, REG_X);
- CPU_REG(CV_AMD64_CX, Rcx, REG_X);
- CPU_REG(CV_AMD64_DX, Rdx, REG_X);
- CPU_REG(CV_AMD64_BX, Rbx, REG_X);
- CPU_REG(CV_AMD64_SP, Rsp, REG_X);
- CPU_REG(CV_AMD64_BP, Rbp, REG_X);
- CPU_REG(CV_AMD64_SI, Rsi, REG_X);
- CPU_REG(CV_AMD64_DI, Rdi, REG_X);
- CPU_REG(CV_AMD64_R8W, R8, REG_X);
- CPU_REG(CV_AMD64_R9W, R9, REG_X);
- CPU_REG(CV_AMD64_R10W, R10, REG_X);
- CPU_REG(CV_AMD64_R11W, R11, REG_X);
- CPU_REG(CV_AMD64_R12W, R12, REG_X);
- CPU_REG(CV_AMD64_R13W, R13, REG_X);
- CPU_REG(CV_AMD64_R14W, R14, REG_X);
- CPU_REG(CV_AMD64_R15W, R15, REG_X);
- CPU_REG(CV_AMD64_EAX, Rax, REG_E);
- CPU_REG(CV_AMD64_ECX, Rcx, REG_E);
- CPU_REG(CV_AMD64_EDX, Rdx, REG_E);
- CPU_REG(CV_AMD64_EBX, Rbx, REG_E);
- CPU_REG(CV_AMD64_ESP, Rsp, REG_E);
- CPU_REG(CV_AMD64_EBP, Rbp, REG_E);
- CPU_REG(CV_AMD64_ESI, Rsi, REG_E);
- CPU_REG(CV_AMD64_EDI, Rdi, REG_E);
- CPU_REG(CV_AMD64_R8D, R8, REG_E);
- CPU_REG(CV_AMD64_R9D, R9, REG_E);
- CPU_REG(CV_AMD64_R10D, R10, REG_E);
- CPU_REG(CV_AMD64_R11D, R11, REG_E);
- CPU_REG(CV_AMD64_R12D, R12, REG_E);
- CPU_REG(CV_AMD64_R13D, R13, REG_E);
- CPU_REG(CV_AMD64_R14D, R14, REG_E);
- CPU_REG(CV_AMD64_R15D, R15, REG_E);
- CPU_REG(CV_AMD64_RIP, Rip, REG_R);
- CPU_REG(CV_AMD64_RAX, Rax, REG_R);
- CPU_REG(CV_AMD64_RBX, Rbx, REG_R);
- CPU_REG(CV_AMD64_RCX, Rcx, REG_R);
- CPU_REG(CV_AMD64_RDX, Rdx, REG_R);
- CPU_REG(CV_AMD64_RSI, Rsi, REG_R);
- CPU_REG(CV_AMD64_RDI, Rdi, REG_R);
- CPU_REG(CV_AMD64_RBP, Rbp, REG_R);
- CPU_REG(CV_AMD64_RSP, Rsp, REG_R);
- CPU_REG(CV_AMD64_R8, R8, REG_R);
- CPU_REG(CV_AMD64_R9, R9, REG_R);
- CPU_REG(CV_AMD64_R10, R10, REG_R);
- CPU_REG(CV_AMD64_R11, R11, REG_R);
- CPU_REG(CV_AMD64_R12, R12, REG_R);
- CPU_REG(CV_AMD64_R13, R13, REG_R);
- CPU_REG(CV_AMD64_R14, R14, REG_R);
- CPU_REG(CV_AMD64_R15, R15, REG_R);
-#elif defined(_M_ARM64)
- CPU_REG(CV_ARM64_W0, X0, REG_E);
- CPU_REG(CV_ARM64_W1, X1, REG_E);
- CPU_REG(CV_ARM64_W2, X2, REG_E);
- CPU_REG(CV_ARM64_W3, X3, REG_E);
- CPU_REG(CV_ARM64_W4, X4, REG_E);
- CPU_REG(CV_ARM64_W5, X5, REG_E);
- CPU_REG(CV_ARM64_W6, X6, REG_E);
- CPU_REG(CV_ARM64_W7, X7, REG_E);
- CPU_REG(CV_ARM64_W8, X8, REG_E);
- CPU_REG(CV_ARM64_W9, X9, REG_E);
- CPU_REG(CV_ARM64_W10, X10, REG_E);
- CPU_REG(CV_ARM64_W11, X11, REG_E);
- CPU_REG(CV_ARM64_W12, X12, REG_E);
- CPU_REG(CV_ARM64_W13, X13, REG_E);
- CPU_REG(CV_ARM64_W14, X14, REG_E);
- CPU_REG(CV_ARM64_W15, X15, REG_E);
- CPU_REG(CV_ARM64_W16, X16, REG_E);
- CPU_REG(CV_ARM64_W17, X17, REG_E);
- CPU_REG(CV_ARM64_W18, X18, REG_E);
- CPU_REG(CV_ARM64_W19, X19, REG_E);
- CPU_REG(CV_ARM64_W20, X20, REG_E);
- CPU_REG(CV_ARM64_W21, X21, REG_E);
- CPU_REG(CV_ARM64_W22, X22, REG_E);
- CPU_REG(CV_ARM64_W23, X23, REG_E);
- CPU_REG(CV_ARM64_W24, X24, REG_E);
- CPU_REG(CV_ARM64_W25, X25, REG_E);
- CPU_REG(CV_ARM64_W26, X26, REG_E);
- CPU_REG(CV_ARM64_W27, X27, REG_E);
- CPU_REG(CV_ARM64_W28, X28, REG_E);
- CPU_REG(CV_ARM64_W29, Fp, REG_E);
- CPU_REG(CV_ARM64_W30, Lr, REG_E);
- case CV_ARM64_WZR: return 0;
- CPU_REG(CV_ARM64_X0, X0, REG_R);
- CPU_REG(CV_ARM64_X1, X1, REG_R);
- CPU_REG(CV_ARM64_X2, X2, REG_R);
- CPU_REG(CV_ARM64_X3, X3, REG_R);
- CPU_REG(CV_ARM64_X4, X4, REG_R);
- CPU_REG(CV_ARM64_X5, X5, REG_R);
- CPU_REG(CV_ARM64_X6, X6, REG_R);
- CPU_REG(CV_ARM64_X7, X7, REG_R);
- CPU_REG(CV_ARM64_X8, X8, REG_R);
- CPU_REG(CV_ARM64_X9, X9, REG_R);
- CPU_REG(CV_ARM64_X10, X10, REG_R);
- CPU_REG(CV_ARM64_X11, X11, REG_R);
- CPU_REG(CV_ARM64_X12, X12, REG_R);
- CPU_REG(CV_ARM64_X13, X13, REG_R);
- CPU_REG(CV_ARM64_X14, X14, REG_R);
- CPU_REG(CV_ARM64_X15, X15, REG_R);
- CPU_REG(CV_ARM64_IP0, X16, REG_R);
- CPU_REG(CV_ARM64_IP1, X17, REG_R);
- CPU_REG(CV_ARM64_X18, X18, REG_R);
- CPU_REG(CV_ARM64_X19, X19, REG_R);
- CPU_REG(CV_ARM64_X20, X20, REG_R);
- CPU_REG(CV_ARM64_X21, X21, REG_R);
- CPU_REG(CV_ARM64_X22, X22, REG_R);
- CPU_REG(CV_ARM64_X23, X23, REG_R);
- CPU_REG(CV_ARM64_X24, X24, REG_R);
- CPU_REG(CV_ARM64_X25, X25, REG_R);
- CPU_REG(CV_ARM64_X26, X26, REG_R);
- CPU_REG(CV_ARM64_X27, X27, REG_R);
- CPU_REG(CV_ARM64_X28, X28, REG_R);
- CPU_REG(CV_ARM64_FP, Fp, REG_R);
- CPU_REG(CV_ARM64_LR, Lr, REG_R);
- CPU_REG(CV_ARM64_SP, Sp, REG_R);
- case CV_ARM64_ZR: return 0;
-#endif
- default:
- break;
- }
- return {};
-#undef CPU_REG
-#undef REG_R
-#undef REG_E
-#undef REG_X
-#undef REG_H
-#undef REG_L
-}
-
-//////////////////////////////////////////////////////////////////////////////
-// Given a SYMBOL_INFO representing a particular variable, displays its
-// contents. If it's a user defined type, display the members and their
-// values.
-//////////////////////////////////////////////////////////////////////////////
-bool WheatyExceptionReport::FormatSymbolValue(
-PSYMBOL_INFO pSym,
-EnumerateSymbolsCallbackContext* pCtx)
-{
- // If it's a function, don't do anything.
- if (pSym->Tag == SymTagFunction) // SymTagFunction from CVCONST.H from the DIA SDK
- return false;
-
- DWORD_PTR pVariable = pSym->Address; // Will point to the variable's data in memory
- Optional<DWORD_PTR> registerVariableStorage;
-
- if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_FRAMERELATIVE
- || (pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGRELATIVE && pSym->Register == CV_ALLREG_VFRAME))
- {
- pVariable += pCtx->sf->AddrFrame.Offset;
- }
- else if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGRELATIVE)
- {
- Optional<DWORD_PTR> registerValue = GetIntegerRegisterValue(pCtx->context, pSym->Register);
- if (!registerValue)
- return false;
-
- pVariable += *registerValue;
- }
- else if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGISTER)
- {
- registerVariableStorage = GetIntegerRegisterValue(pCtx->context, pSym->Register);
- if (!registerVariableStorage)
- return false; // Don't try to report non-integer register variable
-
- pVariable = reinterpret_cast<DWORD_PTR>(&*registerVariableStorage);
- }
- else
- {
- // It must be a global variable
- }
-
- PushSymbolDetail();
-
- // Indicate if the variable is a local or parameter
- if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_PARAMETER)
- symbolDetails.top().Prefix = "Parameter ";
- else if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_LOCAL)
- symbolDetails.top().Prefix = "Local ";
-
- // Determine if the variable is a user defined type (UDT). IF so, bHandled
- // will return true.
- bool bHandled;
- DumpTypeIndex(pSym->ModBase, pSym->TypeIndex, pVariable, bHandled, pSym->Name, "", false, true);
-
- if (!bHandled)
- {
- // The symbol wasn't a UDT, so do basic, stupid formatting of the
- // variable. Based on the size, we're assuming it's a char, WORD, or
- // DWORD.
- BasicType basicType = GetBasicType(pSym->TypeIndex, pSym->ModBase);
- if (symbolDetails.top().Type.empty())
- symbolDetails.top().Type = rgBaseType[basicType];
-
- // Emit the variable name
- if (pSym->Name[0] != '\0')
- symbolDetails.top().Name = pSym->Name;
-
- char buffer[50];
- FormatOutputValue(buffer, basicType, pSym->Size, (PVOID)pVariable, sizeof(buffer));
- symbolDetails.top().Value = buffer;
- }
-
- PopSymbolDetail();
- return true;
-}
-
-//////////////////////////////////////////////////////////////////////////////
-// If it's a user defined type (UDT), recurse through its members until we're
-// at fundamental types. When he hit fundamental types, return
-// bHandled = false, so that FormatSymbolValue() will format them.
-//////////////////////////////////////////////////////////////////////////////
-void WheatyExceptionReport::DumpTypeIndex(
-DWORD64 modBase,
-DWORD dwTypeIndex,
-DWORD_PTR offset,
-bool & bHandled,
-char const* Name,
-char const* /*suffix*/,
-bool newSymbol,
-bool logChildren)
-{
- bHandled = false;
-
- if (newSymbol)
- PushSymbolDetail();
-
- DWORD typeTag;
- if (!SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_SYMTAG, &typeTag))
- return;
-
- // Get the name of the symbol. This will either be a Type name (if a UDT),
- // or the structure member name.
- WCHAR * pwszTypeName;
- if (SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_SYMNAME,
- &pwszTypeName))
- {
- // handle special cases
- if (wcscmp(pwszTypeName, L"std::basic_string<char,std::char_traits<char>,std::allocator<char> >") == 0)
- {
- LocalFree(pwszTypeName);
- symbolDetails.top().Type = "std::string";
- char buffer[50];
- FormatOutputValue(buffer, btStdString, 0, (PVOID)offset, sizeof(buffer));
- symbolDetails.top().Value = buffer;
- if (Name != nullptr && Name[0] != '\0')
- symbolDetails.top().Name = Name;
- bHandled = true;
- return;
- }
-
- char buffer[WER_SMALL_BUFFER_SIZE];
- wcstombs(buffer, pwszTypeName, sizeof(buffer));
- buffer[WER_SMALL_BUFFER_SIZE - 1] = '\0';
- if (Name != nullptr && Name[0] != '\0')
- {
- symbolDetails.top().Type = buffer;
- symbolDetails.top().Name = Name;
- }
- else if (buffer[0] != '\0')
- symbolDetails.top().Name = buffer;
-
- LocalFree(pwszTypeName);
- }
- else if (Name != nullptr && Name[0] != '\0')
- symbolDetails.top().Name = Name;
-
- if (!StoreSymbol(dwTypeIndex, offset))
- {
- // Skip printing address and base class if it has been printed already
- if (typeTag == SymTagBaseClass)
- bHandled = true;
- return;
- }
-
- DWORD innerTypeID;
- switch (typeTag)
- {
- case SymTagPointerType:
- if (SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_TYPEID, &innerTypeID))
- {
- if (Name != nullptr && Name[0] != '\0')
- symbolDetails.top().Name = Name;
-
- BOOL isReference;
- SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_IS_REFERENCE, &isReference);
-
- char addressStr[40];
- memset(addressStr, 0, sizeof(addressStr));
-
- if (isReference)
- symbolDetails.top().Suffix += "&";
- else
- symbolDetails.top().Suffix += "*";
-
- // Try to dereference the pointer in a try/except block since it might be invalid
- DWORD_PTR address = DereferenceUnsafePointer(offset);
-
- char buffer[WER_SMALL_BUFFER_SIZE];
- FormatOutputValue(buffer, btVoid, sizeof(PVOID), (PVOID)offset, sizeof(buffer));
- symbolDetails.top().Value = buffer;
-
- if (symbolDetails.size() >= WER_MAX_NESTING_LEVEL)
- logChildren = false;
-
- // no need to log any children since the address is invalid anyway
- if (address == 0 || address == DWORD_PTR(-1))
- logChildren = false;
-
- DumpTypeIndex(modBase, innerTypeID, address, bHandled, Name, addressStr, false, logChildren);
-
- if (!bHandled)
- {
- BasicType basicType = GetBasicType(dwTypeIndex, modBase);
- if (symbolDetails.top().Type.empty())
- symbolDetails.top().Type = rgBaseType[basicType];
-
- if (address == 0)
- symbolDetails.top().Value = "NULL";
- else if (address == DWORD_PTR(-1))
- symbolDetails.top().Value = "<Unable to read memory>";
- else
- {
- // Get the size of the child member
- ULONG64 length;
- SymGetTypeInfo(m_hProcess, modBase, innerTypeID, TI_GET_LENGTH, &length);
- char buffer2[50];
- FormatOutputValue(buffer2, basicType, length, (PVOID)address, sizeof(buffer2));
- symbolDetails.top().Value = buffer2;
- }
- bHandled = true;
- return;
- }
- else if (address == 0)
- symbolDetails.top().Value = "NULL";
- else if (address == DWORD_PTR(-1))
- {
- symbolDetails.top().Value = "<Unable to read memory>";
- bHandled = true;
- return;
- }
- }
- break;
- case SymTagData:
- if (SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_TYPEID, &innerTypeID))
- {
- DWORD innerTypeTag;
- if (!SymGetTypeInfo(m_hProcess, modBase, innerTypeID, TI_GET_SYMTAG, &innerTypeTag))
- break;
-
- switch (innerTypeTag)
- {
- case SymTagUDT:
- if (symbolDetails.size() >= WER_MAX_NESTING_LEVEL)
- logChildren = false;
- DumpTypeIndex(modBase, innerTypeID,
- offset, bHandled, symbolDetails.top().Name.c_str(), "", false, logChildren);
- break;
- case SymTagPointerType:
- if (Name != nullptr && Name[0] != '\0')
- symbolDetails.top().Name = Name;
- DumpTypeIndex(modBase, innerTypeID,
- offset, bHandled, symbolDetails.top().Name.c_str(), "", false, logChildren);
- break;
- case SymTagArrayType:
- DumpTypeIndex(modBase, innerTypeID,
- offset, bHandled, symbolDetails.top().Name.c_str(), "", false, logChildren);
- break;
- default:
- break;
- }
- }
- break;
- case SymTagArrayType:
- if (SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_TYPEID, &innerTypeID))
- {
- symbolDetails.top().HasChildren = true;
-
- BasicType basicType = btNoType;
- DumpTypeIndex(modBase, innerTypeID,
- offset, bHandled, Name, "", false, false);
-
- // Set Value back to an empty string since the Array object itself has no value, only its elements have
- std::string firstElementValue = symbolDetails.top().Value;
- symbolDetails.top().Value.clear();
-
- DWORD elementsCount;
- if (SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_COUNT, &elementsCount))
- symbolDetails.top().Suffix += "[" + std::to_string(elementsCount) + "]";
- else
- symbolDetails.top().Suffix += "[<unknown count>]";
-
- if (!bHandled)
- {
- basicType = GetBasicType(dwTypeIndex, modBase);
- if (symbolDetails.top().Type.empty())
- symbolDetails.top().Type = rgBaseType[basicType];
- bHandled = true;
- }
-
- // Get the size of the child member
- ULONG64 length;
- SymGetTypeInfo(m_hProcess, modBase, innerTypeID, TI_GET_LENGTH, &length);
-
- char buffer[50];
- switch (basicType)
- {
- case btChar:
- case btStdString:
- FormatOutputValue(buffer, basicType, length, (PVOID)offset, sizeof(buffer), elementsCount);
- symbolDetails.top().Value = buffer;
- break;
- default:
- for (DWORD index = 0; index < elementsCount && index < WER_MAX_ARRAY_ELEMENTS_COUNT; index++)
- {
- bool elementHandled = false;
- PushSymbolDetail();
- if (index == 0)
- {
- if (firstElementValue.empty())
- {
- FormatOutputValue(buffer, basicType, length, (PVOID)(offset + length * index), sizeof(buffer));
- firstElementValue = buffer;
- }
- symbolDetails.top().Value = firstElementValue;
- }
- else
- {
- DumpTypeIndex(modBase, innerTypeID, offset + length * index, elementHandled, "", "", false, false);
- if (!elementHandled)
- {
- FormatOutputValue(buffer, basicType, length, (PVOID)(offset + length * index), sizeof(buffer));
- symbolDetails.top().Value = buffer;
- }
- }
- symbolDetails.top().Prefix.clear();
- symbolDetails.top().Type.clear();
- symbolDetails.top().Suffix = "[" + std::to_string(index) + "]";
- symbolDetails.top().Name.clear();
- PopSymbolDetail();
- }
- break;
- }
-
- return;
- }
- break;
- case SymTagBaseType:
- break;
- case SymTagEnum:
- return;
- default:
- break;
- }
-
- // Determine how many children this type has.
- DWORD dwChildrenCount = 0;
- SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_CHILDRENCOUNT, &dwChildrenCount);
-
- if (!dwChildrenCount) // If no children, we're done
- return;
-
- // Prepare to get an array of "TypeIds", representing each of the children.
- // SymGetTypeInfo(TI_FINDCHILDREN) expects more memory than just a
- // TI_FINDCHILDREN_PARAMS struct has. Use derivation to accomplish this.
- struct FINDCHILDREN : TI_FINDCHILDREN_PARAMS
- {
- ULONG MoreChildIds[1024*2];
- FINDCHILDREN(){Count = sizeof(MoreChildIds) / sizeof(MoreChildIds[0]);}
- } children;
-
- children.Count = dwChildrenCount;
- children.Start= 0;
-
- // Get the array of TypeIds, one for each child type
- if (!SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_FINDCHILDREN,
- &children))
- {
- return;
- }
-
- // Iterate through each of the children
- for (unsigned i = 0; i < dwChildrenCount; i++)
- {
- DWORD symTag;
- SymGetTypeInfo(m_hProcess, modBase, children.ChildId[i], TI_GET_SYMTAG, &symTag);
-
- if (symTag == SymTagFunction ||
- symTag == SymTagEnum ||
- symTag == SymTagTypedef ||
- symTag == SymTagVTable)
- continue;
-
- // Ignore static fields
- DWORD dataKind;
- SymGetTypeInfo(m_hProcess, modBase, children.ChildId[i], TI_GET_DATAKIND, &dataKind);
- if (dataKind == DataIsStaticLocal ||
- dataKind == DataIsGlobal ||
- dataKind == DataIsStaticMember)
- continue;
-
- symbolDetails.top().HasChildren = true;
- if (!logChildren)
- {
- bHandled = false;
- return;
- }
-
- // Recurse for each of the child types
- bool bHandled2;
- BasicType basicType = GetBasicType(children.ChildId[i], modBase);
-
- // Get the offset of the child member, relative to its parent
- DWORD dwMemberOffset;
- SymGetTypeInfo(m_hProcess, modBase, children.ChildId[i],
- TI_GET_OFFSET, &dwMemberOffset);
-
- // Calculate the address of the member
- DWORD_PTR dwFinalOffset = offset + dwMemberOffset;
-
- DumpTypeIndex(modBase,
- children.ChildId[i],
- dwFinalOffset, bHandled2, ""/*Name */, "", true, true);
-
- // If the child wasn't a UDT, format it appropriately
- if (!bHandled2)
- {
- if (symbolDetails.top().Type.empty())
- symbolDetails.top().Type = rgBaseType[basicType];
-
- // Get the real "TypeId" of the child. We need this for the
- // SymGetTypeInfo(TI_GET_TYPEID) call below.
- DWORD typeId;
- SymGetTypeInfo(m_hProcess, modBase, children.ChildId[i],
- TI_GET_TYPEID, &typeId);
-
- // Get the size of the child member
- ULONG64 length;
- SymGetTypeInfo(m_hProcess, modBase, typeId, TI_GET_LENGTH, &length);
-
- char buffer[50];
- FormatOutputValue(buffer, basicType, length, (PVOID)dwFinalOffset, sizeof(buffer));
- symbolDetails.top().Value = buffer;
- }
-
- PopSymbolDetail();
- }
-
- bHandled = true;
-}
-
-void WheatyExceptionReport::FormatOutputValue(char * pszCurrBuffer,
-BasicType basicType,
-DWORD64 length,
-PVOID pAddress,
-size_t bufferSize,
-size_t countOverride)
-{
- __try
- {
- switch (basicType)
- {
- case btChar:
- {
- // Special case handling for char[] type
- if (countOverride != 0)
- length = countOverride;
- else
- length = strlen((char*)pAddress);
- if (length > bufferSize - 6)
- pszCurrBuffer += snprintf(pszCurrBuffer, bufferSize, "\"%.*s...\"", (int)(bufferSize - 6), (char*)pAddress);
- else
- pszCurrBuffer += snprintf(pszCurrBuffer, bufferSize, "\"%.*s\"", (int)length, (char*)pAddress);
- break;
- }
- case btStdString:
- {
- std::string* value = static_cast<std::string*>(pAddress);
- if (value->length() > bufferSize - 6)
- pszCurrBuffer += snprintf(pszCurrBuffer, bufferSize, "\"%.*s...\"", (int)(bufferSize - 6), value->c_str());
- else
- pszCurrBuffer += snprintf(pszCurrBuffer, bufferSize, "\"%s\"", value->c_str());
- break;
- }
- default:
- // Format appropriately (assuming it's a 1, 2, or 4 bytes (!!!)
- if (length == 1)
- pszCurrBuffer += snprintf(pszCurrBuffer, bufferSize, "0x%X", *(PBYTE)pAddress);
- else if (length == 2)
- pszCurrBuffer += snprintf(pszCurrBuffer, bufferSize, "0x%X", *(PWORD)pAddress);
- else if (length == 4)
- {
- if (basicType == btFloat)
- pszCurrBuffer += snprintf(pszCurrBuffer, bufferSize, "%f", *(PFLOAT)pAddress);
- else
- pszCurrBuffer += snprintf(pszCurrBuffer, bufferSize, "0x%X", *(PDWORD)pAddress);
- }
- else if (length == 8)
- {
- if (basicType == btFloat)
- {
- pszCurrBuffer += snprintf(pszCurrBuffer, bufferSize, "%f",
- *(double *)pAddress);
- }
- else
- pszCurrBuffer += snprintf(pszCurrBuffer, bufferSize, "0x%I64X",
- *(DWORD64*)pAddress);
- }
- else
- {
- #if _WIN64
- pszCurrBuffer += snprintf(pszCurrBuffer, bufferSize, "0x%I64X", (DWORD64)pAddress);
- #else
- pszCurrBuffer += snprintf(pszCurrBuffer, bufferSize, "0x%X", (DWORD)pAddress);
- #endif
- }
- break;
- }
- }
- __except (EXCEPTION_EXECUTE_HANDLER)
- {
-#if _WIN64
- pszCurrBuffer += snprintf(pszCurrBuffer, bufferSize, "0x%I64X <Unable to read memory>", (DWORD64)pAddress);
-#else
- pszCurrBuffer += snprintf(pszCurrBuffer, bufferSize, "0x%X <Unable to read memory>", (DWORD)pAddress);
-#endif
- }
-}
-
-BasicType
-WheatyExceptionReport::GetBasicType(DWORD typeIndex, DWORD64 modBase)
-{
- BasicType basicType;
- if (SymGetTypeInfo(m_hProcess, modBase, typeIndex,
- TI_GET_BASETYPE, &basicType))
- {
- return basicType;
- }
-
- // Get the real "TypeId" of the child. We need this for the
- // SymGetTypeInfo(TI_GET_TYPEID) call below.
- DWORD typeId;
- if (SymGetTypeInfo(m_hProcess, modBase, typeIndex, TI_GET_TYPEID, &typeId))
- {
- if (SymGetTypeInfo(m_hProcess, modBase, typeId, TI_GET_BASETYPE,
- &basicType))
- {
- return basicType;
- }
- }
-
- return btNoType;
-}
-
-DWORD_PTR WheatyExceptionReport::DereferenceUnsafePointer(DWORD_PTR address)
-{
- __try
- {
- return *(PDWORD_PTR)address;
- }
- __except (EXCEPTION_EXECUTE_HANDLER)
- {
- return DWORD_PTR(-1);
- }
-}
-
-//============================================================================
-// Helper function that writes to the report file, and allows the user to use
-// printf style formating
-//============================================================================
-int __cdecl WheatyExceptionReport::Log(const TCHAR * format, ...)
-{
- va_list argptr;
- va_start(argptr, format);
- int retValue = _vftprintf(m_hReportFile, format, argptr);
- va_end(argptr);
- return retValue;
-}
-
-bool WheatyExceptionReport::StoreSymbol(DWORD type, DWORD_PTR offset)
-{
- return symbols.insert(SymbolPair(type, offset)).second;
-}
-
-void WheatyExceptionReport::ClearSymbols()
-{
- symbols.clear();
- while (!symbolDetails.empty())
- symbolDetails.pop();
-}
-
-void WheatyExceptionReport::PushSymbolDetail()
-{
- // Log current symbol and then add another to the stack to keep the hierarchy format
- PrintSymbolDetail();
- symbolDetails.emplace();
-}
-
-void WheatyExceptionReport::PopSymbolDetail()
-{
- PrintSymbolDetail();
- symbolDetails.pop();
-}
-
-void WheatyExceptionReport::PrintSymbolDetail()
-{
- if (symbolDetails.empty())
- return;
-
- // Don't log anything if has been logged already or if it's empty
- if (symbolDetails.top().Logged || symbolDetails.top().empty())
- return;
-
- // Add appropriate indentation level (since this routine is recursive)
- for (size_t i = 0; i < symbolDetails.size(); i++)
- Log(_T("\t"));
-
- Log(_T("%s\r\n"), symbolDetails.top().ToString().c_str());
-}
-
-std::string SymbolDetail::ToString()
-{
- Logged = true;
- std::string formatted = Prefix + Type + Suffix;
- if (!Name.empty())
- {
- if (!formatted.empty())
- formatted += " ";
- formatted += Name;
- }
- if (!Value.empty())
- {
- if (Name == "passwd" || Name == "password")
- Value = "<sensitive data>";
- formatted += " = " + Value;
- }
- return formatted;
-}