Index: clang/include/clang/Driver/Driver.h =================================================================== --- clang/include/clang/Driver/Driver.h +++ clang/include/clang/Driver/Driver.h @@ -204,6 +204,13 @@ /// Whether the driver is generating diagnostics for debugging purposes. unsigned CCGenDiagnostics : 1; + /// Pointer to the ExecuteCC1Tool function, if available. + /// When the clangDriver lib is used through clang.exe, this provides a + /// shortcut for executing the -cc1 command-line directly, in the same + /// process. + typedef int (*CC1ToolFunc)(ArrayRef argv); + static CC1ToolFunc CC1Main; + private: /// Raw target triple. std::string TargetTriple; 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; +Driver::CC1ToolFunc Driver::CC1Main; + // static std::string Driver::GetResourcesPath(StringRef BinaryPath, StringRef CustomResourceDir) { 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 @@ -321,7 +323,7 @@ llvm::outs().flush(); } - SmallVector Argv; + SmallVector Argv; Optional> Env; std::vector ArgvVectorStorage; @@ -332,42 +334,80 @@ Env = makeArrayRef(ArgvVectorStorage); } - if (ResponseFile == nullptr) { + Driver::CC1ToolFunc CC1Main = nullptr; + + // If we are called by the clang tool, we can possibly invoke the -cc1 + // cmd-line in the the same process. This is an optimization for speed on + // Windows, it prevents creating too many child processes. + const Driver &D = getCreator().getToolChain().getDriver(); + if (!D.CCGenDiagnostics) { + 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 flag 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); }; + + // Enter ExecuteCC1Tool() 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,13 @@ TheDriver.setInstalledDir(InstalledPathParent); } -static int ExecuteCC1Tool(ArrayRef argv, StringRef Tool) { +static int ExecuteCC1Tool(ArrayRef argv) { + // If we call the cc1 tool from the clangDriver library (through + // Driver::CC1Main), we need to cleanup the options usage count. The options + // are currently global, and they might have been used previously by the + // driver. + 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 +334,12 @@ return 1; llvm::InitializeAllTargets(); + + // Here we provide a shortcut for calling the -cc1 cmd-line within the same + // process, instead of starting a new process. 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 +392,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 +516,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 = 0; + + /// 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 = false; }; /// Abstract base class of cleanup handlers. Index: llvm/include/llvm/Support/Signals.h =================================================================== --- llvm/include/llvm/Support/Signals.h +++ llvm/include/llvm/Support/Signals.h @@ -106,6 +106,14 @@ /// On Unix systems, this function exits with an "IO error" exit code. /// This is a no-op on Windows. void DefaultOneShotPipeSignalHandler(); + + /// Forcibly triggers the exception processing, but does not throw the + /// exception afterwards. Let the handler modify the RetCode to properly + /// handle diagnostics in the clang tool. + /// The return value is platform-specific: on Windows it indicates what action + /// the exception handler should take next; on Linux, the return value is + /// ignored and is always zero. + signed InvokeExceptionHandler(int &RetCode, void *ExceptionInfo); } // End sys namespace } // End llvm namespace Index: llvm/lib/Support/CrashRecoveryContext.cpp =================================================================== --- llvm/lib/Support/CrashRecoveryContext.cpp +++ llvm/lib/Support/CrashRecoveryContext.cpp @@ -10,9 +10,13 @@ #include "llvm/Config/llvm-config.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/Signals.h" #include "llvm/Support/ThreadLocal.h" #include #include +#ifdef _WIN32 +#include // for GetExceptionInformation +#endif using namespace llvm; namespace { @@ -54,7 +58,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 +66,11 @@ assert(!Failed && "Crash recovery context already failed!"); Failed = true; - // FIXME: Stash the backtrace. + if (CRC->EnableExceptionHandler) + sys::InvokeExceptionHandler(RetCode, ExceptionInfo); + + // Should come after InvokeExceptionHandler(), it could modify RetCode. + CRC->RetCode = RetCode; // Jump back to the RunSafely we were called under. longjmp(JumpBuffer, 1); @@ -171,19 +179,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 ? sys::InvokeExceptionHandler( + RetCode, 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 +251,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 +335,7 @@ sigprocmask(SIG_UNBLOCK, &SigMask, nullptr); if (CRCI) - const_cast(CRCI)->HandleCrash(); + const_cast(CRCI)->HandleCrash(Signal, nullptr); } 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, nullptr); } // FIXME: Portability. Index: llvm/lib/Support/Unix/Signals.inc =================================================================== --- llvm/lib/Support/Unix/Signals.inc +++ llvm/lib/Support/Unix/Signals.inc @@ -345,6 +345,17 @@ FileToRemoveList::removeAllFiles(FilesToRemove); } +signed sys::InvokeExceptionHandler(int &RetCode, void *) { + SignalHandler(RetCode); + // llvm/lib/Support/Unix/Program.inc:Wait() returns -2 if a crash occurs, + // not the actual error code. If we want to diagnose a crash in the same + // way as invoking/forking a new process (in + // clang/tools/driver/driver.cpp), we need to do this here. + if (WIFSIGNALED(RetCode)) + RetCode = -2; + return 0; +} + // The signal handler that runs. static RETSIGTYPE SignalHandler(int Sig) { // Restore the signal behavior to default, so that the program actually @@ -361,8 +372,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(); @@ -371,9 +382,9 @@ OneShotPipeSignalFunction.exchange(nullptr)) return OldOneShotPipeFunction(); - 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 @@ -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(); @@ -792,6 +798,10 @@ return std::error_code(); } +signed sys::InvokeExceptionHandler(int &, void *ExceptionInfo) { + return LLVMUnhandledExceptionFilter((LPEXCEPTION_POINTERS)ExceptionInfo); +} + static LONG WINAPI LLVMUnhandledExceptionFilter(LPEXCEPTION_POINTERS ep) { Cleanup(); @@ -810,42 +820,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) {