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