[ThinLTO] Add code comment. NFC
[llvm-complete.git] / lib / Support / CrashRecoveryContext.cpp
blob9d13fce9cc52a428773011a7fec06835dc9b45cb
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 //===----------------------------------------------------------------------===//
9 #include "llvm/Support/CrashRecoveryContext.h"
10 #include "llvm/Config/llvm-config.h"
11 #include "llvm/Support/ErrorHandling.h"
12 #include "llvm/Support/ManagedStatic.h"
13 #include "llvm/Support/ThreadLocal.h"
14 #include <mutex>
15 #include <setjmp.h>
16 using namespace llvm;
18 namespace {
20 struct CrashRecoveryContextImpl;
22 static ManagedStatic<
23 sys::ThreadLocal<const CrashRecoveryContextImpl> > CurrentContext;
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;
32 CrashRecoveryContext *CRC;
33 ::jmp_buf JumpBuffer;
34 volatile unsigned Failed : 1;
35 unsigned SwitchedThread : 1;
37 public:
38 CrashRecoveryContextImpl(CrashRecoveryContext *CRC) : CRC(CRC),
39 Failed(false),
40 SwitchedThread(false) {
41 Next = CurrentContext->get();
42 CurrentContext->set(this);
44 ~CrashRecoveryContextImpl() {
45 if (!SwitchedThread)
46 CurrentContext->set(Next);
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
57 void HandleCrash() {
58 // Eliminate the current context entry, to avoid re-entering in case the
59 // cleanup code crashes.
60 CurrentContext->set(Next);
62 assert(!Failed && "Crash recovery context already failed!");
63 Failed = true;
65 // FIXME: Stash the backtrace.
67 // Jump back to the RunSafely we were called under.
68 longjmp(JumpBuffer, 1);
74 static ManagedStatic<std::mutex> gCrashRecoveryContextMutex;
75 static bool gCrashRecoveryEnabled = false;
77 static ManagedStatic<sys::ThreadLocal<const CrashRecoveryContext>>
78 tlIsRecoveringFromCrash;
80 static void installExceptionOrSignalHandlers();
81 static void uninstallExceptionOrSignalHandlers();
83 CrashRecoveryContextCleanup::~CrashRecoveryContextCleanup() {}
85 CrashRecoveryContext::~CrashRecoveryContext() {
86 // Reclaim registered resources.
87 CrashRecoveryContextCleanup *i = head;
88 const CrashRecoveryContext *PC = tlIsRecoveringFromCrash->get();
89 tlIsRecoveringFromCrash->set(this);
90 while (i) {
91 CrashRecoveryContextCleanup *tmp = i;
92 i = tmp->next;
93 tmp->cleanupFired = true;
94 tmp->recoverResources();
95 delete tmp;
97 tlIsRecoveringFromCrash->set(PC);
99 CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl;
100 delete CRCI;
103 bool CrashRecoveryContext::isRecoveringFromCrash() {
104 return tlIsRecoveringFromCrash->get() != nullptr;
107 CrashRecoveryContext *CrashRecoveryContext::GetCurrent() {
108 if (!gCrashRecoveryEnabled)
109 return nullptr;
111 const CrashRecoveryContextImpl *CRCI = CurrentContext->get();
112 if (!CRCI)
113 return nullptr;
115 return CRCI->CRC;
118 void CrashRecoveryContext::Enable() {
119 std::lock_guard<std::mutex> L(*gCrashRecoveryContextMutex);
120 // FIXME: Shouldn't this be a refcount or something?
121 if (gCrashRecoveryEnabled)
122 return;
123 gCrashRecoveryEnabled = true;
124 installExceptionOrSignalHandlers();
127 void CrashRecoveryContext::Disable() {
128 std::lock_guard<std::mutex> L(*gCrashRecoveryContextMutex);
129 if (!gCrashRecoveryEnabled)
130 return;
131 gCrashRecoveryEnabled = false;
132 uninstallExceptionOrSignalHandlers();
135 void CrashRecoveryContext::registerCleanup(CrashRecoveryContextCleanup *cleanup)
137 if (!cleanup)
138 return;
139 if (head)
140 head->prev = cleanup;
141 cleanup->next = head;
142 head = cleanup;
145 void
146 CrashRecoveryContext::unregisterCleanup(CrashRecoveryContextCleanup *cleanup) {
147 if (!cleanup)
148 return;
149 if (cleanup == head) {
150 head = cleanup->next;
151 if (head)
152 head->prev = nullptr;
154 else {
155 cleanup->prev->next = cleanup->next;
156 if (cleanup->next)
157 cleanup->next->prev = cleanup->prev;
159 delete cleanup;
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.
171 static void installExceptionOrSignalHandlers() {}
172 static void uninstallExceptionOrSignalHandlers() {}
174 bool CrashRecoveryContext::RunSafely(function_ref<void()> Fn) {
175 if (!gCrashRecoveryEnabled) {
176 Fn();
177 return true;
180 bool Result = true;
181 __try {
182 Fn();
183 } __except (1) { // Catch any exception.
184 Result = false;
186 return Result;
189 #else // !_MSC_VER
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).
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.
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.
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.
211 #include "Windows/WindowsSupport.h"
213 static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo)
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)
220 case DBG_PRINTEXCEPTION_C:
221 case DbgPrintExceptionWideC:
222 case 0x406D1388: // set debugger thread name
223 return EXCEPTION_CONTINUE_EXECUTION;
226 // Lookup the current thread local recovery object.
227 const CrashRecoveryContextImpl *CRCI = CurrentContext->get();
229 if (!CRCI) {
230 // Something has gone horribly wrong, so let's just tell everyone
231 // to keep searching
232 CrashRecoveryContext::Disable();
233 return EXCEPTION_CONTINUE_SEARCH;
236 // TODO: We can capture the stack backtrace here and store it on the
237 // implementation if we so choose.
239 // Handle the crash
240 const_cast<CrashRecoveryContextImpl*>(CRCI)->HandleCrash();
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");
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;
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);
263 static void uninstallExceptionOrSignalHandlers() {
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);
269 // Reset the handle in our thread-local set.
270 sCurrentExceptionHandle.set(NULL);
274 #else // !_WIN32
276 // Generic POSIX implementation.
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.
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.
287 #include <signal.h>
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];
294 static void CrashRecoverySignalHandler(int Signal) {
295 // Lookup the current thread local recovery object.
296 const CrashRecoveryContextImpl *CRCI = CurrentContext->get();
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.
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.
308 // This call of Disable isn't thread safe, but it doesn't actually matter.
309 CrashRecoveryContext::Disable();
310 raise(Signal);
312 // The signal will be thrown once the signal mask is restored.
313 return;
316 // Unblock the signal we received.
317 sigset_t SigMask;
318 sigemptyset(&SigMask);
319 sigaddset(&SigMask, Signal);
320 sigprocmask(SIG_UNBLOCK, &SigMask, nullptr);
322 if (CRCI)
323 const_cast<CrashRecoveryContextImpl*>(CRCI)->HandleCrash();
326 static void installExceptionOrSignalHandlers() {
327 // Setup the signal handler.
328 struct sigaction Handler;
329 Handler.sa_handler = CrashRecoverySignalHandler;
330 Handler.sa_flags = 0;
331 sigemptyset(&Handler.sa_mask);
333 for (unsigned i = 0; i != NumSignals; ++i) {
334 sigaction(Signals[i], &Handler, &PrevActions[i]);
338 static void uninstallExceptionOrSignalHandlers() {
339 // Restore the previous signal handlers.
340 for (unsigned i = 0; i != NumSignals; ++i)
341 sigaction(Signals[i], &PrevActions[i], nullptr);
344 #endif // !_WIN32
346 bool CrashRecoveryContext::RunSafely(function_ref<void()> Fn) {
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;
353 if (setjmp(CRCI->JumpBuffer) != 0) {
354 return false;
358 Fn();
359 return true;
362 #endif // !_MSC_VER
364 void CrashRecoveryContext::HandleCrash() {
365 CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl;
366 assert(CRCI && "Crash recovery context never initialized!");
367 CRCI->HandleCrash();
370 // FIXME: Portability.
371 static void setThreadBackgroundPriority() {
372 #ifdef __APPLE__
373 setpriority(PRIO_DARWIN_THREAD, 0, PRIO_DARWIN_BG);
374 #endif
377 static bool hasThreadBackgroundPriority() {
378 #ifdef __APPLE__
379 return getpriority(PRIO_DARWIN_THREAD, 0) == 1;
380 #else
381 return false;
382 #endif
385 namespace {
386 struct RunSafelyOnThreadInfo {
387 function_ref<void()> Fn;
388 CrashRecoveryContext *CRC;
389 bool UseBackgroundPriority;
390 bool Result;
394 static void RunSafelyOnThread_Dispatch(void *UserData) {
395 RunSafelyOnThreadInfo *Info =
396 reinterpret_cast<RunSafelyOnThreadInfo*>(UserData);
398 if (Info->UseBackgroundPriority)
399 setThreadBackgroundPriority();
401 Info->Result = Info->CRC->RunSafely(Info->Fn);
403 bool CrashRecoveryContext::RunSafelyOnThread(function_ref<void()> Fn,
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;