[Lldb-commits] [lldb] 32ce14e - Disable exit-on-SIGPIPE in lldb
Vedant Kumar via lldb-commits
lldb-commits at lists.llvm.org
Fri Oct 18 14:04:26 PDT 2019
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)
More information about the lldb-commits
mailing list