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