diff options
Diffstat (limited to 'llvm/lib/Support/Windows')
-rw-r--r-- | llvm/lib/Support/Windows/Process.inc | 2 | ||||
-rw-r--r-- | llvm/lib/Support/Windows/Signals.inc | 217 | ||||
-rw-r--r-- | llvm/lib/Support/Windows/WindowsSupport.h | 17 |
3 files changed, 235 insertions, 1 deletions
diff --git a/llvm/lib/Support/Windows/Process.inc b/llvm/lib/Support/Windows/Process.inc index dae35a88132..3aef79fee84 100644 --- a/llvm/lib/Support/Windows/Process.inc +++ b/llvm/lib/Support/Windows/Process.inc @@ -123,6 +123,8 @@ void Process::PreventCoreFiles() { SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); + + coreFilesPrevented = true; } /// Returns the environment variable \arg Name's value as a string encoded in diff --git a/llvm/lib/Support/Windows/Signals.inc b/llvm/lib/Support/Windows/Signals.inc index 552bbff5b0c..3a0e5569f93 100644 --- a/llvm/lib/Support/Windows/Signals.inc +++ b/llvm/lib/Support/Windows/Signals.inc @@ -11,7 +11,11 @@ // //===----------------------------------------------------------------------===// #include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/WindowsError.h" #include <algorithm> +#include <io.h> #include <signal.h> #include <stdio.h> @@ -117,6 +121,12 @@ typedef DWORD64 (__stdcall *PGET_MODULE_BASE_ROUTINE64)(HANDLE hProcess, typedef DWORD64 (__stdcall *PTRANSLATE_ADDRESS_ROUTINE64)(HANDLE hProcess, HANDLE hThread, LPADDRESS64 lpaddr); +typedef BOOL(WINAPI *fpMiniDumpWriteDump)(HANDLE, DWORD, HANDLE, MINIDUMP_TYPE, + PMINIDUMP_EXCEPTION_INFORMATION, + PMINIDUMP_USER_STREAM_INFORMATION, + PMINIDUMP_CALLBACK_INFORMATION); +static fpMiniDumpWriteDump fMiniDumpWriteDump; + typedef BOOL (WINAPI *fpStackWalk64)(DWORD, HANDLE, HANDLE, LPSTACKFRAME64, PVOID, PREAD_PROCESS_MEMORY_ROUTINE64, PFUNCTION_TABLE_ACCESS_ROUTINE64, @@ -154,6 +164,8 @@ static fpEnumerateLoadedModules fEnumerateLoadedModules; static bool load64BitDebugHelp(void) { HMODULE hLib = ::LoadLibraryW(L"Dbghelp.dll"); if (hLib) { + fMiniDumpWriteDump = (fpMiniDumpWriteDump) + ::GetProcAddress(hLib, "MiniDumpWriteDump"); fStackWalk64 = (fpStackWalk64) ::GetProcAddress(hLib, "StackWalk64"); fSymGetModuleBase64 = (fpSymGetModuleBase64) @@ -171,7 +183,7 @@ static bool load64BitDebugHelp(void) { fEnumerateLoadedModules = (fpEnumerateLoadedModules) ::GetProcAddress(hLib, "EnumerateLoadedModules64"); } - return fStackWalk64 && fSymInitialize && fSymSetOptions; + return fStackWalk64 && fSymInitialize && fSymSetOptions && fMiniDumpWriteDump; } using namespace llvm; @@ -485,6 +497,9 @@ void sys::DisableSystemDialogsOnCrash() { /// PrintStackTraceOnErrorSignal - When an error signal (such as SIBABRT or /// SIGSEGV) is delivered to the process, print a stack trace and then exit. void sys::PrintStackTraceOnErrorSignal(bool DisableCrashReporting) { + if (DisableCrashReporting || getenv("LLVM_DISABLE_CRASH_REPORT")) + Process::PreventCoreFiles(); + DisableSystemDialogsOnCrash(); RegisterHandler(); LeaveCriticalSection(&CriticalSection); @@ -563,9 +578,209 @@ void llvm::sys::RunInterruptHandlers() { Cleanup(); } +/// \brief Find the Windows Registry Key for a given location. +/// +/// \returns a valid HKEY if the location exists, else NULL. +static HKEY FindWERKey(const llvm::Twine &RegistryLocation) { + HKEY Key; + if (ERROR_SUCCESS != ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, + RegistryLocation.str().c_str(), 0, + KEY_QUERY_VALUE | KEY_READ, &Key)) + return NULL; + + return Key; +} + +/// \brief Populate ResultDirectory with the value for "DumpFolder" for a given +/// Windows Registry key. +/// +/// \returns true if a valid value for DumpFolder exists, false otherwise. +static bool GetDumpFolder(HKEY Key, + llvm::SmallVectorImpl<char> &ResultDirectory) { + using llvm::sys::windows::UTF16ToUTF8; + + if (!Key) + return false; + + DWORD BufferLengthBytes = 0; + + if (ERROR_SUCCESS != ::RegGetValueW(Key, 0, L"DumpFolder", REG_EXPAND_SZ, + NULL, NULL, &BufferLengthBytes)) + return false; + + SmallVector<wchar_t, MAX_PATH> Buffer(BufferLengthBytes); + + if (ERROR_SUCCESS != ::RegGetValueW(Key, 0, L"DumpFolder", REG_EXPAND_SZ, + NULL, Buffer.data(), &BufferLengthBytes)) + return false; + + DWORD ExpandBufferSize = ::ExpandEnvironmentStringsW(Buffer.data(), NULL, 0); + + if (!ExpandBufferSize) + return false; + + SmallVector<wchar_t, MAX_PATH> ExpandBuffer(ExpandBufferSize); + + if (ExpandBufferSize != ::ExpandEnvironmentStringsW(Buffer.data(), + ExpandBuffer.data(), + ExpandBufferSize)) + return false; + + if (UTF16ToUTF8(ExpandBuffer.data(), ExpandBufferSize - 1, ResultDirectory)) + return false; + + return true; +} + +/// \brief Populate ResultType with a valid MINIDUMP_TYPE based on the value of +/// "DumpType" for a given Windows Registry key. +/// +/// According to +/// https://msdn.microsoft.com/en-us/library/windows/desktop/bb787181(v=vs.85).aspx +/// valid values for DumpType are: +/// * 0: Custom dump +/// * 1: Mini dump +/// * 2: Full dump +/// If "Custom dump" is specified then the "CustomDumpFlags" field is read +/// containing a bitwise combination of MINIDUMP_TYPE values. +/// +/// \returns true if a valid value for ResultType can be set, false otherwise. +static bool GetDumpType(HKEY Key, MINIDUMP_TYPE &ResultType) { + if (!Key) + return false; + + DWORD DumpType; + DWORD TypeSize = sizeof(DumpType); + if (ERROR_SUCCESS != ::RegGetValueW(Key, NULL, L"DumpType", RRF_RT_REG_DWORD, + NULL, &DumpType, + &TypeSize)) + return false; + + switch (DumpType) { + case 0: { + DWORD Flags = 0; + if (ERROR_SUCCESS != ::RegGetValueW(Key, NULL, L"CustomDumpFlags", + RRF_RT_REG_DWORD, NULL, &Flags, + &TypeSize)) + return false; + + ResultType = static_cast<MINIDUMP_TYPE>(Flags); + break; + } + case 1: + ResultType = MiniDumpNormal; + break; + case 2: + ResultType = MiniDumpWithFullMemory; + break; + default: + return false; + } + return true; +} + +/// \brief Write a Windows dump file containing process information that can be +/// used for post-mortem debugging. +/// +/// \returns zero error code if a mini dump created, actual error code +/// otherwise. +static std::error_code WINAPI +WriteWindowsDumpFile(PMINIDUMP_EXCEPTION_INFORMATION ExceptionInfo) { + using namespace llvm; + using namespace llvm::sys; + + std::string MainExecutableName = fs::getMainExecutable(nullptr, nullptr); + StringRef ProgramName; + + if (MainExecutableName.empty()) { + // If we can't get the executable filename, + // things are in worse shape than we realize + // and we should just bail out. + return mapWindowsError(::GetLastError()); + } + + ProgramName = path::filename(MainExecutableName.c_str()); + + // The Windows Registry location as specified at + // https://msdn.microsoft.com/en-us/library/windows/desktop/bb787181%28v=vs.85%29.aspx + // "Collecting User-Mode Dumps" that may optionally be set to collect crash + // dumps in a specified location. + StringRef LocalDumpsRegistryLocation = + "SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\LocalDumps"; + + // The key pointing to the Registry location that may contain global crash + // dump settings. This will be NULL if the location can not be found. + ScopedRegHandle DefaultLocalDumpsKey(FindWERKey(LocalDumpsRegistryLocation)); + + // The key pointing to the Registry location that may contain + // application-specific crash dump settings. This will be NULL if the + // location can not be found. + ScopedRegHandle AppSpecificKey( + FindWERKey(Twine(LocalDumpsRegistryLocation) + "\\" + ProgramName)); + + // Look to see if a dump type is specified in the registry; first with the + // app-specific key and failing that with the global key. If none are found + // default to a normal dump (GetDumpType will return false either if the key + // is NULL or if there is no valid DumpType value at its location). + MINIDUMP_TYPE DumpType; + if (!GetDumpType(AppSpecificKey, DumpType)) + if (!GetDumpType(DefaultLocalDumpsKey, DumpType)) + DumpType = MiniDumpNormal; + + // Look to see if a dump location is specified in the registry; first with the + // app-specific key and failing that with the global key. If none are found + // we'll just create the dump file in the default temporary file location + // (GetDumpFolder will return false either if the key is NULL or if there is + // no valid DumpFolder value at its location). + bool ExplicitDumpDirectorySet = true; + SmallString<MAX_PATH> DumpDirectory; + if (!GetDumpFolder(AppSpecificKey, DumpDirectory)) + if (!GetDumpFolder(DefaultLocalDumpsKey, DumpDirectory)) + ExplicitDumpDirectorySet = false; + + int FD; + SmallString<MAX_PATH> DumpPath; + + if (ExplicitDumpDirectorySet) { + if (std::error_code EC = fs::create_directories(DumpDirectory)) + return EC; + if (std::error_code EC = fs::createUniqueFile( + Twine(DumpDirectory) + "\\" + ProgramName + ".%%%%%%.dmp", FD, + DumpPath)) + return EC; + } else if (std::error_code EC = + fs::createTemporaryFile(ProgramName, "dmp", FD, DumpPath)) + return EC; + + // Our support functions return a file descriptor but Windows wants a handle. + ScopedCommonHandle FileHandle(reinterpret_cast<HANDLE>(_get_osfhandle(FD))); + + if (!fMiniDumpWriteDump(::GetCurrentProcess(), ::GetCurrentProcessId(), + FileHandle, DumpType, ExceptionInfo, NULL, NULL)) + return mapWindowsError(::GetLastError()); + + llvm::errs() << "Wrote crash dump file \"" << DumpPath << "\"\n"; + return std::error_code(); +} + static LONG WINAPI LLVMUnhandledExceptionFilter(LPEXCEPTION_POINTERS ep) { Cleanup(); + // We'll automatically write a Minidump file here to help diagnose + // the nasty sorts of crashes that aren't 100% reproducible from a set of + // inputs (or in the event that the user is unable or unwilling to provide a + // reproducible case). + if (!llvm::Process::AreCoreFilesPrevented()) { + MINIDUMP_EXCEPTION_INFORMATION ExceptionInfo; + ExceptionInfo.ThreadId = ::GetCurrentThreadId(); + ExceptionInfo.ExceptionPointers = ep; + ExceptionInfo.ClientPointers = FALSE; + + if (std::error_code EC = WriteWindowsDumpFile(&ExceptionInfo)) + llvm::errs() << "Could not write crash dump file: " << EC.message() + << "\n"; + } + // Initialize the STACKFRAME structure. STACKFRAME64 StackFrame = {}; diff --git a/llvm/lib/Support/Windows/WindowsSupport.h b/llvm/lib/Support/Windows/WindowsSupport.h index a28c79f40da..fa611a955af 100644 --- a/llvm/lib/Support/Windows/WindowsSupport.h +++ b/llvm/lib/Support/Windows/WindowsSupport.h @@ -167,6 +167,22 @@ struct CryptContextTraits : CommonHandleTraits { } }; +struct RegTraits : CommonHandleTraits { + typedef HKEY handle_type; + + static handle_type GetInvalid() { + return NULL; + } + + static void Close(handle_type h) { + ::RegCloseKey(h); + } + + static bool IsValid(handle_type h) { + return h != GetInvalid(); + } +}; + struct FindHandleTraits : CommonHandleTraits { static void Close(handle_type h) { ::FindClose(h); @@ -178,6 +194,7 @@ struct FileHandleTraits : CommonHandleTraits {}; typedef ScopedHandle<CommonHandleTraits> ScopedCommonHandle; typedef ScopedHandle<FileHandleTraits> ScopedFileHandle; typedef ScopedHandle<CryptContextTraits> ScopedCryptContext; +typedef ScopedHandle<RegTraits> ScopedRegHandle; typedef ScopedHandle<FindHandleTraits> ScopedFindHandle; typedef ScopedHandle<JobHandleTraits> ScopedJobHandle; |