Skip to content
This repository was archived by the owner on Mar 28, 2020. It is now read-only.

Commit f7a03a4

Browse files
author
Leny Kholodov
committed
[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
1 parent d1310b7 commit f7a03a4

File tree

6 files changed

+247
-1
lines changed

6 files changed

+247
-1
lines changed

include/llvm/Support/Process.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ class Process {
6969
/// @brief Prevent core file generation.
7070
static void PreventCoreFiles();
7171

72+
/// \brief true if PreventCoreFiles has been called, false otherwise.
73+
static bool AreCoreFilesPrevented();
74+
7275
// This function returns the environment variable \arg name's value as a UTF-8
7376
// string. \arg Name is assumed to be in UTF-8 encoding too.
7477
static Optional<std::string> GetEnv(StringRef name);

lib/Support/Process.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,13 @@ static const char colorcodes[2][2][8][10] = {
7373
{ ALLCOLORS("4",""), ALLCOLORS("4","1;") }
7474
};
7575

76+
// This is set to true when Process::PreventCoreFiles() is called.
77+
static bool coreFilesPrevented = false;
78+
79+
bool Process::AreCoreFilesPrevented() {
80+
return coreFilesPrevented;
81+
}
82+
7683
// Include the platform-specific parts of this class.
7784
#ifdef LLVM_ON_UNIX
7885
#include "Unix/Process.inc"

lib/Support/Unix/Process.inc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@ void Process::PreventCoreFiles() {
169169
signal(SIGSEGV, _exit);
170170
signal(SIGBUS, _exit);
171171
#endif
172+
173+
coreFilesPrevented = true;
172174
}
173175

174176
Optional<std::string> Process::GetEnv(StringRef Name) {

lib/Support/Windows/Process.inc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ void Process::PreventCoreFiles() {
123123
SetErrorMode(SEM_FAILCRITICALERRORS |
124124
SEM_NOGPFAULTERRORBOX |
125125
SEM_NOOPENFILEERRORBOX);
126+
127+
coreFilesPrevented = true;
126128
}
127129

128130
/// Returns the environment variable \arg Name's value as a string encoded in

lib/Support/Windows/Signals.inc

Lines changed: 216 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@
1111
//
1212
//===----------------------------------------------------------------------===//
1313
#include "llvm/Support/FileSystem.h"
14+
#include "llvm/Support/Path.h"
15+
#include "llvm/Support/Process.h"
16+
#include "llvm/Support/WindowsError.h"
1417
#include <algorithm>
18+
#include <io.h>
1519
#include <signal.h>
1620
#include <stdio.h>
1721

@@ -117,6 +121,12 @@ typedef DWORD64 (__stdcall *PGET_MODULE_BASE_ROUTINE64)(HANDLE hProcess,
117121
typedef DWORD64 (__stdcall *PTRANSLATE_ADDRESS_ROUTINE64)(HANDLE hProcess,
118122
HANDLE hThread, LPADDRESS64 lpaddr);
119123

124+
typedef BOOL(WINAPI *fpMiniDumpWriteDump)(HANDLE, DWORD, HANDLE, MINIDUMP_TYPE,
125+
PMINIDUMP_EXCEPTION_INFORMATION,
126+
PMINIDUMP_USER_STREAM_INFORMATION,
127+
PMINIDUMP_CALLBACK_INFORMATION);
128+
static fpMiniDumpWriteDump fMiniDumpWriteDump;
129+
120130
typedef BOOL (WINAPI *fpStackWalk64)(DWORD, HANDLE, HANDLE, LPSTACKFRAME64,
121131
PVOID, PREAD_PROCESS_MEMORY_ROUTINE64,
122132
PFUNCTION_TABLE_ACCESS_ROUTINE64,
@@ -154,6 +164,8 @@ static fpEnumerateLoadedModules fEnumerateLoadedModules;
154164
static bool load64BitDebugHelp(void) {
155165
HMODULE hLib = ::LoadLibraryW(L"Dbghelp.dll");
156166
if (hLib) {
167+
fMiniDumpWriteDump = (fpMiniDumpWriteDump)
168+
::GetProcAddress(hLib, "MiniDumpWriteDump");
157169
fStackWalk64 = (fpStackWalk64)
158170
::GetProcAddress(hLib, "StackWalk64");
159171
fSymGetModuleBase64 = (fpSymGetModuleBase64)
@@ -171,7 +183,7 @@ static bool load64BitDebugHelp(void) {
171183
fEnumerateLoadedModules = (fpEnumerateLoadedModules)
172184
::GetProcAddress(hLib, "EnumerateLoadedModules64");
173185
}
174-
return fStackWalk64 && fSymInitialize && fSymSetOptions;
186+
return fStackWalk64 && fSymInitialize && fSymSetOptions && fMiniDumpWriteDump;
175187
}
176188

177189
using namespace llvm;
@@ -485,6 +497,9 @@ void sys::DisableSystemDialogsOnCrash() {
485497
/// PrintStackTraceOnErrorSignal - When an error signal (such as SIBABRT or
486498
/// SIGSEGV) is delivered to the process, print a stack trace and then exit.
487499
void sys::PrintStackTraceOnErrorSignal(bool DisableCrashReporting) {
500+
if (DisableCrashReporting || getenv("LLVM_DISABLE_CRASH_REPORT"))
501+
Process::PreventCoreFiles();
502+
488503
DisableSystemDialogsOnCrash();
489504
RegisterHandler();
490505
LeaveCriticalSection(&CriticalSection);
@@ -563,9 +578,209 @@ void llvm::sys::RunInterruptHandlers() {
563578
Cleanup();
564579
}
565580

581+
/// \brief Find the Windows Registry Key for a given location.
582+
///
583+
/// \returns a valid HKEY if the location exists, else NULL.
584+
static HKEY FindWERKey(const llvm::Twine &RegistryLocation) {
585+
HKEY Key;
586+
if (ERROR_SUCCESS != ::RegOpenKeyEx(HKEY_LOCAL_MACHINE,
587+
RegistryLocation.str().c_str(), 0,
588+
KEY_QUERY_VALUE | KEY_READ, &Key))
589+
return NULL;
590+
591+
return Key;
592+
}
593+
594+
/// \brief Populate ResultDirectory with the value for "DumpFolder" for a given
595+
/// Windows Registry key.
596+
///
597+
/// \returns true if a valid value for DumpFolder exists, false otherwise.
598+
static bool GetDumpFolder(HKEY Key,
599+
llvm::SmallVectorImpl<char> &ResultDirectory) {
600+
using llvm::sys::windows::UTF16ToUTF8;
601+
602+
if (!Key)
603+
return false;
604+
605+
DWORD BufferLengthBytes = 0;
606+
607+
if (ERROR_SUCCESS != ::RegGetValueW(Key, 0, L"DumpFolder", REG_EXPAND_SZ,
608+
NULL, NULL, &BufferLengthBytes))
609+
return false;
610+
611+
SmallVector<wchar_t, MAX_PATH> Buffer(BufferLengthBytes);
612+
613+
if (ERROR_SUCCESS != ::RegGetValueW(Key, 0, L"DumpFolder", REG_EXPAND_SZ,
614+
NULL, Buffer.data(), &BufferLengthBytes))
615+
return false;
616+
617+
DWORD ExpandBufferSize = ::ExpandEnvironmentStringsW(Buffer.data(), NULL, 0);
618+
619+
if (!ExpandBufferSize)
620+
return false;
621+
622+
SmallVector<wchar_t, MAX_PATH> ExpandBuffer(ExpandBufferSize);
623+
624+
if (ExpandBufferSize != ::ExpandEnvironmentStringsW(Buffer.data(),
625+
ExpandBuffer.data(),
626+
ExpandBufferSize))
627+
return false;
628+
629+
if (UTF16ToUTF8(ExpandBuffer.data(), ExpandBufferSize - 1, ResultDirectory))
630+
return false;
631+
632+
return true;
633+
}
634+
635+
/// \brief Populate ResultType with a valid MINIDUMP_TYPE based on the value of
636+
/// "DumpType" for a given Windows Registry key.
637+
///
638+
/// According to
639+
/// https://msdn.microsoft.com/en-us/library/windows/desktop/bb787181(v=vs.85).aspx
640+
/// valid values for DumpType are:
641+
/// * 0: Custom dump
642+
/// * 1: Mini dump
643+
/// * 2: Full dump
644+
/// If "Custom dump" is specified then the "CustomDumpFlags" field is read
645+
/// containing a bitwise combination of MINIDUMP_TYPE values.
646+
///
647+
/// \returns true if a valid value for ResultType can be set, false otherwise.
648+
static bool GetDumpType(HKEY Key, MINIDUMP_TYPE &ResultType) {
649+
if (!Key)
650+
return false;
651+
652+
DWORD DumpType;
653+
DWORD TypeSize = sizeof(DumpType);
654+
if (ERROR_SUCCESS != ::RegGetValueW(Key, NULL, L"DumpType", RRF_RT_REG_DWORD,
655+
NULL, &DumpType,
656+
&TypeSize))
657+
return false;
658+
659+
switch (DumpType) {
660+
case 0: {
661+
DWORD Flags = 0;
662+
if (ERROR_SUCCESS != ::RegGetValueW(Key, NULL, L"CustomDumpFlags",
663+
RRF_RT_REG_DWORD, NULL, &Flags,
664+
&TypeSize))
665+
return false;
666+
667+
ResultType = static_cast<MINIDUMP_TYPE>(Flags);
668+
break;
669+
}
670+
case 1:
671+
ResultType = MiniDumpNormal;
672+
break;
673+
case 2:
674+
ResultType = MiniDumpWithFullMemory;
675+
break;
676+
default:
677+
return false;
678+
}
679+
return true;
680+
}
681+
682+
/// \brief Write a Windows dump file containing process information that can be
683+
/// used for post-mortem debugging.
684+
///
685+
/// \returns zero error code if a mini dump created, actual error code
686+
/// otherwise.
687+
static std::error_code WINAPI
688+
WriteWindowsDumpFile(PMINIDUMP_EXCEPTION_INFORMATION ExceptionInfo) {
689+
using namespace llvm;
690+
using namespace llvm::sys;
691+
692+
std::string MainExecutableName = fs::getMainExecutable(nullptr, nullptr);
693+
StringRef ProgramName;
694+
695+
if (MainExecutableName.empty()) {
696+
// If we can't get the executable filename,
697+
// things are in worse shape than we realize
698+
// and we should just bail out.
699+
return mapWindowsError(::GetLastError());
700+
}
701+
702+
ProgramName = path::filename(MainExecutableName.c_str());
703+
704+
// The Windows Registry location as specified at
705+
// https://msdn.microsoft.com/en-us/library/windows/desktop/bb787181%28v=vs.85%29.aspx
706+
// "Collecting User-Mode Dumps" that may optionally be set to collect crash
707+
// dumps in a specified location.
708+
StringRef LocalDumpsRegistryLocation =
709+
"SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\LocalDumps";
710+
711+
// The key pointing to the Registry location that may contain global crash
712+
// dump settings. This will be NULL if the location can not be found.
713+
ScopedRegHandle DefaultLocalDumpsKey(FindWERKey(LocalDumpsRegistryLocation));
714+
715+
// The key pointing to the Registry location that may contain
716+
// application-specific crash dump settings. This will be NULL if the
717+
// location can not be found.
718+
ScopedRegHandle AppSpecificKey(
719+
FindWERKey(Twine(LocalDumpsRegistryLocation) + "\\" + ProgramName));
720+
721+
// Look to see if a dump type is specified in the registry; first with the
722+
// app-specific key and failing that with the global key. If none are found
723+
// default to a normal dump (GetDumpType will return false either if the key
724+
// is NULL or if there is no valid DumpType value at its location).
725+
MINIDUMP_TYPE DumpType;
726+
if (!GetDumpType(AppSpecificKey, DumpType))
727+
if (!GetDumpType(DefaultLocalDumpsKey, DumpType))
728+
DumpType = MiniDumpNormal;
729+
730+
// Look to see if a dump location is specified in the registry; first with the
731+
// app-specific key and failing that with the global key. If none are found
732+
// we'll just create the dump file in the default temporary file location
733+
// (GetDumpFolder will return false either if the key is NULL or if there is
734+
// no valid DumpFolder value at its location).
735+
bool ExplicitDumpDirectorySet = true;
736+
SmallString<MAX_PATH> DumpDirectory;
737+
if (!GetDumpFolder(AppSpecificKey, DumpDirectory))
738+
if (!GetDumpFolder(DefaultLocalDumpsKey, DumpDirectory))
739+
ExplicitDumpDirectorySet = false;
740+
741+
int FD;
742+
SmallString<MAX_PATH> DumpPath;
743+
744+
if (ExplicitDumpDirectorySet) {
745+
if (std::error_code EC = fs::create_directories(DumpDirectory))
746+
return EC;
747+
if (std::error_code EC = fs::createUniqueFile(
748+
Twine(DumpDirectory) + "\\" + ProgramName + ".%%%%%%.dmp", FD,
749+
DumpPath))
750+
return EC;
751+
} else if (std::error_code EC =
752+
fs::createTemporaryFile(ProgramName, "dmp", FD, DumpPath))
753+
return EC;
754+
755+
// Our support functions return a file descriptor but Windows wants a handle.
756+
ScopedCommonHandle FileHandle(reinterpret_cast<HANDLE>(_get_osfhandle(FD)));
757+
758+
if (!fMiniDumpWriteDump(::GetCurrentProcess(), ::GetCurrentProcessId(),
759+
FileHandle, DumpType, ExceptionInfo, NULL, NULL))
760+
return mapWindowsError(::GetLastError());
761+
762+
llvm::errs() << "Wrote crash dump file \"" << DumpPath << "\"\n";
763+
return std::error_code();
764+
}
765+
566766
static LONG WINAPI LLVMUnhandledExceptionFilter(LPEXCEPTION_POINTERS ep) {
567767
Cleanup();
568768

769+
// We'll automatically write a Minidump file here to help diagnose
770+
// the nasty sorts of crashes that aren't 100% reproducible from a set of
771+
// inputs (or in the event that the user is unable or unwilling to provide a
772+
// reproducible case).
773+
if (!llvm::Process::AreCoreFilesPrevented()) {
774+
MINIDUMP_EXCEPTION_INFORMATION ExceptionInfo;
775+
ExceptionInfo.ThreadId = ::GetCurrentThreadId();
776+
ExceptionInfo.ExceptionPointers = ep;
777+
ExceptionInfo.ClientPointers = FALSE;
778+
779+
if (std::error_code EC = WriteWindowsDumpFile(&ExceptionInfo))
780+
llvm::errs() << "Could not write crash dump file: " << EC.message()
781+
<< "\n";
782+
}
783+
569784
// Initialize the STACKFRAME structure.
570785
STACKFRAME64 StackFrame = {};
571786

lib/Support/Windows/WindowsSupport.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,22 @@ struct CryptContextTraits : CommonHandleTraits {
167167
}
168168
};
169169

170+
struct RegTraits : CommonHandleTraits {
171+
typedef HKEY handle_type;
172+
173+
static handle_type GetInvalid() {
174+
return NULL;
175+
}
176+
177+
static void Close(handle_type h) {
178+
::RegCloseKey(h);
179+
}
180+
181+
static bool IsValid(handle_type h) {
182+
return h != GetInvalid();
183+
}
184+
};
185+
170186
struct FindHandleTraits : CommonHandleTraits {
171187
static void Close(handle_type h) {
172188
::FindClose(h);
@@ -178,6 +194,7 @@ struct FileHandleTraits : CommonHandleTraits {};
178194
typedef ScopedHandle<CommonHandleTraits> ScopedCommonHandle;
179195
typedef ScopedHandle<FileHandleTraits> ScopedFileHandle;
180196
typedef ScopedHandle<CryptContextTraits> ScopedCryptContext;
197+
typedef ScopedHandle<RegTraits> ScopedRegHandle;
181198
typedef ScopedHandle<FindHandleTraits> ScopedFindHandle;
182199
typedef ScopedHandle<JobHandleTraits> ScopedJobHandle;
183200

0 commit comments

Comments
 (0)