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