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