1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-22 18:54:02 +01:00

[Signal] Allow llvm clients to opt into one-shot SIGPIPE handling

Allow clients of the llvm library to opt-in to one-shot SIGPIPE
handling, instead of forcing them to undo llvm's SIGPIPE handler
registration (which is brittle).

The current behavior is preserved for all llvm-derived tools (except
lldb) by means of a default-`true` flag in the InitLLVM constructor.

This prevents "IO error" crashes in long-lived processes (lldb is the
motivating example) which both a) load llvm as a dynamic library and b)
*really* need to ignore SIGPIPE.

As llvm signal handlers can be installed when calling into libclang
(say, via RemoveFileOnSignal), thereby overriding a previous SIG_IGN for
SIGPIPE, there is no clean way to opt-out of "exit-on-SIGPIPE" in the
current model.

Differential Revision: https://reviews.llvm.org/D70277
This commit is contained in:
Vedant Kumar 2019-11-14 14:30:56 -08:00
parent 053d07b664
commit 4f6955c715
5 changed files with 61 additions and 9 deletions

View File

@ -17,7 +17,8 @@
// the following one-time initializations:
//
// 1. Setting up a signal handler so that pretty stack trace is printed out
// if a process crashes.
// if a process crashes. A signal handler that exits when a failed write to
// a pipe occurs may optionally be installed: this is on-by-default.
//
// 2. Set up the global new-handler which is called when a memory allocation
// attempt fails.
@ -32,9 +33,11 @@
namespace llvm {
class InitLLVM {
public:
InitLLVM(int &Argc, const char **&Argv);
InitLLVM(int &Argc, char **&Argv)
: InitLLVM(Argc, const_cast<const char **&>(Argv)) {}
InitLLVM(int &Argc, const char **&Argv,
bool InstallPipeSignalExitHandler = true);
InitLLVM(int &Argc, char **&Argv, bool InstallPipeSignalExitHandler = true)
: InitLLVM(Argc, const_cast<const char **&>(Argv),
InstallPipeSignalExitHandler) {}
~InitLLVM();

View File

@ -84,6 +84,28 @@ namespace sys {
/// function. Note also that the handler may be executed on a different
/// thread on some platforms.
void SetInfoSignalFunction(void (*Handler)());
/// Registers a function to be called in a "one-shot" manner when a pipe
/// signal is delivered to the process (i.e., on a failed write to a pipe).
/// After the pipe signal is handled once, the handler is unregistered.
///
/// The LLVM signal handling code will not install any handler for the pipe
/// signal unless one is provided with this API (see \ref
/// DefaultOneShotPipeSignalHandler). This handler must be provided before
/// any other LLVM signal handlers are installed: the \ref InitLLVM
/// constructor has a flag that can simplify this setup.
///
/// Note that the handler is not allowed to call any non-reentrant
/// functions. A null handler pointer disables the current installed
/// function. Note also that the handler may be executed on a
/// different thread on some platforms.
///
/// This is a no-op on Windows.
void SetOneShotPipeSignalFunction(void (*Handler)());
/// On Unix systems, this function exits with an "IO error" exit code.
/// This is a no-op on Windows.
void DefaultOneShotPipeSignalHandler();
} // End sys namespace
} // End llvm namespace

View File

@ -21,7 +21,11 @@
using namespace llvm;
using namespace llvm::sys;
InitLLVM::InitLLVM(int &Argc, const char **&Argv) : StackPrinter(Argc, Argv) {
InitLLVM::InitLLVM(int &Argc, const char **&Argv,
bool InstallPipeSignalExitHandler)
: StackPrinter(Argc, Argv) {
if (InstallPipeSignalExitHandler)
sys::SetOneShotPipeSignalFunction(sys::DefaultOneShotPipeSignalHandler);
sys::PrintStackTraceOnErrorSignal(Argv[0]);
install_out_of_memory_new_handler();

View File

@ -88,6 +88,9 @@ static std::atomic<SignalHandlerFunctionType> InterruptFunction =
ATOMIC_VAR_INIT(nullptr);
static std::atomic<SignalHandlerFunctionType> InfoSignalFunction =
ATOMIC_VAR_INIT(nullptr);
/// The function to call on SIGPIPE (one-time use only).
static std::atomic<SignalHandlerFunctionType> OneShotPipeSignalFunction =
ATOMIC_VAR_INIT(nullptr);
namespace {
/// Signal-safe removal of files.
@ -206,7 +209,7 @@ static StringRef Argv0;
/// if there is, it's not our direct responsibility. For whatever reason, our
/// continued execution is no longer desirable.
static const int IntSigs[] = {
SIGHUP, SIGINT, SIGPIPE, SIGTERM, SIGUSR2
SIGHUP, SIGINT, SIGTERM, SIGUSR2
};
/// Signals that represent that we have a bug, and our prompt termination has
@ -237,7 +240,7 @@ static const int InfoSigs[] = {
static const size_t NumSigs =
array_lengthof(IntSigs) + array_lengthof(KillSigs) +
array_lengthof(InfoSigs);
array_lengthof(InfoSigs) + 1 /* SIGPIPE */;
static std::atomic<unsigned> NumRegisteredSignals = ATOMIC_VAR_INIT(0);
@ -322,6 +325,8 @@ static void RegisterHandlers() { // Not signal-safe.
registerHandler(S, SignalKind::IsKill);
for (auto S : KillSigs)
registerHandler(S, SignalKind::IsKill);
if (OneShotPipeSignalFunction)
registerHandler(SIGPIPE, SignalKind::IsKill);
for (auto S : InfoSigs)
registerHandler(S, SignalKind::IsInfo);
}
@ -361,9 +366,10 @@ static RETSIGTYPE SignalHandler(int Sig) {
if (auto OldInterruptFunction = InterruptFunction.exchange(nullptr))
return OldInterruptFunction();
// Send a special return code that drivers can check for, from sysexits.h.
if (Sig == SIGPIPE)
exit(EX_IOERR);
if (auto OldOneShotPipeFunction =
OneShotPipeSignalFunction.exchange(nullptr))
return OldOneShotPipeFunction();
raise(Sig); // Execute the default handler.
return;
@ -403,6 +409,16 @@ void llvm::sys::SetInfoSignalFunction(void (*Handler)()) {
RegisterHandlers();
}
void llvm::sys::SetOneShotPipeSignalFunction(void (*Handler)()) {
OneShotPipeSignalFunction.exchange(Handler);
RegisterHandlers();
}
void llvm::sys::DefaultOneShotPipeSignalHandler() {
// Send a special return code that drivers can check for, from sysexits.h.
exit(EX_IOERR);
}
// The public API
bool llvm::sys::RemoveFileOnSignal(StringRef Filename,
std::string* ErrMsg) {

View File

@ -560,6 +560,13 @@ void llvm::sys::SetInfoSignalFunction(void (*Handler)()) {
// Unimplemented.
}
void llvm::sys::SetOneShotPipeSignalFunction(void (*Handler)()) {
// Unimplemented.
}
void llvm::sys::DefaultOneShotPipeSignalHandler() {
// 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