LLVM  9.0.0svn
CrashRecoveryContext.cpp
Go to the documentation of this file.
1 //===--- CrashRecoveryContext.cpp - Crash Recovery ------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
10 #include "llvm/Config/llvm-config.h"
13 #include "llvm/Support/Mutex.h"
15 #include <setjmp.h>
16 using namespace llvm;
17 
18 namespace {
19 
20 struct CrashRecoveryContextImpl;
21 
22 static ManagedStatic<
24 
25 struct CrashRecoveryContextImpl {
26  // When threads are disabled, this links up all active
27  // CrashRecoveryContextImpls. When threads are enabled there's one thread
28  // per CrashRecoveryContext and CurrentContext is a thread-local, so only one
29  // CrashRecoveryContextImpl is active per thread and this is always null.
30  const CrashRecoveryContextImpl *Next;
31 
33  ::jmp_buf JumpBuffer;
34  volatile unsigned Failed : 1;
35  unsigned SwitchedThread : 1;
36 
37 public:
38  CrashRecoveryContextImpl(CrashRecoveryContext *CRC) : CRC(CRC),
39  Failed(false),
40  SwitchedThread(false) {
41  Next = CurrentContext->get();
42  CurrentContext->set(this);
43  }
44  ~CrashRecoveryContextImpl() {
45  if (!SwitchedThread)
46  CurrentContext->set(Next);
47  }
48 
49  /// Called when the separate crash-recovery thread was finished, to
50  /// indicate that we don't need to clear the thread-local CurrentContext.
51  void setSwitchedThread() {
52 #if defined(LLVM_ENABLE_THREADS) && LLVM_ENABLE_THREADS != 0
53  SwitchedThread = true;
54 #endif
55  }
56 
57  void HandleCrash() {
58  // Eliminate the current context entry, to avoid re-entering in case the
59  // cleanup code crashes.
60  CurrentContext->set(Next);
61 
62  assert(!Failed && "Crash recovery context already failed!");
63  Failed = true;
64 
65  // FIXME: Stash the backtrace.
66 
67  // Jump back to the RunSafely we were called under.
68  longjmp(JumpBuffer, 1);
69  }
70 };
71 
72 }
73 
75 static bool gCrashRecoveryEnabled = false;
76 
79 
82 
84 
86  // Reclaim registered resources.
89  tlIsRecoveringFromCrash->set(this);
90  while (i) {
92  i = tmp->next;
93  tmp->cleanupFired = true;
94  tmp->recoverResources();
95  delete tmp;
96  }
97  tlIsRecoveringFromCrash->set(PC);
98 
99  CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl;
100  delete CRCI;
101 }
102 
104  return tlIsRecoveringFromCrash->get() != nullptr;
105 }
106 
108  if (!gCrashRecoveryEnabled)
109  return nullptr;
110 
111  const CrashRecoveryContextImpl *CRCI = CurrentContext->get();
112  if (!CRCI)
113  return nullptr;
114 
115  return CRCI->CRC;
116 }
117 
119  sys::ScopedLock L(*gCrashRecoveryContextMutex);
120  // FIXME: Shouldn't this be a refcount or something?
121  if (gCrashRecoveryEnabled)
122  return;
123  gCrashRecoveryEnabled = true;
125 }
126 
128  sys::ScopedLock L(*gCrashRecoveryContextMutex);
129  if (!gCrashRecoveryEnabled)
130  return;
131  gCrashRecoveryEnabled = false;
133 }
134 
136 {
137  if (!cleanup)
138  return;
139  if (head)
140  head->prev = cleanup;
141  cleanup->next = head;
142  head = cleanup;
143 }
144 
145 void
147  if (!cleanup)
148  return;
149  if (cleanup == head) {
150  head = cleanup->next;
151  if (head)
152  head->prev = nullptr;
153  }
154  else {
155  cleanup->prev->next = cleanup->next;
156  if (cleanup->next)
157  cleanup->next->prev = cleanup->prev;
158  }
159  delete cleanup;
160 }
161 
162 #if defined(_MSC_VER)
163 // If _MSC_VER is defined, we must have SEH. Use it if it's available. It's way
164 // better than VEH. Vectored exception handling catches all exceptions happening
165 // on the thread with installed exception handlers, so it can interfere with
166 // internal exception handling of other libraries on that thread. SEH works
167 // exactly as you would expect normal exception handling to work: it only
168 // catches exceptions if they would bubble out from the stack frame with __try /
169 // __except.
170 
171 static void installExceptionOrSignalHandlers() {}
172 static void uninstallExceptionOrSignalHandlers() {}
173 
175  if (!gCrashRecoveryEnabled) {
176  Fn();
177  return true;
178  }
179 
180  bool Result = true;
181  __try {
182  Fn();
183  } __except (1) { // Catch any exception.
184  Result = false;
185  }
186  return Result;
187 }
188 
189 #else // !_MSC_VER
190 
191 #if defined(_WIN32)
192 // This is a non-MSVC compiler, probably mingw gcc or clang without
193 // -fms-extensions. Use vectored exception handling (VEH).
194 //
195 // On Windows, we can make use of vectored exception handling to catch most
196 // crashing situations. Note that this does mean we will be alerted of
197 // exceptions *before* structured exception handling has the opportunity to
198 // catch it. Unfortunately, this causes problems in practice with other code
199 // running on threads with LLVM crash recovery contexts, so we would like to
200 // eventually move away from VEH.
201 //
202 // Vectored works on a per-thread basis, which is an advantage over
203 // SetUnhandledExceptionFilter. SetUnhandledExceptionFilter also doesn't have
204 // any native support for chaining exception handlers, but VEH allows more than
205 // one.
206 //
207 // The vectored exception handler functionality was added in Windows
208 // XP, so if support for older versions of Windows is required,
209 // it will have to be added.
210 
211 #include "Windows/WindowsSupport.h"
212 
213 static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo)
214 {
215  // DBG_PRINTEXCEPTION_WIDE_C is not properly defined on all supported
216  // compilers and platforms, so we define it manually.
217  constexpr ULONG DbgPrintExceptionWideC = 0x4001000AL;
218  switch (ExceptionInfo->ExceptionRecord->ExceptionCode)
219  {
220  case DBG_PRINTEXCEPTION_C:
221  case DbgPrintExceptionWideC:
222  case 0x406D1388: // set debugger thread name
223  return EXCEPTION_CONTINUE_EXECUTION;
224  }
225 
226  // Lookup the current thread local recovery object.
227  const CrashRecoveryContextImpl *CRCI = CurrentContext->get();
228 
229  if (!CRCI) {
230  // Something has gone horribly wrong, so let's just tell everyone
231  // to keep searching
233  return EXCEPTION_CONTINUE_SEARCH;
234  }
235 
236  // TODO: We can capture the stack backtrace here and store it on the
237  // implementation if we so choose.
238 
239  // Handle the crash
240  const_cast<CrashRecoveryContextImpl*>(CRCI)->HandleCrash();
241 
242  // Note that we don't actually get here because HandleCrash calls
243  // longjmp, which means the HandleCrash function never returns.
244  llvm_unreachable("Handled the crash, should have longjmp'ed out of here");
245 }
246 
247 // Because the Enable and Disable calls are static, it means that
248 // there may not actually be an Impl available, or even a current
249 // CrashRecoveryContext at all. So we make use of a thread-local
250 // exception table. The handles contained in here will either be
251 // non-NULL, valid VEH handles, or NULL.
252 static sys::ThreadLocal<const void> sCurrentExceptionHandle;
253 
254 static void installExceptionOrSignalHandlers() {
255  // We can set up vectored exception handling now. We will install our
256  // handler as the front of the list, though there's no assurances that
257  // it will remain at the front (another call could install itself before
258  // our handler). This 1) isn't likely, and 2) shouldn't cause problems.
259  PVOID handle = ::AddVectoredExceptionHandler(1, ExceptionHandler);
260  sCurrentExceptionHandle.set(handle);
261 }
262 
264  PVOID currentHandle = const_cast<PVOID>(sCurrentExceptionHandle.get());
265  if (currentHandle) {
266  // Now we can remove the vectored exception handler from the chain
267  ::RemoveVectoredExceptionHandler(currentHandle);
268 
269  // Reset the handle in our thread-local set.
270  sCurrentExceptionHandle.set(NULL);
271  }
272 }
273 
274 #else // !_WIN32
275 
276 // Generic POSIX implementation.
277 //
278 // This implementation relies on synchronous signals being delivered to the
279 // current thread. We use a thread local object to keep track of the active
280 // crash recovery context, and install signal handlers to invoke HandleCrash on
281 // the active object.
282 //
283 // This implementation does not to attempt to chain signal handlers in any
284 // reliable fashion -- if we get a signal outside of a crash recovery context we
285 // simply disable crash recovery and raise the signal again.
286 
287 #include <signal.h>
288 
289 static const int Signals[] =
290  { SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGTRAP };
291 static const unsigned NumSignals = array_lengthof(Signals);
292 static struct sigaction PrevActions[NumSignals];
293 
294 static void CrashRecoverySignalHandler(int Signal) {
295  // Lookup the current thread local recovery object.
296  const CrashRecoveryContextImpl *CRCI = CurrentContext->get();
297 
298  if (!CRCI) {
299  // We didn't find a crash recovery context -- this means either we got a
300  // signal on a thread we didn't expect it on, the application got a signal
301  // outside of a crash recovery context, or something else went horribly
302  // wrong.
303  //
304  // Disable crash recovery and raise the signal again. The assumption here is
305  // that the enclosing application will terminate soon, and we won't want to
306  // attempt crash recovery again.
307  //
308  // This call of Disable isn't thread safe, but it doesn't actually matter.
310  raise(Signal);
311 
312  // The signal will be thrown once the signal mask is restored.
313  return;
314  }
315 
316  // Unblock the signal we received.
317  sigset_t SigMask;
318  sigemptyset(&SigMask);
319  sigaddset(&SigMask, Signal);
320  sigprocmask(SIG_UNBLOCK, &SigMask, nullptr);
321 
322  if (CRCI)
323  const_cast<CrashRecoveryContextImpl*>(CRCI)->HandleCrash();
324 }
325 
327  // Setup the signal handler.
328  struct sigaction Handler;
329  Handler.sa_handler = CrashRecoverySignalHandler;
330  Handler.sa_flags = 0;
331  sigemptyset(&Handler.sa_mask);
332 
333  for (unsigned i = 0; i != NumSignals; ++i) {
334  sigaction(Signals[i], &Handler, &PrevActions[i]);
335  }
336 }
337 
339  // Restore the previous signal handlers.
340  for (unsigned i = 0; i != NumSignals; ++i)
341  sigaction(Signals[i], &PrevActions[i], nullptr);
342 }
343 
344 #endif // !_WIN32
345 
347  // If crash recovery is disabled, do nothing.
348  if (gCrashRecoveryEnabled) {
349  assert(!Impl && "Crash recovery context already initialized!");
350  CrashRecoveryContextImpl *CRCI = new CrashRecoveryContextImpl(this);
351  Impl = CRCI;
352 
353  if (setjmp(CRCI->JumpBuffer) != 0) {
354  return false;
355  }
356  }
357 
358  Fn();
359  return true;
360 }
361 
362 #endif // !_MSC_VER
363 
365  CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl;
366  assert(CRCI && "Crash recovery context never initialized!");
367  CRCI->HandleCrash();
368 }
369 
370 // FIXME: Portability.
372 #ifdef __APPLE__
373  setpriority(PRIO_DARWIN_THREAD, 0, PRIO_DARWIN_BG);
374 #endif
375 }
376 
378 #ifdef __APPLE__
379  return getpriority(PRIO_DARWIN_THREAD, 0) == 1;
380 #else
381  return false;
382 #endif
383 }
384 
385 namespace {
386 struct RunSafelyOnThreadInfo {
389  bool UseBackgroundPriority;
390  bool Result;
391 };
392 }
393 
394 static void RunSafelyOnThread_Dispatch(void *UserData) {
395  RunSafelyOnThreadInfo *Info =
396  reinterpret_cast<RunSafelyOnThreadInfo*>(UserData);
397 
398  if (Info->UseBackgroundPriority)
400 
401  Info->Result = Info->CRC->RunSafely(Info->Fn);
402 }
404  unsigned RequestedStackSize) {
405  bool UseBackgroundPriority = hasThreadBackgroundPriority();
406  RunSafelyOnThreadInfo Info = { Fn, this, UseBackgroundPriority, false };
407  llvm_execute_on_thread(RunSafelyOnThread_Dispatch, &Info, RequestedStackSize);
408  if (CrashRecoveryContextImpl *CRC = (CrashRecoveryContextImpl *)Impl)
409  CRC->setSwitchedThread();
410  return Info.Result;
411 }
This class represents lattice values for constants.
Definition: AllocatorList.h:23
ThreadLocal - A class used to abstract thread-local storage.
Definition: ThreadLocal.h:45
static const int Signals[]
An efficient, type-erasing, non-owning reference to a callable.
Definition: STLExtras.h:116
static CrashRecoveryContext * GetCurrent()
Return the active context, if the code is currently executing in a thread which is in a protected con...
static bool hasThreadBackgroundPriority()
static void cleanup(BlockFrequencyInfoImplBase &BFI)
Clear all memory not needed downstream.
void HandleCrash()
Explicitly trigger a crash recovery in the current process, and return failure from RunSafely()...
void unregisterCleanup(CrashRecoveryContextCleanup *cleanup)
static void Enable()
Enable crash recovery.
Abstract base class of cleanup handlers.
static ManagedStatic< sys::Mutex > gCrashRecoveryContextMutex
Analysis containing CSE Info
Definition: CSEInfo.cpp:20
T * get()
get - Fetches a pointer to the object associated with the current thread.
Definition: ThreadLocal.h:51
bool RunSafelyOnThread(function_ref< void()>, unsigned RequestedStackSize=0)
Execute the provide callback function (with the given arguments) in a protected context which is run ...
static void uninstallExceptionOrSignalHandlers()
void registerCleanup(CrashRecoveryContextCleanup *cleanup)
Register cleanup handler, which is used when the recovery context is finished.
static void CrashRecoverySignalHandler(int Signal)
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
void llvm_execute_on_thread(void(*UserFn)(void *), void *UserData, unsigned RequestedStackSize=0)
llvm_execute_on_thread - Execute the given UserFn on a separate thread, passing it the provided UserD...
testing::Matcher< const detail::ErrorHolder & > Failed()
Definition: Error.h:147
static ManagedStatic< sys::ThreadLocal< const CrashRecoveryContext > > tlIsRecoveringFromCrash
constexpr size_t array_lengthof(T(&)[N])
Find the length of an array.
Definition: STLExtras.h:1043
static const unsigned NumSignals
bool RunSafely(function_ref< void()> Fn)
Execute the provided callback function (with the given arguments) in a protected context.
static void installExceptionOrSignalHandlers()
Crash recovery helper object.
static void setThreadBackgroundPriority()
static bool gCrashRecoveryEnabled
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
ManagedStatic - This transparently changes the behavior of global statics to be lazily constructed on...
Definition: ManagedStatic.h:83
static bool isRecoveringFromCrash()
Return true if the current thread is recovering from a crash.
static void RunSafelyOnThread_Dispatch(void *UserData)
static void Disable()
Disable crash recovery.
static struct sigaction PrevActions[NumSignals]