diff --git a/clang/include/clang/Driver/Job.h b/clang/include/clang/Driver/Job.h --- a/clang/include/clang/Driver/Job.h +++ b/clang/include/clang/Driver/Job.h @@ -130,6 +130,11 @@ /// Set whether to print the input filenames when executing. void setPrintInputFilenames(bool P) { PrintInputFilenames = P; } + + /// When set, we will try re-entering this process for the CC1 command + /// execution, instead of creating a new process. This is an optimization for + /// speed. + static LLVM_THREAD_LOCAL bool ReenterTool; }; /// Like Command, but with a fallback which is executed in case diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -1333,6 +1333,10 @@ BuildJobs(C); + // Call a new clang.exe for rendering the preprocessed output, to minimize the + // chances of a crash (in case this function was called due to an exception) + Command::ReenterTool = false; + // If there were errors building the compilation, quit now. if (Trap.hasErrorOccurred()) { Diag(clang::diag::note_drv_command_failed_diag_msg) diff --git a/clang/lib/Driver/Job.cpp b/clang/lib/Driver/Job.cpp --- a/clang/lib/Driver/Job.cpp +++ b/clang/lib/Driver/Job.cpp @@ -19,8 +19,11 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/CrashRecoveryContext.h" +#include "llvm/Support/DynamicLibrary.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" +#include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Program.h" #include "llvm/Support/raw_ostream.h" #include @@ -313,6 +316,8 @@ Environment.push_back(nullptr); } +LLVM_THREAD_LOCAL bool Command::ReenterTool = true; + int Command::Execute(ArrayRef> Redirects, std::string *ErrMsg, bool *ExecutionFailed) const { if (PrintInputFilenames) { @@ -321,7 +326,7 @@ llvm::outs().flush(); } - SmallVector Argv; + SmallVector Argv; Optional> Env; std::vector ArgvVectorStorage; @@ -332,42 +337,81 @@ Env = makeArrayRef(ArgvVectorStorage); } + typedef int (*ClangDriverMainFunc)(SmallVectorImpl &); + ClangDriverMainFunc ClangDriverMain = nullptr; + + if (ReenterTool) { + StringRef F = llvm::sys::path::filename(Executable); + if (F.endswith_lower(".exe")) + F = F.drop_back(4); + for (auto &A : {"clang", "clang++", "clang-cl", "clang-cpp"}) { + if (F.equals_lower(A)) { + ClangDriverMain = (ClangDriverMainFunc)(intptr_t) + llvm::sys::DynamicLibrary::SearchForAddressOfSymbol( + "ClangDriverMain"); + break; + } + } + } + if (ResponseFile == nullptr) { Argv.push_back(Executable); Argv.append(Arguments.begin(), Arguments.end()); - Argv.push_back(nullptr); - - auto Args = llvm::toStringRefArray(Argv.data()); - return llvm::sys::ExecuteAndWait( - Executable, Args, Env, Redirects, /*secondsToWait*/ 0, - /*memoryLimit*/ 0, ErrMsg, ExecutionFailed); + if (!ClangDriverMain) + Argv.push_back(nullptr); + } else { + // If the command is too large, we need to put arguments in a response file. + std::string RespContents; + llvm::raw_string_ostream SS(RespContents); + + // Write file contents and build the Argv vector + writeResponseFile(SS); + buildArgvForResponseFile(Argv); + if (!ClangDriverMain) + Argv.push_back(nullptr); + SS.flush(); + + // Save the response file in the appropriate encoding + if (std::error_code EC = writeFileWithEncoding( + ResponseFile, RespContents, Creator.getResponseFileEncoding())) { + if (ErrMsg) + *ErrMsg = EC.message(); + if (ExecutionFailed) + *ExecutionFailed = true; + return -1; + } } - // We need to put arguments in a response file (command is too large) - // Open stream to store the response file contents - std::string RespContents; - llvm::raw_string_ostream SS(RespContents); - - // Write file contents and build the Argv vector - writeResponseFile(SS); - buildArgvForResponseFile(Argv); - Argv.push_back(nullptr); - SS.flush(); - - // Save the response file in the appropriate encoding - if (std::error_code EC = writeFileWithEncoding( - ResponseFile, RespContents, Creator.getResponseFileEncoding())) { - if (ErrMsg) - *ErrMsg = EC.message(); + if (ClangDriverMain) { if (ExecutionFailed) - *ExecutionFailed = true; - return -1; - } + *ExecutionFailed = false; - auto Args = llvm::toStringRefArray(Argv.data()); - return llvm::sys::ExecuteAndWait(Executable, Args, Env, Redirects, - /*secondsToWait*/ 0, - /*memoryLimit*/ 0, ErrMsg, ExecutionFailed); + static bool CRCEnabled{}; + if (!CRCEnabled) { + llvm::CrashRecoveryContext::Enable(); + CRCEnabled = true; + } + + llvm::CrashRecoveryContext CRC; + CRC.EnableExceptionHandler = true; + + const void *PrettyState = llvm::SavePrettyStackState(); + + int Ret = 0; + auto ExecuteClangMain = [&]() { Ret = ClangDriverMain(Argv); }; + + if (!CRC.RunSafely(ExecuteClangMain)) { + llvm::RestorePrettyStackState(PrettyState); + return CRC.RetCode; + } + return Ret; + } else { + auto Args = llvm::toStringRefArray(Argv.data()); + return llvm::sys::ExecuteAndWait(Executable, Args, Env, Redirects, + /*secondsToWait*/ 0, + /*memoryLimit*/ 0, ErrMsg, + ExecutionFailed); + } } FallbackCommand::FallbackCommand(const Action &Source_, const Tool &Creator_, diff --git a/clang/tools/driver/driver.cpp b/clang/tools/driver/driver.cpp --- a/clang/tools/driver/driver.cpp +++ b/clang/tools/driver/driver.cpp @@ -30,6 +30,8 @@ #include "llvm/Option/OptTable.h" #include "llvm/Option/Option.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/CrashRecoveryContext.h" +#include "llvm/Support/DynamicLibrary.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Host.h" @@ -318,6 +320,8 @@ return 1; } +int ClangDriverMain(SmallVectorImpl &argv); + int main(int argc_, const char **argv_) { noteBottomOfStack(); llvm::InitLLVM X(argc_, argv_); @@ -327,6 +331,19 @@ return 1; llvm::InitializeAllTargets(); + return ClangDriverMain(argv); +} + +int ClangDriverMain(SmallVectorImpl &argv) { + static LLVM_THREAD_LOCAL bool EnterPE = true; + if (EnterPE) { + llvm::sys::DynamicLibrary::AddSymbol("ClangDriverMain", + (void *)(intptr_t)&ClangDriverMain); + EnterPE = false; + } else { + llvm::cl::ResetAllOptionOccurrences(); + } + auto TargetAndMode = ToolChain::getTargetAndModeFromProgramName(argv[0]); llvm::BumpPtrAllocator A; @@ -503,7 +520,7 @@ #ifdef _WIN32 // Exit status should not be negative on Win32, unless abnormal termination. - // Once abnormal termiation was caught, negative status should not be + // Once abnormal termination was caught, negative status should not be // propagated. if (Res < 0) Res = 1; diff --git a/llvm/include/llvm/Support/CrashRecoveryContext.h b/llvm/include/llvm/Support/CrashRecoveryContext.h --- a/llvm/include/llvm/Support/CrashRecoveryContext.h +++ b/llvm/include/llvm/Support/CrashRecoveryContext.h @@ -100,6 +100,15 @@ /// Explicitly trigger a crash recovery in the current process, and /// return failure from RunSafely(). This function does not return. void HandleCrash(); + + /// In case of a crash, this is the crash identifier + int RetCode{}; + + /// Selects whether the global exception handler should be called. When this + /// is active, a crash has the same side-effect as uncatched code (callstack + /// print and minidump), except that here we recover the execution flow after + /// the call to RunSafely(). + bool EnableExceptionHandler{}; }; /// Abstract base class of cleanup handlers. diff --git a/llvm/lib/Support/CrashRecoveryContext.cpp b/llvm/lib/Support/CrashRecoveryContext.cpp --- a/llvm/lib/Support/CrashRecoveryContext.cpp +++ b/llvm/lib/Support/CrashRecoveryContext.cpp @@ -13,6 +13,9 @@ #include "llvm/Support/ThreadLocal.h" #include #include +#if defined(_WIN32) +#include +#endif using namespace llvm; namespace { @@ -54,7 +57,7 @@ #endif } - void HandleCrash() { + void HandleCrash(int retCode) { // Eliminate the current context entry, to avoid re-entering in case the // cleanup code crashes. CurrentContext->set(Next); @@ -62,6 +65,8 @@ assert(!Failed && "Crash recovery context already failed!"); Failed = true; + CRC->RetCode = retCode; + // FIXME: Stash the backtrace. // Jump back to the RunSafely we were called under. @@ -171,6 +176,9 @@ static void installExceptionOrSignalHandlers() {} static void uninstallExceptionOrSignalHandlers() {} +// In Signals.inc +LONG WINAPI LLVMUnhandledExceptionFilter(LPEXCEPTION_POINTERS ep); + bool CrashRecoveryContext::RunSafely(function_ref Fn) { if (!gCrashRecoveryEnabled) { Fn(); @@ -178,9 +186,15 @@ } bool Result = true; + // FIXME error: cannot compile this 'this' captured by SEH yet + CrashRecoveryContext *This = this; __try { Fn(); - } __except (1) { // Catch any exception. + } __except (This->EnableExceptionHandler + ? LLVMUnhandledExceptionFilter(GetExceptionInformation()) + : 1) { + // assert(false); + This->RetCode = GetExceptionCode(); Result = false; } return Result; @@ -237,7 +251,8 @@ // implementation if we so choose. // Handle the crash - const_cast(CRCI)->HandleCrash(); + const_cast(CRCI)->HandleCrash( + (int)ExceptionInfo->ExceptionRecord->ExceptionCode); // Note that we don't actually get here because HandleCrash calls // longjmp, which means the HandleCrash function never returns. @@ -320,7 +335,7 @@ sigprocmask(SIG_UNBLOCK, &SigMask, nullptr); if (CRCI) - const_cast(CRCI)->HandleCrash(); + const_cast(CRCI)->HandleCrash(Signal); } static void installExceptionOrSignalHandlers() { @@ -364,7 +379,7 @@ void CrashRecoveryContext::HandleCrash() { CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl; assert(CRCI && "Crash recovery context never initialized!"); - CRCI->HandleCrash(); + CRCI->HandleCrash(-1); } // FIXME: Portability. diff --git a/llvm/lib/Support/Windows/Signals.inc b/llvm/lib/Support/Windows/Signals.inc --- a/llvm/lib/Support/Windows/Signals.inc +++ b/llvm/lib/Support/Windows/Signals.inc @@ -187,7 +187,7 @@ using namespace llvm; // Forward declare. -static LONG WINAPI LLVMUnhandledExceptionFilter(LPEXCEPTION_POINTERS ep); +LONG WINAPI LLVMUnhandledExceptionFilter(LPEXCEPTION_POINTERS ep); static BOOL WINAPI LLVMConsoleCtrlHandler(DWORD dwCtrlType); // The function to call if ctrl-c is pressed. @@ -521,10 +521,13 @@ extern "C" VOID WINAPI RtlCaptureContext(PCONTEXT ContextRecord); #endif -void llvm::sys::PrintStackTrace(raw_ostream &OS) { - STACKFRAME64 StackFrame = {}; - CONTEXT Context = {}; - ::RtlCaptureContext(&Context); +static void LocalPrintStackTrace(raw_ostream &OS, PCONTEXT C) { + STACKFRAME64 StackFrame{}; + CONTEXT Context{}; + if (!C) { + ::RtlCaptureContext(&Context); + C = &Context; + } #if defined(_M_X64) StackFrame.AddrPC.Offset = Context.Rip; StackFrame.AddrStack.Offset = Context.Rsp; @@ -546,9 +549,12 @@ StackFrame.AddrStack.Mode = AddrModeFlat; StackFrame.AddrFrame.Mode = AddrModeFlat; PrintStackTraceForThread(OS, GetCurrentProcess(), GetCurrentThread(), - StackFrame, &Context); + StackFrame, C); } +void llvm::sys::PrintStackTrace(raw_ostream &OS) { + LocalPrintStackTrace(OS, nullptr); +} void llvm::sys::SetInterruptFunction(void (*IF)()) { RegisterHandler(); @@ -785,7 +791,7 @@ return std::error_code(); } -static LONG WINAPI LLVMUnhandledExceptionFilter(LPEXCEPTION_POINTERS ep) { +LONG WINAPI LLVMUnhandledExceptionFilter(LPEXCEPTION_POINTERS ep) { Cleanup(); // We'll automatically write a Minidump file here to help diagnose @@ -803,42 +809,9 @@ << "\n"; } - // Initialize the STACKFRAME structure. - STACKFRAME64 StackFrame = {}; - -#if defined(_M_X64) - StackFrame.AddrPC.Offset = ep->ContextRecord->Rip; - StackFrame.AddrPC.Mode = AddrModeFlat; - StackFrame.AddrStack.Offset = ep->ContextRecord->Rsp; - StackFrame.AddrStack.Mode = AddrModeFlat; - StackFrame.AddrFrame.Offset = ep->ContextRecord->Rbp; - StackFrame.AddrFrame.Mode = AddrModeFlat; -#elif defined(_M_IX86) - StackFrame.AddrPC.Offset = ep->ContextRecord->Eip; - StackFrame.AddrPC.Mode = AddrModeFlat; - StackFrame.AddrStack.Offset = ep->ContextRecord->Esp; - StackFrame.AddrStack.Mode = AddrModeFlat; - StackFrame.AddrFrame.Offset = ep->ContextRecord->Ebp; - StackFrame.AddrFrame.Mode = AddrModeFlat; -#elif defined(_M_ARM64) || defined(_M_ARM) - StackFrame.AddrPC.Offset = ep->ContextRecord->Pc; - StackFrame.AddrPC.Mode = AddrModeFlat; - StackFrame.AddrStack.Offset = ep->ContextRecord->Sp; - StackFrame.AddrStack.Mode = AddrModeFlat; -#if defined(_M_ARM64) - StackFrame.AddrFrame.Offset = ep->ContextRecord->Fp; -#else - StackFrame.AddrFrame.Offset = ep->ContextRecord->R11; -#endif - StackFrame.AddrFrame.Mode = AddrModeFlat; -#endif - - HANDLE hProcess = GetCurrentProcess(); - HANDLE hThread = GetCurrentThread(); - PrintStackTraceForThread(llvm::errs(), hProcess, hThread, StackFrame, - ep->ContextRecord); - - _exit(ep->ExceptionRecord->ExceptionCode); + LocalPrintStackTrace(llvm::errs(), ep->ContextRecord); + + return EXCEPTION_EXECUTE_HANDLER; } static BOOL WINAPI LLVMConsoleCtrlHandler(DWORD dwCtrlType) {