Index: lldb/tools/driver/Driver.cpp =================================================================== --- lldb/tools/driver/Driver.cpp +++ lldb/tools/driver/Driver.cpp @@ -853,6 +853,16 @@ signal(SIGCONT, sigcont_handler); #endif + // Occasionally, during test teardown, there is a write to a closed pipe in of + // LLDB's handful of IPC channels. Sometimes the communication is inherently + // unreliable, so LLDB tries to avoid being killed due to SIGPIPE. However, + // LLVM's default SIGPIPE behavior is to exit with IO_ERR. Opt out of that. + // + // We don't disable LLVM's signal handling entirely because we still want + // pretty stack traces, and file cleanup (for when, say, the clang embedded + // in LLDB leaves behind temporary objects). + llvm::sys::SetExitOnFailedPipeWrite(/*ExitOnFailedWrite=*/false); + int exit_code = 0; // Create a scope for driver so that the driver object will destroy itself // before SBDebugger::Terminate() is called. Index: llvm/include/llvm/Support/Signals.h =================================================================== --- llvm/include/llvm/Support/Signals.h +++ llvm/include/llvm/Support/Signals.h @@ -84,6 +84,12 @@ /// function. Note also that the handler may be executed on a different /// thread on some platforms. void SetInfoSignalFunction(void (*Handler)()); + + /// Control whether the process exits with a POSIX IO error in response to + /// a failed write to a closed pipe (i.e. a SIGPIPE). The default behavior is + /// to exit. This is only applicable on POSIX systems. + void SetExitOnFailedPipeWrite(bool ExitOnFailedWrite); + } // End sys namespace } // End llvm namespace Index: llvm/lib/Support/Unix/Signals.inc =================================================================== --- llvm/lib/Support/Unix/Signals.inc +++ llvm/lib/Support/Unix/Signals.inc @@ -88,6 +88,7 @@ ATOMIC_VAR_INIT(nullptr); static std::atomic InfoSignalFunction = ATOMIC_VAR_INIT(nullptr); +static std::atomic ExitOnSIGPIPE = ATOMIC_VAR_INIT(true); namespace { /// Signal-safe removal of files. @@ -362,7 +363,7 @@ return OldInterruptFunction(); // Send a special return code that drivers can check for, from sysexits.h. - if (Sig == SIGPIPE) + if (Sig == SIGPIPE && ExitOnSIGPIPE) exit(EX_IOERR); raise(Sig); // Execute the default handler. @@ -403,6 +404,10 @@ RegisterHandlers(); } +void llvm::sys::SetExitOnFailedPipeWrite(bool ExitOnFailedWrite) { + ExitOnSIGPIPE.store(ExitOnFailedWrite); +} + // The public API bool llvm::sys::RemoveFileOnSignal(StringRef Filename, std::string* ErrMsg) { Index: llvm/lib/Support/Windows/Signals.inc =================================================================== --- llvm/lib/Support/Windows/Signals.inc +++ llvm/lib/Support/Windows/Signals.inc @@ -560,6 +560,9 @@ // Unimplemented. } +void llvm::sys::SetExitOnFailedPipeWrite(bool) { + // Unimplemented. +} /// Add a function to be called when a signal is delivered to the process. The /// handler can have a cookie passed to it to identify what instance of the Index: llvm/unittests/Support/CMakeLists.txt =================================================================== --- llvm/unittests/Support/CMakeLists.txt +++ llvm/unittests/Support/CMakeLists.txt @@ -60,6 +60,7 @@ ScaledNumberTest.cpp SourceMgrTest.cpp SpecialCaseListTest.cpp + SignalsTest.cpp StringPool.cpp SwapByteOrderTest.cpp SymbolRemappingReaderTest.cpp Index: llvm/unittests/Support/SignalsTest.cpp =================================================================== --- /dev/null +++ llvm/unittests/Support/SignalsTest.cpp @@ -0,0 +1,65 @@ +//========- unittests/Support/SignalsTest.cpp - Signal handling test =========// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#if !defined(_WIN32) +#include +#include +#endif // !defined(_WIN32) + +#include "llvm/Support/Signals.h" + +#include "gtest/gtest.h" + +using namespace llvm; + +#if !defined(_WIN32) +void NoOpSigHandler(void *) {} + +TEST(SignalTest, EnableExitOnSIGPIPE) { + // Register default signal handlers. + sys::AddSignalHandler(NoOpSigHandler, nullptr); + + // Create unidirectional read/write pipes. + int fds[2]; + int err = pipe(fds); + if (err != 0) + return; // If we can't make pipes, this isn't testing anything. + + // Close the read pipe. + close(fds[0]); + + // Attempt to write to the write pipe. Expect exit(IOERR). + const void *buf = (const void *)&fds; + + // LLVM's default handler should catch SIGPIPE and exit with IOERR. + EXPECT_EXIT(write(fds[1], buf, 1), ::testing::ExitedWithCode(EX_IOERR), ""); +} + +TEST(SignalTest, DisableExitOnSIGPIPE) { + // Register default signal handlers. + sys::AddSignalHandler(NoOpSigHandler, nullptr); + + // Disable exit-on-SIGPIPE. + sys::SetExitOnFailedPipeWrite(false); + + // Create unidirectional read/write pipes. + int fds[2]; + int err = pipe(fds); + if (err != 0) + return; // If we can't make pipes, this isn't testing anything. + + // Close the read pipe. + close(fds[0]); + + // Attempt to write to the write pipe. + const void *buf = (const void *)&fds; + + // No handler should be installed: we expect kill-by-SIGPIPE. + EXPECT_EXIT(write(fds[1], buf, 1), ::testing::KilledBySignal(SIGPIPE), ""); +} +#endif // !defined(_WIN32)