Support: Add CrashRecoveryContext helper object.

- Designed as a simple wrapper to allow clients to attempt to catch crashes
   (memory errors, assertion violations, etc.) and do some kind of recovery.

 - Currently doesn't actually attempt to catch crashes.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@109586 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Daniel Dunbar 2010-07-28 15:40:20 +00:00
parent 5d4b32ef96
commit a309dac868
2 changed files with 170 additions and 0 deletions

View File

@ -0,0 +1,82 @@
//===--- CrashRecoveryContext.h - Crash Recovery ----------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_SUPPORT_CRASHRECOVERYCONTEXT_H
#define LLVM_SUPPORT_CRASHRECOVERYCONTEXT_H
#include <string>
namespace llvm {
class StringRef;
/// \brief Crash recovery helper object.
///
/// This class implements support for running operations in a safe context so
/// that crashes (memory errors, stack overflow, assertion violations) can be
/// detected and control restored to the crashing thread. Crash detection is
/// purely "best effort", the exact set of failures which can be recovered from
/// is platform dependent.
///
/// Clients make use of this code by first calling
/// CrashRecoveryContext::Enable(), and then executing unsafe operations via a
/// CrashRecoveryContext object. For example:
///
/// void actual_work(void *);
///
/// void foo() {
/// CrashRecoveryContext CRC;
///
/// if (!CRC.RunSafely(actual_work, 0)) {
/// ... a crash was detected, report error to user ...
/// }
///
/// ... no crash was detected ...
/// }
///
/// Crash recovery contexts may not be nested.
class CrashRecoveryContext {
void *Impl;
public:
CrashRecoveryContext() : Impl(0) {}
~CrashRecoveryContext();
/// \brief Enable crash recovery. This function is not thread safe, clients
/// should call it during startup or with a lock held.
static void Enable();
/// \brief Disable crash recovery. This function is not thread safe, clients
/// should call it during startup or with a lock held.
static void Disable();
/// \brief Execute the provide callback function (with the given arguments) in
/// a protected context.
///
/// \return True if the function completed successfully, and false if the
/// function crashed (or HandleCrash was called explicitly). Clients should
/// make as little assumptions as possible about the program state when
/// RunSafely has returned false. Clients can use getBacktrace() to retrieve
/// the backtrace of the crash on failures.
bool RunSafely(void (*Fn)(void*), void *UserData);
/// \brief Explicitly trigger a crash recovery in the current process, and
/// return failure from RunSafely(). This function does not return.
void HandleCrash();
/// \brief Return a string containing the backtrace where the crash was
/// detected; or empty if the backtrace wasn't recovered.
///
/// This function is only valid when a crash has been detected (i.e.,
/// RunSafely() has returned false.
const std::string &getBacktrace() const;
};
}
#endif

View File

@ -0,0 +1,88 @@
//===--- 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 <setjmp.h>
using namespace llvm;
namespace {
struct CrashRecoveryContextImpl;
struct CrashRecoveryContextImpl {
std::string Backtrace;
::jmp_buf JumpBuffer;
volatile unsigned Failed : 1;
public:
CrashRecoveryContextImpl() : Failed(false) {}
void HandleCrash() {
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 bool gCrashRecoveryEnabled = false;
CrashRecoveryContext::~CrashRecoveryContext() {
CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl;
delete CRCI;
}
void CrashRecoveryContext::Enable() {
if (gCrashRecoveryEnabled)
return;
gCrashRecoveryEnabled = true;
}
void CrashRecoveryContext::Disable() {
if (!gCrashRecoveryEnabled)
return;
gCrashRecoveryEnabled = false;
}
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;
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;
}