mirror of
https://github.com/RPCS3/llvm.git
synced 2025-02-24 12:50:42 +00:00
[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 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@268519 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
d1310b73a3
commit
f7a03a4ea3
@ -69,6 +69,9 @@ public:
|
||||
/// @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<std::string> GetEnv(StringRef name);
|
||||
|
@ -73,6 +73,13 @@ static const char colorcodes[2][2][8][10] = {
|
||||
{ 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"
|
||||
|
@ -169,6 +169,8 @@ void Process::PreventCoreFiles() {
|
||||
signal(SIGSEGV, _exit);
|
||||
signal(SIGBUS, _exit);
|
||||
#endif
|
||||
|
||||
coreFilesPrevented = true;
|
||||
}
|
||||
|
||||
Optional<std::string> Process::GetEnv(StringRef Name) {
|
||||
|
@ -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
|
||||
|
@ -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 = {};
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user