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