Author: Vedant Kumar Date: 2019-10-18T21:05:30Z New Revision: 32ce14e55e5a99dd99c3b4fd4bd0ccaaf2948c30 URL: https://github.com/llvm/llvm-project/commit/32ce14e55e5a99dd99c3b4fd4bd0ccaaf2948c30 DIFF: https://github.com/llvm/llvm-project/commit/32ce14e55e5a99dd99c3b4fd4bd0ccaaf2948c30.diff LOG: Disable exit-on-SIGPIPE in lldb
Occasionally, during test teardown, LLDB writes to a closed pipe. Sometimes the communication is inherently unreliable, so LLDB tries to avoid being killed due to SIGPIPE (it calls `signal(SIGPIPE, SIG_IGN)`). However, LLVM's default SIGPIPE behavior overrides LLDB's, causing it to exit with IO_ERR. Opt LLDB out of the default SIGPIPE behavior. I expect that this will resolve some LLDB test suite flakiness (tests randomly failing with IO_ERR) that we've seen since r344372. rdar://55750240 Differential Revision: https://reviews.llvm.org/D69148 llvm-svn: 375288 Added: llvm/unittests/Support/SignalsTest.cpp Modified: lldb/tools/driver/Driver.cpp llvm/include/llvm/Support/Signals.h llvm/lib/Support/Unix/Signals.inc llvm/lib/Support/Windows/Signals.inc llvm/unittests/Support/CMakeLists.txt Removed: ################################################################################ diff --git a/lldb/tools/driver/Driver.cpp b/lldb/tools/driver/Driver.cpp index 4a403a7ffb46..6ab2bd93659f 100644 --- a/lldb/tools/driver/Driver.cpp +++ b/lldb/tools/driver/Driver.cpp @@ -853,6 +853,16 @@ int main(int argc, char const *argv[]) signal(SIGCONT, sigcont_handler); #endif + // Occasionally, during test teardown, LLDB writes to a closed pipe. + // 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 LLDB 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::SetPipeSignalFunction(nullptr); + int exit_code = 0; // Create a scope for driver so that the driver object will destroy itself // before SBDebugger::Terminate() is called. diff --git a/llvm/include/llvm/Support/Signals.h b/llvm/include/llvm/Support/Signals.h index a6b215a24311..a4f1fad22dd5 100644 --- a/llvm/include/llvm/Support/Signals.h +++ b/llvm/include/llvm/Support/Signals.h @@ -84,6 +84,17 @@ namespace sys { /// function. Note also that the handler may be executed on a diff erent /// thread on some platforms. void SetInfoSignalFunction(void (*Handler)()); + + /// Registers a function to be called when a "pipe" signal is delivered to + /// the process. + /// + /// The "pipe" signal typically indicates a failed write to a pipe (SIGPIPE). + /// The default installed handler calls `exit(EX_IOERR)`, causing the process + /// to immediately exit with an IO error exit code. + /// + /// This function is only applicable on POSIX systems. + void SetPipeSignalFunction(void (*Handler)()); + } // End sys namespace } // End llvm namespace diff --git a/llvm/lib/Support/Unix/Signals.inc b/llvm/lib/Support/Unix/Signals.inc index be05eabfb2e2..5e0cde4a81ed 100644 --- a/llvm/lib/Support/Unix/Signals.inc +++ b/llvm/lib/Support/Unix/Signals.inc @@ -82,12 +82,18 @@ using namespace llvm; static RETSIGTYPE SignalHandler(int Sig); // defined below. static RETSIGTYPE InfoSignalHandler(int Sig); // defined below. +static void DefaultPipeSignalFunction() { + exit(EX_IOERR); +} + using SignalHandlerFunctionType = void (*)(); /// The function to call if ctrl-c is pressed. static std::atomic<SignalHandlerFunctionType> InterruptFunction = ATOMIC_VAR_INIT(nullptr); static std::atomic<SignalHandlerFunctionType> InfoSignalFunction = ATOMIC_VAR_INIT(nullptr); +static std::atomic<SignalHandlerFunctionType> PipeSignalFunction = + ATOMIC_VAR_INIT(DefaultPipeSignalFunction); namespace { /// Signal-safe removal of files. @@ -363,7 +369,8 @@ static RETSIGTYPE SignalHandler(int Sig) { // Send a special return code that drivers can check for, from sysexits.h. if (Sig == SIGPIPE) - exit(EX_IOERR); + if (SignalHandlerFunctionType CurrentPipeFunction = PipeSignalFunction) + CurrentPipeFunction(); raise(Sig); // Execute the default handler. return; @@ -403,6 +410,11 @@ void llvm::sys::SetInfoSignalFunction(void (*Handler)()) { RegisterHandlers(); } +void llvm::sys::SetPipeSignalFunction(void (*Handler)()) { + PipeSignalFunction.exchange(Handler); + RegisterHandlers(); +} + // The public API bool llvm::sys::RemoveFileOnSignal(StringRef Filename, std::string* ErrMsg) { diff --git a/llvm/lib/Support/Windows/Signals.inc b/llvm/lib/Support/Windows/Signals.inc index 6a820ef22b1e..d962daf79348 100644 --- a/llvm/lib/Support/Windows/Signals.inc +++ b/llvm/lib/Support/Windows/Signals.inc @@ -560,6 +560,9 @@ void llvm::sys::SetInfoSignalFunction(void (*Handler)()) { // Unimplemented. } +void llvm::sys::SetPipeSignalFunction(void (*Handler)()) { + // 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 diff --git a/llvm/unittests/Support/CMakeLists.txt b/llvm/unittests/Support/CMakeLists.txt index 161891517cf3..385142278e48 100644 --- a/llvm/unittests/Support/CMakeLists.txt +++ b/llvm/unittests/Support/CMakeLists.txt @@ -58,6 +58,7 @@ add_llvm_unittest(SupportTests ReverseIterationTest.cpp ReplaceFileTest.cpp ScaledNumberTest.cpp + SignalsTest.cpp SourceMgrTest.cpp SpecialCaseListTest.cpp StringPool.cpp diff --git a/llvm/unittests/Support/SignalsTest.cpp b/llvm/unittests/Support/SignalsTest.cpp new file mode 100644 index 000000000000..6dfa4bf996de --- /dev/null +++ b/llvm/unittests/Support/SignalsTest.cpp @@ -0,0 +1,53 @@ +//========- 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 <unistd.h> +#include <sysexits.h> +#endif // !defined(_WIN32) + +#include "llvm/Support/Signals.h" + +#include "gtest/gtest.h" + +using namespace llvm; + +#if !defined(_WIN32) +TEST(SignalTest, IgnoreMultipleSIGPIPEs) { + // Ignore SIGPIPE. + signal(SIGPIPE, SIG_IGN); + + // Disable exit-on-SIGPIPE. + sys::SetPipeSignalFunction(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. Currently we're asserting that the + // write fails, which isn't great. + // + // What we really want is a death test that checks that this block exits + // with a special exit "success" code, as opposed to unexpectedly exiting due + // to a kill-by-SIGNAL or due to the default SIGPIPE handler. + // + // Unfortunately llvm's unit tests aren't set up to support death tests well. + // For one, death tests are flaky in a multithreaded context. And sigactions + // inherited from llvm-lit interfere with what's being tested. + const void *buf = (const void *)&fds; + err = write(fds[1], buf, 1); + ASSERT_EQ(err, -1); + err = write(fds[1], buf, 1); + ASSERT_EQ(err, -1); +} +#endif // !defined(_WIN32) _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits