865 lines · plain
1//===- Win32/Signals.cpp - Win32 Signals Implementation ---------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file provides the Win32 specific implementation of the Signals class.10//11//===----------------------------------------------------------------------===//12#include "llvm/Config/llvm-config.h"13#include "llvm/Support/ConvertUTF.h"14#include "llvm/Support/ExitCodes.h"15#include "llvm/Support/FileSystem.h"16#include "llvm/Support/Path.h"17#include "llvm/Support/Process.h"18#include "llvm/Support/WindowsError.h"19#include <io.h>20#include <signal.h>21#include <stdio.h>22 23#include "llvm/Support/Format.h"24#include "llvm/Support/raw_ostream.h"25 26// The Windows.h header must be after LLVM and standard headers.27#include "llvm/Support/Windows/WindowsSupport.h"28 29#ifdef __MINGW32__30#include <imagehlp.h>31#else32#include <crtdbg.h>33#include <dbghelp.h>34#endif35#include <psapi.h>36 37#ifdef _MSC_VER38#pragma comment(lib, "psapi.lib")39#elif __MINGW32__40// The version of g++ that comes with MinGW does *not* properly understand41// the ll format specifier for printf. However, MinGW passes the format42// specifiers on to the MSVCRT entirely, and the CRT understands the ll43// specifier. So these warnings are spurious in this case. Since we compile44// with -Wall, this will generate these warnings which should be ignored. So45// we will turn off the warnings for this just file. However, MinGW also does46// not support push and pop for diagnostics, so we have to manually turn it47// back on at the end of the file.48#pragma GCC diagnostic ignored "-Wformat"49#pragma GCC diagnostic ignored "-Wformat-extra-args"50#endif // __MINGW32__51 52typedef BOOL(__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64)(53 HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize,54 LPDWORD lpNumberOfBytesRead);55 56typedef PVOID(__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64)(HANDLE ahProcess,57 DWORD64 AddrBase);58 59typedef DWORD64(__stdcall *PGET_MODULE_BASE_ROUTINE64)(HANDLE hProcess,60 DWORD64 Address);61 62typedef DWORD64(__stdcall *PTRANSLATE_ADDRESS_ROUTINE64)(HANDLE hProcess,63 HANDLE hThread,64 LPADDRESS64 lpaddr);65 66typedef BOOL(WINAPI *fpMiniDumpWriteDump)(HANDLE, DWORD, HANDLE, MINIDUMP_TYPE,67 PMINIDUMP_EXCEPTION_INFORMATION,68 PMINIDUMP_USER_STREAM_INFORMATION,69 PMINIDUMP_CALLBACK_INFORMATION);70static fpMiniDumpWriteDump fMiniDumpWriteDump;71 72typedef BOOL(WINAPI *fpStackWalk64)(DWORD, HANDLE, HANDLE, LPSTACKFRAME64,73 PVOID, PREAD_PROCESS_MEMORY_ROUTINE64,74 PFUNCTION_TABLE_ACCESS_ROUTINE64,75 PGET_MODULE_BASE_ROUTINE64,76 PTRANSLATE_ADDRESS_ROUTINE64);77static fpStackWalk64 fStackWalk64;78 79typedef DWORD64(WINAPI *fpSymGetModuleBase64)(HANDLE, DWORD64);80static fpSymGetModuleBase64 fSymGetModuleBase64;81 82typedef BOOL(WINAPI *fpSymGetSymFromAddr64)(HANDLE, DWORD64, PDWORD64,83 PIMAGEHLP_SYMBOL64);84static fpSymGetSymFromAddr64 fSymGetSymFromAddr64;85 86typedef BOOL(WINAPI *fpSymGetLineFromAddr64)(HANDLE, DWORD64, PDWORD,87 PIMAGEHLP_LINE64);88static fpSymGetLineFromAddr64 fSymGetLineFromAddr64;89 90typedef BOOL(WINAPI *fpSymGetModuleInfo64)(HANDLE hProcess, DWORD64 dwAddr,91 PIMAGEHLP_MODULE64 ModuleInfo);92static fpSymGetModuleInfo64 fSymGetModuleInfo64;93 94typedef PVOID(WINAPI *fpSymFunctionTableAccess64)(HANDLE, DWORD64);95static fpSymFunctionTableAccess64 fSymFunctionTableAccess64;96 97typedef DWORD(WINAPI *fpSymSetOptions)(DWORD);98static fpSymSetOptions fSymSetOptions;99 100typedef BOOL(WINAPI *fpSymInitialize)(HANDLE, PCSTR, BOOL);101static fpSymInitialize fSymInitialize;102 103typedef BOOL(WINAPI *fpEnumerateLoadedModules)(HANDLE,104 PENUMLOADED_MODULES_CALLBACK64,105 PVOID);106static fpEnumerateLoadedModules fEnumerateLoadedModules;107 108static bool isDebugHelpInitialized() {109 return fStackWalk64 && fSymInitialize && fSymSetOptions && fMiniDumpWriteDump;110}111 112static bool load64BitDebugHelp(void) {113 HMODULE hLib =114 ::LoadLibraryExA("Dbghelp.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);115 if (hLib) {116 fMiniDumpWriteDump = (fpMiniDumpWriteDump)(void *)::GetProcAddress(117 hLib, "MiniDumpWriteDump");118 fStackWalk64 = (fpStackWalk64)(void *)::GetProcAddress(hLib, "StackWalk64");119 fSymGetModuleBase64 = (fpSymGetModuleBase64)(void *)::GetProcAddress(120 hLib, "SymGetModuleBase64");121 fSymGetSymFromAddr64 = (fpSymGetSymFromAddr64)(void *)::GetProcAddress(122 hLib, "SymGetSymFromAddr64");123 fSymGetLineFromAddr64 = (fpSymGetLineFromAddr64)(void *)::GetProcAddress(124 hLib, "SymGetLineFromAddr64");125 fSymGetModuleInfo64 = (fpSymGetModuleInfo64)(void *)::GetProcAddress(126 hLib, "SymGetModuleInfo64");127 fSymFunctionTableAccess64 =128 (fpSymFunctionTableAccess64)(void *)::GetProcAddress(129 hLib, "SymFunctionTableAccess64");130 fSymSetOptions =131 (fpSymSetOptions)(void *)::GetProcAddress(hLib, "SymSetOptions");132 fSymInitialize =133 (fpSymInitialize)(void *)::GetProcAddress(hLib, "SymInitialize");134 fEnumerateLoadedModules =135 (fpEnumerateLoadedModules)(void *)::GetProcAddress(136 hLib, "EnumerateLoadedModules64");137 }138 return isDebugHelpInitialized();139}140 141using namespace llvm;142 143// Forward declare.144static LONG WINAPI LLVMUnhandledExceptionFilter(LPEXCEPTION_POINTERS ep);145static BOOL WINAPI LLVMConsoleCtrlHandler(DWORD dwCtrlType);146 147// The function to call if ctrl-c is pressed.148static void (*InterruptFunction)() = 0;149 150static std::vector<std::string> *FilesToRemove = NULL;151static bool RegisteredUnhandledExceptionFilter = false;152static bool CleanupExecuted = false;153static PTOP_LEVEL_EXCEPTION_FILTER OldFilter = NULL;154 155/// The function to call on "SIGPIPE" (one-time use only).156static std::atomic<void (*)()> OneShotPipeSignalFunction(nullptr);157 158// Windows creates a new thread to execute the console handler when an event159// (such as CTRL/C) occurs. This causes concurrency issues with the above160// globals which this critical section addresses.161static CRITICAL_SECTION CriticalSection;162static bool CriticalSectionInitialized = false;163 164static StringRef Argv0;165 166enum {167#if defined(_M_X64)168 NativeMachineType = IMAGE_FILE_MACHINE_AMD64169#elif defined(_M_ARM64)170 NativeMachineType = IMAGE_FILE_MACHINE_ARM64171#elif defined(_M_IX86)172 NativeMachineType = IMAGE_FILE_MACHINE_I386173#elif defined(_M_ARM)174 NativeMachineType = IMAGE_FILE_MACHINE_ARMNT175#else176 NativeMachineType = IMAGE_FILE_MACHINE_UNKNOWN177#endif178};179 180static bool printStackTraceWithLLVMSymbolizer(llvm::raw_ostream &OS,181 HANDLE hProcess, HANDLE hThread,182 STACKFRAME64 &StackFrameOrig,183 CONTEXT *ContextOrig) {184 // StackWalk64 modifies the incoming stack frame and context, so copy them.185 STACKFRAME64 StackFrame = StackFrameOrig;186 187 // Copy the register context so that we don't modify it while we unwind. We188 // could use InitializeContext + CopyContext, but that's only required to get189 // at AVX registers, which typically aren't needed by StackWalk64. Reduce the190 // flag set to indicate that there's less data.191 CONTEXT Context = *ContextOrig;192 Context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;193 194 static void *StackTrace[256];195 size_t Depth = 0;196 while (fStackWalk64(NativeMachineType, hProcess, hThread, &StackFrame,197 &Context, 0, fSymFunctionTableAccess64,198 fSymGetModuleBase64, 0)) {199 if (StackFrame.AddrFrame.Offset == 0)200 break;201 StackTrace[Depth++] = (void *)(uintptr_t)StackFrame.AddrPC.Offset;202 if (Depth >= std::size(StackTrace))203 break;204 }205 206 return printSymbolizedStackTrace(Argv0, &StackTrace[0], Depth, OS);207}208 209namespace {210struct FindModuleData {211 void **StackTrace;212 int Depth;213 const char **Modules;214 intptr_t *Offsets;215 StringSaver *StrPool;216};217} // namespace218 219static BOOL CALLBACK findModuleCallback(PCSTR ModuleName, DWORD64 ModuleBase,220 ULONG ModuleSize, void *VoidData) {221 FindModuleData *Data = (FindModuleData *)VoidData;222 intptr_t Beg = ModuleBase;223 intptr_t End = Beg + ModuleSize;224 for (int I = 0; I < Data->Depth; I++) {225 if (Data->Modules[I])226 continue;227 intptr_t Addr = (intptr_t)Data->StackTrace[I];228 if (Beg <= Addr && Addr < End) {229 Data->Modules[I] = Data->StrPool->save(ModuleName).data();230 Data->Offsets[I] = Addr - Beg;231 }232 }233 return TRUE;234}235 236static bool findModulesAndOffsets(void **StackTrace, int Depth,237 const char **Modules, intptr_t *Offsets,238 const char *MainExecutableName,239 StringSaver &StrPool) {240 if (!fEnumerateLoadedModules)241 return false;242 FindModuleData Data;243 Data.StackTrace = StackTrace;244 Data.Depth = Depth;245 Data.Modules = Modules;246 Data.Offsets = Offsets;247 Data.StrPool = &StrPool;248 fEnumerateLoadedModules(GetCurrentProcess(), findModuleCallback, &Data);249 return true;250}251 252static bool printMarkupContext(llvm::raw_ostream &OS,253 const char *MainExecutableName) {254 return false;255}256 257static void PrintStackTraceForThread(llvm::raw_ostream &OS, HANDLE hProcess,258 HANDLE hThread, STACKFRAME64 &StackFrame,259 CONTEXT *Context) {260 // It's possible that DbgHelp.dll hasn't been loaded yet (e.g. if this261 // function is called before the main program called `llvm::InitLLVM`).262 // In this case just return, not stacktrace will be printed.263 if (!isDebugHelpInitialized())264 return;265 266 // Initialize the symbol handler.267 fSymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES);268 fSymInitialize(hProcess, NULL, TRUE);269 270 // Try llvm-symbolizer first. llvm-symbolizer knows how to deal with both PDBs271 // and DWARF, so it should do a good job regardless of what debug info or272 // linker is in use.273 if (printStackTraceWithLLVMSymbolizer(OS, hProcess, hThread, StackFrame,274 Context)) {275 return;276 }277 278 while (true) {279 if (!fStackWalk64(NativeMachineType, hProcess, hThread, &StackFrame,280 Context, 0, fSymFunctionTableAccess64,281 fSymGetModuleBase64, 0)) {282 break;283 }284 285 if (StackFrame.AddrFrame.Offset == 0)286 break;287 288 using namespace llvm;289 // Print the PC in hexadecimal.290 DWORD64 PC = StackFrame.AddrPC.Offset;291#if defined(_M_X64) || defined(_M_ARM64)292 OS << format("0x%016llX", PC);293#elif defined(_M_IX86) || defined(_M_ARM)294 OS << format("0x%08lX", static_cast<DWORD>(PC));295#endif296 297 // Verify the PC belongs to a module in this process.298 if (!fSymGetModuleBase64(hProcess, PC)) {299 OS << " <unknown module>\n";300 continue;301 }302 303 IMAGEHLP_MODULE64 M;304 memset(&M, 0, sizeof(IMAGEHLP_MODULE64));305 M.SizeOfStruct = sizeof(IMAGEHLP_MODULE64);306 if (fSymGetModuleInfo64(hProcess, fSymGetModuleBase64(hProcess, PC), &M)) {307 DWORD64 const disp = PC - M.BaseOfImage;308 OS << format(", %s(0x%016llX) + 0x%llX byte(s)",309 static_cast<char *>(M.ImageName), M.BaseOfImage,310 static_cast<long long>(disp));311 } else {312 OS << ", <unknown module>";313 }314 315 // Print the symbol name.316 char buffer[512];317 IMAGEHLP_SYMBOL64 *symbol = reinterpret_cast<IMAGEHLP_SYMBOL64 *>(buffer);318 memset(symbol, 0, sizeof(IMAGEHLP_SYMBOL64));319 symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);320 symbol->MaxNameLength = 512 - sizeof(IMAGEHLP_SYMBOL64);321 322 DWORD64 dwDisp;323 if (!fSymGetSymFromAddr64(hProcess, PC, &dwDisp, symbol)) {324 OS << '\n';325 continue;326 }327 328 buffer[511] = 0;329 OS << format(", %s() + 0x%llX byte(s)", static_cast<char *>(symbol->Name),330 static_cast<long long>(dwDisp));331 332 // Print the source file and line number information.333 IMAGEHLP_LINE64 line = {};334 DWORD dwLineDisp;335 line.SizeOfStruct = sizeof(line);336 if (fSymGetLineFromAddr64(hProcess, PC, &dwLineDisp, &line)) {337 OS << format(", %s, line %lu + 0x%lX byte(s)", line.FileName,338 line.LineNumber, dwLineDisp);339 }340 341 OS << '\n';342 }343}344 345namespace llvm {346 347//===----------------------------------------------------------------------===//348//=== WARNING: Implementation here must contain only Win32 specific code349//=== and must not be UNIX code350//===----------------------------------------------------------------------===//351 352#ifdef _MSC_VER353/// Emulates hitting "retry" from an "abort, retry, ignore" CRT debug report354/// dialog. "retry" raises an exception which ultimately triggers our stack355/// dumper.356[[maybe_unused]] static int AvoidMessageBoxHook(int ReportType, char *Message,357 int *Return) {358 // Set *Return to the retry code for the return value of _CrtDbgReport:359 // http://msdn.microsoft.com/en-us/library/8hyw4sy7(v=vs.71).aspx360 // This may also trigger just-in-time debugging via DebugBreak().361 if (Return)362 *Return = 1;363 // Don't call _CrtDbgReport.364 return TRUE;365}366 367#endif368 369extern "C" void HandleAbort(int Sig) {370 if (Sig == SIGABRT) {371 LLVM_BUILTIN_TRAP;372 }373}374 375static void InitializeThreading() {376 if (CriticalSectionInitialized)377 return;378 379 // Now's the time to create the critical section. This is the first time380 // through here, and there's only one thread.381 InitializeCriticalSection(&CriticalSection);382 CriticalSectionInitialized = true;383}384 385static void RegisterHandler() {386 // If we cannot load up the APIs (which would be unexpected as they should387 // exist on every version of Windows we support), we will bail out since388 // there would be nothing to report.389 if (!load64BitDebugHelp()) {390 assert(false && "These APIs should always be available");391 return;392 }393 394 if (RegisteredUnhandledExceptionFilter) {395 EnterCriticalSection(&CriticalSection);396 return;397 }398 399 InitializeThreading();400 401 // Enter it immediately. Now if someone hits CTRL/C, the console handler402 // can't proceed until the globals are updated.403 EnterCriticalSection(&CriticalSection);404 405 RegisteredUnhandledExceptionFilter = true;406 OldFilter = SetUnhandledExceptionFilter(LLVMUnhandledExceptionFilter);407 SetConsoleCtrlHandler(LLVMConsoleCtrlHandler, TRUE);408 409 // IMPORTANT NOTE: Caller must call LeaveCriticalSection(&CriticalSection) or410 // else multi-threading problems will ensue.411}412 413// The public API414bool sys::RemoveFileOnSignal(StringRef Filename, std::string *ErrMsg) {415 RegisterHandler();416 417 if (CleanupExecuted) {418 if (ErrMsg)419 *ErrMsg = "Process terminating -- cannot register for removal";420 return true;421 }422 423 if (FilesToRemove == NULL) {424 FilesToRemove = new std::vector<std::string>;425 std::atexit([]() {426 delete FilesToRemove;427 FilesToRemove = NULL;428 });429 }430 431 FilesToRemove->push_back(std::string(Filename));432 433 LeaveCriticalSection(&CriticalSection);434 return false;435}436 437// The public API438void sys::DontRemoveFileOnSignal(StringRef Filename) {439 if (FilesToRemove == NULL)440 return;441 442 RegisterHandler();443 444 std::vector<std::string>::reverse_iterator I =445 find(reverse(*FilesToRemove), Filename);446 if (I != FilesToRemove->rend())447 FilesToRemove->erase(I.base() - 1);448 449 LeaveCriticalSection(&CriticalSection);450}451 452void sys::DisableSystemDialogsOnCrash() {453 // Crash to stack trace handler on abort.454 signal(SIGABRT, HandleAbort);455 456 // The following functions are not reliably accessible on MinGW.457#ifdef _MSC_VER458 // We're already handling writing a "something went wrong" message.459 _set_abort_behavior(0, _WRITE_ABORT_MSG);460 // Disable Dr. Watson.461 _set_abort_behavior(0, _CALL_REPORTFAULT);462 _CrtSetReportHook(AvoidMessageBoxHook);463#endif464 465 // Disable standard error dialog box.466 SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX |467 SEM_NOOPENFILEERRORBOX);468 _set_error_mode(_OUT_TO_STDERR);469}470 471/// When an error signal (such as SIGABRT or SIGSEGV) is delivered to the472/// process, print a stack trace and then exit.473void sys::PrintStackTraceOnErrorSignal(StringRef Argv0,474 bool DisableCrashReporting) {475 ::Argv0 = Argv0;476 477 if (DisableCrashReporting || getenv("LLVM_DISABLE_CRASH_REPORT"))478 Process::PreventCoreFiles();479 480 DisableSystemDialogsOnCrash();481 RegisterHandler();482 LeaveCriticalSection(&CriticalSection);483}484} // namespace llvm485 486#if LLVM_ENABLE_DEBUGLOC_TRACKING_ORIGIN487#error DebugLoc origin-tracking currently unimplemented for Windows.488#endif489 490static void LocalPrintStackTrace(raw_ostream &OS, PCONTEXT C) {491 STACKFRAME64 StackFrame{};492 CONTEXT Context{};493 if (!C) {494 ::RtlCaptureContext(&Context);495 C = &Context;496 }497#if defined(_M_X64)498 StackFrame.AddrPC.Offset = Context.Rip;499 StackFrame.AddrStack.Offset = Context.Rsp;500 StackFrame.AddrFrame.Offset = Context.Rbp;501#elif defined(_M_IX86)502 StackFrame.AddrPC.Offset = Context.Eip;503 StackFrame.AddrStack.Offset = Context.Esp;504 StackFrame.AddrFrame.Offset = Context.Ebp;505#elif defined(_M_ARM64)506 StackFrame.AddrPC.Offset = Context.Pc;507 StackFrame.AddrStack.Offset = Context.Sp;508 StackFrame.AddrFrame.Offset = Context.Fp;509#elif defined(_M_ARM)510 StackFrame.AddrPC.Offset = Context.Pc;511 StackFrame.AddrStack.Offset = Context.Sp;512 StackFrame.AddrFrame.Offset = Context.R11;513#endif514 StackFrame.AddrPC.Mode = AddrModeFlat;515 StackFrame.AddrStack.Mode = AddrModeFlat;516 StackFrame.AddrFrame.Mode = AddrModeFlat;517 PrintStackTraceForThread(OS, GetCurrentProcess(), GetCurrentThread(),518 StackFrame, C);519}520 521void llvm::sys::PrintStackTrace(raw_ostream &OS, int Depth) {522 // FIXME: Handle "Depth" parameter to print stack trace upto specified Depth523 LocalPrintStackTrace(OS, nullptr);524}525 526void llvm::sys::SetInterruptFunction(void (*IF)()) {527 RegisterHandler();528 InterruptFunction = IF;529 LeaveCriticalSection(&CriticalSection);530}531 532void llvm::sys::SetInfoSignalFunction(void (*Handler)()) {533 // Unimplemented.534}535 536void llvm::sys::SetOneShotPipeSignalFunction(void (*Handler)()) {537 OneShotPipeSignalFunction.exchange(Handler);538}539 540void llvm::sys::DefaultOneShotPipeSignalHandler() {541 llvm::sys::Process::Exit(EX_IOERR, /*NoCleanup=*/true);542}543 544void llvm::sys::CallOneShotPipeSignalHandler() {545 if (auto OldOneShotPipeFunction = OneShotPipeSignalFunction.exchange(nullptr))546 OldOneShotPipeFunction();547}548 549/// Add a function to be called when a signal is delivered to the process. The550/// handler can have a cookie passed to it to identify what instance of the551/// handler it is.552void llvm::sys::AddSignalHandler(sys::SignalHandlerCallback FnPtr,553 void *Cookie) {554 insertSignalHandler(FnPtr, Cookie);555 RegisterHandler();556 LeaveCriticalSection(&CriticalSection);557}558 559static void Cleanup(bool ExecuteSignalHandlers) {560 if (CleanupExecuted)561 return;562 563 EnterCriticalSection(&CriticalSection);564 565 // Prevent other thread from registering new files and directories for566 // removal, should we be executing because of the console handler callback.567 CleanupExecuted = true;568 569 // FIXME: open files cannot be deleted.570 if (FilesToRemove != NULL)571 while (!FilesToRemove->empty()) {572 llvm::sys::fs::remove(FilesToRemove->back());573 FilesToRemove->pop_back();574 }575 576 if (ExecuteSignalHandlers)577 llvm::sys::RunSignalHandlers();578 579 LeaveCriticalSection(&CriticalSection);580}581 582void llvm::sys::RunInterruptHandlers() {583 // The interrupt handler may be called from an interrupt, but it may also be584 // called manually (such as the case of report_fatal_error with no registered585 // error handler). We must ensure that the critical section is properly586 // initialized.587 InitializeThreading();588 Cleanup(true);589}590 591/// Find the Windows Registry Key for a given location.592///593/// \returns a valid HKEY if the location exists, else NULL.594static HKEY FindWERKey(const llvm::Twine &RegistryLocation) {595 HKEY Key;596 if (ERROR_SUCCESS != ::RegOpenKeyExA(HKEY_LOCAL_MACHINE,597 RegistryLocation.str().c_str(), 0,598 KEY_QUERY_VALUE | KEY_READ, &Key))599 return NULL;600 601 return Key;602}603 604/// Populate ResultDirectory with the value for "DumpFolder" for a given605/// Windows Registry key.606///607/// \returns true if a valid value for DumpFolder exists, false otherwise.608static bool GetDumpFolder(HKEY Key,609 llvm::SmallVectorImpl<char> &ResultDirectory) {610 using llvm::sys::windows::UTF16ToUTF8;611 612 if (!Key)613 return false;614 615 DWORD BufferLengthBytes = 0;616 617 if (ERROR_SUCCESS != ::RegGetValueW(Key, 0, L"DumpFolder", REG_EXPAND_SZ,618 NULL, NULL, &BufferLengthBytes))619 return false;620 621 SmallVector<wchar_t, MAX_PATH> Buffer(BufferLengthBytes);622 623 if (ERROR_SUCCESS != ::RegGetValueW(Key, 0, L"DumpFolder", REG_EXPAND_SZ,624 NULL, Buffer.data(), &BufferLengthBytes))625 return false;626 627 DWORD ExpandBufferSize = ::ExpandEnvironmentStringsW(Buffer.data(), NULL, 0);628 629 if (!ExpandBufferSize)630 return false;631 632 SmallVector<wchar_t, MAX_PATH> ExpandBuffer(ExpandBufferSize);633 634 if (ExpandBufferSize != ::ExpandEnvironmentStringsW(Buffer.data(),635 ExpandBuffer.data(),636 ExpandBufferSize))637 return false;638 639 if (UTF16ToUTF8(ExpandBuffer.data(), ExpandBufferSize - 1, ResultDirectory))640 return false;641 642 return true;643}644 645/// Populate ResultType with a valid MINIDUMP_TYPE based on the value of646/// "DumpType" for a given Windows Registry key.647///648/// According to649/// https://msdn.microsoft.com/en-us/library/windows/desktop/bb787181(v=vs.85).aspx650/// valid values for DumpType are:651/// * 0: Custom dump652/// * 1: Mini dump653/// * 2: Full dump654/// If "Custom dump" is specified then the "CustomDumpFlags" field is read655/// containing a bitwise combination of MINIDUMP_TYPE values.656///657/// \returns true if a valid value for ResultType can be set, false otherwise.658static bool GetDumpType(HKEY Key, MINIDUMP_TYPE &ResultType) {659 if (!Key)660 return false;661 662 DWORD DumpType;663 DWORD TypeSize = sizeof(DumpType);664 if (ERROR_SUCCESS != ::RegGetValueW(Key, NULL, L"DumpType", RRF_RT_REG_DWORD,665 NULL, &DumpType, &TypeSize))666 return false;667 668 switch (DumpType) {669 case 0: {670 DWORD Flags = 0;671 if (ERROR_SUCCESS != ::RegGetValueW(Key, NULL, L"CustomDumpFlags",672 RRF_RT_REG_DWORD, NULL, &Flags,673 &TypeSize))674 return false;675 676 ResultType = static_cast<MINIDUMP_TYPE>(Flags);677 break;678 }679 case 1:680 ResultType = MiniDumpNormal;681 break;682 case 2:683 ResultType = MiniDumpWithFullMemory;684 break;685 default:686 return false;687 }688 return true;689}690 691/// Write a Windows dump file containing process information that can be692/// used for post-mortem debugging.693///694/// \returns zero error code if a mini dump created, actual error code695/// otherwise.696static std::error_code WINAPI697WriteWindowsDumpFile(PMINIDUMP_EXCEPTION_INFORMATION ExceptionInfo) {698 struct ScopedCriticalSection {699 ScopedCriticalSection() { EnterCriticalSection(&CriticalSection); }700 ~ScopedCriticalSection() { LeaveCriticalSection(&CriticalSection); }701 } SCS;702 703 using namespace llvm;704 using namespace llvm::sys;705 706 std::string MainExecutableName = fs::getMainExecutable(nullptr, nullptr);707 StringRef ProgramName;708 709 if (MainExecutableName.empty()) {710 // If we can't get the executable filename,711 // things are in worse shape than we realize712 // and we should just bail out.713 return mapWindowsError(::GetLastError());714 }715 716 ProgramName = path::filename(MainExecutableName.c_str());717 718 // The Windows Registry location as specified at719 // https://msdn.microsoft.com/en-us/library/windows/desktop/bb787181%28v=vs.85%29.aspx720 // "Collecting User-Mode Dumps" that may optionally be set to collect crash721 // dumps in a specified location.722 StringRef LocalDumpsRegistryLocation =723 "SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\LocalDumps";724 725 // The key pointing to the Registry location that may contain global crash726 // dump settings. This will be NULL if the location can not be found.727 ScopedRegHandle DefaultLocalDumpsKey(FindWERKey(LocalDumpsRegistryLocation));728 729 // The key pointing to the Registry location that may contain730 // application-specific crash dump settings. This will be NULL if the731 // location can not be found.732 ScopedRegHandle AppSpecificKey(733 FindWERKey(Twine(LocalDumpsRegistryLocation) + "\\" + ProgramName));734 735 // Look to see if a dump type is specified in the registry; first with the736 // app-specific key and failing that with the global key. If none are found737 // default to a normal dump (GetDumpType will return false either if the key738 // is NULL or if there is no valid DumpType value at its location).739 MINIDUMP_TYPE DumpType;740 if (!GetDumpType(AppSpecificKey, DumpType))741 if (!GetDumpType(DefaultLocalDumpsKey, DumpType))742 DumpType = MiniDumpNormal;743 744 // Look to see if a dump location is specified on the command line. If not,745 // look to see if a dump location is specified in the registry; first with the746 // app-specific key and failing that with the global key. If none are found747 // we'll just create the dump file in the default temporary file location748 // (GetDumpFolder will return false either if the key is NULL or if there is749 // no valid DumpFolder value at its location).750 bool ExplicitDumpDirectorySet = true;751 SmallString<MAX_PATH> DumpDirectory(*CrashDiagnosticsDirectory);752 if (DumpDirectory.empty())753 if (!GetDumpFolder(AppSpecificKey, DumpDirectory))754 if (!GetDumpFolder(DefaultLocalDumpsKey, DumpDirectory))755 ExplicitDumpDirectorySet = false;756 757 int FD;758 SmallString<MAX_PATH> DumpPath;759 760 if (ExplicitDumpDirectorySet) {761 if (std::error_code EC = fs::create_directories(DumpDirectory))762 return EC;763 if (std::error_code EC = fs::createUniqueFile(764 Twine(DumpDirectory) + "\\" + ProgramName + ".%%%%%%.dmp", FD,765 DumpPath))766 return EC;767 } else if (std::error_code EC =768 fs::createTemporaryFile(ProgramName, "dmp", FD, DumpPath))769 return EC;770 771 // Our support functions return a file descriptor but Windows wants a handle.772 ScopedCommonHandle FileHandle(reinterpret_cast<HANDLE>(_get_osfhandle(FD)));773 774 if (!fMiniDumpWriteDump(::GetCurrentProcess(), ::GetCurrentProcessId(),775 FileHandle, DumpType, ExceptionInfo, NULL, NULL))776 return mapWindowsError(::GetLastError());777 778 llvm::errs() << "Wrote crash dump file \"" << DumpPath << "\"\n";779 return std::error_code();780}781 782void sys::CleanupOnSignal(uintptr_t Context) {783 LPEXCEPTION_POINTERS EP = (LPEXCEPTION_POINTERS)Context;784 // Broken pipe is not a crash.785 //786 // 0xE0000000 is combined with the return code in the exception raised in787 // CrashRecoveryContext::HandleExit().788 unsigned RetCode = EP->ExceptionRecord->ExceptionCode;789 if (RetCode == (0xE0000000 | EX_IOERR))790 return;791 LLVMUnhandledExceptionFilter(EP);792}793 794static LONG WINAPI LLVMUnhandledExceptionFilter(LPEXCEPTION_POINTERS ep) {795 Cleanup(true);796 797 // Write out the exception code.798 if (ep && ep->ExceptionRecord)799 llvm::errs() << format("Exception Code: 0x%08X",800 ep->ExceptionRecord->ExceptionCode)801 << "\n";802 803 // We'll automatically write a Minidump file here to help diagnose804 // the nasty sorts of crashes that aren't 100% reproducible from a set of805 // inputs (or in the event that the user is unable or unwilling to provide a806 // reproducible case).807 if (!llvm::sys::Process::AreCoreFilesPrevented()) {808 MINIDUMP_EXCEPTION_INFORMATION ExceptionInfo;809 ExceptionInfo.ThreadId = ::GetCurrentThreadId();810 ExceptionInfo.ExceptionPointers = ep;811 ExceptionInfo.ClientPointers = FALSE;812 813 if (std::error_code EC = WriteWindowsDumpFile(&ExceptionInfo))814 llvm::errs() << "Could not write crash dump file: " << EC.message()815 << "\n";816 }817 818 // Stack unwinding appears to modify the context. Copy it to preserve the819 // caller's context.820 CONTEXT ContextCopy;821 if (ep)822 memcpy(&ContextCopy, ep->ContextRecord, sizeof(ContextCopy));823 824 LocalPrintStackTrace(llvm::errs(), ep ? &ContextCopy : nullptr);825 826 return EXCEPTION_EXECUTE_HANDLER;827}828 829static BOOL WINAPI LLVMConsoleCtrlHandler(DWORD dwCtrlType) {830 // We are running in our very own thread, courtesy of Windows.831 EnterCriticalSection(&CriticalSection);832 // This function is only ever called when a CTRL-C or similar control signal833 // is fired. Killing a process in this way is normal, so don't trigger the834 // signal handlers.835 Cleanup(false);836 837 // If an interrupt function has been set, go and run one it; otherwise,838 // the process dies.839 void (*IF)() = InterruptFunction;840 InterruptFunction = 0; // Don't run it on another CTRL-C.841 842 if (IF) {843 // Note: if the interrupt function throws an exception, there is nothing844 // to catch it in this thread so it will kill the process.845 IF(); // Run it now.846 LeaveCriticalSection(&CriticalSection);847 return TRUE; // Don't kill the process.848 }849 850 // Allow normal processing to take place; i.e., the process dies.851 LeaveCriticalSection(&CriticalSection);852 return FALSE;853}854 855#if __MINGW32__856// We turned these warnings off for this file so that MinGW-g++ doesn't857// complain about the ll format specifiers used. Now we are turning the858// warnings back on. If MinGW starts to support diagnostic stacks, we can859// replace this with a pop.860#pragma GCC diagnostic warning "-Wformat"861#pragma GCC diagnostic warning "-Wformat-extra-args"862#endif863 864void sys::unregisterHandlers() {}865