/home/arjun/llvm-project/llvm/lib/Support/CrashRecoveryContext.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | |
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/Signals.h" |
14 | | #include "llvm/Support/ThreadLocal.h" |
15 | | #include <mutex> |
16 | | #include <setjmp.h> |
17 | | #if LLVM_ON_UNIX |
18 | | #include <sysexits.h> // EX_IOERR |
19 | | #endif |
20 | | |
21 | | using namespace llvm; |
22 | | |
23 | | namespace { |
24 | | |
25 | | struct CrashRecoveryContextImpl; |
26 | | |
27 | | static 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 | | unsigned ValidJumpBuffer : 1; |
42 | | |
43 | | public: |
44 | | CrashRecoveryContextImpl(CrashRecoveryContext *CRC) noexcept |
45 | 0 | : CRC(CRC), Failed(false), SwitchedThread(false), ValidJumpBuffer(false) { |
46 | 0 | Next = CurrentContext->get(); |
47 | 0 | CurrentContext->set(this); |
48 | 0 | } |
49 | 0 | ~CrashRecoveryContextImpl() { |
50 | 0 | if (!SwitchedThread) |
51 | 0 | CurrentContext->set(Next); |
52 | 0 | } |
53 | | |
54 | | /// Called when the separate crash-recovery thread was finished, to |
55 | | /// indicate that we don't need to clear the thread-local CurrentContext. |
56 | 0 | void setSwitchedThread() { |
57 | 0 | #if defined(LLVM_ENABLE_THREADS) && LLVM_ENABLE_THREADS != 0 |
58 | 0 | SwitchedThread = true; |
59 | 0 | #endif |
60 | 0 | } |
61 | | |
62 | | // If the function ran by the CrashRecoveryContext crashes or fails, then |
63 | | // 'RetCode' represents the returned error code, as if it was returned by a |
64 | | // process. 'Context' represents the signal type on Unix; on Windows, it is |
65 | | // the ExceptionContext. |
66 | 0 | void HandleCrash(int RetCode, uintptr_t Context) { |
67 | 0 | // Eliminate the current context entry, to avoid re-entering in case the |
68 | 0 | // cleanup code crashes. |
69 | 0 | CurrentContext->set(Next); |
70 | 0 |
|
71 | 0 | assert(!Failed && "Crash recovery context already failed!"); |
72 | 0 | Failed = true; |
73 | 0 |
|
74 | 0 | if (CRC->DumpStackAndCleanupOnFailure) |
75 | 0 | sys::CleanupOnSignal(Context); |
76 | 0 |
|
77 | 0 | CRC->RetCode = RetCode; |
78 | 0 |
|
79 | 0 | // Jump back to the RunSafely we were called under. |
80 | 0 | if (ValidJumpBuffer) |
81 | 0 | longjmp(JumpBuffer, 1); |
82 | 0 |
|
83 | 0 | // Otherwise let the caller decide of the outcome of the crash. Currently |
84 | 0 | // this occurs when using SEH on Windows with MSVC or clang-cl. |
85 | 0 | } |
86 | | }; |
87 | | } |
88 | | |
89 | | static ManagedStatic<std::mutex> gCrashRecoveryContextMutex; |
90 | | static bool gCrashRecoveryEnabled = false; |
91 | | |
92 | | static ManagedStatic<sys::ThreadLocal<const CrashRecoveryContext>> |
93 | | tlIsRecoveringFromCrash; |
94 | | |
95 | | static void installExceptionOrSignalHandlers(); |
96 | | static void uninstallExceptionOrSignalHandlers(); |
97 | | |
98 | 0 | CrashRecoveryContextCleanup::~CrashRecoveryContextCleanup() {} |
99 | | |
100 | 0 | CrashRecoveryContext::~CrashRecoveryContext() { |
101 | 0 | // Reclaim registered resources. |
102 | 0 | CrashRecoveryContextCleanup *i = head; |
103 | 0 | const CrashRecoveryContext *PC = tlIsRecoveringFromCrash->get(); |
104 | 0 | tlIsRecoveringFromCrash->set(this); |
105 | 0 | while (i) { |
106 | 0 | CrashRecoveryContextCleanup *tmp = i; |
107 | 0 | i = tmp->next; |
108 | 0 | tmp->cleanupFired = true; |
109 | 0 | tmp->recoverResources(); |
110 | 0 | delete tmp; |
111 | 0 | } |
112 | 0 | tlIsRecoveringFromCrash->set(PC); |
113 | 0 |
|
114 | 0 | CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl; |
115 | 0 | delete CRCI; |
116 | 0 | } |
117 | | |
118 | 0 | bool CrashRecoveryContext::isRecoveringFromCrash() { |
119 | 0 | return tlIsRecoveringFromCrash->get() != nullptr; |
120 | 0 | } |
121 | | |
122 | 0 | CrashRecoveryContext *CrashRecoveryContext::GetCurrent() { |
123 | 0 | if (!gCrashRecoveryEnabled) |
124 | 0 | return nullptr; |
125 | 0 | |
126 | 0 | const CrashRecoveryContextImpl *CRCI = CurrentContext->get(); |
127 | 0 | if (!CRCI) |
128 | 0 | return nullptr; |
129 | 0 | |
130 | 0 | return CRCI->CRC; |
131 | 0 | } |
132 | | |
133 | 0 | void CrashRecoveryContext::Enable() { |
134 | 0 | std::lock_guard<std::mutex> L(*gCrashRecoveryContextMutex); |
135 | 0 | // FIXME: Shouldn't this be a refcount or something? |
136 | 0 | if (gCrashRecoveryEnabled) |
137 | 0 | return; |
138 | 0 | gCrashRecoveryEnabled = true; |
139 | 0 | installExceptionOrSignalHandlers(); |
140 | 0 | } |
141 | | |
142 | 0 | void CrashRecoveryContext::Disable() { |
143 | 0 | std::lock_guard<std::mutex> L(*gCrashRecoveryContextMutex); |
144 | 0 | if (!gCrashRecoveryEnabled) |
145 | 0 | return; |
146 | 0 | gCrashRecoveryEnabled = false; |
147 | 0 | uninstallExceptionOrSignalHandlers(); |
148 | 0 | } |
149 | | |
150 | | void CrashRecoveryContext::registerCleanup(CrashRecoveryContextCleanup *cleanup) |
151 | 0 | { |
152 | 0 | if (!cleanup) |
153 | 0 | return; |
154 | 0 | if (head) |
155 | 0 | head->prev = cleanup; |
156 | 0 | cleanup->next = head; |
157 | 0 | head = cleanup; |
158 | 0 | } |
159 | | |
160 | | void |
161 | 0 | CrashRecoveryContext::unregisterCleanup(CrashRecoveryContextCleanup *cleanup) { |
162 | 0 | if (!cleanup) |
163 | 0 | return; |
164 | 0 | if (cleanup == head) { |
165 | 0 | head = cleanup->next; |
166 | 0 | if (head) |
167 | 0 | head->prev = nullptr; |
168 | 0 | } |
169 | 0 | else { |
170 | 0 | cleanup->prev->next = cleanup->next; |
171 | 0 | if (cleanup->next) |
172 | 0 | cleanup->next->prev = cleanup->prev; |
173 | 0 | } |
174 | 0 | delete cleanup; |
175 | 0 | } |
176 | | |
177 | | #if defined(_MSC_VER) |
178 | | |
179 | | #include <windows.h> // for GetExceptionInformation |
180 | | |
181 | | // If _MSC_VER is defined, we must have SEH. Use it if it's available. It's way |
182 | | // better than VEH. Vectored exception handling catches all exceptions happening |
183 | | // on the thread with installed exception handlers, so it can interfere with |
184 | | // internal exception handling of other libraries on that thread. SEH works |
185 | | // exactly as you would expect normal exception handling to work: it only |
186 | | // catches exceptions if they would bubble out from the stack frame with __try / |
187 | | // __except. |
188 | | |
189 | | static void installExceptionOrSignalHandlers() {} |
190 | | static void uninstallExceptionOrSignalHandlers() {} |
191 | | |
192 | | // We need this function because the call to GetExceptionInformation() can only |
193 | | // occur inside the __except evaluation block |
194 | | static int ExceptionFilter(_EXCEPTION_POINTERS *Except) { |
195 | | // Lookup the current thread local recovery object. |
196 | | const CrashRecoveryContextImpl *CRCI = CurrentContext->get(); |
197 | | |
198 | | if (!CRCI) { |
199 | | // Something has gone horribly wrong, so let's just tell everyone |
200 | | // to keep searching |
201 | | CrashRecoveryContext::Disable(); |
202 | | return EXCEPTION_CONTINUE_SEARCH; |
203 | | } |
204 | | |
205 | | int RetCode = (int)Except->ExceptionRecord->ExceptionCode; |
206 | | if ((RetCode & 0xF0000000) == 0xE0000000) |
207 | | RetCode &= ~0xF0000000; // this crash was generated by sys::Process::Exit |
208 | | |
209 | | // Handle the crash |
210 | | const_cast<CrashRecoveryContextImpl *>(CRCI)->HandleCrash( |
211 | | RetCode, reinterpret_cast<uintptr_t>(Except)); |
212 | | |
213 | | return EXCEPTION_EXECUTE_HANDLER; |
214 | | } |
215 | | |
216 | | #if defined(__clang__) && defined(_M_IX86) |
217 | | // Work around PR44697. |
218 | | __attribute__((optnone)) |
219 | | #endif |
220 | | bool CrashRecoveryContext::RunSafely(function_ref<void()> Fn) { |
221 | | if (!gCrashRecoveryEnabled) { |
222 | | Fn(); |
223 | | return true; |
224 | | } |
225 | | assert(!Impl && "Crash recovery context already initialized!"); |
226 | | Impl = new CrashRecoveryContextImpl(this); |
227 | | __try { |
228 | | Fn(); |
229 | | } __except (ExceptionFilter(GetExceptionInformation())) { |
230 | | return false; |
231 | | } |
232 | | return true; |
233 | | } |
234 | | |
235 | | #else // !_MSC_VER |
236 | | |
237 | | #if defined(_WIN32) |
238 | | // This is a non-MSVC compiler, probably mingw gcc or clang without |
239 | | // -fms-extensions. Use vectored exception handling (VEH). |
240 | | // |
241 | | // On Windows, we can make use of vectored exception handling to catch most |
242 | | // crashing situations. Note that this does mean we will be alerted of |
243 | | // exceptions *before* structured exception handling has the opportunity to |
244 | | // catch it. Unfortunately, this causes problems in practice with other code |
245 | | // running on threads with LLVM crash recovery contexts, so we would like to |
246 | | // eventually move away from VEH. |
247 | | // |
248 | | // Vectored works on a per-thread basis, which is an advantage over |
249 | | // SetUnhandledExceptionFilter. SetUnhandledExceptionFilter also doesn't have |
250 | | // any native support for chaining exception handlers, but VEH allows more than |
251 | | // one. |
252 | | // |
253 | | // The vectored exception handler functionality was added in Windows |
254 | | // XP, so if support for older versions of Windows is required, |
255 | | // it will have to be added. |
256 | | |
257 | | #include "llvm/Support/Windows/WindowsSupport.h" |
258 | | |
259 | | static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) |
260 | | { |
261 | | // DBG_PRINTEXCEPTION_WIDE_C is not properly defined on all supported |
262 | | // compilers and platforms, so we define it manually. |
263 | | constexpr ULONG DbgPrintExceptionWideC = 0x4001000AL; |
264 | | switch (ExceptionInfo->ExceptionRecord->ExceptionCode) |
265 | | { |
266 | | case DBG_PRINTEXCEPTION_C: |
267 | | case DbgPrintExceptionWideC: |
268 | | case 0x406D1388: // set debugger thread name |
269 | | return EXCEPTION_CONTINUE_EXECUTION; |
270 | | } |
271 | | |
272 | | // Lookup the current thread local recovery object. |
273 | | const CrashRecoveryContextImpl *CRCI = CurrentContext->get(); |
274 | | |
275 | | if (!CRCI) { |
276 | | // Something has gone horribly wrong, so let's just tell everyone |
277 | | // to keep searching |
278 | | CrashRecoveryContext::Disable(); |
279 | | return EXCEPTION_CONTINUE_SEARCH; |
280 | | } |
281 | | |
282 | | // TODO: We can capture the stack backtrace here and store it on the |
283 | | // implementation if we so choose. |
284 | | |
285 | | int RetCode = (int)ExceptionInfo->ExceptionRecord->ExceptionCode; |
286 | | if ((RetCode & 0xF0000000) == 0xE0000000) |
287 | | RetCode &= ~0xF0000000; // this crash was generated by sys::Process::Exit |
288 | | |
289 | | // Handle the crash |
290 | | const_cast<CrashRecoveryContextImpl *>(CRCI)->HandleCrash( |
291 | | RetCode, reinterpret_cast<uintptr_t>(ExceptionInfo)); |
292 | | |
293 | | // Note that we don't actually get here because HandleCrash calls |
294 | | // longjmp, which means the HandleCrash function never returns. |
295 | | llvm_unreachable("Handled the crash, should have longjmp'ed out of here"); |
296 | | } |
297 | | |
298 | | // Because the Enable and Disable calls are static, it means that |
299 | | // there may not actually be an Impl available, or even a current |
300 | | // CrashRecoveryContext at all. So we make use of a thread-local |
301 | | // exception table. The handles contained in here will either be |
302 | | // non-NULL, valid VEH handles, or NULL. |
303 | | static sys::ThreadLocal<const void> sCurrentExceptionHandle; |
304 | | |
305 | | static void installExceptionOrSignalHandlers() { |
306 | | // We can set up vectored exception handling now. We will install our |
307 | | // handler as the front of the list, though there's no assurances that |
308 | | // it will remain at the front (another call could install itself before |
309 | | // our handler). This 1) isn't likely, and 2) shouldn't cause problems. |
310 | | PVOID handle = ::AddVectoredExceptionHandler(1, ExceptionHandler); |
311 | | sCurrentExceptionHandle.set(handle); |
312 | | } |
313 | | |
314 | | static void uninstallExceptionOrSignalHandlers() { |
315 | | PVOID currentHandle = const_cast<PVOID>(sCurrentExceptionHandle.get()); |
316 | | if (currentHandle) { |
317 | | // Now we can remove the vectored exception handler from the chain |
318 | | ::RemoveVectoredExceptionHandler(currentHandle); |
319 | | |
320 | | // Reset the handle in our thread-local set. |
321 | | sCurrentExceptionHandle.set(NULL); |
322 | | } |
323 | | } |
324 | | |
325 | | #else // !_WIN32 |
326 | | |
327 | | // Generic POSIX implementation. |
328 | | // |
329 | | // This implementation relies on synchronous signals being delivered to the |
330 | | // current thread. We use a thread local object to keep track of the active |
331 | | // crash recovery context, and install signal handlers to invoke HandleCrash on |
332 | | // the active object. |
333 | | // |
334 | | // This implementation does not attempt to chain signal handlers in any |
335 | | // reliable fashion -- if we get a signal outside of a crash recovery context we |
336 | | // simply disable crash recovery and raise the signal again. |
337 | | |
338 | | #include <signal.h> |
339 | | |
340 | | static const int Signals[] = |
341 | | { SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGTRAP }; |
342 | | static const unsigned NumSignals = array_lengthof(Signals); |
343 | | static struct sigaction PrevActions[NumSignals]; |
344 | | |
345 | 0 | static void CrashRecoverySignalHandler(int Signal) { |
346 | 0 | // Lookup the current thread local recovery object. |
347 | 0 | const CrashRecoveryContextImpl *CRCI = CurrentContext->get(); |
348 | 0 |
|
349 | 0 | if (!CRCI) { |
350 | 0 | // We didn't find a crash recovery context -- this means either we got a |
351 | 0 | // signal on a thread we didn't expect it on, the application got a signal |
352 | 0 | // outside of a crash recovery context, or something else went horribly |
353 | 0 | // wrong. |
354 | 0 | // |
355 | 0 | // Disable crash recovery and raise the signal again. The assumption here is |
356 | 0 | // that the enclosing application will terminate soon, and we won't want to |
357 | 0 | // attempt crash recovery again. |
358 | 0 | // |
359 | 0 | // This call of Disable isn't thread safe, but it doesn't actually matter. |
360 | 0 | CrashRecoveryContext::Disable(); |
361 | 0 | raise(Signal); |
362 | 0 |
|
363 | 0 | // The signal will be thrown once the signal mask is restored. |
364 | 0 | return; |
365 | 0 | } |
366 | 0 | |
367 | 0 | // Unblock the signal we received. |
368 | 0 | sigset_t SigMask; |
369 | 0 | sigemptyset(&SigMask); |
370 | 0 | sigaddset(&SigMask, Signal); |
371 | 0 | sigprocmask(SIG_UNBLOCK, &SigMask, nullptr); |
372 | 0 |
|
373 | 0 | // As per convention, -2 indicates a crash or timeout as opposed to failure to |
374 | 0 | // execute (see llvm/include/llvm/Support/Program.h) |
375 | 0 | int RetCode = -2; |
376 | 0 |
|
377 | 0 | // Don't consider a broken pipe as a crash (see clang/lib/Driver/Driver.cpp) |
378 | 0 | if (Signal == SIGPIPE) |
379 | 0 | RetCode = EX_IOERR; |
380 | 0 |
|
381 | 0 | if (CRCI) |
382 | 0 | const_cast<CrashRecoveryContextImpl *>(CRCI)->HandleCrash(RetCode, Signal); |
383 | 0 | } |
384 | | |
385 | 0 | static void installExceptionOrSignalHandlers() { |
386 | 0 | // Setup the signal handler. |
387 | 0 | struct sigaction Handler; |
388 | 0 | Handler.sa_handler = CrashRecoverySignalHandler; |
389 | 0 | Handler.sa_flags = 0; |
390 | 0 | sigemptyset(&Handler.sa_mask); |
391 | 0 |
|
392 | 0 | for (unsigned i = 0; i != NumSignals; ++i) { |
393 | 0 | sigaction(Signals[i], &Handler, &PrevActions[i]); |
394 | 0 | } |
395 | 0 | } |
396 | | |
397 | 0 | static void uninstallExceptionOrSignalHandlers() { |
398 | 0 | // Restore the previous signal handlers. |
399 | 0 | for (unsigned i = 0; i != NumSignals; ++i) |
400 | 0 | sigaction(Signals[i], &PrevActions[i], nullptr); |
401 | 0 | } |
402 | | |
403 | | #endif // !_WIN32 |
404 | | |
405 | 0 | bool CrashRecoveryContext::RunSafely(function_ref<void()> Fn) { |
406 | 0 | // If crash recovery is disabled, do nothing. |
407 | 0 | if (gCrashRecoveryEnabled) { |
408 | 0 | assert(!Impl && "Crash recovery context already initialized!"); |
409 | 0 | CrashRecoveryContextImpl *CRCI = new CrashRecoveryContextImpl(this); |
410 | 0 | Impl = CRCI; |
411 | 0 |
|
412 | 0 | CRCI->ValidJumpBuffer = true; |
413 | 0 | if (setjmp(CRCI->JumpBuffer) != 0) { |
414 | 0 | return false; |
415 | 0 | } |
416 | 0 | } |
417 | 0 | |
418 | 0 | Fn(); |
419 | 0 | return true; |
420 | 0 | } |
421 | | |
422 | | #endif // !_MSC_VER |
423 | | |
424 | | LLVM_ATTRIBUTE_NORETURN |
425 | 0 | void CrashRecoveryContext::HandleExit(int RetCode) { |
426 | | #if defined(_WIN32) |
427 | | // SEH and VEH |
428 | | ::RaiseException(0xE0000000 | RetCode, 0, 0, NULL); |
429 | | #else |
430 | | // On Unix we don't need to raise an exception, we go directly to |
431 | 0 | // HandleCrash(), then longjmp will unwind the stack for us. |
432 | 0 | CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *)Impl; |
433 | 0 | assert(CRCI && "Crash recovery context never initialized!"); |
434 | 0 | CRCI->HandleCrash(RetCode, 0 /*no sig num*/); |
435 | 0 | #endif |
436 | 0 | llvm_unreachable("Most likely setjmp wasn't called!"); |
437 | 0 | } |
438 | | |
439 | | // FIXME: Portability. |
440 | 0 | static void setThreadBackgroundPriority() { |
441 | | #ifdef __APPLE__ |
442 | | setpriority(PRIO_DARWIN_THREAD, 0, PRIO_DARWIN_BG); |
443 | | #endif |
444 | | } |
445 | | |
446 | 0 | static bool hasThreadBackgroundPriority() { |
447 | | #ifdef __APPLE__ |
448 | | return getpriority(PRIO_DARWIN_THREAD, 0) == 1; |
449 | | #else |
450 | | return false; |
451 | 0 | #endif |
452 | 0 | } |
453 | | |
454 | | namespace { |
455 | | struct RunSafelyOnThreadInfo { |
456 | | function_ref<void()> Fn; |
457 | | CrashRecoveryContext *CRC; |
458 | | bool UseBackgroundPriority; |
459 | | bool Result; |
460 | | }; |
461 | | } |
462 | | |
463 | 0 | static void RunSafelyOnThread_Dispatch(void *UserData) { |
464 | 0 | RunSafelyOnThreadInfo *Info = |
465 | 0 | reinterpret_cast<RunSafelyOnThreadInfo*>(UserData); |
466 | 0 |
|
467 | 0 | if (Info->UseBackgroundPriority) |
468 | 0 | setThreadBackgroundPriority(); |
469 | 0 |
|
470 | 0 | Info->Result = Info->CRC->RunSafely(Info->Fn); |
471 | 0 | } |
472 | | bool CrashRecoveryContext::RunSafelyOnThread(function_ref<void()> Fn, |
473 | 0 | unsigned RequestedStackSize) { |
474 | 0 | bool UseBackgroundPriority = hasThreadBackgroundPriority(); |
475 | 0 | RunSafelyOnThreadInfo Info = { Fn, this, UseBackgroundPriority, false }; |
476 | 0 | llvm_execute_on_thread(RunSafelyOnThread_Dispatch, &Info, |
477 | 0 | RequestedStackSize == 0 |
478 | 0 | ? llvm::None |
479 | 0 | : llvm::Optional<unsigned>(RequestedStackSize)); |
480 | 0 | if (CrashRecoveryContextImpl *CRC = (CrashRecoveryContextImpl *)Impl) |
481 | 0 | CRC->setSwitchedThread(); |
482 | 0 | return Info.Result; |
483 | 0 | } |