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/ErrorHandling.h"
14#include "llvm/Support/ManagedStatic.h"
15#include "llvm/Support/Mutex.h"
16#include "llvm/Support/ThreadLocal.h"
17#include <cstdio>
18#include <setjmp.h>
19using namespace llvm;
20
21namespace {
22
23struct CrashRecoveryContextImpl;
24
25static ManagedStatic<
26    sys::ThreadLocal<const CrashRecoveryContextImpl> > CurrentContext;
27
28struct CrashRecoveryContextImpl {
29  CrashRecoveryContext *CRC;
30  std::string Backtrace;
31  ::jmp_buf JumpBuffer;
32  volatile unsigned Failed : 1;
33  unsigned SwitchedThread : 1;
34
35public:
36  CrashRecoveryContextImpl(CrashRecoveryContext *CRC) : CRC(CRC),
37                                                        Failed(false),
38                                                        SwitchedThread(false) {
39    CurrentContext->set(this);
40  }
41  ~CrashRecoveryContextImpl() {
42    if (!SwitchedThread)
43      CurrentContext->erase();
44  }
45
46  /// \brief Called when the separate crash-recovery thread was finished, to
47  /// indicate that we don't need to clear the thread-local CurrentContext.
48  void setSwitchedThread() { SwitchedThread = true; }
49
50  void HandleCrash() {
51    // Eliminate the current context entry, to avoid re-entering in case the
52    // cleanup code crashes.
53    CurrentContext->erase();
54
55    assert(!Failed && "Crash recovery context already failed!");
56    Failed = true;
57
58    // FIXME: Stash the backtrace.
59
60    // Jump back to the RunSafely we were called under.
61    longjmp(JumpBuffer, 1);
62  }
63};
64
65}
66
67static ManagedStatic<sys::Mutex> gCrashRecoveryContextMutex;
68static bool gCrashRecoveryEnabled = false;
69
70static ManagedStatic<sys::ThreadLocal<const CrashRecoveryContextCleanup> >
71       tlIsRecoveringFromCrash;
72
73CrashRecoveryContextCleanup::~CrashRecoveryContextCleanup() {}
74
75CrashRecoveryContext::~CrashRecoveryContext() {
76  // Reclaim registered resources.
77  CrashRecoveryContextCleanup *i = head;
78  tlIsRecoveringFromCrash->set(head);
79  while (i) {
80    CrashRecoveryContextCleanup *tmp = i;
81    i = tmp->next;
82    tmp->cleanupFired = true;
83    tmp->recoverResources();
84    delete tmp;
85  }
86  tlIsRecoveringFromCrash->erase();
87
88  CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl;
89  delete CRCI;
90}
91
92bool CrashRecoveryContext::isRecoveringFromCrash() {
93  return tlIsRecoveringFromCrash->get() != nullptr;
94}
95
96CrashRecoveryContext *CrashRecoveryContext::GetCurrent() {
97  if (!gCrashRecoveryEnabled)
98    return nullptr;
99
100  const CrashRecoveryContextImpl *CRCI = CurrentContext->get();
101  if (!CRCI)
102    return nullptr;
103
104  return CRCI->CRC;
105}
106
107void CrashRecoveryContext::registerCleanup(CrashRecoveryContextCleanup *cleanup)
108{
109  if (!cleanup)
110    return;
111  if (head)
112    head->prev = cleanup;
113  cleanup->next = head;
114  head = cleanup;
115}
116
117void
118CrashRecoveryContext::unregisterCleanup(CrashRecoveryContextCleanup *cleanup) {
119  if (!cleanup)
120    return;
121  if (cleanup == head) {
122    head = cleanup->next;
123    if (head)
124      head->prev = nullptr;
125  }
126  else {
127    cleanup->prev->next = cleanup->next;
128    if (cleanup->next)
129      cleanup->next->prev = cleanup->prev;
130  }
131  delete cleanup;
132}
133
134#ifdef LLVM_ON_WIN32
135
136#include "Windows/WindowsSupport.h"
137
138// On Windows, we can make use of vectored exception handling to
139// catch most crashing situations.  Note that this does mean
140// we will be alerted of exceptions *before* structured exception
141// handling has the opportunity to catch it.  But that isn't likely
142// to cause problems because nowhere in the project is SEH being
143// used.
144//
145// Vectored exception handling is built on top of SEH, and so it
146// works on a per-thread basis.
147//
148// The vectored exception handler functionality was added in Windows
149// XP, so if support for older versions of Windows is required,
150// it will have to be added.
151//
152// If we want to support as far back as Win2k, we could use the
153// SetUnhandledExceptionFilter API, but there's a risk of that
154// being entirely overwritten (it's not a chain).
155
156static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo)
157{
158  // Lookup the current thread local recovery object.
159  const CrashRecoveryContextImpl *CRCI = CurrentContext->get();
160
161  if (!CRCI) {
162    // Something has gone horribly wrong, so let's just tell everyone
163    // to keep searching
164    CrashRecoveryContext::Disable();
165    return EXCEPTION_CONTINUE_SEARCH;
166  }
167
168  // TODO: We can capture the stack backtrace here and store it on the
169  // implementation if we so choose.
170
171  // Handle the crash
172  const_cast<CrashRecoveryContextImpl*>(CRCI)->HandleCrash();
173
174  // Note that we don't actually get here because HandleCrash calls
175  // longjmp, which means the HandleCrash function never returns.
176  llvm_unreachable("Handled the crash, should have longjmp'ed out of here");
177}
178
179// Because the Enable and Disable calls are static, it means that
180// there may not actually be an Impl available, or even a current
181// CrashRecoveryContext at all.  So we make use of a thread-local
182// exception table.  The handles contained in here will either be
183// non-NULL, valid VEH handles, or NULL.
184static sys::ThreadLocal<const void> sCurrentExceptionHandle;
185
186void CrashRecoveryContext::Enable() {
187  sys::ScopedLock L(*gCrashRecoveryContextMutex);
188
189  if (gCrashRecoveryEnabled)
190    return;
191
192  gCrashRecoveryEnabled = true;
193
194  // We can set up vectored exception handling now.  We will install our
195  // handler as the front of the list, though there's no assurances that
196  // it will remain at the front (another call could install itself before
197  // our handler).  This 1) isn't likely, and 2) shouldn't cause problems.
198  PVOID handle = ::AddVectoredExceptionHandler(1, ExceptionHandler);
199  sCurrentExceptionHandle.set(handle);
200}
201
202void CrashRecoveryContext::Disable() {
203  sys::ScopedLock L(*gCrashRecoveryContextMutex);
204
205  if (!gCrashRecoveryEnabled)
206    return;
207
208  gCrashRecoveryEnabled = false;
209
210  PVOID currentHandle = const_cast<PVOID>(sCurrentExceptionHandle.get());
211  if (currentHandle) {
212    // Now we can remove the vectored exception handler from the chain
213    ::RemoveVectoredExceptionHandler(currentHandle);
214
215    // Reset the handle in our thread-local set.
216    sCurrentExceptionHandle.set(NULL);
217  }
218}
219
220#else
221
222// Generic POSIX implementation.
223//
224// This implementation relies on synchronous signals being delivered to the
225// current thread. We use a thread local object to keep track of the active
226// crash recovery context, and install signal handlers to invoke HandleCrash on
227// the active object.
228//
229// This implementation does not to attempt to chain signal handlers in any
230// reliable fashion -- if we get a signal outside of a crash recovery context we
231// simply disable crash recovery and raise the signal again.
232
233#include <signal.h>
234
235static const int Signals[] =
236    { SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGTRAP };
237static const unsigned NumSignals = sizeof(Signals) / sizeof(Signals[0]);
238static struct sigaction PrevActions[NumSignals];
239
240static void CrashRecoverySignalHandler(int Signal) {
241  // Lookup the current thread local recovery object.
242  const CrashRecoveryContextImpl *CRCI = CurrentContext->get();
243
244  if (!CRCI) {
245    // We didn't find a crash recovery context -- this means either we got a
246    // signal on a thread we didn't expect it on, the application got a signal
247    // outside of a crash recovery context, or something else went horribly
248    // wrong.
249    //
250    // Disable crash recovery and raise the signal again. The assumption here is
251    // that the enclosing application will terminate soon, and we won't want to
252    // attempt crash recovery again.
253    //
254    // This call of Disable isn't thread safe, but it doesn't actually matter.
255    CrashRecoveryContext::Disable();
256    raise(Signal);
257
258    // The signal will be thrown once the signal mask is restored.
259    return;
260  }
261
262  // Unblock the signal we received.
263  sigset_t SigMask;
264  sigemptyset(&SigMask);
265  sigaddset(&SigMask, Signal);
266  sigprocmask(SIG_UNBLOCK, &SigMask, nullptr);
267
268  if (CRCI)
269    const_cast<CrashRecoveryContextImpl*>(CRCI)->HandleCrash();
270}
271
272void CrashRecoveryContext::Enable() {
273  sys::ScopedLock L(*gCrashRecoveryContextMutex);
274
275  if (gCrashRecoveryEnabled)
276    return;
277
278  gCrashRecoveryEnabled = true;
279
280  // Setup the signal handler.
281  struct sigaction Handler;
282  Handler.sa_handler = CrashRecoverySignalHandler;
283  Handler.sa_flags = 0;
284  sigemptyset(&Handler.sa_mask);
285
286  for (unsigned i = 0; i != NumSignals; ++i) {
287    sigaction(Signals[i], &Handler, &PrevActions[i]);
288  }
289}
290
291void CrashRecoveryContext::Disable() {
292  sys::ScopedLock L(*gCrashRecoveryContextMutex);
293
294  if (!gCrashRecoveryEnabled)
295    return;
296
297  gCrashRecoveryEnabled = false;
298
299  // Restore the previous signal handlers.
300  for (unsigned i = 0; i != NumSignals; ++i)
301    sigaction(Signals[i], &PrevActions[i], nullptr);
302}
303
304#endif
305
306bool CrashRecoveryContext::RunSafely(function_ref<void()> Fn) {
307  // If crash recovery is disabled, do nothing.
308  if (gCrashRecoveryEnabled) {
309    assert(!Impl && "Crash recovery context already initialized!");
310    CrashRecoveryContextImpl *CRCI = new CrashRecoveryContextImpl(this);
311    Impl = CRCI;
312
313    if (setjmp(CRCI->JumpBuffer) != 0) {
314      return false;
315    }
316  }
317
318  Fn();
319  return true;
320}
321
322void CrashRecoveryContext::HandleCrash() {
323  CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl;
324  assert(CRCI && "Crash recovery context never initialized!");
325  CRCI->HandleCrash();
326}
327
328const std::string &CrashRecoveryContext::getBacktrace() const {
329  CrashRecoveryContextImpl *CRC = (CrashRecoveryContextImpl *) Impl;
330  assert(CRC && "Crash recovery context never initialized!");
331  assert(CRC->Failed && "No crash was detected!");
332  return CRC->Backtrace;
333}
334
335// FIXME: Portability.
336static void setThreadBackgroundPriority() {
337#ifdef __APPLE__
338  setpriority(PRIO_DARWIN_THREAD, 0, PRIO_DARWIN_BG);
339#endif
340}
341
342static bool hasThreadBackgroundPriority() {
343#ifdef __APPLE__
344  return getpriority(PRIO_DARWIN_THREAD, 0) == 1;
345#else
346  return false;
347#endif
348}
349
350namespace {
351struct RunSafelyOnThreadInfo {
352  function_ref<void()> Fn;
353  CrashRecoveryContext *CRC;
354  bool UseBackgroundPriority;
355  bool Result;
356};
357}
358
359static void RunSafelyOnThread_Dispatch(void *UserData) {
360  RunSafelyOnThreadInfo *Info =
361    reinterpret_cast<RunSafelyOnThreadInfo*>(UserData);
362
363  if (Info->UseBackgroundPriority)
364    setThreadBackgroundPriority();
365
366  Info->Result = Info->CRC->RunSafely(Info->Fn);
367}
368bool CrashRecoveryContext::RunSafelyOnThread(function_ref<void()> Fn,
369                                             unsigned RequestedStackSize) {
370  bool UseBackgroundPriority = hasThreadBackgroundPriority();
371  RunSafelyOnThreadInfo Info = { Fn, this, UseBackgroundPriority, false };
372  llvm_execute_on_thread(RunSafelyOnThread_Dispatch, &Info, RequestedStackSize);
373  if (CrashRecoveryContextImpl *CRC = (CrashRecoveryContextImpl *)Impl)
374    CRC->setSwitchedThread();
375  return Info.Result;
376}
377