Index: clang/include/clang/Driver/Driver.h =================================================================== --- clang/include/clang/Driver/Driver.h +++ clang/include/clang/Driver/Driver.h @@ -204,6 +204,10 @@ /// Whether the driver is generating diagnostics for debugging purposes. unsigned CCGenDiagnostics : 1; + /// Callback to the CC1 tool, if available + typedef int(*CC1ToolFunc)(ArrayRef argv); + static LLVM_THREAD_LOCAL CC1ToolFunc CC1Main; + private: /// Raw target triple. std::string TargetTriple; Index: clang/include/clang/Driver/Job.h =================================================================== --- clang/include/clang/Driver/Job.h +++ clang/include/clang/Driver/Job.h @@ -130,6 +130,10 @@ /// Set whether to print the input filenames when executing. void setPrintInputFilenames(bool P) { PrintInputFilenames = P; } + + /// When set, we will try executing the CC1 tool in the current process, + /// instead of creating a new process. This is an optimization for speed. + static LLVM_THREAD_LOCAL bool ExecuteCC1Tool; }; /// Like Command, but with a fallback which is executed in case Index: clang/lib/Driver/Driver.cpp =================================================================== --- clang/lib/Driver/Driver.cpp +++ clang/lib/Driver/Driver.cpp @@ -91,6 +91,8 @@ using namespace clang; using namespace llvm::opt; +LLVM_THREAD_LOCAL Driver::CC1ToolFunc Driver::CC1Main; + // static std::string Driver::GetResourcesPath(StringRef BinaryPath, StringRef CustomResourceDir) { @@ -1333,6 +1335,11 @@ BuildJobs(C); + // Ensure we don't re-enter this process, and instead call a new clang.exe for + // rendering the preprocessed output. This is to minimize the chances of a + // secondary crash (in case this function was called due to an exception) + Command::ExecuteCC1Tool = false; + // If there were errors building the compilation, quit now. if (Trap.hasErrorOccurred()) { Diag(clang::diag::note_drv_command_failed_diag_msg) Index: clang/lib/Driver/Job.cpp =================================================================== --- clang/lib/Driver/Job.cpp +++ clang/lib/Driver/Job.cpp @@ -19,8 +19,10 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/CrashRecoveryContext.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 +315,8 @@ Environment.push_back(nullptr); } +LLVM_THREAD_LOCAL bool Command::ExecuteCC1Tool = true; + int Command::Execute(ArrayRef> Redirects, std::string *ErrMsg, bool *ExecutionFailed) const { if (PrintInputFilenames) { @@ -321,7 +325,7 @@ llvm::outs().flush(); } - SmallVector Argv; + SmallVector Argv; Optional> Env; std::vector ArgvVectorStorage; @@ -332,42 +336,79 @@ Env = makeArrayRef(ArgvVectorStorage); } - if (ResponseFile == nullptr) { + Driver::CC1ToolFunc CC1Main{}; + + // If we can re-use the same process, we reenter a variant of main() through a + // callback. This is an optimization for speed on Windows. + if (ExecuteCC1Tool) { + const Driver &D = getCreator().getToolChain().getDriver(); + StringRef CommandExe = llvm::sys::path::stem(Executable); + StringRef DriverExe = llvm::sys::path::stem(D.ClangExecutable); + if (CommandExe.equals_lower(DriverExe)) + CC1Main = Driver::CC1Main; + } + + if (ResponseFile == nullptr || CC1Main) { 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 (!CC1Main) + 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 (!CC1Main) + 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 (CC1Main) { + // This simply indicates that the program couldn't start, which isn't + // applicable here. 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 ExecuteCC1Main = [&]() { Ret = CC1Main(Argv); }; + + // Reenter a variant of main() instead of starting up a new process + if (!CRC.RunSafely(ExecuteCC1Main)) { + 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_, Index: clang/tools/driver/driver.cpp =================================================================== --- clang/tools/driver/driver.cpp +++ clang/tools/driver/driver.cpp @@ -30,6 +30,7 @@ #include "llvm/Option/OptTable.h" #include "llvm/Option/Option.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Host.h" @@ -303,7 +304,11 @@ TheDriver.setInstalledDir(InstalledPathParent); } -static int ExecuteCC1Tool(ArrayRef argv, StringRef Tool) { +int ExecuteCC1Tool(ArrayRef argv) { + // If we re-enter the cc1 tool from the driver process, we should cleanup the + // usage count for the driver options (which might be used in the cc1 tool) + llvm::cl::ResetAllOptionOccurrences(); + StringRef Tool = argv[1] + 4; void *GetExecutablePathVP = (void *)(intptr_t) GetExecutablePath; if (Tool == "") return cc1_main(argv.slice(2), argv[0], GetExecutablePathVP); @@ -327,6 +332,13 @@ return 1; llvm::InitializeAllTargets(); + + // When calling any clang driver other than -cc1, we optimize the + // clangDriver lib to call back into this function, instead of starting a new + // clang.exe. This saves a huge amount of time of Windows, as process creation + // can be expensive on that platform. + Driver::CC1Main = &ExecuteCC1Tool; + auto TargetAndMode = ToolChain::getTargetAndModeFromProgramName(argv[0]); llvm::BumpPtrAllocator A; @@ -379,7 +391,7 @@ auto newEnd = std::remove(argv.begin(), argv.end(), nullptr); argv.resize(newEnd - argv.begin()); } - return ExecuteCC1Tool(argv, argv[1] + 4); + return ExecuteCC1Tool(argv); } bool CanonicalPrefixes = true; @@ -503,7 +515,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; Index: llvm/include/llvm/Support/CrashRecoveryContext.h =================================================================== --- llvm/include/llvm/Support/CrashRecoveryContext.h +++ 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 coredump/minidump), except that here we recover the execution + /// flow after the call to RunSafely(). + bool EnableExceptionHandler{}; }; /// Abstract base class of cleanup handlers. Index: llvm/lib/Support/CrashRecoveryContext.cpp =================================================================== --- llvm/lib/Support/CrashRecoveryContext.cpp +++ llvm/lib/Support/CrashRecoveryContext.cpp @@ -13,8 +13,20 @@ #include "llvm/Support/ThreadLocal.h" #include #include +#ifdef _WIN32 +#include +#endif using namespace llvm; +#ifdef _WIN32 +// In llvm/lib/Support/Windows/Signals.inc +LONG WINAPI LLVMUnhandledExceptionFilter(LPEXCEPTION_POINTERS ep); +#endif +#ifdef LLVM_ON_UNIX +// In llvm/lib/Support/Unix/Signals.inc +void SignalHandler(int Sig); +#endif + namespace { struct CrashRecoveryContextImpl; @@ -54,7 +66,7 @@ #endif } - void HandleCrash() { + void HandleCrash(int RetCode, void *ExceptionInfo) { // Eliminate the current context entry, to avoid re-entering in case the // cleanup code crashes. CurrentContext->set(Next); @@ -62,7 +74,20 @@ assert(!Failed && "Crash recovery context already failed!"); Failed = true; - // FIXME: Stash the backtrace. + if (CRC->EnableExceptionHandler) { +#ifdef _WIN32 + LLVMUnhandledExceptionFilter((LPEXCEPTION_POINTERS)ExceptionInfo); +#endif +#ifdef LLVM_ON_UNIX + SignalHandler(RetCode); + // llvm/lib/Support/Unix/Program.inc:Wait() returns -2 if a crash occurs, + // not the actual error code. + if (WIFSIGNALED(RetCode)) + RetCode = -2; +#endif + } + + CRC->RetCode = RetCode; // Jump back to the RunSafely we were called under. longjmp(JumpBuffer, 1); @@ -171,19 +196,25 @@ static void installExceptionOrSignalHandlers() {} static void uninstallExceptionOrSignalHandlers() {} -bool CrashRecoveryContext::RunSafely(function_ref Fn) { - if (!gCrashRecoveryEnabled) { +static bool InvokeFunctionCall(function_ref Fn, bool ExceptionHandler, + int &RetCode) { + __try { Fn(); - return true; + } __except (ExceptionHandler + ? LLVMUnhandledExceptionFilter(GetExceptionInformation()) + : 1) { + RetCode = GetExceptionCode(); + return false; } + return true; +} - bool Result = true; - __try { +bool CrashRecoveryContext::RunSafely(function_ref Fn) { + if (!gCrashRecoveryEnabled) { Fn(); - } __except (1) { // Catch any exception. - Result = false; + return true; } - return Result; + return InvokeFunctionCall(Fn, EnableExceptionHandler, RetCode); } #else // !_MSC_VER @@ -237,7 +268,8 @@ // implementation if we so choose. // Handle the crash - const_cast(CRCI)->HandleCrash(); + const_cast(CRCI)->HandleCrash( + (int)ExceptionInfo->ExceptionRecord->ExceptionCode, ExceptionInfo); // Note that we don't actually get here because HandleCrash calls // longjmp, which means the HandleCrash function never returns. @@ -320,7 +352,7 @@ sigprocmask(SIG_UNBLOCK, &SigMask, nullptr); if (CRCI) - const_cast(CRCI)->HandleCrash(); + const_cast(CRCI)->HandleCrash(Signal, nullptr); } static void installExceptionOrSignalHandlers() { @@ -364,7 +396,7 @@ void CrashRecoveryContext::HandleCrash() { CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl; assert(CRCI && "Crash recovery context never initialized!"); - CRCI->HandleCrash(); + CRCI->HandleCrash(-1, nullptr); } // FIXME: Portability. Index: llvm/lib/Support/Unix/Signals.inc =================================================================== --- llvm/lib/Support/Unix/Signals.inc +++ llvm/lib/Support/Unix/Signals.inc @@ -79,7 +79,7 @@ using namespace llvm; -static RETSIGTYPE SignalHandler(int Sig); // defined below. +RETSIGTYPE SignalHandler(int Sig); // defined below. static RETSIGTYPE InfoSignalHandler(int Sig); // defined below. using SignalHandlerFunctionType = void (*)(); @@ -341,7 +341,7 @@ } // The signal handler that runs. -static RETSIGTYPE SignalHandler(int Sig) { +RETSIGTYPE SignalHandler(int Sig) { // Restore the signal behavior to default, so that the program actually // crashes when we return and the signal reissues. This also ensures that if // we crash in our signal handler that the program will terminate immediately @@ -356,8 +356,8 @@ { RemoveFilesToRemove(); - if (std::find(std::begin(IntSigs), std::end(IntSigs), Sig) - != std::end(IntSigs)) { + if (std::find(std::begin(IntSigs), std::end(IntSigs), Sig) != + std::end(IntSigs)) { if (auto OldInterruptFunction = InterruptFunction.exchange(nullptr)) return OldInterruptFunction(); @@ -365,9 +365,9 @@ if (Sig == SIGPIPE) exit(EX_IOERR); - raise(Sig); // Execute the default handler. + raise(Sig); // Execute the default handler. return; - } + } } // Otherwise if it is a fault (like SEGV) run any handler. Index: llvm/lib/Support/Windows/Signals.inc =================================================================== --- llvm/lib/Support/Windows/Signals.inc +++ 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); + LocalPrintStackTrace(llvm::errs(), ep ? ep->ContextRecord : nullptr); - _exit(ep->ExceptionRecord->ExceptionCode); + return EXCEPTION_EXECUTE_HANDLER; } static BOOL WINAPI LLVMConsoleCtrlHandler(DWORD dwCtrlType) {