aboutsummaryrefslogtreecommitdiff
path: root/lib/Support/PrettyStackTrace.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Support/PrettyStackTrace.cpp')
-rw-r--r--lib/Support/PrettyStackTrace.cpp92
1 files changed, 75 insertions, 17 deletions
diff --git a/lib/Support/PrettyStackTrace.cpp b/lib/Support/PrettyStackTrace.cpp
index 206de91ae239..aec00baec0e3 100644
--- a/lib/Support/PrettyStackTrace.cpp
+++ b/lib/Support/PrettyStackTrace.cpp
@@ -1,9 +1,8 @@
//===- PrettyStackTrace.cpp - Pretty Crash Handling -----------------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -15,12 +14,14 @@
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm-c/ErrorHandling.h"
#include "llvm/ADT/SmallString.h"
-#include "llvm/Config/config.h" // Get autoconf configuration settings
+#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"
+#include <atomic>
#include <cstdarg>
#include <cstdio>
#include <tuple>
@@ -34,7 +35,7 @@ using namespace llvm;
// If backtrace support is not enabled, compile out support for pretty stack
// traces. This has the secondary effect of not requiring thread local storage
// when backtrace support is disabled.
-#if defined(HAVE_BACKTRACE) && ENABLE_BACKTRACES
+#if ENABLE_BACKTRACES
// We need a thread local pointer to manage the stack of our stack trace
// objects, but we *really* cannot tolerate destructors running and do not want
@@ -42,6 +43,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;
@@ -57,8 +74,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";
@@ -68,7 +86,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;
@@ -128,11 +149,24 @@ static void CrashHandler(void *) {
#endif
}
-// defined(HAVE_BACKTRACE) && ENABLE_BACKTRACES
-#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 defined(HAVE_BACKTRACE) && ENABLE_BACKTRACES
+#if ENABLE_BACKTRACES
+ // Handle SIGINFO first, because we haven't finished constructing yet.
+ printForSigInfoIfNeeded();
// Link ourselves.
NextEntry = PrettyStackTraceHead;
PrettyStackTraceHead = this;
@@ -140,10 +174,12 @@ PrettyStackTraceEntry::PrettyStackTraceEntry() {
}
PrettyStackTraceEntry::~PrettyStackTraceEntry() {
-#if defined(HAVE_BACKTRACE) && ENABLE_BACKTRACES
+#if ENABLE_BACKTRACES
assert(PrettyStackTraceHead == this &&
"Pretty stack trace entry destruction is out of order");
PrettyStackTraceHead = NextEntry;
+ // Handle SIGINFO first, because we already started destructing.
+ printForSigInfoIfNeeded();
#endif
}
@@ -175,7 +211,7 @@ void PrettyStackTraceProgram::print(raw_ostream &OS) const {
OS << '\n';
}
-#if defined(HAVE_BACKTRACE) && ENABLE_BACKTRACES
+#if ENABLE_BACKTRACES
static bool RegisterCrashPrinter() {
sys::AddSignalHandler(CrashHandler, nullptr);
return false;
@@ -183,15 +219,37 @@ static bool RegisterCrashPrinter() {
#endif
void llvm::EnablePrettyStackTrace() {
-#if defined(HAVE_BACKTRACE) && ENABLE_BACKTRACES
+#if ENABLE_BACKTRACES
// The first time this is called, we register the crash printer.
static bool HandlerRegistered = RegisterCrashPrinter();
(void)HandlerRegistered;
#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 defined(HAVE_BACKTRACE) && ENABLE_BACKTRACES
+#if ENABLE_BACKTRACES
return PrettyStackTraceHead;
#else
return nullptr;
@@ -199,7 +257,7 @@ const void *llvm::SavePrettyStackState() {
}
void llvm::RestorePrettyStackState(const void *Top) {
-#if defined(HAVE_BACKTRACE) && ENABLE_BACKTRACES
+#if ENABLE_BACKTRACES
PrettyStackTraceHead =
static_cast<PrettyStackTraceEntry *>(const_cast<void *>(Top));
#endif