diff options
author | Shauren <shauren.trinity@gmail.com> | 2024-09-23 00:18:06 +0200 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2024-09-23 00:18:06 +0200 |
commit | e76df583f034d2bf9355b0d77967774f3f15ece7 (patch) | |
tree | 82f3c4cf08f4878c43922ac76a0b85a4280ce55d /src | |
parent | 4f3e73afaee89a3efb0a403eb6719ea8e8f1a16a (diff) |
Core/CrashHandler: Make WheatyExceptionReport fields not static to ensure consistent destruction order
Diffstat (limited to 'src')
-rw-r--r-- | src/common/Debugging/Windows/WheatyExceptionReport.cpp | 290 | ||||
-rw-r--r-- | src/common/Debugging/Windows/WheatyExceptionReport.h | 60 |
2 files changed, 178 insertions, 172 deletions
diff --git a/src/common/Debugging/Windows/WheatyExceptionReport.cpp b/src/common/Debugging/Windows/WheatyExceptionReport.cpp index cec3a27e002..87bca0add80 100644 --- a/src/common/Debugging/Windows/WheatyExceptionReport.cpp +++ b/src/common/Debugging/Windows/WheatyExceptionReport.cpp @@ -6,7 +6,9 @@ #include "WheatyExceptionReport.h" #include "Errors.h" #include "GitRevision.h" +#include <stdexcept> #include <algorithm> +#include <utility> #ifdef __clang__ // clang-cl doesn't have these hardcoded types available, correct ehdata_forceinclude.h that relies on it @@ -25,6 +27,12 @@ #pragma comment(linker, "/DEFAULTLIB:dbghelp.lib") #pragma comment(linker, "/DEFAULTLIB:wbemuuid.lib") +#ifdef _UNICODE +#define PRSTRc "S" // format specifier for char* strings +#else +#define PRSTRc "s" // format specifier for char* strings +#endif + inline LPTSTR ErrorMessage(DWORD dw) { LPVOID lpMsgBuf; @@ -48,37 +56,24 @@ inline LPTSTR ErrorMessage(DWORD dw) } //============================== 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; -#pragma warning(pop) +namespace +{ +WheatyExceptionReport* g_WheatyExceptionReport; +} //============================== Class Methods ============================= -WheatyExceptionReport::WheatyExceptionReport() // Constructor +WheatyExceptionReport::WheatyExceptionReport() : // Constructor + m_logFileName(), + m_dumpFileName(), + m_previousFilter(SetUnhandledExceptionFilter(WheatyUnhandledExceptionFilter)), + m_previousCrtHandler(_set_invalid_parameter_handler(WheatyCrtHandler)), + m_reportFile(nullptr), + m_dumpFile(), + m_process(GetCurrentProcess()), + m_alreadyCrashed(false), + RtlGetVersion((pRtlGetVersion)GetProcAddress(GetModuleHandle(_T("ntdll.dll")), "RtlGetVersion")) { - // 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); @@ -86,6 +81,9 @@ WheatyExceptionReport::WheatyExceptionReport() // Constructor _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE); _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); } + + if (std::exchange(g_WheatyExceptionReport, this) != nullptr) + throw std::logic_error("Only one instance of WheatyExceptionReport can exist"); } //============ @@ -98,6 +96,8 @@ WheatyExceptionReport::~WheatyExceptionReport() if (m_previousCrtHandler) _set_invalid_parameter_handler(m_previousCrtHandler); ClearSymbols(); + + g_WheatyExceptionReport = nullptr; } //=========================================================== @@ -106,12 +106,17 @@ WheatyExceptionReport::~WheatyExceptionReport() LONG WINAPI WheatyExceptionReport::WheatyUnhandledExceptionFilter( PEXCEPTION_POINTERS pExceptionInfo) { - std::unique_lock<std::mutex> guard(alreadyCrashedLock); + return g_WheatyExceptionReport->UnhandledExceptionFilterImpl(pExceptionInfo); +} + +LONG WheatyExceptionReport::UnhandledExceptionFilterImpl(PEXCEPTION_POINTERS pExceptionInfo) +{ + std::unique_lock<std::mutex> guard(m_alreadyCrashedLock); // Handle only 1 exception in the whole process lifetime - if (alreadyCrashed) + if (m_alreadyCrashed) return EXCEPTION_EXECUTE_HANDLER; - alreadyCrashed = true; + m_alreadyCrashed = true; TCHAR module_folder_name[MAX_PATH]; GetModuleFileName(nullptr, module_folder_name, MAX_PATH); @@ -129,21 +134,15 @@ PEXCEPTION_POINTERS pExceptionInfo) 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"), + _stprintf_s(m_dumpFileName, _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"), + _stprintf_s(m_logFileName, _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, + m_dumpFile = CreateFile(m_dumpFileName, GENERIC_WRITE, 0, nullptr, @@ -151,7 +150,7 @@ PEXCEPTION_POINTERS pExceptionInfo) FILE_FLAG_WRITE_THROUGH, nullptr); - if (m_hDumpFile) + if (m_dumpFile) { MINIDUMP_EXCEPTION_INFORMATION info; info.ClientPointers = FALSE; @@ -171,20 +170,20 @@ PEXCEPTION_POINTERS pExceptionInfo) additionalStreamInfo.UserStreamCount = 1; } - MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), - m_hDumpFile, MiniDumpWithIndirectlyReferencedMemory, &info, &additionalStreamInfo, nullptr); + MiniDumpWriteDump(m_process, GetCurrentProcessId(), + m_dumpFile, MiniDumpWithIndirectlyReferencedMemory, &info, &additionalStreamInfo, nullptr); - CloseHandle(m_hDumpFile); + CloseHandle(m_dumpFile); } - m_hReportFile = _tfopen(m_szLogFileName, _T("wb")); + m_reportFile = _tfopen(m_logFileName, _T("wb")); - if (m_hReportFile) + if (m_reportFile) { GenerateExceptionReport(pExceptionInfo); - fclose(m_hReportFile); - m_hReportFile = nullptr; + fclose(m_reportFile); + m_reportFile = nullptr; } if (m_previousFilter) @@ -598,7 +597,6 @@ void WheatyExceptionReport::printTracesForAllThreads(bool bWriteVariables) 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) @@ -652,7 +650,7 @@ PEXCEPTION_POINTERS pExceptionInfo) GetLocalTime(&systime); // Start out with a banner - Log(_T("Revision: %s\r\n"), GitRevision::GetFullVersion()); + Log(_T("Revision: %" PRSTRc "\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; @@ -665,7 +663,7 @@ PEXCEPTION_POINTERS pExceptionInfo) 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]); + Log(_T("Assertion message: %" PRSTRc "\r\n"), pExceptionRecord->ExceptionInformation[0]); } // Now print information about where the fault occured @@ -739,7 +737,7 @@ PEXCEPTION_POINTERS pExceptionInfo) SymSetOptions(SYMOPT_DEFERRED_LOADS); // Initialize DbgHelp - if (!SymInitialize(GetCurrentProcess(), nullptr, TRUE)) + if (!SymInitialize(m_process, nullptr, TRUE)) { Log(_T("\r\n")); Log(_T("----\r\n")); @@ -817,7 +815,7 @@ PEXCEPTION_POINTERS pExceptionInfo) 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)) + if (SymGetTypeFromName(m_process, (ULONG64)GetModuleHandle(nullptr), undName, sym)) { sym->Address = pExceptionRecord->ExceptionInformation[1]; sym->Flags = 0; @@ -844,7 +842,7 @@ PEXCEPTION_POINTERS pExceptionInfo) WriteStackDetails(&trashableContext, true, nullptr); printTracesForAllThreads(true); - SymCleanup(GetCurrentProcess()); + SymCleanup(m_process); Log(_T("\r\n")); } @@ -1013,7 +1011,7 @@ bool bWriteVariables, HANDLE pThreadHandle) { // Get the next stack frame if (! StackWalk64(dwMachineType, - m_hProcess, + m_process, pThreadHandle != nullptr ? pThreadHandle : GetCurrentThread(), &sf, pContext, @@ -1036,12 +1034,12 @@ bool bWriteVariables, HANDLE pThreadHandle) // Get the name of the function for this stack frame entry CSymbolInfoPackage sip; if (SymFromAddr( - m_hProcess, // Process handle of the current process + m_process, // 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); + Log(_T("%h" PRSTRc "+%I64X"), sip.si.Name, symDisplacement); } else // No symbol found. Print out the logical address instead. @@ -1062,10 +1060,10 @@ bool bWriteVariables, HANDLE pThreadHandle) // Get the source line for this stack frame entry IMAGEHLP_LINE64 lineInfo = { sizeof(IMAGEHLP_LINE64) }; DWORD dwLineDisplacement; - if (SymGetLineFromAddr64(m_hProcess, sf.AddrPC.Offset, + if (SymGetLineFromAddr64(m_process, sf.AddrPC.Offset, &dwLineDisplacement, &lineInfo)) { - Log(_T(" %s line %u"), lineInfo.FileName, lineInfo.LineNumber); + Log(_T(" %" PRSTRc " line %u"), lineInfo.FileName, lineInfo.LineNumber); } Log(_T("\r\n")); @@ -1076,13 +1074,14 @@ bool bWriteVariables, HANDLE pThreadHandle) // 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); + SymSetContext(m_process, &imagehlpStackFrame, nullptr); // Enumerate the locals/parameters EnumerateSymbolsCallbackContext ctx; ctx.sf = &sf; ctx.context = pContext; - SymEnumSymbols(m_hProcess, 0, nullptr, EnumerateSymbolsCallback, &ctx); + ctx.report = this; + SymEnumSymbols(m_process, 0, nullptr, EnumerateSymbolsCallback, &ctx); Log(_T("\r\n")); } @@ -1100,15 +1099,16 @@ PSYMBOL_INFO pSymInfo, ULONG /*SymbolSize*/, PVOID UserContext) { + EnumerateSymbolsCallbackContext* context = static_cast<EnumerateSymbolsCallbackContext*>(UserContext); __try { - ClearSymbols(); - FormatSymbolValue(pSymInfo, (EnumerateSymbolsCallbackContext*)UserContext); + context->report->ClearSymbols(); + context->report->FormatSymbolValue(pSymInfo, context); } __except (EXCEPTION_EXECUTE_HANDLER) { - Log(_T("punting on symbol %s, partial output:\r\n"), pSymInfo->Name); + context->report->Log(_T("punting on symbol %" PRSTRc ", partial output:\r\n"), pSymInfo->Name); } return TRUE; @@ -1121,7 +1121,7 @@ Optional<DWORD_PTR> WheatyExceptionReport::GetIntegerRegisterValue(PCONTEXT cont #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); +#define CPU_REG(reg, field, part) case reg: return part(context->field) switch (registerId) { #ifdef _M_IX86 @@ -1345,9 +1345,9 @@ EnumerateSymbolsCallbackContext* pCtx) // Indicate if the variable is a local or parameter if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_PARAMETER) - symbolDetails.top().Prefix = "Parameter "; + m_symbolDetails.top().Prefix = "Parameter "; else if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_LOCAL) - symbolDetails.top().Prefix = "Local "; + m_symbolDetails.top().Prefix = "Local "; // Determine if the variable is a user defined type (UDT). IF so, bHandled // will return true. @@ -1360,16 +1360,16 @@ EnumerateSymbolsCallbackContext* pCtx) // 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]; + if (m_symbolDetails.top().Type.empty()) + m_symbolDetails.top().Type = rgBaseType[basicType]; // Emit the variable name if (pSym->Name[0] != '\0') - symbolDetails.top().Name = pSym->Name; + m_symbolDetails.top().Name = pSym->Name; char buffer[50]; FormatOutputValue(buffer, basicType, pSym->Size, (PVOID)pVariable, sizeof(buffer)); - symbolDetails.top().Value = buffer; + m_symbolDetails.top().Value = buffer; } PopSymbolDetail(); @@ -1397,25 +1397,25 @@ bool logChildren) PushSymbolDetail(); DWORD typeTag; - if (!SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_SYMTAG, &typeTag)) + if (!SymGetTypeInfo(m_process, 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, + if (SymGetTypeInfo(m_process, 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"; + m_symbolDetails.top().Type = "std::string"; char buffer[50]; FormatOutputValue(buffer, btStdString, 0, (PVOID)offset, sizeof(buffer)); - symbolDetails.top().Value = buffer; + m_symbolDetails.top().Value = buffer; if (Name != nullptr && Name[0] != '\0') - symbolDetails.top().Name = Name; + m_symbolDetails.top().Name = Name; bHandled = true; return; } @@ -1425,16 +1425,16 @@ bool logChildren) buffer[WER_SMALL_BUFFER_SIZE - 1] = '\0'; if (Name != nullptr && Name[0] != '\0') { - symbolDetails.top().Type = buffer; - symbolDetails.top().Name = Name; + m_symbolDetails.top().Type = buffer; + m_symbolDetails.top().Name = Name; } else if (buffer[0] != '\0') - symbolDetails.top().Name = buffer; + m_symbolDetails.top().Name = buffer; LocalFree(pwszTypeName); } else if (Name != nullptr && Name[0] != '\0') - symbolDetails.top().Name = Name; + m_symbolDetails.top().Name = Name; if (!StoreSymbol(dwTypeIndex, offset)) { @@ -1448,30 +1448,30 @@ bool logChildren) switch (typeTag) { case SymTagPointerType: - if (SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_TYPEID, &innerTypeID)) + if (SymGetTypeInfo(m_process, modBase, dwTypeIndex, TI_GET_TYPEID, &innerTypeID)) { if (Name != nullptr && Name[0] != '\0') - symbolDetails.top().Name = Name; + m_symbolDetails.top().Name = Name; BOOL isReference; - SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_IS_REFERENCE, &isReference); + SymGetTypeInfo(m_process, modBase, dwTypeIndex, TI_GET_IS_REFERENCE, &isReference); char addressStr[40]; memset(addressStr, 0, sizeof(addressStr)); if (isReference) - symbolDetails.top().Suffix += "&"; + m_symbolDetails.top().Suffix += "&"; else - symbolDetails.top().Suffix += "*"; + m_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; + m_symbolDetails.top().Value = buffer; - if (symbolDetails.size() >= WER_MAX_NESTING_LEVEL) + if (m_symbolDetails.size() >= WER_MAX_NESTING_LEVEL) logChildren = false; // no need to log any children since the address is invalid anyway @@ -1483,59 +1483,59 @@ bool logChildren) if (!bHandled) { BasicType basicType = GetBasicType(dwTypeIndex, modBase); - if (symbolDetails.top().Type.empty()) - symbolDetails.top().Type = rgBaseType[basicType]; + if (m_symbolDetails.top().Type.empty()) + m_symbolDetails.top().Type = rgBaseType[basicType]; if (address == 0) - symbolDetails.top().Value = "NULL"; + m_symbolDetails.top().Value = "NULL"; else if (address == DWORD_PTR(-1)) - symbolDetails.top().Value = "<Unable to read memory>"; + m_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); + SymGetTypeInfo(m_process, modBase, innerTypeID, TI_GET_LENGTH, &length); char buffer2[50]; FormatOutputValue(buffer2, basicType, length, (PVOID)address, sizeof(buffer2)); - symbolDetails.top().Value = buffer2; + m_symbolDetails.top().Value = buffer2; } bHandled = true; return; } else if (address == 0) - symbolDetails.top().Value = "NULL"; + m_symbolDetails.top().Value = "NULL"; else if (address == DWORD_PTR(-1)) { - symbolDetails.top().Value = "<Unable to read memory>"; + m_symbolDetails.top().Value = "<Unable to read memory>"; bHandled = true; return; } } break; case SymTagData: - if (SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_TYPEID, &innerTypeID)) + if (SymGetTypeInfo(m_process, modBase, dwTypeIndex, TI_GET_TYPEID, &innerTypeID)) { DWORD innerTypeTag; - if (!SymGetTypeInfo(m_hProcess, modBase, innerTypeID, TI_GET_SYMTAG, &innerTypeTag)) + if (!SymGetTypeInfo(m_process, modBase, innerTypeID, TI_GET_SYMTAG, &innerTypeTag)) break; switch (innerTypeTag) { case SymTagUDT: - if (symbolDetails.size() >= WER_MAX_NESTING_LEVEL) + if (m_symbolDetails.size() >= WER_MAX_NESTING_LEVEL) logChildren = false; DumpTypeIndex(modBase, innerTypeID, - offset, bHandled, symbolDetails.top().Name.c_str(), "", false, logChildren); + offset, bHandled, m_symbolDetails.top().Name.c_str(), "", false, logChildren); break; case SymTagPointerType: if (Name != nullptr && Name[0] != '\0') - symbolDetails.top().Name = Name; + m_symbolDetails.top().Name = Name; DumpTypeIndex(modBase, innerTypeID, - offset, bHandled, symbolDetails.top().Name.c_str(), "", false, logChildren); + offset, bHandled, m_symbolDetails.top().Name.c_str(), "", false, logChildren); break; case SymTagArrayType: DumpTypeIndex(modBase, innerTypeID, - offset, bHandled, symbolDetails.top().Name.c_str(), "", false, logChildren); + offset, bHandled, m_symbolDetails.top().Name.c_str(), "", false, logChildren); break; default: break; @@ -1543,35 +1543,35 @@ bool logChildren) } break; case SymTagArrayType: - if (SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_TYPEID, &innerTypeID)) + if (SymGetTypeInfo(m_process, modBase, dwTypeIndex, TI_GET_TYPEID, &innerTypeID)) { - symbolDetails.top().HasChildren = true; + m_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(); + std::string firstElementValue = m_symbolDetails.top().Value; + m_symbolDetails.top().Value.clear(); DWORD elementsCount; - if (SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_COUNT, &elementsCount)) - symbolDetails.top().Suffix += "[" + std::to_string(elementsCount) + "]"; + if (SymGetTypeInfo(m_process, modBase, dwTypeIndex, TI_GET_COUNT, &elementsCount)) + m_symbolDetails.top().Suffix += "[" + std::to_string(elementsCount) + "]"; else - symbolDetails.top().Suffix += "[<unknown count>]"; + m_symbolDetails.top().Suffix += "[<unknown count>]"; if (!bHandled) { basicType = GetBasicType(dwTypeIndex, modBase); - if (symbolDetails.top().Type.empty()) - symbolDetails.top().Type = rgBaseType[basicType]; + if (m_symbolDetails.top().Type.empty()) + m_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); + SymGetTypeInfo(m_process, modBase, innerTypeID, TI_GET_LENGTH, &length); char buffer[50]; switch (basicType) @@ -1579,7 +1579,7 @@ bool logChildren) case btChar: case btStdString: FormatOutputValue(buffer, basicType, length, (PVOID)offset, sizeof(buffer), elementsCount); - symbolDetails.top().Value = buffer; + m_symbolDetails.top().Value = buffer; break; default: for (DWORD index = 0; index < elementsCount && index < WER_MAX_ARRAY_ELEMENTS_COUNT; index++) @@ -1593,7 +1593,7 @@ bool logChildren) FormatOutputValue(buffer, basicType, length, (PVOID)(offset + length * index), sizeof(buffer)); firstElementValue = buffer; } - symbolDetails.top().Value = firstElementValue; + m_symbolDetails.top().Value = firstElementValue; } else { @@ -1601,13 +1601,13 @@ bool logChildren) if (!elementHandled) { FormatOutputValue(buffer, basicType, length, (PVOID)(offset + length * index), sizeof(buffer)); - symbolDetails.top().Value = buffer; + m_symbolDetails.top().Value = buffer; } } - symbolDetails.top().Prefix.clear(); - symbolDetails.top().Type.clear(); - symbolDetails.top().Suffix = "[" + std::to_string(index) + "]"; - symbolDetails.top().Name.clear(); + m_symbolDetails.top().Prefix.clear(); + m_symbolDetails.top().Type.clear(); + m_symbolDetails.top().Suffix = "[" + std::to_string(index) + "]"; + m_symbolDetails.top().Name.clear(); PopSymbolDetail(); } break; @@ -1626,7 +1626,7 @@ bool logChildren) // Determine how many children this type has. DWORD dwChildrenCount = 0; - SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_CHILDRENCOUNT, &dwChildrenCount); + SymGetTypeInfo(m_process, modBase, dwTypeIndex, TI_GET_CHILDRENCOUNT, &dwChildrenCount); if (!dwChildrenCount) // If no children, we're done return; @@ -1644,7 +1644,7 @@ bool logChildren) children.Start= 0; // Get the array of TypeIds, one for each child type - if (!SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_FINDCHILDREN, + if (!SymGetTypeInfo(m_process, modBase, dwTypeIndex, TI_FINDCHILDREN, &children)) { return; @@ -1654,7 +1654,7 @@ bool logChildren) for (unsigned i = 0; i < dwChildrenCount; i++) { DWORD symTag; - SymGetTypeInfo(m_hProcess, modBase, children.ChildId[i], TI_GET_SYMTAG, &symTag); + SymGetTypeInfo(m_process, modBase, children.ChildId[i], TI_GET_SYMTAG, &symTag); if (symTag == SymTagFunction || symTag == SymTagEnum || @@ -1664,13 +1664,13 @@ bool logChildren) // Ignore static fields DWORD dataKind; - SymGetTypeInfo(m_hProcess, modBase, children.ChildId[i], TI_GET_DATAKIND, &dataKind); + SymGetTypeInfo(m_process, modBase, children.ChildId[i], TI_GET_DATAKIND, &dataKind); if (dataKind == DataIsStaticLocal || dataKind == DataIsGlobal || dataKind == DataIsStaticMember) continue; - symbolDetails.top().HasChildren = true; + m_symbolDetails.top().HasChildren = true; if (!logChildren) { bHandled = false; @@ -1683,7 +1683,7 @@ bool logChildren) // Get the offset of the child member, relative to its parent DWORD dwMemberOffset; - SymGetTypeInfo(m_hProcess, modBase, children.ChildId[i], + SymGetTypeInfo(m_process, modBase, children.ChildId[i], TI_GET_OFFSET, &dwMemberOffset); // Calculate the address of the member @@ -1696,22 +1696,22 @@ bool logChildren) // If the child wasn't a UDT, format it appropriately if (!bHandled2) { - if (symbolDetails.top().Type.empty()) - symbolDetails.top().Type = rgBaseType[basicType]; + if (m_symbolDetails.top().Type.empty()) + m_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], + SymGetTypeInfo(m_process, 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); + SymGetTypeInfo(m_process, modBase, typeId, TI_GET_LENGTH, &length); char buffer[50]; FormatOutputValue(buffer, basicType, length, (PVOID)dwFinalOffset, sizeof(buffer)); - symbolDetails.top().Value = buffer; + m_symbolDetails.top().Value = buffer; } PopSymbolDetail(); @@ -1799,10 +1799,10 @@ size_t countOverride) } BasicType -WheatyExceptionReport::GetBasicType(DWORD typeIndex, DWORD64 modBase) +WheatyExceptionReport::GetBasicType(DWORD typeIndex, DWORD64 modBase) const { BasicType basicType; - if (SymGetTypeInfo(m_hProcess, modBase, typeIndex, + if (SymGetTypeInfo(m_process, modBase, typeIndex, TI_GET_BASETYPE, &basicType)) { return basicType; @@ -1811,9 +1811,9 @@ WheatyExceptionReport::GetBasicType(DWORD typeIndex, DWORD64 modBase) // 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_process, modBase, typeIndex, TI_GET_TYPEID, &typeId)) { - if (SymGetTypeInfo(m_hProcess, modBase, typeId, TI_GET_BASETYPE, + if (SymGetTypeInfo(m_process, modBase, typeId, TI_GET_BASETYPE, &basicType)) { return basicType; @@ -1839,54 +1839,54 @@ DWORD_PTR WheatyExceptionReport::DereferenceUnsafePointer(DWORD_PTR address) // Helper function that writes to the report file, and allows the user to use // printf style formating //============================================================================ -int __cdecl WheatyExceptionReport::Log(const TCHAR * format, ...) +int WheatyExceptionReport::Log(const TCHAR * format, ...) { va_list argptr; va_start(argptr, format); - int retValue = _vftprintf(m_hReportFile, format, argptr); + int retValue = _vftprintf(m_reportFile, format, argptr); va_end(argptr); return retValue; } bool WheatyExceptionReport::StoreSymbol(DWORD type, DWORD_PTR offset) { - return symbols.insert(SymbolPair(type, offset)).second; + return m_symbols.insert(SymbolPair(type, offset)).second; } void WheatyExceptionReport::ClearSymbols() { - symbols.clear(); - while (!symbolDetails.empty()) - symbolDetails.pop(); + m_symbols.clear(); + while (!m_symbolDetails.empty()) + m_symbolDetails.pop(); } void WheatyExceptionReport::PushSymbolDetail() { // Log current symbol and then add another to the stack to keep the hierarchy format PrintSymbolDetail(); - symbolDetails.emplace(); + m_symbolDetails.emplace(); } void WheatyExceptionReport::PopSymbolDetail() { PrintSymbolDetail(); - symbolDetails.pop(); + m_symbolDetails.pop(); } void WheatyExceptionReport::PrintSymbolDetail() { - if (symbolDetails.empty()) + if (m_symbolDetails.empty()) return; // Don't log anything if has been logged already or if it's empty - if (symbolDetails.top().Logged || symbolDetails.top().empty()) + if (m_symbolDetails.top().Logged || m_symbolDetails.top().empty()) return; // Add appropriate indentation level (since this routine is recursive) - for (size_t i = 0; i < symbolDetails.size(); i++) + for (size_t i = 0; i < m_symbolDetails.size(); i++) Log(_T("\t")); - Log(_T("%s\r\n"), symbolDetails.top().ToString().c_str()); + Log(_T("%" PRSTRc "\r\n"), m_symbolDetails.top().ToString().c_str()); } std::string SymbolDetail::ToString() diff --git a/src/common/Debugging/Windows/WheatyExceptionReport.h b/src/common/Debugging/Windows/WheatyExceptionReport.h index d154fce0d13..2ff30da91b9 100644 --- a/src/common/Debugging/Windows/WheatyExceptionReport.h +++ b/src/common/Debugging/Windows/WheatyExceptionReport.h @@ -328,20 +328,26 @@ class TC_COMMON_API WheatyExceptionReport public: WheatyExceptionReport(); + WheatyExceptionReport(WheatyExceptionReport const&) = delete; + WheatyExceptionReport(WheatyExceptionReport&&) = delete; + WheatyExceptionReport& operator=(WheatyExceptionReport const&) = delete; + WheatyExceptionReport& operator=(WheatyExceptionReport&&) = delete; ~WheatyExceptionReport(); // entry point where control comes on an unhandled exception static LONG WINAPI WheatyUnhandledExceptionFilter( PEXCEPTION_POINTERS pExceptionInfo); + LONG UnhandledExceptionFilterImpl(PEXCEPTION_POINTERS pExceptionInfo); + static void __cdecl WheatyCrtHandler(wchar_t const* expression, wchar_t const* function, wchar_t const* file, unsigned int line, uintptr_t pReserved); - static void printTracesForAllThreads(bool); + void printTracesForAllThreads(bool bWriteVariables); private: // where report info is extracted and generated - static void GenerateExceptionReport(PEXCEPTION_POINTERS pExceptionInfo); - static void PrintSystemInfo(); - static BOOL _GetWindowsVersion(TCHAR* szVersion, DWORD cntMax); + void GenerateExceptionReport(PEXCEPTION_POINTERS pExceptionInfo); + void PrintSystemInfo(); + BOOL _GetWindowsVersion(TCHAR* szVersion, DWORD cntMax); static BOOL _GetWindowsVersionFromWMI(TCHAR* szVersion, DWORD cntMax); static BOOL _GetProcessorName(TCHAR* sProcessorName, DWORD maxcount); @@ -350,51 +356,51 @@ class TC_COMMON_API WheatyExceptionReport static BOOL GetLogicalAddress(PVOID addr, PTSTR szModule, DWORD len, DWORD& section, DWORD_PTR& offset); - static void WriteStackDetails(PCONTEXT pContext, bool bWriteVariables, HANDLE pThreadHandle); + void WriteStackDetails(PCONTEXT pContext, bool bWriteVariables, HANDLE pThreadHandle); struct EnumerateSymbolsCallbackContext { LPSTACKFRAME64 sf; PCONTEXT context; + WheatyExceptionReport* report; }; static BOOL CALLBACK EnumerateSymbolsCallback(PSYMBOL_INFO, ULONG, PVOID); - static bool FormatSymbolValue(PSYMBOL_INFO, EnumerateSymbolsCallbackContext*); + bool FormatSymbolValue(PSYMBOL_INFO, EnumerateSymbolsCallbackContext*); - static void DumpTypeIndex(DWORD64, DWORD, DWORD_PTR, bool &, char const*, char const*, bool, bool); + void DumpTypeIndex(DWORD64, DWORD, DWORD_PTR, bool &, char const*, char const*, bool, bool); static void FormatOutputValue(char * pszCurrBuffer, BasicType basicType, DWORD64 length, PVOID pAddress, size_t bufferSize, size_t countOverride = 0); - static BasicType GetBasicType(DWORD typeIndex, DWORD64 modBase); + BasicType GetBasicType(DWORD typeIndex, DWORD64 modBase) const; static DWORD_PTR DereferenceUnsafePointer(DWORD_PTR address); - static int __cdecl Log(const TCHAR * format, ...); + int Log(const TCHAR * format, ...); - static bool StoreSymbol(DWORD type , DWORD_PTR offset); - static void ClearSymbols(); + bool StoreSymbol(DWORD type , DWORD_PTR offset); + void ClearSymbols(); static Optional<DWORD_PTR> GetIntegerRegisterValue(PCONTEXT context, ULONG registerId); // Variables used by the class - static TCHAR m_szLogFileName[MAX_PATH]; - static TCHAR m_szDumpFileName[MAX_PATH]; - static LPTOP_LEVEL_EXCEPTION_FILTER m_previousFilter; - static _invalid_parameter_handler m_previousCrtHandler; - static FILE* m_hReportFile; - static HANDLE m_hDumpFile; - static HANDLE m_hProcess; - static SymbolPairs symbols; - static std::stack<SymbolDetail> symbolDetails; - static bool alreadyCrashed; - static std::mutex alreadyCrashedLock; + TCHAR m_logFileName[MAX_PATH]; + TCHAR m_dumpFileName[MAX_PATH]; + LPTOP_LEVEL_EXCEPTION_FILTER m_previousFilter; + _invalid_parameter_handler m_previousCrtHandler; + FILE* m_reportFile; + HANDLE m_dumpFile; + HANDLE m_process; + SymbolPairs m_symbols; + std::stack<SymbolDetail> m_symbolDetails; + bool m_alreadyCrashed; + std::mutex m_alreadyCrashedLock; typedef NTSTATUS(NTAPI* pRtlGetVersion)(PRTL_OSVERSIONINFOW lpVersionInformation); - static pRtlGetVersion RtlGetVersion; - - static void PushSymbolDetail(); - static void PopSymbolDetail(); - static void PrintSymbolDetail(); + pRtlGetVersion RtlGetVersion; + void PushSymbolDetail(); + void PopSymbolDetail(); + void PrintSymbolDetail(); }; #define INIT_CRASH_HANDLER() \ |