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