CrashRecoveryContext.cpp revision d49e2aa5b89758b3b8841fa427e3c9e90f2e30b2
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    // The signal will be thrown once the signal mask is restored.
133    return;
134  }
135
136  // Unblock the signal we received.
137  sigset_t SigMask;
138  sigemptyset(&SigMask);
139  sigaddset(&SigMask, Signal);
140  sigprocmask(SIG_UNBLOCK, &SigMask, 0);
141
142  if (CRCI)
143    const_cast<CrashRecoveryContextImpl*>(CRCI)->HandleCrash();
144}
145
146void CrashRecoveryContext::Enable() {
147  sys::ScopedLock L(gCrashRecoveryContexMutex);
148
149  if (gCrashRecoveryEnabled)
150    return;
151
152  gCrashRecoveryEnabled = true;
153
154  // Setup the signal handler.
155  struct sigaction Handler;
156  Handler.sa_handler = CrashRecoverySignalHandler;
157  Handler.sa_flags = 0;
158  sigemptyset(&Handler.sa_mask);
159
160  for (unsigned i = 0; i != NumSignals; ++i) {
161    sigaction(Signals[i], &Handler, &PrevActions[i]);
162  }
163}
164
165void CrashRecoveryContext::Disable() {
166  sys::ScopedLock L(gCrashRecoveryContexMutex);
167
168  if (!gCrashRecoveryEnabled)
169    return;
170
171  gCrashRecoveryEnabled = false;
172
173  // Restore the previous signal handlers.
174  for (unsigned i = 0; i != NumSignals; ++i)
175    sigaction(Signals[i], &PrevActions[i], 0);
176}
177
178#endif
179
180bool CrashRecoveryContext::RunSafely(void (*Fn)(void*), void *UserData) {
181  // If crash recovery is disabled, do nothing.
182  if (gCrashRecoveryEnabled) {
183    assert(!Impl && "Crash recovery context already initialized!");
184    CrashRecoveryContextImpl *CRCI = new CrashRecoveryContextImpl(this);
185    Impl = CRCI;
186
187    if (setjmp(CRCI->JumpBuffer) != 0) {
188      return false;
189    }
190  }
191
192  Fn(UserData);
193  return true;
194}
195
196void CrashRecoveryContext::HandleCrash() {
197  CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl;
198  assert(CRCI && "Crash recovery context never initialized!");
199  CRCI->HandleCrash();
200}
201
202const std::string &CrashRecoveryContext::getBacktrace() const {
203  CrashRecoveryContextImpl *CRC = (CrashRecoveryContextImpl *) Impl;
204  assert(CRC && "Crash recovery context never initialized!");
205  assert(CRC->Failed && "No crash was detected!");
206  return CRC->Backtrace;
207}
208