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