1212793Sdim//===--- CrashRecoveryContext.h - Crash Recovery ----------------*- C++ -*-===//
2212793Sdim//
3212793Sdim//                     The LLVM Compiler Infrastructure
4212793Sdim//
5212793Sdim// This file is distributed under the University of Illinois Open Source
6212793Sdim// License. See LICENSE.TXT for details.
7212793Sdim//
8212793Sdim//===----------------------------------------------------------------------===//
9212793Sdim
10212793Sdim#ifndef LLVM_SUPPORT_CRASHRECOVERYCONTEXT_H
11212793Sdim#define LLVM_SUPPORT_CRASHRECOVERYCONTEXT_H
12212793Sdim
13212793Sdim#include <string>
14212793Sdim
15212793Sdimnamespace llvm {
16212793Sdimclass StringRef;
17212793Sdim
18221345Sdimclass CrashRecoveryContextCleanup;
19221345Sdim
20212793Sdim/// \brief Crash recovery helper object.
21212793Sdim///
22212793Sdim/// This class implements support for running operations in a safe context so
23212793Sdim/// that crashes (memory errors, stack overflow, assertion violations) can be
24212793Sdim/// detected and control restored to the crashing thread. Crash detection is
25212793Sdim/// purely "best effort", the exact set of failures which can be recovered from
26212793Sdim/// is platform dependent.
27212793Sdim///
28212793Sdim/// Clients make use of this code by first calling
29212793Sdim/// CrashRecoveryContext::Enable(), and then executing unsafe operations via a
30212793Sdim/// CrashRecoveryContext object. For example:
31212793Sdim///
32212793Sdim///    void actual_work(void *);
33212793Sdim///
34212793Sdim///    void foo() {
35212793Sdim///      CrashRecoveryContext CRC;
36212793Sdim///
37212793Sdim///      if (!CRC.RunSafely(actual_work, 0)) {
38212793Sdim///         ... a crash was detected, report error to user ...
39212793Sdim///      }
40212793Sdim///
41212793Sdim///      ... no crash was detected ...
42212793Sdim///    }
43212793Sdim///
44212793Sdim/// Crash recovery contexts may not be nested.
45212793Sdimclass CrashRecoveryContext {
46212793Sdim  void *Impl;
47221345Sdim  CrashRecoveryContextCleanup *head;
48212793Sdim
49212793Sdimpublic:
50221345Sdim  CrashRecoveryContext() : Impl(0), head(0) {}
51212793Sdim  ~CrashRecoveryContext();
52221345Sdim
53221345Sdim  void registerCleanup(CrashRecoveryContextCleanup *cleanup);
54221345Sdim  void unregisterCleanup(CrashRecoveryContextCleanup *cleanup);
55212793Sdim
56212793Sdim  /// \brief Enable crash recovery.
57212793Sdim  static void Enable();
58212793Sdim
59212793Sdim  /// \brief Disable crash recovery.
60212793Sdim  static void Disable();
61212793Sdim
62212793Sdim  /// \brief Return the active context, if the code is currently executing in a
63212793Sdim  /// thread which is in a protected context.
64212793Sdim  static CrashRecoveryContext *GetCurrent();
65212793Sdim
66221345Sdim  /// \brief Return true if the current thread is recovering from a
67221345Sdim  /// crash.
68221345Sdim  static bool isRecoveringFromCrash();
69221345Sdim
70212793Sdim  /// \brief Execute the provide callback function (with the given arguments) in
71212793Sdim  /// a protected context.
72212793Sdim  ///
73212793Sdim  /// \return True if the function completed successfully, and false if the
74212793Sdim  /// function crashed (or HandleCrash was called explicitly). Clients should
75212793Sdim  /// make as little assumptions as possible about the program state when
76212793Sdim  /// RunSafely has returned false. Clients can use getBacktrace() to retrieve
77212793Sdim  /// the backtrace of the crash on failures.
78212793Sdim  bool RunSafely(void (*Fn)(void*), void *UserData);
79212793Sdim
80218893Sdim  /// \brief Execute the provide callback function (with the given arguments) in
81218893Sdim  /// a protected context which is run in another thread (optionally with a
82218893Sdim  /// requested stack size).
83218893Sdim  ///
84218893Sdim  /// See RunSafely() and llvm_execute_on_thread().
85218893Sdim  bool RunSafelyOnThread(void (*Fn)(void*), void *UserData,
86218893Sdim                         unsigned RequestedStackSize = 0);
87218893Sdim
88212793Sdim  /// \brief Explicitly trigger a crash recovery in the current process, and
89212793Sdim  /// return failure from RunSafely(). This function does not return.
90212793Sdim  void HandleCrash();
91212793Sdim
92212793Sdim  /// \brief Return a string containing the backtrace where the crash was
93212793Sdim  /// detected; or empty if the backtrace wasn't recovered.
94212793Sdim  ///
95212793Sdim  /// This function is only valid when a crash has been detected (i.e.,
96212793Sdim  /// RunSafely() has returned false.
97212793Sdim  const std::string &getBacktrace() const;
98212793Sdim};
99212793Sdim
100221345Sdimclass CrashRecoveryContextCleanup {
101221345Sdimprotected:
102221345Sdim  CrashRecoveryContext *context;
103221345Sdim  CrashRecoveryContextCleanup(CrashRecoveryContext *context)
104221345Sdim    : context(context), cleanupFired(false) {}
105221345Sdimpublic:
106221345Sdim  bool cleanupFired;
107221345Sdim
108221345Sdim  virtual ~CrashRecoveryContextCleanup();
109221345Sdim  virtual void recoverResources() = 0;
110221345Sdim
111221345Sdim  CrashRecoveryContext *getContext() const {
112221345Sdim    return context;
113221345Sdim  }
114221345Sdim
115221345Sdimprivate:
116221345Sdim  friend class CrashRecoveryContext;
117221345Sdim  CrashRecoveryContextCleanup *prev, *next;
118221345Sdim};
119221345Sdim
120221345Sdimtemplate<typename DERIVED, typename T>
121221345Sdimclass CrashRecoveryContextCleanupBase : public CrashRecoveryContextCleanup {
122221345Sdimprotected:
123221345Sdim  T *resource;
124221345Sdim  CrashRecoveryContextCleanupBase(CrashRecoveryContext *context, T* resource)
125221345Sdim    : CrashRecoveryContextCleanup(context), resource(resource) {}
126221345Sdimpublic:
127221345Sdim  static DERIVED *create(T *x) {
128221345Sdim    if (x) {
129221345Sdim      if (CrashRecoveryContext *context = CrashRecoveryContext::GetCurrent())
130221345Sdim        return new DERIVED(context, x);
131221345Sdim    }
132221345Sdim    return 0;
133221345Sdim  }
134221345Sdim};
135221345Sdim
136221345Sdimtemplate <typename T>
137221345Sdimclass CrashRecoveryContextDestructorCleanup : public
138221345Sdim  CrashRecoveryContextCleanupBase<CrashRecoveryContextDestructorCleanup<T>, T> {
139221345Sdimpublic:
140221345Sdim  CrashRecoveryContextDestructorCleanup(CrashRecoveryContext *context,
141221345Sdim                                        T *resource)
142221345Sdim    : CrashRecoveryContextCleanupBase<
143221345Sdim        CrashRecoveryContextDestructorCleanup<T>, T>(context, resource) {}
144221345Sdim
145221345Sdim  virtual void recoverResources() {
146221345Sdim    this->resource->~T();
147221345Sdim  }
148221345Sdim};
149221345Sdim
150221345Sdimtemplate <typename T>
151221345Sdimclass CrashRecoveryContextDeleteCleanup : public
152221345Sdim  CrashRecoveryContextCleanupBase<CrashRecoveryContextDeleteCleanup<T>, T> {
153221345Sdimpublic:
154221345Sdim  CrashRecoveryContextDeleteCleanup(CrashRecoveryContext *context, T *resource)
155221345Sdim    : CrashRecoveryContextCleanupBase<
156221345Sdim        CrashRecoveryContextDeleteCleanup<T>, T>(context, resource) {}
157221345Sdim
158221345Sdim  virtual void recoverResources() {
159221345Sdim    delete this->resource;
160221345Sdim  }
161221345Sdim};
162221345Sdim
163221345Sdimtemplate <typename T>
164221345Sdimclass CrashRecoveryContextReleaseRefCleanup : public
165221345Sdim  CrashRecoveryContextCleanupBase<CrashRecoveryContextReleaseRefCleanup<T>, T>
166221345Sdim{
167221345Sdimpublic:
168221345Sdim  CrashRecoveryContextReleaseRefCleanup(CrashRecoveryContext *context,
169221345Sdim                                        T *resource)
170221345Sdim    : CrashRecoveryContextCleanupBase<CrashRecoveryContextReleaseRefCleanup<T>,
171221345Sdim          T>(context, resource) {}
172221345Sdim
173221345Sdim  virtual void recoverResources() {
174221345Sdim    this->resource->Release();
175221345Sdim  }
176221345Sdim};
177221345Sdim
178221345Sdimtemplate <typename T, typename Cleanup = CrashRecoveryContextDeleteCleanup<T> >
179221345Sdimclass CrashRecoveryContextCleanupRegistrar {
180221345Sdim  CrashRecoveryContextCleanup *cleanup;
181221345Sdimpublic:
182221345Sdim  CrashRecoveryContextCleanupRegistrar(T *x)
183221345Sdim    : cleanup(Cleanup::create(x)) {
184221345Sdim    if (cleanup)
185221345Sdim      cleanup->getContext()->registerCleanup(cleanup);
186221345Sdim  }
187221345Sdim
188221345Sdim  ~CrashRecoveryContextCleanupRegistrar() {
189223017Sdim    unregister();
190223017Sdim  }
191223017Sdim
192223017Sdim  void unregister() {
193221345Sdim    if (cleanup && !cleanup->cleanupFired)
194223017Sdim      cleanup->getContext()->unregisterCleanup(cleanup);
195223017Sdim    cleanup = 0;
196221345Sdim  }
197221345Sdim};
198212793Sdim}
199212793Sdim
200212793Sdim#endif
201