1//===--- CrashRecoveryContext.h - Crash Recovery ----------------*- C++ -*-===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9 10#ifndef LLVM_SUPPORT_CRASHRECOVERYCONTEXT_H 11#define LLVM_SUPPORT_CRASHRECOVERYCONTEXT_H 12 13#include <string> 14 15#include "llvm/ADT/STLExtras.h" 16 17namespace llvm { 18class StringRef; 19 20class CrashRecoveryContextCleanup; 21 22/// \brief Crash recovery helper object. 23/// 24/// This class implements support for running operations in a safe context so 25/// that crashes (memory errors, stack overflow, assertion violations) can be 26/// detected and control restored to the crashing thread. Crash detection is 27/// purely "best effort", the exact set of failures which can be recovered from 28/// is platform dependent. 29/// 30/// Clients make use of this code by first calling 31/// CrashRecoveryContext::Enable(), and then executing unsafe operations via a 32/// CrashRecoveryContext object. For example: 33/// 34/// void actual_work(void *); 35/// 36/// void foo() { 37/// CrashRecoveryContext CRC; 38/// 39/// if (!CRC.RunSafely(actual_work, 0)) { 40/// ... a crash was detected, report error to user ... 41/// } 42/// 43/// ... no crash was detected ... 44/// } 45/// 46/// Crash recovery contexts may not be nested. 47class CrashRecoveryContext { 48 void *Impl; 49 CrashRecoveryContextCleanup *head; 50 51public: 52 CrashRecoveryContext() : Impl(nullptr), head(nullptr) {} 53 ~CrashRecoveryContext(); 54 55 void registerCleanup(CrashRecoveryContextCleanup *cleanup); 56 void unregisterCleanup(CrashRecoveryContextCleanup *cleanup); 57 58 /// \brief Enable crash recovery. 59 static void Enable(); 60 61 /// \brief Disable crash recovery. 62 static void Disable(); 63 64 /// \brief Return the active context, if the code is currently executing in a 65 /// thread which is in a protected context. 66 static CrashRecoveryContext *GetCurrent(); 67 68 /// \brief Return true if the current thread is recovering from a 69 /// crash. 70 static bool isRecoveringFromCrash(); 71 72 /// \brief Execute the provide callback function (with the given arguments) in 73 /// a protected context. 74 /// 75 /// \return True if the function completed successfully, and false if the 76 /// function crashed (or HandleCrash was called explicitly). Clients should 77 /// make as little assumptions as possible about the program state when 78 /// RunSafely has returned false. Clients can use getBacktrace() to retrieve 79 /// the backtrace of the crash on failures. 80 bool RunSafely(function_ref<void()> Fn); 81 bool RunSafely(void (*Fn)(void*), void *UserData) { 82 return RunSafely([&]() { Fn(UserData); }); 83 } 84 85 /// \brief Execute the provide callback function (with the given arguments) in 86 /// a protected context which is run in another thread (optionally with a 87 /// requested stack size). 88 /// 89 /// See RunSafely() and llvm_execute_on_thread(). 90 /// 91 /// On Darwin, if PRIO_DARWIN_BG is set on the calling thread, it will be 92 /// propagated to the new thread as well. 93 bool RunSafelyOnThread(function_ref<void()>, unsigned RequestedStackSize = 0); 94 bool RunSafelyOnThread(void (*Fn)(void*), void *UserData, 95 unsigned RequestedStackSize = 0) { 96 return RunSafelyOnThread([&]() { Fn(UserData); }, RequestedStackSize); 97 } 98 99 /// \brief Explicitly trigger a crash recovery in the current process, and 100 /// return failure from RunSafely(). This function does not return. 101 void HandleCrash(); 102 103 /// \brief Return a string containing the backtrace where the crash was 104 /// detected; or empty if the backtrace wasn't recovered. 105 /// 106 /// This function is only valid when a crash has been detected (i.e., 107 /// RunSafely() has returned false. 108 const std::string &getBacktrace() const; 109}; 110 111class CrashRecoveryContextCleanup { 112protected: 113 CrashRecoveryContext *context; 114 CrashRecoveryContextCleanup(CrashRecoveryContext *context) 115 : context(context), cleanupFired(false) {} 116public: 117 bool cleanupFired; 118 119 virtual ~CrashRecoveryContextCleanup(); 120 virtual void recoverResources() = 0; 121 122 CrashRecoveryContext *getContext() const { 123 return context; 124 } 125 126private: 127 friend class CrashRecoveryContext; 128 CrashRecoveryContextCleanup *prev, *next; 129}; 130 131template<typename DERIVED, typename T> 132class CrashRecoveryContextCleanupBase : public CrashRecoveryContextCleanup { 133protected: 134 T *resource; 135 CrashRecoveryContextCleanupBase(CrashRecoveryContext *context, T* resource) 136 : CrashRecoveryContextCleanup(context), resource(resource) {} 137public: 138 static DERIVED *create(T *x) { 139 if (x) { 140 if (CrashRecoveryContext *context = CrashRecoveryContext::GetCurrent()) 141 return new DERIVED(context, x); 142 } 143 return 0; 144 } 145}; 146 147template <typename T> 148class CrashRecoveryContextDestructorCleanup : public 149 CrashRecoveryContextCleanupBase<CrashRecoveryContextDestructorCleanup<T>, T> { 150public: 151 CrashRecoveryContextDestructorCleanup(CrashRecoveryContext *context, 152 T *resource) 153 : CrashRecoveryContextCleanupBase< 154 CrashRecoveryContextDestructorCleanup<T>, T>(context, resource) {} 155 156 virtual void recoverResources() { 157 this->resource->~T(); 158 } 159}; 160 161template <typename T> 162class CrashRecoveryContextDeleteCleanup : public 163 CrashRecoveryContextCleanupBase<CrashRecoveryContextDeleteCleanup<T>, T> { 164public: 165 CrashRecoveryContextDeleteCleanup(CrashRecoveryContext *context, T *resource) 166 : CrashRecoveryContextCleanupBase< 167 CrashRecoveryContextDeleteCleanup<T>, T>(context, resource) {} 168 169 virtual void recoverResources() { 170 delete this->resource; 171 } 172}; 173 174template <typename T> 175class CrashRecoveryContextReleaseRefCleanup : public 176 CrashRecoveryContextCleanupBase<CrashRecoveryContextReleaseRefCleanup<T>, T> 177{ 178public: 179 CrashRecoveryContextReleaseRefCleanup(CrashRecoveryContext *context, 180 T *resource) 181 : CrashRecoveryContextCleanupBase<CrashRecoveryContextReleaseRefCleanup<T>, 182 T>(context, resource) {} 183 184 virtual void recoverResources() { 185 this->resource->Release(); 186 } 187}; 188 189template <typename T, typename Cleanup = CrashRecoveryContextDeleteCleanup<T> > 190class CrashRecoveryContextCleanupRegistrar { 191 CrashRecoveryContextCleanup *cleanup; 192public: 193 CrashRecoveryContextCleanupRegistrar(T *x) 194 : cleanup(Cleanup::create(x)) { 195 if (cleanup) 196 cleanup->getContext()->registerCleanup(cleanup); 197 } 198 199 ~CrashRecoveryContextCleanupRegistrar() { 200 unregister(); 201 } 202 203 void unregister() { 204 if (cleanup && !cleanup->cleanupFired) 205 cleanup->getContext()->unregisterCleanup(cleanup); 206 cleanup = 0; 207 } 208}; 209} 210 211#endif 212