Index: llvm/trunk/include/llvm/Support/Process.h =================================================================== --- llvm/trunk/include/llvm/Support/Process.h +++ llvm/trunk/include/llvm/Support/Process.h @@ -69,6 +69,9 @@ /// @brief Prevent core file generation. static void PreventCoreFiles(); + /// \brief true if PreventCoreFiles has been called, false otherwise. + static bool AreCoreFilesPrevented(); + // This function returns the environment variable \arg name's value as a UTF-8 // string. \arg Name is assumed to be in UTF-8 encoding too. static Optional GetEnv(StringRef name); Index: llvm/trunk/lib/Support/Process.cpp =================================================================== --- llvm/trunk/lib/Support/Process.cpp +++ llvm/trunk/lib/Support/Process.cpp @@ -73,6 +73,13 @@ { ALLCOLORS("4",""), ALLCOLORS("4","1;") } }; +// This is set to true when Process::PreventCoreFiles() is called. +static bool coreFilesPrevented = false; + +bool Process::AreCoreFilesPrevented() { + return coreFilesPrevented; +} + // Include the platform-specific parts of this class. #ifdef LLVM_ON_UNIX #include "Unix/Process.inc" Index: llvm/trunk/lib/Support/Unix/Process.inc =================================================================== --- llvm/trunk/lib/Support/Unix/Process.inc +++ llvm/trunk/lib/Support/Unix/Process.inc @@ -169,6 +169,8 @@ signal(SIGSEGV, _exit); signal(SIGBUS, _exit); #endif + + coreFilesPrevented = true; } Optional Process::GetEnv(StringRef Name) { Index: llvm/trunk/lib/Support/Windows/Process.inc =================================================================== --- llvm/trunk/lib/Support/Windows/Process.inc +++ llvm/trunk/lib/Support/Windows/Process.inc @@ -123,6 +123,8 @@ SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); + + coreFilesPrevented = true; } /// Returns the environment variable \arg Name's value as a string encoded in Index: llvm/trunk/lib/Support/Windows/Signals.inc =================================================================== --- llvm/trunk/lib/Support/Windows/Signals.inc +++ llvm/trunk/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 +#include #include #include @@ -117,6 +121,12 @@ 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 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 @@ fEnumerateLoadedModules = (fpEnumerateLoadedModules) ::GetProcAddress(hLib, "EnumerateLoadedModules64"); } - return fStackWalk64 && fSymInitialize && fSymSetOptions; + return fStackWalk64 && fSymInitialize && fSymSetOptions && fMiniDumpWriteDump; } using namespace llvm; @@ -485,6 +497,9 @@ /// 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 @@ 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 &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 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 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(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 DumpDirectory; + if (!GetDumpFolder(AppSpecificKey, DumpDirectory)) + if (!GetDumpFolder(DefaultLocalDumpsKey, DumpDirectory)) + ExplicitDumpDirectorySet = false; + + int FD; + SmallString 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(_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 = {}; Index: llvm/trunk/lib/Support/Windows/WindowsSupport.h =================================================================== --- llvm/trunk/lib/Support/Windows/WindowsSupport.h +++ llvm/trunk/lib/Support/Windows/WindowsSupport.h @@ -167,6 +167,22 @@ } }; +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 @@ typedef ScopedHandle ScopedCommonHandle; typedef ScopedHandle ScopedFileHandle; typedef ScopedHandle ScopedCryptContext; +typedef ScopedHandle ScopedRegHandle; typedef ScopedHandle ScopedFindHandle; typedef ScopedHandle ScopedJobHandle;