Index: include/llvm/Support/Process.h =================================================================== --- include/llvm/Support/Process.h +++ include/llvm/Support/Process.h @@ -43,6 +43,9 @@ /// current executing process. class Process { public: + /// \brief true if PreventCoreFiles has been called, false otherwise. + static bool CoreFilesPrevented; + static unsigned getPageSize(); /// \brief Return process memory usage. Index: lib/Support/Process.cpp =================================================================== --- lib/Support/Process.cpp +++ lib/Support/Process.cpp @@ -73,6 +73,9 @@ { ALLCOLORS("4",""), ALLCOLORS("4","1;") } }; +// This is set to true when Process::PreventCoreFiles() is called. +bool Process::CoreFilesPrevented = false; + // Include the platform-specific parts of this class. #ifdef LLVM_ON_UNIX #include "Unix/Process.inc" Index: lib/Support/Unix/Process.inc =================================================================== --- lib/Support/Unix/Process.inc +++ lib/Support/Unix/Process.inc @@ -169,6 +169,8 @@ signal(SIGSEGV, _exit); signal(SIGBUS, _exit); #endif + + Process::CoreFilesPrevented = true; } Optional Process::GetEnv(StringRef Name) { Index: lib/Support/Windows/Process.inc =================================================================== --- lib/Support/Windows/Process.inc +++ lib/Support/Windows/Process.inc @@ -123,6 +123,8 @@ SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); + + Process::CoreFilesPrevented = true; } /// Returns the environment variable \arg Name's value as a string encoded in Index: lib/Support/Windows/Signals.inc =================================================================== --- lib/Support/Windows/Signals.inc +++ 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, @@ -147,6 +157,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) @@ -391,6 +403,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); @@ -470,9 +485,182 @@ Cleanup(); } +#ifdef _MSC_VER + +/// \brief Find the Windows Registry Key for a given location. +/// +/// \returns a valid HKEY if the location exists, else NULL. +static HKEY FindRegistryKey(const llvm::Twine &RegistryLocation, + HKEY RegistryRoot = HKEY_LOCAL_MACHINE, + DWORD AccessRights = KEY_QUERY_VALUE | KEY_READ | + KEY_WOW64_64KEY) { + HKEY Key = NULL; + if (RegOpenKeyEx(RegistryRoot, RegistryLocation.str().c_str(), 0, + AccessRights, &Key) != ERROR_SUCCESS) + 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) { + if (!Key) return false; + + const std::size_t BufferLength = MAX_PATH + 1; + char Buffer[BufferLength]; + DWORD BufferLengthBytes = BufferLength; + + LONG Error = RegGetValue(Key, 0, "DumpFolder", RRF_RT_REG_SZ, NULL, &Buffer, + &BufferLengthBytes); + ResultDirectory = llvm::SmallString<128>(Buffer); + return (Error == ERROR_SUCCESS); +} + +/// \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 = 1; + DWORD TypeSize = sizeof(DumpType); + if (RegGetValue(Key, NULL, "DumpType", RRF_RT_REG_DWORD, NULL, &DumpType, + &TypeSize) != ERROR_SUCCESS) + return false; + + switch (DumpType) { + case 0: { + DWORD Flags = 0; + if (RegGetValue(Key, NULL, "CustomDumpFlags", RRF_RT_REG_DWORD, NULL, + &Flags, &TypeSize) != ERROR_SUCCESS) + return false; + + ResultType = static_cast(MiniDumpValidTypeFlags & 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. +static std::error_code WINAPI +WriteWindowsDumpFile(PMINIDUMP_EXCEPTION_INFORMATION ExceptionInfo) { + using namespace llvm; + using namespace llvm::sys; + + StringRef ProgramName = path::filename(__argv[0]); + + // 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. + const SmallString<128> 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. + HKEY DefaultLocalDumpsKey = + FindRegistryKey(Twine(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. + HKEY AppSpecificKey = + FindRegistryKey(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<128> DumpDirectory; + if (!GetDumpFolder(AppSpecificKey, DumpDirectory)) + if (!GetDumpFolder(DefaultLocalDumpsKey, DumpDirectory)) + ExplicitDumpDirectorySet = false; + + int FD; + SmallString<128> DumpPath; + + if (ExplicitDumpDirectorySet) { + if (std::error_code EC = fs::create_directories(Twine(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. + HANDLE FileHandle = reinterpret_cast(_get_osfhandle(FD)); + + // Actually write the contents of the dump file using our open handle. + if (!FileHandle || + !fMiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), + FileHandle, DumpType, ExceptionInfo, NULL, NULL)) + return std::error_code(mapWindowsError(GetLastError())); + + if (!CloseHandle(FileHandle)) + return std::error_code(mapWindowsError(GetLastError())); + + llvm::errs() << "Wrote crash dump file \"" << DumpPath << "\"\n"; + return std::error_code(); +} +#endif + static LONG WINAPI LLVMUnhandledExceptionFilter(LPEXCEPTION_POINTERS ep) { Cleanup(); +#ifdef _MSC_VER + // With MSVC 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::CoreFilesPrevented) { + 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"; + } +#endif + // Initialize the STACKFRAME structure. STACKFRAME64 StackFrame = {};