llvm.org GIT mirror llvm / release_29 lib / Support / CrashRecoveryContext.cpp
release_29

Tree @release_29 (Download .tar.gz)

CrashRecoveryContext.cpp @release_29

a309dac
 
 
 
 
 
 
 
 
 
 
dade28e
1f6efa3
 
a309dac
d9082df
a309dac
 
 
 
 
 
d9082df
 
a309dac
a8fa798
a309dac
 
 
 
 
a8fa798
 
d9082df
 
 
a685582
d9082df
a309dac
 
ebe7eb8
 
 
 
a309dac
 
 
 
 
 
 
 
 
 
 
 
c0c815e
a309dac
 
 
 
 
 
 
a8fa798
 
 
 
 
 
 
 
d9082df
 
 
 
a309dac
c0c815e
 
a309dac
 
 
 
 
 
 
c0c815e
 
a309dac
 
 
 
 
 
d9082df
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63cc2e1
 
 
d9082df
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d49e2aa
 
 
d9082df
 
 
 
 
 
 
 
 
 
 
 
 
c0c815e
 
d9082df
 
 
 
 
 
 
 
 
 
 
 
63cc2e1
d9082df
 
 
 
c0c815e
 
d9082df
 
 
 
 
 
 
63cc2e1
d9082df
 
 
 
a309dac
 
 
 
a8fa798
a309dac
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f8254d6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
//===--- CrashRecoveryContext.cpp - Crash Recovery ------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "llvm/Support/CrashRecoveryContext.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Config/config.h"
#include "llvm/Support/Mutex.h"
#include "llvm/Support/ThreadLocal.h"
#include <setjmp.h>
#include <cstdio>
using namespace llvm;

namespace {

struct CrashRecoveryContextImpl;

static sys::ThreadLocal<const CrashRecoveryContextImpl> CurrentContext;

struct CrashRecoveryContextImpl {
  CrashRecoveryContext *CRC;
  std::string Backtrace;
  ::jmp_buf JumpBuffer;
  volatile unsigned Failed : 1;

public:
  CrashRecoveryContextImpl(CrashRecoveryContext *CRC) : CRC(CRC),
                                                        Failed(false) {
    CurrentContext.set(this);
  }
  ~CrashRecoveryContextImpl() {
    CurrentContext.erase();
  }

  void HandleCrash() {
    // Eliminate the current context entry, to avoid re-entering in case the
    // cleanup code crashes.
    CurrentContext.erase();

    assert(!Failed && "Crash recovery context already failed!");
    Failed = true;

    // FIXME: Stash the backtrace.

    // Jump back to the RunSafely we were called under.
    longjmp(JumpBuffer, 1);
  }
};

}

static sys::Mutex gCrashRecoveryContexMutex;
static bool gCrashRecoveryEnabled = false;

CrashRecoveryContext::~CrashRecoveryContext() {
  CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl;
  delete CRCI;
}

CrashRecoveryContext *CrashRecoveryContext::GetCurrent() {
  const CrashRecoveryContextImpl *CRCI = CurrentContext.get();
  if (!CRCI)
    return 0;

  return CRCI->CRC;
}

#ifdef LLVM_ON_WIN32

// FIXME: No real Win32 implementation currently.

void CrashRecoveryContext::Enable() {
  sys::ScopedLock L(gCrashRecoveryContexMutex);

  if (gCrashRecoveryEnabled)
    return;

  gCrashRecoveryEnabled = true;
}

void CrashRecoveryContext::Disable() {
  sys::ScopedLock L(gCrashRecoveryContexMutex);

  if (!gCrashRecoveryEnabled)
    return;

  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 int Signals[] = { SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGTRAP };
static const unsigned NumSignals = sizeof(Signals) / sizeof(Signals[0]);
static struct sigaction PrevActions[NumSignals];

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);

    // The signal will be thrown once the signal mask is restored.
    return;
  }

  // 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() {
  sys::ScopedLock L(gCrashRecoveryContexMutex);

  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(Signals[i], &Handler, &PrevActions[i]);
  }
}

void CrashRecoveryContext::Disable() {
  sys::ScopedLock L(gCrashRecoveryContexMutex);

  if (!gCrashRecoveryEnabled)
    return;

  gCrashRecoveryEnabled = false;

  // Restore the previous signal handlers.
  for (unsigned i = 0; i != NumSignals; ++i)
    sigaction(Signals[i], &PrevActions[i], 0);
}

#endif

bool CrashRecoveryContext::RunSafely(void (*Fn)(void*), void *UserData) {
  // If crash recovery is disabled, do nothing.
  if (gCrashRecoveryEnabled) {
    assert(!Impl && "Crash recovery context already initialized!");
    CrashRecoveryContextImpl *CRCI = new CrashRecoveryContextImpl(this);
    Impl = CRCI;

    if (setjmp(CRCI->JumpBuffer) != 0) {
      return false;
    }
  }

  Fn(UserData);
  return true;
}

void CrashRecoveryContext::HandleCrash() {
  CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl;
  assert(CRCI && "Crash recovery context never initialized!");
  CRCI->HandleCrash();
}

const std::string &CrashRecoveryContext::getBacktrace() const {
  CrashRecoveryContextImpl *CRC = (CrashRecoveryContextImpl *) Impl;
  assert(CRC && "Crash recovery context never initialized!");
  assert(CRC->Failed && "No crash was detected!");
  return CRC->Backtrace;
}

//

namespace {
struct RunSafelyOnThreadInfo {
  void (*UserFn)(void*);
  void *UserData;
  CrashRecoveryContext *CRC;
  bool Result;
};
}

static void RunSafelyOnThread_Dispatch(void *UserData) {
  RunSafelyOnThreadInfo *Info =
    reinterpret_cast<RunSafelyOnThreadInfo*>(UserData);
  Info->Result = Info->CRC->RunSafely(Info->UserFn, Info->UserData);
}
bool CrashRecoveryContext::RunSafelyOnThread(void (*Fn)(void*), void *UserData,
                                             unsigned RequestedStackSize) {
  RunSafelyOnThreadInfo Info = { Fn, UserData, this, false };
  llvm_execute_on_thread(RunSafelyOnThread_Dispatch, &Info, RequestedStackSize);
  return Info.Result;
}