| //===--- CrashRecoveryContext.h - Crash Recovery ----------------*- C++ -*-===// | |
| // | |
| // The LLVM Compiler Infrastructure | |
| // | |
| // This file is distributed under the University of Illinois Open Source | |
| // License. See LICENSE.TXT for details. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #ifndef LLVM_SUPPORT_CRASHRECOVERYCONTEXT_H | |
| #define LLVM_SUPPORT_CRASHRECOVERYCONTEXT_H | |
| #include <string> | |
| namespace llvm { | |
| class StringRef; | |
| class CrashRecoveryContextCleanup; | |
| /// \brief Crash recovery helper object. | |
| /// | |
| /// This class implements support for running operations in a safe context so | |
| /// that crashes (memory errors, stack overflow, assertion violations) can be | |
| /// detected and control restored to the crashing thread. Crash detection is | |
| /// purely "best effort", the exact set of failures which can be recovered from | |
| /// is platform dependent. | |
| /// | |
| /// Clients make use of this code by first calling | |
| /// CrashRecoveryContext::Enable(), and then executing unsafe operations via a | |
| /// CrashRecoveryContext object. For example: | |
| /// | |
| /// void actual_work(void *); | |
| /// | |
| /// void foo() { | |
| /// CrashRecoveryContext CRC; | |
| /// | |
| /// if (!CRC.RunSafely(actual_work, 0)) { | |
| /// ... a crash was detected, report error to user ... | |
| /// } | |
| /// | |
| /// ... no crash was detected ... | |
| /// } | |
| /// | |
| /// Crash recovery contexts may not be nested. | |
| class CrashRecoveryContext { | |
| void *Impl; | |
| CrashRecoveryContextCleanup *head; | |
| public: | |
| CrashRecoveryContext() : Impl(0), head(0) {} | |
| ~CrashRecoveryContext(); | |
| void registerCleanup(CrashRecoveryContextCleanup *cleanup); | |
| void unregisterCleanup(CrashRecoveryContextCleanup *cleanup); | |
| /// \brief Enable crash recovery. | |
| static void Enable(); | |
| /// \brief Disable crash recovery. | |
| static void Disable(); | |
| /// \brief Return the active context, if the code is currently executing in a | |
| /// thread which is in a protected context. | |
| static CrashRecoveryContext *GetCurrent(); | |
| /// \brief Return true if the current thread is recovering from a | |
| /// crash. | |
| static bool isRecoveringFromCrash(); | |
| /// \brief Execute the provide callback function (with the given arguments) in | |
| /// a protected context. | |
| /// | |
| /// \return True if the function completed successfully, and false if the | |
| /// function crashed (or HandleCrash was called explicitly). Clients should | |
| /// make as little assumptions as possible about the program state when | |
| /// RunSafely has returned false. Clients can use getBacktrace() to retrieve | |
| /// the backtrace of the crash on failures. | |
| bool RunSafely(void (*Fn)(void*), void *UserData); | |
| /// \brief Execute the provide callback function (with the given arguments) in | |
| /// a protected context which is run in another thread (optionally with a | |
| /// requested stack size). | |
| /// | |
| /// See RunSafely() and llvm_execute_on_thread(). | |
| bool RunSafelyOnThread(void (*Fn)(void*), void *UserData, | |
| unsigned RequestedStackSize = 0); | |
| /// \brief Explicitly trigger a crash recovery in the current process, and | |
| /// return failure from RunSafely(). This function does not return. | |
| void HandleCrash(); | |
| /// \brief Return a string containing the backtrace where the crash was | |
| /// detected; or empty if the backtrace wasn't recovered. | |
| /// | |
| /// This function is only valid when a crash has been detected (i.e., | |
| /// RunSafely() has returned false. | |
| const std::string &getBacktrace() const; | |
| }; | |
| class CrashRecoveryContextCleanup { | |
| protected: | |
| CrashRecoveryContext *context; | |
| CrashRecoveryContextCleanup(CrashRecoveryContext *context) | |
| : context(context), cleanupFired(false) {} | |
| public: | |
| bool cleanupFired; | |
| virtual ~CrashRecoveryContextCleanup(); | |
| virtual void recoverResources() = 0; | |
| CrashRecoveryContext *getContext() const { | |
| return context; | |
| } | |
| private: | |
| friend class CrashRecoveryContext; | |
| CrashRecoveryContextCleanup *prev, *next; | |
| }; | |
| template<typename DERIVED, typename T> | |
| class CrashRecoveryContextCleanupBase : public CrashRecoveryContextCleanup { | |
| protected: | |
| T *resource; | |
| CrashRecoveryContextCleanupBase(CrashRecoveryContext *context, T* resource) | |
| : CrashRecoveryContextCleanup(context), resource(resource) {} | |
| public: | |
| static DERIVED *create(T *x) { | |
| if (x) { | |
| if (CrashRecoveryContext *context = CrashRecoveryContext::GetCurrent()) | |
| return new DERIVED(context, x); | |
| } | |
| return 0; | |
| } | |
| }; | |
| template <typename T> | |
| class CrashRecoveryContextDestructorCleanup : public | |
| CrashRecoveryContextCleanupBase<CrashRecoveryContextDestructorCleanup<T>, T> { | |
| public: | |
| CrashRecoveryContextDestructorCleanup(CrashRecoveryContext *context, | |
| T *resource) | |
| : CrashRecoveryContextCleanupBase< | |
| CrashRecoveryContextDestructorCleanup<T>, T>(context, resource) {} | |
| virtual void recoverResources() { | |
| this->resource->~T(); | |
| } | |
| }; | |
| template <typename T> | |
| class CrashRecoveryContextDeleteCleanup : public | |
| CrashRecoveryContextCleanupBase<CrashRecoveryContextDeleteCleanup<T>, T> { | |
| public: | |
| CrashRecoveryContextDeleteCleanup(CrashRecoveryContext *context, T *resource) | |
| : CrashRecoveryContextCleanupBase< | |
| CrashRecoveryContextDeleteCleanup<T>, T>(context, resource) {} | |
| virtual void recoverResources() { | |
| delete this->resource; | |
| } | |
| }; | |
| template <typename T> | |
| class CrashRecoveryContextReleaseRefCleanup : public | |
| CrashRecoveryContextCleanupBase<CrashRecoveryContextReleaseRefCleanup<T>, T> | |
| { | |
| public: | |
| CrashRecoveryContextReleaseRefCleanup(CrashRecoveryContext *context, | |
| T *resource) | |
| : CrashRecoveryContextCleanupBase<CrashRecoveryContextReleaseRefCleanup<T>, | |
| T>(context, resource) {} | |
| virtual void recoverResources() { | |
| this->resource->Release(); | |
| } | |
| }; | |
| template <typename T, typename Cleanup = CrashRecoveryContextDeleteCleanup<T> > | |
| class CrashRecoveryContextCleanupRegistrar { | |
| CrashRecoveryContextCleanup *cleanup; | |
| public: | |
| CrashRecoveryContextCleanupRegistrar(T *x) | |
| : cleanup(Cleanup::create(x)) { | |
| if (cleanup) | |
| cleanup->getContext()->registerCleanup(cleanup); | |
| } | |
| ~CrashRecoveryContextCleanupRegistrar() { | |
| unregister(); | |
| } | |
| void unregister() { | |
| if (cleanup && !cleanup->cleanupFired) | |
| cleanup->getContext()->unregisterCleanup(cleanup); | |
| cleanup = 0; | |
| } | |
| }; | |
| } | |
| #endif |