CrashRecoveryContext.cpp revision 1a06d5721acb9a2b69217fc8872ed5b14a482104
1//===--- CrashRecoveryContext.cpp - Crash Recovery ------------------------===//
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#include "llvm/Support/CrashRecoveryContext.h"
11#include "llvm/ADT/SmallString.h"
12#include "llvm/Config/config.h"
13#include "llvm/Support/Mutex.h"
14#include "llvm/Support/ThreadLocal.h"
15#include <setjmp.h>
16#include <cstdio>
17using namespace llvm;
18
19namespace {
20
21struct CrashRecoveryContextImpl;
22
23static sys::ThreadLocal<const CrashRecoveryContextImpl> CurrentContext;
24
25struct CrashRecoveryContextImpl {
26  CrashRecoveryContext *CRC;
27  std::string Backtrace;
28  ::jmp_buf JumpBuffer;
29  volatile unsigned Failed : 1;
30
31public:
32  CrashRecoveryContextImpl(CrashRecoveryContext *CRC) : CRC(CRC),
33                                                        Failed(false) {
34    CurrentContext.set(this);
35  }
36  ~CrashRecoveryContextImpl() {
37    CurrentContext.erase();
38  }
39
40  void HandleCrash() {
41    // Eliminate the current context entry, to avoid re-entering in case the
42    // cleanup code crashes.
43    CurrentContext.erase();
44
45    assert(!Failed && "Crash recovery context already failed!");
46    Failed = true;
47
48    // FIXME: Stash the backtrace.
49
50    // Jump back to the RunSafely we were called under.
51    longjmp(JumpBuffer, 1);
52  }
53};
54
55}
56
57static sys::Mutex gCrashRecoveryContexMutex;
58static bool gCrashRecoveryEnabled = false;
59
60CrashRecoveryContextCleanup::~CrashRecoveryContextCleanup() {}
61
62CrashRecoveryContext::~CrashRecoveryContext() {
63  // Reclaim registered resources.
64  CrashRecoveryContextCleanup *i = head;
65  while (i) {
66    CrashRecoveryContextCleanup *tmp = i;
67    i = tmp->next;
68    tmp->cleanupFired = true;
69    tmp->recoverResources();
70    delete tmp;
71  }
72
73  CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl;
74  delete CRCI;
75}
76
77CrashRecoveryContext *CrashRecoveryContext::GetCurrent() {
78  if (!gCrashRecoveryEnabled)
79    return 0;
80
81  const CrashRecoveryContextImpl *CRCI = CurrentContext.get();
82  if (!CRCI)
83    return 0;
84
85  return CRCI->CRC;
86}
87
88void CrashRecoveryContext::registerCleanup(CrashRecoveryContextCleanup *cleanup)
89{
90  if (!cleanup)
91    return;
92  if (head)
93    head->prev = cleanup;
94  cleanup->next = head;
95  head = cleanup;
96}
97
98void
99CrashRecoveryContext::unregisterCleanup(CrashRecoveryContextCleanup *cleanup) {
100  if (!cleanup)
101    return;
102  if (cleanup == head) {
103    head = cleanup->next;
104    if (head)
105      head->prev = 0;
106  }
107  else {
108    cleanup->prev->next = cleanup->next;
109    if (cleanup->next)
110      cleanup->next->prev = cleanup->prev;
111  }
112  delete cleanup;
113}
114
115#ifdef LLVM_ON_WIN32
116
117// FIXME: No real Win32 implementation currently.
118
119void CrashRecoveryContext::Enable() {
120  sys::ScopedLock L(gCrashRecoveryContexMutex);
121
122  if (gCrashRecoveryEnabled)
123    return;
124
125  gCrashRecoveryEnabled = true;
126}
127
128void CrashRecoveryContext::Disable() {
129  sys::ScopedLock L(gCrashRecoveryContexMutex);
130
131  if (!gCrashRecoveryEnabled)
132    return;
133
134  gCrashRecoveryEnabled = false;
135}
136
137#else
138
139// Generic POSIX implementation.
140//
141// This implementation relies on synchronous signals being delivered to the
142// current thread. We use a thread local object to keep track of the active
143// crash recovery context, and install signal handlers to invoke HandleCrash on
144// the active object.
145//
146// This implementation does not to attempt to chain signal handlers in any
147// reliable fashion -- if we get a signal outside of a crash recovery context we
148// simply disable crash recovery and raise the signal again.
149
150#include <signal.h>
151
152static int Signals[] = { SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGTRAP };
153static const unsigned NumSignals = sizeof(Signals) / sizeof(Signals[0]);
154static struct sigaction PrevActions[NumSignals];
155
156static void CrashRecoverySignalHandler(int Signal) {
157  // Lookup the current thread local recovery object.
158  const CrashRecoveryContextImpl *CRCI = CurrentContext.get();
159
160  if (!CRCI) {
161    // We didn't find a crash recovery context -- this means either we got a
162    // signal on a thread we didn't expect it on, the application got a signal
163    // outside of a crash recovery context, or something else went horribly
164    // wrong.
165    //
166    // Disable crash recovery and raise the signal again. The assumption here is
167    // that the enclosing application will terminate soon, and we won't want to
168    // attempt crash recovery again.
169    //
170    // This call of Disable isn't thread safe, but it doesn't actually matter.
171    CrashRecoveryContext::Disable();
172    raise(Signal);
173
174    // The signal will be thrown once the signal mask is restored.
175    return;
176  }
177
178  // Unblock the signal we received.
179  sigset_t SigMask;
180  sigemptyset(&SigMask);
181  sigaddset(&SigMask, Signal);
182  sigprocmask(SIG_UNBLOCK, &SigMask, 0);
183
184  if (CRCI)
185    const_cast<CrashRecoveryContextImpl*>(CRCI)->HandleCrash();
186}
187
188void CrashRecoveryContext::Enable() {
189  sys::ScopedLock L(gCrashRecoveryContexMutex);
190
191  if (gCrashRecoveryEnabled)
192    return;
193
194  gCrashRecoveryEnabled = true;
195
196  // Setup the signal handler.
197  struct sigaction Handler;
198  Handler.sa_handler = CrashRecoverySignalHandler;
199  Handler.sa_flags = 0;
200  sigemptyset(&Handler.sa_mask);
201
202  for (unsigned i = 0; i != NumSignals; ++i) {
203    sigaction(Signals[i], &Handler, &PrevActions[i]);
204  }
205}
206
207void CrashRecoveryContext::Disable() {
208  sys::ScopedLock L(gCrashRecoveryContexMutex);
209
210  if (!gCrashRecoveryEnabled)
211    return;
212
213  gCrashRecoveryEnabled = false;
214
215  // Restore the previous signal handlers.
216  for (unsigned i = 0; i != NumSignals; ++i)
217    sigaction(Signals[i], &PrevActions[i], 0);
218}
219
220#endif
221
222bool CrashRecoveryContext::RunSafely(void (*Fn)(void*), void *UserData) {
223  // If crash recovery is disabled, do nothing.
224  if (gCrashRecoveryEnabled) {
225    assert(!Impl && "Crash recovery context already initialized!");
226    CrashRecoveryContextImpl *CRCI = new CrashRecoveryContextImpl(this);
227    Impl = CRCI;
228
229    if (setjmp(CRCI->JumpBuffer) != 0) {
230      return false;
231    }
232  }
233
234  Fn(UserData);
235  return true;
236}
237
238void CrashRecoveryContext::HandleCrash() {
239  CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl;
240  assert(CRCI && "Crash recovery context never initialized!");
241  CRCI->HandleCrash();
242}
243
244const std::string &CrashRecoveryContext::getBacktrace() const {
245  CrashRecoveryContextImpl *CRC = (CrashRecoveryContextImpl *) Impl;
246  assert(CRC && "Crash recovery context never initialized!");
247  assert(CRC->Failed && "No crash was detected!");
248  return CRC->Backtrace;
249}
250
251//
252
253namespace {
254struct RunSafelyOnThreadInfo {
255  void (*UserFn)(void*);
256  void *UserData;
257  CrashRecoveryContext *CRC;
258  bool Result;
259};
260}
261
262static void RunSafelyOnThread_Dispatch(void *UserData) {
263  RunSafelyOnThreadInfo *Info =
264    reinterpret_cast<RunSafelyOnThreadInfo*>(UserData);
265  Info->Result = Info->CRC->RunSafely(Info->UserFn, Info->UserData);
266}
267bool CrashRecoveryContext::RunSafelyOnThread(void (*Fn)(void*), void *UserData,
268                                             unsigned RequestedStackSize) {
269  RunSafelyOnThreadInfo Info = { Fn, UserData, this, false };
270  llvm_execute_on_thread(RunSafelyOnThread_Dispatch, &Info, RequestedStackSize);
271  return Info.Result;
272}
273