summaryrefslogtreecommitdiffstats
path: root/llvm/lib/Support/PrettyStackTrace.cpp
diff options
context:
space:
mode:
authorJordan Rose <jordan_rose@apple.com>2019-07-12 16:05:09 +0000
committerJordan Rose <jordan_rose@apple.com>2019-07-12 16:05:09 +0000
commitbe28cddeeaa6e4377de9365e506645ee1eb97e35 (patch)
treef827146dfe75e117c032ac5493ff8c5c778922a8 /llvm/lib/Support/PrettyStackTrace.cpp
parent27ec195f391cf1bcf39fe25f345bc63061566c3c (diff)
downloadbcm5719-llvm-be28cddeeaa6e4377de9365e506645ee1eb97e35.tar.gz
bcm5719-llvm-be28cddeeaa6e4377de9365e506645ee1eb97e35.zip
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 llvm-svn: 365911
Diffstat (limited to 'llvm/lib/Support/PrettyStackTrace.cpp')
-rw-r--r--llvm/lib/Support/PrettyStackTrace.cpp65
1 files changed, 62 insertions, 3 deletions
diff --git a/llvm/lib/Support/PrettyStackTrace.cpp b/llvm/lib/Support/PrettyStackTrace.cpp
index 497084ecda9..e566440f49a 100644
--- a/llvm/lib/Support/PrettyStackTrace.cpp
+++ b/llvm/lib/Support/PrettyStackTrace.cpp
@@ -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::~PrettyStackTraceEntry() {
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;
OpenPOWER on IntegriCloud