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:
parent
053d07b664
commit
4f6955c715
@ -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();
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user