[Support] Creation of minidump after compiler crash on Windows
In the current implementation compiler only prints stack trace to console after crash. This patch adds saving of minidump files which contain a useful subset of the information for further debugging. Differential Revision: http://reviews.llvm.org/D18216 llvm-svn: 268519
This commit is contained in:
parent
b034526853
commit
1b73e66b5d
|
@ -69,6 +69,9 @@ public:
|
||||||
/// @brief Prevent core file generation.
|
/// @brief Prevent core file generation.
|
||||||
static void PreventCoreFiles();
|
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
|
// 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.
|
// string. \arg Name is assumed to be in UTF-8 encoding too.
|
||||||
static Optional<std::string> GetEnv(StringRef name);
|
static Optional<std::string> GetEnv(StringRef name);
|
||||||
|
|
|
@ -73,6 +73,13 @@ static const char colorcodes[2][2][8][10] = {
|
||||||
{ ALLCOLORS("4",""), ALLCOLORS("4","1;") }
|
{ 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.
|
// Include the platform-specific parts of this class.
|
||||||
#ifdef LLVM_ON_UNIX
|
#ifdef LLVM_ON_UNIX
|
||||||
#include "Unix/Process.inc"
|
#include "Unix/Process.inc"
|
||||||
|
|
|
@ -169,6 +169,8 @@ void Process::PreventCoreFiles() {
|
||||||
signal(SIGSEGV, _exit);
|
signal(SIGSEGV, _exit);
|
||||||
signal(SIGBUS, _exit);
|
signal(SIGBUS, _exit);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
coreFilesPrevented = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional<std::string> Process::GetEnv(StringRef Name) {
|
Optional<std::string> Process::GetEnv(StringRef Name) {
|
||||||
|
|
|
@ -123,6 +123,8 @@ void Process::PreventCoreFiles() {
|
||||||
SetErrorMode(SEM_FAILCRITICALERRORS |
|
SetErrorMode(SEM_FAILCRITICALERRORS |
|
||||||
SEM_NOGPFAULTERRORBOX |
|
SEM_NOGPFAULTERRORBOX |
|
||||||
SEM_NOOPENFILEERRORBOX);
|
SEM_NOOPENFILEERRORBOX);
|
||||||
|
|
||||||
|
coreFilesPrevented = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the environment variable \arg Name's value as a string encoded in
|
/// Returns the environment variable \arg Name's value as a string encoded in
|
||||||
|
|
|
@ -11,7 +11,11 @@
|
||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
#include "llvm/Support/FileSystem.h"
|
#include "llvm/Support/FileSystem.h"
|
||||||
|
#include "llvm/Support/Path.h"
|
||||||
|
#include "llvm/Support/Process.h"
|
||||||
|
#include "llvm/Support/WindowsError.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <io.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdio.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,
|
typedef DWORD64 (__stdcall *PTRANSLATE_ADDRESS_ROUTINE64)(HANDLE hProcess,
|
||||||
HANDLE hThread, LPADDRESS64 lpaddr);
|
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,
|
typedef BOOL (WINAPI *fpStackWalk64)(DWORD, HANDLE, HANDLE, LPSTACKFRAME64,
|
||||||
PVOID, PREAD_PROCESS_MEMORY_ROUTINE64,
|
PVOID, PREAD_PROCESS_MEMORY_ROUTINE64,
|
||||||
PFUNCTION_TABLE_ACCESS_ROUTINE64,
|
PFUNCTION_TABLE_ACCESS_ROUTINE64,
|
||||||
|
@ -154,6 +164,8 @@ static fpEnumerateLoadedModules fEnumerateLoadedModules;
|
||||||
static bool load64BitDebugHelp(void) {
|
static bool load64BitDebugHelp(void) {
|
||||||
HMODULE hLib = ::LoadLibraryW(L"Dbghelp.dll");
|
HMODULE hLib = ::LoadLibraryW(L"Dbghelp.dll");
|
||||||
if (hLib) {
|
if (hLib) {
|
||||||
|
fMiniDumpWriteDump = (fpMiniDumpWriteDump)
|
||||||
|
::GetProcAddress(hLib, "MiniDumpWriteDump");
|
||||||
fStackWalk64 = (fpStackWalk64)
|
fStackWalk64 = (fpStackWalk64)
|
||||||
::GetProcAddress(hLib, "StackWalk64");
|
::GetProcAddress(hLib, "StackWalk64");
|
||||||
fSymGetModuleBase64 = (fpSymGetModuleBase64)
|
fSymGetModuleBase64 = (fpSymGetModuleBase64)
|
||||||
|
@ -171,7 +183,7 @@ static bool load64BitDebugHelp(void) {
|
||||||
fEnumerateLoadedModules = (fpEnumerateLoadedModules)
|
fEnumerateLoadedModules = (fpEnumerateLoadedModules)
|
||||||
::GetProcAddress(hLib, "EnumerateLoadedModules64");
|
::GetProcAddress(hLib, "EnumerateLoadedModules64");
|
||||||
}
|
}
|
||||||
return fStackWalk64 && fSymInitialize && fSymSetOptions;
|
return fStackWalk64 && fSymInitialize && fSymSetOptions && fMiniDumpWriteDump;
|
||||||
}
|
}
|
||||||
|
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
|
@ -485,6 +497,9 @@ void sys::DisableSystemDialogsOnCrash() {
|
||||||
/// PrintStackTraceOnErrorSignal - When an error signal (such as SIBABRT or
|
/// PrintStackTraceOnErrorSignal - When an error signal (such as SIBABRT or
|
||||||
/// SIGSEGV) is delivered to the process, print a stack trace and then exit.
|
/// SIGSEGV) is delivered to the process, print a stack trace and then exit.
|
||||||
void sys::PrintStackTraceOnErrorSignal(bool DisableCrashReporting) {
|
void sys::PrintStackTraceOnErrorSignal(bool DisableCrashReporting) {
|
||||||
|
if (DisableCrashReporting || getenv("LLVM_DISABLE_CRASH_REPORT"))
|
||||||
|
Process::PreventCoreFiles();
|
||||||
|
|
||||||
DisableSystemDialogsOnCrash();
|
DisableSystemDialogsOnCrash();
|
||||||
RegisterHandler();
|
RegisterHandler();
|
||||||
LeaveCriticalSection(&CriticalSection);
|
LeaveCriticalSection(&CriticalSection);
|
||||||
|
@ -563,9 +578,209 @@ void llvm::sys::RunInterruptHandlers() {
|
||||||
Cleanup();
|
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) {
|
static LONG WINAPI LLVMUnhandledExceptionFilter(LPEXCEPTION_POINTERS ep) {
|
||||||
Cleanup();
|
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.
|
// Initialize the STACKFRAME structure.
|
||||||
STACKFRAME64 StackFrame = {};
|
STACKFRAME64 StackFrame = {};
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
struct FindHandleTraits : CommonHandleTraits {
|
||||||
static void Close(handle_type h) {
|
static void Close(handle_type h) {
|
||||||
::FindClose(h);
|
::FindClose(h);
|
||||||
|
@ -178,6 +194,7 @@ struct FileHandleTraits : CommonHandleTraits {};
|
||||||
typedef ScopedHandle<CommonHandleTraits> ScopedCommonHandle;
|
typedef ScopedHandle<CommonHandleTraits> ScopedCommonHandle;
|
||||||
typedef ScopedHandle<FileHandleTraits> ScopedFileHandle;
|
typedef ScopedHandle<FileHandleTraits> ScopedFileHandle;
|
||||||
typedef ScopedHandle<CryptContextTraits> ScopedCryptContext;
|
typedef ScopedHandle<CryptContextTraits> ScopedCryptContext;
|
||||||
|
typedef ScopedHandle<RegTraits> ScopedRegHandle;
|
||||||
typedef ScopedHandle<FindHandleTraits> ScopedFindHandle;
|
typedef ScopedHandle<FindHandleTraits> ScopedFindHandle;
|
||||||
typedef ScopedHandle<JobHandleTraits> ScopedJobHandle;
|
typedef ScopedHandle<JobHandleTraits> ScopedJobHandle;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue