Index: include/llvm/Support/Signals.h =================================================================== --- include/llvm/Support/Signals.h +++ include/llvm/Support/Signals.h @@ -66,13 +66,25 @@ /// This function registers a function to be called when the user "interrupts" /// the program (typically by pressing ctrl-c). When the user interrupts the /// program, the specified interrupt function is called instead of the program - /// being killed, and the interrupt function automatically disabled. Note - /// that interrupt functions are not allowed to call any non-reentrant + /// being killed, and the interrupt function automatically disabled. + /// + /// Note that interrupt functions are not allowed to call any non-reentrant /// functions. An null interrupt function pointer disables the current /// installed function. Note also that the handler may be executed on a /// different thread on some platforms. - /// Register a function to be called when ctrl-c is pressed. void SetInterruptFunction(void (*IF)()); + + /// Registers a function to be called when an "info" signal is delivered to + /// the process. + /// + /// On POSIX systems, this will be SIGUSR1; on systems that have it, SIGINFO + /// will also be used (typically ctrl-t). + /// + /// Note that signal handlers are not allowed to call any non-reentrant + /// functions. An null function pointer disables the current installed + /// function. Note also that the handler may be executed on a different + /// thread on some platforms. + void SetInfoSignalFunction(void (*Handler)()); } // End sys namespace } // End llvm namespace Index: lib/Support/PrettyStackTrace.cpp =================================================================== --- lib/Support/PrettyStackTrace.cpp +++ lib/Support/PrettyStackTrace.cpp @@ -17,6 +17,7 @@ #include "llvm/ADT/SmallString.h" #include "llvm/Config/config.h" // Get autoconf configuration settings #include "llvm/Support/Compiler.h" +#include "llvm/Support/SaveAndRestore.h" #include "llvm/Support/Signals.h" #include "llvm/Support/Watchdog.h" #include "llvm/Support/raw_ostream.h" @@ -42,6 +43,16 @@ // thread-local variable. static LLVM_THREAD_LOCAL PrettyStackTraceEntry *PrettyStackTraceHead = nullptr; +// The use of 'volatile' here is to ensure that any particular thread always +// reloads the value of the counter. The 'std::atomic' allows us to specify that +// this variable is accessed in an unsychronized way (it's not actually +// synchronizing). This does technically mean that the value may not appear to +// be the same across threads running simultaneously on different CPUs, but in +// practice the worst that will happen is that we won't print a stack trace when +// we could have. +static volatile std::atomic GlobalSigInfoGenerationCounter{}; +static LLVM_THREAD_LOCAL unsigned ThreadLocalSigInfoGenerationCounter = 0; + namespace llvm { PrettyStackTraceEntry *ReverseStackTrace(PrettyStackTraceEntry *Head) { PrettyStackTraceEntry *Prev = nullptr; @@ -57,8 +68,9 @@ // to fail if we crashed due to stack overflow), we do an up-front pass to // reverse the stack, then print it, then reverse it again. unsigned ID = 0; - PrettyStackTraceEntry *ReversedStack = - llvm::ReverseStackTrace(PrettyStackTraceHead); + SaveAndRestore SavedStack{PrettyStackTraceHead, + nullptr}; + PrettyStackTraceEntry *ReversedStack = ReverseStackTrace(SavedStack.get()); for (const PrettyStackTraceEntry *Entry = ReversedStack; Entry; Entry = Entry->getNextEntry()) { OS << ID++ << ".\t"; @@ -128,10 +140,22 @@ #endif } +static void printForSigInfoIfNeeded() { + unsigned CurrentSigInfoGeneration = + GlobalSigInfoGenerationCounter.load(std::memory_order_relaxed); + if (CurrentSigInfoGeneration == ThreadLocalSigInfoGenerationCounter) + return; + + PrintCurStackTrace(errs()); + ThreadLocalSigInfoGenerationCounter = CurrentSigInfoGeneration; +} + #endif // ENABLE_BACKTRACES PrettyStackTraceEntry::PrettyStackTraceEntry() { #if ENABLE_BACKTRACES + // Handle SIGINFO first, because we haven't finished constructing yet. + printForSigInfoIfNeeded(); // Link ourselves. NextEntry = PrettyStackTraceHead; PrettyStackTraceHead = this; @@ -143,6 +167,8 @@ assert(PrettyStackTraceHead == this && "Pretty stack trace entry destruction is out of order"); PrettyStackTraceHead = NextEntry; + // Handle SIGINFO first, because we already started destructing. + printForSigInfoIfNeeded(); #endif } @@ -177,6 +203,9 @@ #if ENABLE_BACKTRACES static bool RegisterCrashPrinter() { sys::AddSignalHandler(CrashHandler, nullptr); + sys::SetInfoSignalFunction([]{ + GlobalSigInfoGenerationCounter.fetch_add(1, std::memory_order_relaxed); + }); return false; } #endif Index: lib/Support/Unix/Signals.inc =================================================================== --- lib/Support/Unix/Signals.inc +++ lib/Support/Unix/Signals.inc @@ -81,10 +81,13 @@ using namespace llvm; static RETSIGTYPE SignalHandler(int Sig); // defined below. +static RETSIGTYPE InfoSignalHandler(int Sig); // defined below. +using SignalHandlerFunctionType = void (*)(); /// The function to call if ctrl-c is pressed. -using InterruptFunctionType = void (*)(); -static std::atomic InterruptFunction = +static std::atomic InterruptFunction = + ATOMIC_VAR_INIT(nullptr); +static std::atomic InfoSignalFunction = ATOMIC_VAR_INIT(nullptr); namespace { @@ -200,15 +203,15 @@ static StringRef Argv0; -// Signals that represent requested termination. There's no bug or failure, or -// if there is, it's not our direct responsibility. For whatever reason, our -// continued execution is no longer desirable. +/// Signals that represent requested termination. There's no bug or failure, or +/// 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, SIGUSR1, SIGUSR2 + SIGHUP, SIGINT, SIGPIPE, SIGTERM, SIGUSR2 }; -// Signals that represent that we have a bug, and our prompt termination has -// been ordered. +/// Signals that represent that we have a bug, and our prompt termination has +/// been ordered. static const int KillSigs[] = { SIGILL, SIGTRAP, SIGABRT, SIGFPE, SIGBUS, SIGSEGV, SIGQUIT #ifdef SIGSYS @@ -225,11 +228,24 @@ #endif }; +/// Signals that represent requests for status +static const int InfoSigs[] = { + SIGUSR1 +#ifdef SIGINFO + , SIGINFO +#endif +}; + +static const size_t NumSigs = + array_lengthof(IntSigs) + array_lengthof(KillSigs) + + array_lengthof(InfoSigs); + + static std::atomic NumRegisteredSignals = ATOMIC_VAR_INIT(0); static struct { struct sigaction SA; int SigNo; -} RegisteredSignalInfo[array_lengthof(IntSigs) + array_lengthof(KillSigs)]; +} RegisteredSignalInfo[NumSigs]; #if defined(HAVE_SIGALTSTACK) // Hold onto both the old and new alternate signal stack so that it's not @@ -277,15 +293,20 @@ // be able to reliably handle signals due to stack overflow. CreateSigAltStack(); - auto registerHandler = [&](int Signal) { + auto registerHandler = [&](int Signal, bool IsKillSignal) { unsigned Index = NumRegisteredSignals.load(); assert(Index < array_lengthof(RegisteredSignalInfo) && "Out of space for signal handlers!"); struct sigaction NewHandler; - NewHandler.sa_handler = SignalHandler; - NewHandler.sa_flags = SA_NODEFER | SA_RESETHAND | SA_ONSTACK; + if (IsKillSignal) { + NewHandler.sa_handler = SignalHandler; + NewHandler.sa_flags = SA_NODEFER | SA_RESETHAND | SA_ONSTACK; + } else { + NewHandler.sa_handler = InfoSignalHandler; + NewHandler.sa_flags = SA_ONSTACK; + } sigemptyset(&NewHandler.sa_mask); // Install the new handler, save the old one in RegisteredSignalInfo. @@ -295,9 +316,11 @@ }; for (auto S : IntSigs) - registerHandler(S); + registerHandler(S, true); for (auto S : KillSigs) - registerHandler(S); + registerHandler(S, true); + for (auto S : InfoSigs) + registerHandler(S, false); } static void UnregisterHandlers() { @@ -357,6 +380,11 @@ #endif } +static RETSIGTYPE InfoSignalHandler(int Sig) { + if (SignalHandlerFunctionType CurrentInfoFunction = InfoSignalFunction) + CurrentInfoFunction(); +} + void llvm::sys::RunInterruptHandlers() { RemoveFilesToRemove(); } @@ -366,6 +394,11 @@ RegisterHandlers(); } +void llvm::sys::SetInfoSignalFunction(void (*Handler)()) { + InfoSignalFunction.exchange(Handler); + RegisterHandlers(); +} + // The public API bool llvm::sys::RemoveFileOnSignal(StringRef Filename, std::string* ErrMsg) { Index: lib/Support/Windows/Signals.inc =================================================================== --- lib/Support/Windows/Signals.inc +++ lib/Support/Windows/Signals.inc @@ -557,6 +557,10 @@ LeaveCriticalSection(&CriticalSection); } +void llvm::sys::SetInfoSignalFunction(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