[llvm] r365911 - Support for dumping current PrettyStackTrace on SIGINFO (Ctrl-T)
Jordan Rose via llvm-commits
llvm-commits at lists.llvm.org
Fri Jul 12 09:05:09 PDT 2019
Author: jrose
Date: Fri Jul 12 09:05:09 2019
New Revision: 365911
URL: http://llvm.org/viewvc/llvm-project?rev=365911&view=rev
Log:
Support for dumping current PrettyStackTrace on SIGINFO (Ctrl-T)
Support SIGINFO (and SIGUSR1 for POSIX purposes) to tell what
long-running jobs are doing, as inspired by BSD tools (including on
macOS), by dumping the current PrettyStackTrace.
This adds a new kind of signal handler for non-fatal "info" signals,
similar to the "interrupt" handler that already exists for SIGINT
(Ctrl-C). It then uses that handler to update a "generation count"
managed by the PrettyStackTrace infrastructure, which is then checked
whenever a PrettyStackTraceEntry is pushed or popped on each
thread. If the generation has changed---i.e. if the user has pressed
Ctrl-T---the stack trace is dumped, though unfortunately it can't
include the deepest entry because that one is currently being
constructed/destructed.
https://reviews.llvm.org/D63750
Modified:
llvm/trunk/include/llvm/Support/PrettyStackTrace.h
llvm/trunk/include/llvm/Support/Signals.h
llvm/trunk/lib/Support/PrettyStackTrace.cpp
llvm/trunk/lib/Support/Unix/Signals.inc
llvm/trunk/lib/Support/Windows/Signals.inc
Modified: llvm/trunk/include/llvm/Support/PrettyStackTrace.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/PrettyStackTrace.h?rev=365911&r1=365910&r2=365911&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Support/PrettyStackTrace.h (original)
+++ llvm/trunk/include/llvm/Support/PrettyStackTrace.h Fri Jul 12 09:05:09 2019
@@ -21,8 +21,22 @@
namespace llvm {
class raw_ostream;
+ /// Enables dumping a "pretty" stack trace when the program crashes.
+ ///
+ /// \see PrettyStackTraceEntry
void EnablePrettyStackTrace();
+ /// Enables (or disables) dumping a "pretty" stack trace when the user sends
+ /// SIGINFO or SIGUSR1 to the current process.
+ ///
+ /// This is a per-thread decision so that a program can choose to print stack
+ /// traces only on a primary thread, or on all threads that use
+ /// PrettyStackTraceEntry.
+ ///
+ /// \see EnablePrettyStackTrace
+ /// \see PrettyStackTraceEntry
+ void EnablePrettyStackTraceOnSigInfoForThisThread(bool ShouldEnable = true);
+
/// PrettyStackTraceEntry - This class is used to represent a frame of the
/// "pretty" stack trace that is dumped when a program crashes. You can define
/// subclasses of this and declare them on the program stack: when they are
Modified: llvm/trunk/include/llvm/Support/Signals.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/Signals.h?rev=365911&r1=365910&r2=365911&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Support/Signals.h (original)
+++ llvm/trunk/include/llvm/Support/Signals.h Fri Jul 12 09:05:09 2019
@@ -65,13 +65,25 @@ namespace sys {
/// 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
Modified: llvm/trunk/lib/Support/PrettyStackTrace.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/PrettyStackTrace.cpp?rev=365911&r1=365910&r2=365911&view=diff
==============================================================================
--- llvm/trunk/lib/Support/PrettyStackTrace.cpp (original)
+++ llvm/trunk/lib/Support/PrettyStackTrace.cpp Fri Jul 12 09:05:09 2019
@@ -16,6 +16,7 @@
#include "llvm/ADT/SmallString.h"
#include "llvm/Config/config.h"
#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"
@@ -41,6 +42,22 @@ using namespace llvm;
// 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.
+//
+// This is initialized to 1 because 0 is used as a sentinel for "not enabled on
+// the current thread". If the user happens to overflow an 'unsigned' with
+// SIGINFO requests, it's possible that some threads will stop responding to it,
+// but the program won't crash.
+static volatile std::atomic<unsigned> GlobalSigInfoGenerationCounter =
+ ATOMIC_VAR_INIT(1);
+static LLVM_THREAD_LOCAL unsigned ThreadLocalSigInfoGenerationCounter = 0;
+
namespace llvm {
PrettyStackTraceEntry *ReverseStackTrace(PrettyStackTraceEntry *Head) {
PrettyStackTraceEntry *Prev = nullptr;
@@ -56,8 +73,9 @@ static void PrintStack(raw_ostream &OS)
// 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<PrettyStackTraceEntry *> SavedStack{PrettyStackTraceHead,
+ nullptr};
+ PrettyStackTraceEntry *ReversedStack = ReverseStackTrace(SavedStack.get());
for (const PrettyStackTraceEntry *Entry = ReversedStack; Entry;
Entry = Entry->getNextEntry()) {
OS << ID++ << ".\t";
@@ -67,7 +85,10 @@ static void PrintStack(raw_ostream &OS)
llvm::ReverseStackTrace(ReversedStack);
}
-/// PrintCurStackTrace - Print the current stack trace to the specified stream.
+/// Print the current stack trace to the specified stream.
+///
+/// Marked NOINLINE so it can be called from debuggers.
+LLVM_ATTRIBUTE_NOINLINE
static void PrintCurStackTrace(raw_ostream &OS) {
// Don't print an empty trace.
if (!PrettyStackTraceHead) return;
@@ -127,10 +148,24 @@ static void CrashHandler(void *) {
#endif
}
+static void printForSigInfoIfNeeded() {
+ unsigned CurrentSigInfoGeneration =
+ GlobalSigInfoGenerationCounter.load(std::memory_order_relaxed);
+ if (ThreadLocalSigInfoGenerationCounter == 0 ||
+ ThreadLocalSigInfoGenerationCounter == CurrentSigInfoGeneration) {
+ 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;
@@ -142,6 +177,8 @@ PrettyStackTraceEntry::~PrettyStackTrace
assert(PrettyStackTraceHead == this &&
"Pretty stack trace entry destruction is out of order");
PrettyStackTraceHead = NextEntry;
+ // Handle SIGINFO first, because we already started destructing.
+ printForSigInfoIfNeeded();
#endif
}
@@ -188,6 +225,28 @@ void llvm::EnablePrettyStackTrace() {
#endif
}
+void llvm::EnablePrettyStackTraceOnSigInfoForThisThread(bool ShouldEnable) {
+#if ENABLE_BACKTRACES
+ if (!ShouldEnable) {
+ ThreadLocalSigInfoGenerationCounter = 0;
+ return;
+ }
+
+ // The first time this is called, we register the SIGINFO handler.
+ static bool HandlerRegistered = []{
+ sys::SetInfoSignalFunction([]{
+ GlobalSigInfoGenerationCounter.fetch_add(1, std::memory_order_relaxed);
+ });
+ return false;
+ }();
+ (void)HandlerRegistered;
+
+ // Next, enable it for the current thread.
+ ThreadLocalSigInfoGenerationCounter =
+ GlobalSigInfoGenerationCounter.load(std::memory_order_relaxed);
+#endif
+}
+
const void *llvm::SavePrettyStackState() {
#if ENABLE_BACKTRACES
return PrettyStackTraceHead;
Modified: llvm/trunk/lib/Support/Unix/Signals.inc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/Unix/Signals.inc?rev=365911&r1=365910&r2=365911&view=diff
==============================================================================
--- llvm/trunk/lib/Support/Unix/Signals.inc (original)
+++ llvm/trunk/lib/Support/Unix/Signals.inc Fri Jul 12 09:05:09 2019
@@ -42,6 +42,7 @@
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Mutex.h"
#include "llvm/Support/Program.h"
+#include "llvm/Support/SaveAndRestore.h"
#include "llvm/Support/UniqueLock.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
@@ -80,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<InterruptFunctionType> InterruptFunction =
+static std::atomic<SignalHandlerFunctionType> InterruptFunction =
+ ATOMIC_VAR_INIT(nullptr);
+static std::atomic<SignalHandlerFunctionType> InfoSignalFunction =
ATOMIC_VAR_INIT(nullptr);
namespace {
@@ -199,15 +203,15 @@ struct FilesToRemoveCleanup {
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
@@ -224,11 +228,24 @@ static const int KillSigs[] = {
#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<unsigned> 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
@@ -276,15 +293,24 @@ static void RegisterHandlers() { // Not
// be able to reliably handle signals due to stack overflow.
CreateSigAltStack();
- auto registerHandler = [&](int Signal) {
+ enum class SignalKind { IsKill, IsInfo };
+ auto registerHandler = [&](int Signal, SignalKind Kind) {
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;
+ switch (Kind) {
+ case SignalKind::IsKill:
+ NewHandler.sa_handler = SignalHandler;
+ NewHandler.sa_flags = SA_NODEFER | SA_RESETHAND | SA_ONSTACK;
+ break;
+ case SignalKind::IsInfo:
+ NewHandler.sa_handler = InfoSignalHandler;
+ NewHandler.sa_flags = SA_ONSTACK;
+ break;
+ }
sigemptyset(&NewHandler.sa_mask);
// Install the new handler, save the old one in RegisteredSignalInfo.
@@ -294,9 +320,11 @@ static void RegisterHandlers() { // Not
};
for (auto S : IntSigs)
- registerHandler(S);
+ registerHandler(S, SignalKind::IsKill);
for (auto S : KillSigs)
- registerHandler(S);
+ registerHandler(S, SignalKind::IsKill);
+ for (auto S : InfoSigs)
+ registerHandler(S, SignalKind::IsInfo);
}
static void UnregisterHandlers() {
@@ -356,6 +384,12 @@ static RETSIGTYPE SignalHandler(int Sig)
#endif
}
+static RETSIGTYPE InfoSignalHandler(int Sig) {
+ SaveAndRestore<int> SaveErrnoDuringASignalHandler(errno);
+ if (SignalHandlerFunctionType CurrentInfoFunction = InfoSignalFunction)
+ CurrentInfoFunction();
+}
+
void llvm::sys::RunInterruptHandlers() {
RemoveFilesToRemove();
}
@@ -365,6 +399,11 @@ void llvm::sys::SetInterruptFunction(voi
RegisterHandlers();
}
+void llvm::sys::SetInfoSignalFunction(void (*Handler)()) {
+ InfoSignalFunction.exchange(Handler);
+ RegisterHandlers();
+}
+
// The public API
bool llvm::sys::RemoveFileOnSignal(StringRef Filename,
std::string* ErrMsg) {
Modified: llvm/trunk/lib/Support/Windows/Signals.inc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/Windows/Signals.inc?rev=365911&r1=365910&r2=365911&view=diff
==============================================================================
--- llvm/trunk/lib/Support/Windows/Signals.inc (original)
+++ llvm/trunk/lib/Support/Windows/Signals.inc Fri Jul 12 09:05:09 2019
@@ -556,6 +556,10 @@ void llvm::sys::SetInterruptFunction(voi
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
More information about the llvm-commits
mailing list