CrashRecoveryContext: Add a simple POSIX implementation.
authorDaniel Dunbar <daniel@zuster.org>
Thu, 29 Jul 2010 01:21:47 +0000 (01:21 +0000)
committerDaniel Dunbar <daniel@zuster.org>
Thu, 29 Jul 2010 01:21:47 +0000 (01:21 +0000)
 - This works, but won't handle crashes on stack overflow, or signals delivered
   to a thread other than the one that crashed. The latter is particular annoying
   on Darwin, because SIGABRT tends to go to the main thread.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@109717 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Support/CrashRecoveryContext.cpp

index 870221e1711285d643a4c2ffbf297b325de2130c..c079182bd1c51b8d8e4812b7b501294c80ce9403 100644 (file)
@@ -9,20 +9,29 @@
 
 #include "llvm/Support/CrashRecoveryContext.h"
 #include "llvm/ADT/SmallString.h"
+#include "llvm/System/ThreadLocal.h"
 #include <setjmp.h>
+#include <cstdio>
 using namespace llvm;
 
 namespace {
 
 struct CrashRecoveryContextImpl;
 
+static sys::ThreadLocal<const CrashRecoveryContextImpl> CurrentContext;
+
 struct CrashRecoveryContextImpl {
   std::string Backtrace;
   ::jmp_buf JumpBuffer;
   volatile unsigned Failed : 1;
 
 public:
-  CrashRecoveryContextImpl() : Failed(false) {}
+  CrashRecoveryContextImpl() : Failed(false) {
+    CurrentContext.set(this);
+  }
+  ~CrashRecoveryContextImpl() {
+    CurrentContext.set(0);
+  }
 
   void HandleCrash() {
     assert(!Failed && "Crash recovery context already failed!");
@@ -44,6 +53,10 @@ CrashRecoveryContext::~CrashRecoveryContext() {
   delete CRCI;
 }
 
+#ifdef LLVM_ON_WIN32
+
+// FIXME: No real Win32 implementation currently.
+
 void CrashRecoveryContext::Enable() {
   if (gCrashRecoveryEnabled)
     return;
@@ -58,6 +71,94 @@ void CrashRecoveryContext::Disable() {
   gCrashRecoveryEnabled = false;
 }
 
+#else
+
+// Generic POSIX implementation.
+//
+// This implementation relies on synchronous signals being delivered to the
+// current thread. We use a thread local object to keep track of the active
+// crash recovery context, and install signal handlers to invoke HandleCrash on
+// the active object.
+//
+// This implementation does not to attempt to chain signal handlers in any
+// reliable fashion -- if we get a signal outside of a crash recovery context we
+// simply disable crash recovery and raise the signal again.
+
+#include <signal.h>
+
+static struct {
+  int Signal;
+  struct sigaction PrevAction;
+} SignalInfo[] = {
+  { SIGABRT, {} },
+  { SIGBUS,  {} },
+  { SIGFPE,  {} },
+  { SIGILL,  {} },
+  { SIGSEGV, {} },
+  { SIGTRAP, {} },
+};
+static const unsigned NumSignals = sizeof(SignalInfo) / sizeof(SignalInfo[0]);
+
+static void CrashRecoverySignalHandler(int Signal) {
+  // Lookup the current thread local recovery object.
+  const CrashRecoveryContextImpl *CRCI = CurrentContext.get();
+
+  if (!CRCI) {
+    // We didn't find a crash recovery context -- this means either we got a
+    // signal on a thread we didn't expect it on, the application got a signal
+    // outside of a crash recovery context, or something else went horribly
+    // wrong.
+    //
+    // Disable crash recovery and raise the signal again. The assumption here is
+    // that the enclosing application will terminate soon, and we won't want to
+    // attempt crash recovery again.
+    //
+    // This call of Disable isn't thread safe, but it doesn't actually matter.
+    CrashRecoveryContext::Disable();
+    raise(Signal);
+  }
+
+  // Unblock the signal we received.
+  sigset_t SigMask;
+  sigemptyset(&SigMask);
+  sigaddset(&SigMask, Signal);
+  sigprocmask(SIG_UNBLOCK, &SigMask, 0);
+
+  if (CRCI)
+    const_cast<CrashRecoveryContextImpl*>(CRCI)->HandleCrash();
+}
+
+void CrashRecoveryContext::Enable() {
+  if (gCrashRecoveryEnabled)
+    return;
+
+  gCrashRecoveryEnabled = true;
+
+  // Setup the signal handler.
+  struct sigaction Handler;
+  Handler.sa_handler = CrashRecoverySignalHandler;
+  Handler.sa_flags = 0;
+  sigemptyset(&Handler.sa_mask);
+
+  for (unsigned i = 0; i != NumSignals; ++i) {
+    sigaction(SignalInfo[i].Signal, &Handler,
+              &SignalInfo[i].PrevAction);
+  }
+}
+
+void CrashRecoveryContext::Disable() {
+  if (!gCrashRecoveryEnabled)
+    return;
+
+  gCrashRecoveryEnabled = false;
+
+  // Restore the previous signal handlers.
+  for (unsigned i = 0; i != NumSignals; ++i)
+    sigaction(SignalInfo[i].Signal, &SignalInfo[i].PrevAction, 0);
+}
+
+#endif
+
 bool CrashRecoveryContext::RunSafely(void (*Fn)(void*), void *UserData) {
   // If crash recovery is disabled, do nothing.
   if (gCrashRecoveryEnabled) {