CrashRecoveryContext.cpp revision ebe7eb884e30c8e9e9f44f499d75ee39cc6c6d6e
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/System/Mutex.h"
14#include "llvm/System/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
60CrashRecoveryContext::~CrashRecoveryContext() {
61  CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl;
62  delete CRCI;
63}
64
65CrashRecoveryContext *CrashRecoveryContext::GetCurrent() {
66  const CrashRecoveryContextImpl *CRCI = CurrentContext.get();
67  if (!CRCI)
68    return 0;
69
70  return CRCI->CRC;
71}
72
73#ifdef LLVM_ON_WIN32
74
75// FIXME: No real Win32 implementation currently.
76
77void CrashRecoveryContext::Enable() {
78  sys::ScopedLock L(gCrashRecoveryContexMutex);
79
80  if (gCrashRecoveryEnabled)
81    return;
82
83  gCrashRecoveryEnabled = true;
84}
85
86void CrashRecoveryContext::Disable() {
87  sys::ScopedLock L(gCrashRecoveryContexMutex);
88
89  if (!gCrashRecoveryEnabled)
90    return;
91
92  gCrashRecoveryEnabled = false;
93}
94
95#else
96
97// Generic POSIX implementation.
98//
99// This implementation relies on synchronous signals being delivered to the
100// current thread. We use a thread local object to keep track of the active
101// crash recovery context, and install signal handlers to invoke HandleCrash on
102// the active object.
103//
104// This implementation does not to attempt to chain signal handlers in any
105// reliable fashion -- if we get a signal outside of a crash recovery context we
106// simply disable crash recovery and raise the signal again.
107
108#include <signal.h>
109
110static int Signals[] = { SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGTRAP };
111static const unsigned NumSignals = sizeof(Signals) / sizeof(Signals[0]);
112static struct sigaction PrevActions[NumSignals];
113
114static void CrashRecoverySignalHandler(int Signal) {
115  // Lookup the current thread local recovery object.
116  const CrashRecoveryContextImpl *CRCI = CurrentContext.get();
117
118  if (!CRCI) {
119    // We didn't find a crash recovery context -- this means either we got a
120    // signal on a thread we didn't expect it on, the application got a signal
121    // outside of a crash recovery context, or something else went horribly
122    // wrong.
123    //
124    // Disable crash recovery and raise the signal again. The assumption here is
125    // that the enclosing application will terminate soon, and we won't want to
126    // attempt crash recovery again.
127    //
128    // This call of Disable isn't thread safe, but it doesn't actually matter.
129    CrashRecoveryContext::Disable();
130    raise(Signal);
131  }
132
133  // Unblock the signal we received.
134  sigset_t SigMask;
135  sigemptyset(&SigMask);
136  sigaddset(&SigMask, Signal);
137  sigprocmask(SIG_UNBLOCK, &SigMask, 0);
138
139  if (CRCI)
140    const_cast<CrashRecoveryContextImpl*>(CRCI)->HandleCrash();
141}
142
143void CrashRecoveryContext::Enable() {
144  sys::ScopedLock L(gCrashRecoveryContexMutex);
145
146  if (gCrashRecoveryEnabled)
147    return;
148
149  gCrashRecoveryEnabled = true;
150
151  // Setup the signal handler.
152  struct sigaction Handler;
153  Handler.sa_handler = CrashRecoverySignalHandler;
154  Handler.sa_flags = 0;
155  sigemptyset(&Handler.sa_mask);
156
157  for (unsigned i = 0; i != NumSignals; ++i) {
158    sigaction(Signals[i], &Handler, &PrevActions[i]);
159  }
160}
161
162void CrashRecoveryContext::Disable() {
163  sys::ScopedLock L(gCrashRecoveryContexMutex);
164
165  if (!gCrashRecoveryEnabled)
166    return;
167
168  gCrashRecoveryEnabled = false;
169
170  // Restore the previous signal handlers.
171  for (unsigned i = 0; i != NumSignals; ++i)
172    sigaction(Signals[i], &PrevActions[i], 0);
173}
174
175#endif
176
177bool CrashRecoveryContext::RunSafely(void (*Fn)(void*), void *UserData) {
178  // If crash recovery is disabled, do nothing.
179  if (gCrashRecoveryEnabled) {
180    assert(!Impl && "Crash recovery context already initialized!");
181    CrashRecoveryContextImpl *CRCI = new CrashRecoveryContextImpl(this);
182    Impl = CRCI;
183
184    if (setjmp(CRCI->JumpBuffer) != 0) {
185      return false;
186    }
187  }
188
189  Fn(UserData);
190  return true;
191}
192
193void CrashRecoveryContext::HandleCrash() {
194  CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl;
195  assert(CRCI && "Crash recovery context never initialized!");
196  CRCI->HandleCrash();
197}
198
199const std::string &CrashRecoveryContext::getBacktrace() const {
200  CrashRecoveryContextImpl *CRC = (CrashRecoveryContextImpl *) Impl;
201  assert(CRC && "Crash recovery context never initialized!");
202  assert(CRC->Failed && "No crash was detected!");
203  return CRC->Backtrace;
204}
205