diff --git a/lld/Common/ErrorHandler.cpp b/lld/Common/ErrorHandler.cpp --- a/lld/Common/ErrorHandler.cpp +++ b/lld/Common/ErrorHandler.cpp @@ -47,14 +47,18 @@ } raw_ostream &lld::outs() { - if (errorHandler().disableOutput) - return llvm::nulls(); + if (errorHandler().disableOutput) { + static raw_string_ostream buf(errorHandler().bufferedStdout); + return buf; + } return stdoutOS ? *stdoutOS : llvm::outs(); } raw_ostream &lld::errs() { - if (errorHandler().disableOutput) - return llvm::nulls(); + if (errorHandler().disableOutput) { + static raw_string_ostream buf(errorHandler().bufferedStderr); + return buf; + } return stderrOS ? *stderrOS : llvm::errs(); } @@ -63,6 +67,18 @@ if (errorHandler().outputBuffer) errorHandler().outputBuffer->discard(); + // Emit stdout and stderr if they were hidden. This is only for testing. + if (errorHandler().disableOutput) { + errorHandler().disableOutput = false; + std::lock_guard lock(mu); + lld::outs() << errorHandler().bufferedStdout; + lld::errs() << errorHandler().bufferedStderr; + } + + // Re-throw a possible signal or exception once/if it was catched by + // safeLldMain(). + CrashRecoveryContext::ThrowIfCrash(val); + // Dealloc/destroy ManagedStatic variables before calling _exit(). // In an LTO build, allows us to get the output of -time-passes. // Ensures that the thread pool for the parallel algorithms is stopped to @@ -75,7 +91,10 @@ lld::outs().flush(); lld::errs().flush(); } - llvm::sys::Process::Exit(val); + // When running inside safeLldMain(), this call restores the control flow back + // to the CrashRecoveryContext. Otherwise we simply use _exit(), meanning no + // cleanup, since we want to avoid further crashes on shutdown. + llvm::sys::Process::Exit(val, /*NoCleanup=*/true); } void lld::diagnosticHandler(const DiagnosticInfo &di) { diff --git a/lld/include/lld/Common/Driver.h b/lld/include/lld/Common/Driver.h --- a/lld/include/lld/Common/Driver.h +++ b/lld/include/lld/Common/Driver.h @@ -21,7 +21,9 @@ // Generic entry point when using LLD as a library, safe for re-entry, supports // crash recovery. Returns a general completion code and a boolean telling // whether it can be called again. In some cases, a crash could corrupt memory -// and re-entry would not be possible anymore. +// and re-entry would not be possible anymore. Use exitLld() in that case to +// properly exit your application and avoid intermittent crashes on exit caused +// by cleanup. SafeReturn safeLldMain(int argc, const char **argv, llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS); diff --git a/lld/include/lld/Common/ErrorHandler.h b/lld/include/lld/Common/ErrorHandler.h --- a/lld/include/lld/Common/ErrorHandler.h +++ b/lld/include/lld/Common/ErrorHandler.h @@ -101,6 +101,12 @@ bool vsDiagnostics = false; bool disableOutput = false; std::function cleanupCallback; + // When the output is disabled, we cache stdout/stderr in these buffers + // instead, to emit later if necessary. One such example is if LLD_IN_TEST=2 + // and the first run fails with fatal() we still want to emit the output. This + // is purely for the lit testing. + std::string bufferedStdout; + std::string bufferedStderr; void error(const Twine &msg); LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &msg); diff --git a/lld/tools/lld/lld.cpp b/lld/tools/lld/lld.cpp --- a/lld/tools/lld/lld.cpp +++ b/lld/tools/lld/lld.cpp @@ -179,7 +179,7 @@ if (!crc.RunSafely([&]() { r = lldMain(argc, argv, stdoutOS, stderrOS, /*exitEarly=*/false); })) - r = crc.RetCode; + return {crc.RetCode, /*canRunAgain=*/false}; } // Cleanup memory and reset everything back in pristine condition. This path @@ -221,7 +221,7 @@ // Execute one iteration. auto r = safeLldMain(argc, argv, llvm::outs(), llvm::errs()); if (!r.canRunAgain) - _exit(r.ret); // Exit now, can't re-execute again. + exitLld(r.ret); // Exit now, can't re-execute again. if (!mainRet) { mainRet = r.ret; @@ -230,14 +230,5 @@ return r.ret; } } -#if LLVM_ON_UNIX - // Re-throw the signal so it can be caught by WIFSIGNALED in - // llvm/lib/Support/Unix/Program.inc. This is required to correctly handle - // usages of `not --crash`. - if (*mainRet > 128) { - llvm::sys::unregisterHandlers(); - raise(*mainRet - 128); - } -#endif return *mainRet; } 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 @@ -102,6 +102,9 @@ LLVM_ATTRIBUTE_NORETURN void HandleExit(int RetCode); + /// Throw again a signal or an exception, after it was catched once. + static bool ThrowIfCrash(int RetCode); + /// In case of a crash, this is the crash identifier. int RetCode = 0; diff --git a/llvm/include/llvm/Support/Process.h b/llvm/include/llvm/Support/Process.h --- a/llvm/include/llvm/Support/Process.h +++ b/llvm/include/llvm/Support/Process.h @@ -213,8 +213,9 @@ /// Equivalent to ::exit(), except when running inside a CrashRecoveryContext. /// In that case, the control flow will resume after RunSafely(), like for a /// crash, rather than exiting the current process. + /// Use \arg NoCleanup for calling _exit() instead of exit(). LLVM_ATTRIBUTE_NORETURN - static void Exit(int RetCode); + static void Exit(int RetCode, bool NoCleanup = false); }; } 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 @@ -442,6 +442,21 @@ llvm_unreachable("Most likely setjmp wasn't called!"); } +bool CrashRecoveryContext::ThrowIfCrash(int RetCode) { +#if defined(_WIN32) + unsigned Code = ((unsigned)RetCode & 0xF0000000) >> 28; + if (Code != 0xC && Code != 8) + return false; + ::RaiseException(RetCode, 0, 0, NULL); +#else + if (RetCode <= 128) + return false; + llvm::sys::unregisterHandlers(); + raise(RetCode - 128); +#endif + return true; +} + // FIXME: Portability. static void setThreadBackgroundPriority() { #ifdef __APPLE__ diff --git a/llvm/lib/Support/Process.cpp b/llvm/lib/Support/Process.cpp --- a/llvm/lib/Support/Process.cpp +++ b/llvm/lib/Support/Process.cpp @@ -20,6 +20,10 @@ #include "llvm/Support/Path.h" #include "llvm/Support/Program.h" +#if LLVM_ON_UNIX +#include // _exit +#endif + using namespace llvm; using namespace sys; @@ -91,10 +95,14 @@ bool Process::AreCoreFilesPrevented() { return coreFilesPrevented; } LLVM_ATTRIBUTE_NORETURN -void Process::Exit(int RetCode) { +void Process::Exit(int RetCode, bool NoCleanup) { if (CrashRecoveryContext *CRC = CrashRecoveryContext::GetCurrent()) CRC->HandleExit(RetCode); - ::exit(RetCode); + + if (NoCleanup) + _exit(RetCode); + else + ::exit(RetCode); } // Include the platform-specific parts of this class.