LLVM 23.0.0git
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"
14#include "llvm/Support/thread.h"
15#include <cassert>
16#include <mutex>
17#include <setjmp.h>
18#ifdef __APPLE__
19#include <sys/resource.h>
20#endif
21
22using namespace llvm;
23
24namespace {
25
26struct CrashRecoveryContextImpl;
27static LLVM_THREAD_LOCAL const CrashRecoveryContextImpl *CurrentContext;
28
29struct CrashRecoveryContextImpl {
30 // When threads are disabled, this links up all active
31 // CrashRecoveryContextImpls. When threads are enabled there's one thread
32 // per CrashRecoveryContext and CurrentContext is a thread-local, so only one
33 // CrashRecoveryContextImpl is active per thread and this is always null.
34 const CrashRecoveryContextImpl *Next;
35
36 CrashRecoveryContext *CRC;
37 ::jmp_buf JumpBuffer;
38 volatile unsigned Failed : 1;
39 unsigned SwitchedThread : 1;
40 unsigned ValidJumpBuffer : 1;
41
42public:
43 CrashRecoveryContextImpl(CrashRecoveryContext *CRC) noexcept
44 : CRC(CRC), Failed(false), SwitchedThread(false), ValidJumpBuffer(false) {
45 Next = CurrentContext;
46 CurrentContext = this;
47 }
48 ~CrashRecoveryContextImpl() {
49 if (!SwitchedThread)
50 CurrentContext = Next;
51 }
52
53 /// Called when the separate crash-recovery thread was finished, to
54 /// indicate that we don't need to clear the thread-local CurrentContext.
55 void setSwitchedThread() {
56#if defined(LLVM_ENABLE_THREADS) && LLVM_ENABLE_THREADS != 0
57 SwitchedThread = true;
58#endif
59 }
60
61 // If the function ran by the CrashRecoveryContext crashes or fails, then
62 // 'RetCode' represents the returned error code, as if it was returned by a
63 // process. 'Context' represents the signal type on Unix; on Windows, it is
64 // the ExceptionContext.
65 void HandleCrash(int RetCode, uintptr_t Context) {
66 // Eliminate the current context entry, to avoid re-entering in case the
67 // cleanup code crashes.
68 CurrentContext = Next;
69
70 assert(!Failed && "Crash recovery context already failed!");
71 Failed = true;
72
73 if (CRC->DumpStackAndCleanupOnFailure)
75
76 CRC->RetCode = RetCode;
77
78 // Jump back to the RunSafely we were called under.
79 if (ValidJumpBuffer)
80 longjmp(JumpBuffer, 1);
81
82 // Otherwise let the caller decide of the outcome of the crash. Currently
83 // this occurs when using SEH on Windows with MSVC or clang-cl.
84 }
85};
86
87std::mutex &getCrashRecoveryContextMutex() {
88 static std::mutex CrashRecoveryContextMutex;
89 return CrashRecoveryContextMutex;
90}
91
92static bool gCrashRecoveryEnabled = false;
93
94static LLVM_THREAD_LOCAL const CrashRecoveryContext *IsRecoveringFromCrash;
95
96} // namespace
97
98static void
99installExceptionOrSignalHandlers(bool NeedsPOSIXUtilitySignalHandling);
101
103
105 // On Windows, if abort() was previously triggered (and caught by a previous
106 // CrashRecoveryContext) the Windows CRT removes our installed signal handler,
107 // so we need to install it again.
109}
110
112 // Reclaim registered resources.
114 const CrashRecoveryContext *PC = IsRecoveringFromCrash;
115 IsRecoveringFromCrash = this;
116 while (i) {
118 i = tmp->next;
119 tmp->cleanupFired = true;
120 tmp->recoverResources();
121 delete tmp;
122 }
123 IsRecoveringFromCrash = PC;
124
125 CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl;
126 delete CRCI;
127}
128
130 return IsRecoveringFromCrash != nullptr;
131}
132
134 if (!gCrashRecoveryEnabled)
135 return nullptr;
136
137 const CrashRecoveryContextImpl *CRCI = CurrentContext;
138 if (!CRCI)
139 return nullptr;
140
141 return CRCI->CRC;
142}
143
144void CrashRecoveryContext::Enable(bool NeedsPOSIXUtilitySignalHandling) {
145 std::lock_guard<std::mutex> L(getCrashRecoveryContextMutex());
146 // FIXME: Shouldn't this be a refcount or something?
147 if (gCrashRecoveryEnabled)
148 return;
149 gCrashRecoveryEnabled = true;
150 installExceptionOrSignalHandlers(NeedsPOSIXUtilitySignalHandling);
151}
152
154 std::lock_guard<std::mutex> L(getCrashRecoveryContextMutex());
155 if (!gCrashRecoveryEnabled)
156 return;
157 gCrashRecoveryEnabled = false;
159}
160
162{
163 if (!cleanup)
164 return;
165 if (head)
166 head->prev = cleanup;
167 cleanup->next = head;
168 head = cleanup;
169}
170
171void
173 if (!cleanup)
174 return;
175 if (cleanup == head) {
176 head = cleanup->next;
177 if (head)
178 head->prev = nullptr;
179 }
180 else {
181 cleanup->prev->next = cleanup->next;
182 if (cleanup->next)
183 cleanup->next->prev = cleanup->prev;
184 }
185 delete cleanup;
186}
187
188#if defined(_MSC_VER)
189
190#include <windows.h> // for GetExceptionInformation
191
192// If _MSC_VER is defined, we must have SEH. Use it if it's available. It's way
193// better than VEH. Vectored exception handling catches all exceptions happening
194// on the thread with installed exception handlers, so it can interfere with
195// internal exception handling of other libraries on that thread. SEH works
196// exactly as you would expect normal exception handling to work: it only
197// catches exceptions if they would bubble out from the stack frame with __try /
198// __except.
199
200static void
201installExceptionOrSignalHandlers(bool NeedsPOSIXUtilitySignalHandling) {}
203
204// We need this function because the call to GetExceptionInformation() can only
205// occur inside the __except evaluation block
206static int ExceptionFilter(_EXCEPTION_POINTERS *Except) {
207 // Lookup the current thread local recovery object.
208 const CrashRecoveryContextImpl *CRCI = CurrentContext;
209
210 if (!CRCI) {
211 // Something has gone horribly wrong, so let's just tell everyone
212 // to keep searching
214 return EXCEPTION_CONTINUE_SEARCH;
215 }
216
217 int RetCode = (int)Except->ExceptionRecord->ExceptionCode;
218 if ((RetCode & 0xF0000000) == 0xE0000000)
219 RetCode &= ~0xF0000000; // this crash was generated by sys::Process::Exit
220
221 // Handle the crash
222 const_cast<CrashRecoveryContextImpl *>(CRCI)->HandleCrash(
223 RetCode, reinterpret_cast<uintptr_t>(Except));
224
225 return EXCEPTION_EXECUTE_HANDLER;
226}
227
228#if defined(__clang__) && defined(_M_IX86)
229// Work around PR44697.
230__attribute__((optnone))
231#endif
233 if (!gCrashRecoveryEnabled) {
234 Fn();
235 return true;
236 }
237 assert(!Impl && "Crash recovery context already initialized!");
238 Impl = new CrashRecoveryContextImpl(this);
239 __try {
240 Fn();
241 } __except (ExceptionFilter(GetExceptionInformation())) {
242 return false;
243 }
244 return true;
245}
246
247#else // !_MSC_VER
248
249#if defined(_WIN32)
250// This is a non-MSVC compiler, probably mingw gcc or clang without
251// -fms-extensions. Use vectored exception handling (VEH).
252//
253// On Windows, we can make use of vectored exception handling to catch most
254// crashing situations. Note that this does mean we will be alerted of
255// exceptions *before* structured exception handling has the opportunity to
256// catch it. Unfortunately, this causes problems in practice with other code
257// running on threads with LLVM crash recovery contexts, so we would like to
258// eventually move away from VEH.
259//
260// Vectored works on a per-thread basis, which is an advantage over
261// SetUnhandledExceptionFilter. SetUnhandledExceptionFilter also doesn't have
262// any native support for chaining exception handlers, but VEH allows more than
263// one.
264//
265// The vectored exception handler functionality was added in Windows
266// XP, so if support for older versions of Windows is required,
267// it will have to be added.
268
270
271static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo)
272{
273 // DBG_PRINTEXCEPTION_WIDE_C is not properly defined on all supported
274 // compilers and platforms, so we define it manually.
275 constexpr ULONG DbgPrintExceptionWideC = 0x4001000AL;
276 switch (ExceptionInfo->ExceptionRecord->ExceptionCode)
277 {
278 case DBG_PRINTEXCEPTION_C:
279 case DbgPrintExceptionWideC:
280 case 0x406D1388: // set debugger thread name
281 return EXCEPTION_CONTINUE_EXECUTION;
282 }
283
284 // Lookup the current thread local recovery object.
285 const CrashRecoveryContextImpl *CRCI = CurrentContext;
286
287 if (!CRCI) {
288 // Something has gone horribly wrong, so let's just tell everyone
289 // to keep searching
291 return EXCEPTION_CONTINUE_SEARCH;
292 }
293
294 // TODO: We can capture the stack backtrace here and store it on the
295 // implementation if we so choose.
296
297 int RetCode = (int)ExceptionInfo->ExceptionRecord->ExceptionCode;
298 if ((RetCode & 0xF0000000) == 0xE0000000)
299 RetCode &= ~0xF0000000; // this crash was generated by sys::Process::Exit
300
301 // Handle the crash
302 const_cast<CrashRecoveryContextImpl *>(CRCI)->HandleCrash(
303 RetCode, reinterpret_cast<uintptr_t>(ExceptionInfo));
304
305 // Note that we don't actually get here because HandleCrash calls
306 // longjmp, which means the HandleCrash function never returns.
307 llvm_unreachable("Handled the crash, should have longjmp'ed out of here");
308}
309
310// Because the Enable and Disable calls are static, it means that
311// there may not actually be an Impl available, or even a current
312// CrashRecoveryContext at all. So we make use of a thread-local
313// exception table. The handles contained in here will either be
314// non-NULL, valid VEH handles, or NULL.
315static LLVM_THREAD_LOCAL const void* sCurrentExceptionHandle;
316
317static void
318installExceptionOrSignalHandlers(bool NeedsPOSIXUtilitySignalHandling) {
319 // We can set up vectored exception handling now. We will install our
320 // handler as the front of the list, though there's no assurances that
321 // it will remain at the front (another call could install itself before
322 // our handler). This 1) isn't likely, and 2) shouldn't cause problems.
323 PVOID handle = ::AddVectoredExceptionHandler(1, ExceptionHandler);
324 sCurrentExceptionHandle = handle;
325}
326
328 PVOID currentHandle = const_cast<PVOID>(sCurrentExceptionHandle);
329 if (currentHandle) {
330 // Now we can remove the vectored exception handler from the chain
331 ::RemoveVectoredExceptionHandler(currentHandle);
332
333 // Reset the handle in our thread-local set.
334 sCurrentExceptionHandle = NULL;
335 }
336}
337
338#else // !_WIN32
339
340// Generic POSIX implementation.
341//
342// This implementation relies on synchronous signals being delivered to the
343// current thread. We use a thread local object to keep track of the active
344// crash recovery context, and install signal handlers to invoke HandleCrash on
345// the active object.
346//
347// This implementation does not attempt to chain signal handlers in any
348// reliable fashion -- if we get a signal outside of a crash recovery context we
349// simply disable crash recovery and raise the signal again.
350
351#include <signal.h>
352
353static const int Signals[] =
354 { SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGTRAP };
355static const unsigned NumSignals = std::size(Signals);
356static struct sigaction PrevActions[NumSignals];
357
358static void CrashRecoverySignalHandler(int Signal) {
359 // Lookup the current thread local recovery object.
360 const CrashRecoveryContextImpl *CRCI = CurrentContext;
361
362 if (!CRCI) {
363 // We didn't find a crash recovery context -- this means either we got a
364 // signal on a thread we didn't expect it on, the application got a signal
365 // outside of a crash recovery context, or something else went horribly
366 // wrong.
367 //
368 // Disable crash recovery and raise the signal again. The assumption here is
369 // that the enclosing application will terminate soon, and we won't want to
370 // attempt crash recovery again.
371 //
372 // This call of Disable isn't thread safe, but it doesn't actually matter.
374 raise(Signal);
375
376 // The signal will be thrown once the signal mask is restored.
377 return;
378 }
379
380 // Unblock the signal we received.
381 sigset_t SigMask;
382 sigemptyset(&SigMask);
383 sigaddset(&SigMask, Signal);
384 sigprocmask(SIG_UNBLOCK, &SigMask, nullptr);
385
386 // Return the same error code as if the program crashed, as mentioned in the
387 // section "Exit Status for Commands":
388 // https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xcu_chap02.html
389 int RetCode = 128 + Signal;
390
391 // Don't consider a broken pipe as a crash (see clang/lib/Driver/Driver.cpp)
392 if (Signal == SIGPIPE)
393 RetCode = EX_IOERR;
394
395 if (CRCI)
396 const_cast<CrashRecoveryContextImpl *>(CRCI)->HandleCrash(RetCode, Signal);
397}
398
399static void
400installExceptionOrSignalHandlers(bool NeedsPOSIXUtilitySignalHandling) {
401 // Setup the signal handler.
402 struct sigaction Handler;
403 Handler.sa_handler = CrashRecoverySignalHandler;
404 Handler.sa_flags = 0;
405 sigemptyset(&Handler.sa_mask);
406
407 for (unsigned i = 0; i != NumSignals; ++i) {
408 if (NeedsPOSIXUtilitySignalHandling) {
409 // Don't install the new handler if the signal disposition is SIG_IGN.
410 struct sigaction act;
411 if (sigaction(Signals[i], NULL, &act) == 0 && act.sa_handler != SIG_IGN)
412 sigaction(Signals[i], &Handler, &PrevActions[i]);
413 } else {
414 sigaction(Signals[i], &Handler, &PrevActions[i]);
415 }
416 }
417}
418
420 // Restore the previous signal handlers.
421 for (unsigned i = 0; i != NumSignals; ++i)
422 sigaction(Signals[i], &PrevActions[i], nullptr);
423}
424
425#endif // !_WIN32
426
428 // If crash recovery is disabled, do nothing.
429 if (gCrashRecoveryEnabled) {
430 assert(!Impl && "Crash recovery context already initialized!");
431 CrashRecoveryContextImpl *CRCI = new CrashRecoveryContextImpl(this);
432 Impl = CRCI;
433
434 CRCI->ValidJumpBuffer = true;
435 if (setjmp(CRCI->JumpBuffer) != 0) {
436 return false;
437 }
438 }
439
440 Fn();
441 return true;
442}
443
444#endif // !_MSC_VER
445
447#if defined(_WIN32)
448 // Since the exception code is actually of NTSTATUS type, we use the
449 // Microsoft-recommended 0xE prefix, to signify that this is a user error.
450 // This value is a combination of the customer field (bit 29) and severity
451 // field (bits 30-31) in the NTSTATUS specification.
452 ::RaiseException(0xE0000000 | RetCode, 0, 0, NULL);
453#else
454 // On Unix we don't need to raise an exception, we go directly to
455 // HandleCrash(), then longjmp will unwind the stack for us.
456 CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *)Impl;
457 assert(CRCI && "Crash recovery context never initialized!");
458 CRCI->HandleCrash(RetCode, 0 /*no sig num*/);
459#endif
460 llvm_unreachable("Most likely setjmp wasn't called!");
461}
462
464#if defined(_WIN32)
465 // On Windows, the code is interpreted as NTSTATUS. The two high bits
466 // represent the severity. Values starting with 0x80000000 are reserved for
467 // "warnings"; values of 0xC0000000 and up are for "errors". In practice, both
468 // are interpreted as a non-continuable signal.
469 unsigned Code = ((unsigned)RetCode & 0xF0000000) >> 28;
470 if (Code != 0xC && Code != 8)
471 return false;
472#else
473 // On Unix, signals are represented by return codes of 128 or higher.
474 // Exit code 128 is a reserved value and should not be raised as a signal.
475 if (RetCode <= 128)
476 return false;
477#endif
478 return true;
479}
480
482 if (!isCrash(RetCode))
483 return false;
484#if defined(_WIN32)
485 ::RaiseException(RetCode, 0, 0, NULL);
486#else
488 raise(RetCode - 128);
489#endif
490 return true;
491}
492
493// FIXME: Portability.
495#ifdef __APPLE__
496 setpriority(PRIO_DARWIN_THREAD, 0, PRIO_DARWIN_BG);
497#endif
498}
499
501#ifdef __APPLE__
502 return getpriority(PRIO_DARWIN_THREAD, 0) == 1;
503#else
504 return false;
505#endif
506}
507
508namespace {
509struct RunSafelyOnThreadInfo {
510 function_ref<void()> Fn;
511 CrashRecoveryContext *CRC;
512 bool UseBackgroundPriority;
513 bool Result;
514};
515} // namespace
516
517static void RunSafelyOnThread_Dispatch(void *UserData) {
518 RunSafelyOnThreadInfo *Info =
519 reinterpret_cast<RunSafelyOnThreadInfo*>(UserData);
520
521 if (Info->UseBackgroundPriority)
523
524 Info->Result = Info->CRC->RunSafely(Info->Fn);
525}
527 unsigned RequestedStackSize) {
528 bool UseBackgroundPriority = hasThreadBackgroundPriority();
529 RunSafelyOnThreadInfo Info = { Fn, this, UseBackgroundPriority, false };
530 llvm::thread Thread(RequestedStackSize == 0
531 ? std::nullopt
532 : std::optional<unsigned>(RequestedStackSize),
534 Thread.join();
535
536 if (CrashRecoveryContextImpl *CRC = (CrashRecoveryContextImpl *)Impl)
537 CRC->setSwitchedThread();
538 return Info.Result;
539}
540
542 unsigned RequestedStackSize) {
543 return RunSafelyOnThread(Fn, RequestedStackSize);
544}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static void cleanup(BlockFrequencyInfoImplBase &BFI)
Clear all memory not needed downstream.
#define LLVM_THREAD_LOCAL
\macro LLVM_THREAD_LOCAL A thread-local storage specifier which can be used with globals,...
Definition Compiler.h:713
static void installExceptionOrSignalHandlers(bool NeedsPOSIXUtilitySignalHandling)
static bool hasThreadBackgroundPriority()
static const unsigned NumSignals
static const int Signals[]
static void CrashRecoverySignalHandler(int Signal)
static void RunSafelyOnThread_Dispatch(void *UserData)
static void setThreadBackgroundPriority()
static void uninstallExceptionOrSignalHandlers()
static struct sigaction PrevActions[NumSignals]
This file contains definitions of exit codes for exit() function.
Abstract base class of cleanup handlers.
Crash recovery helper object.
static LLVM_ABI void Enable(bool NeedsPOSIXUtilitySignalHandling=false)
Enable crash recovery.
static LLVM_ABI bool isCrash(int RetCode)
Return true if RetCode indicates that a signal or an exception occurred.
static LLVM_ABI CrashRecoveryContext * GetCurrent()
Return the active context, if the code is currently executing in a thread which is in a protected con...
LLVM_ABI void HandleExit(int RetCode)
Explicitly trigger a crash recovery in the current process, and return failure from RunSafely().
LLVM_ABI bool RunSafelyOnNewStack(function_ref< void()>, unsigned RequestedStackSize=0)
LLVM_ABI void unregisterCleanup(CrashRecoveryContextCleanup *cleanup)
LLVM_ABI bool RunSafely(function_ref< void()> Fn)
Execute the provided callback function (with the given arguments) in a protected context.
static LLVM_ABI void Disable()
Disable crash recovery.
int RetCode
In case of a crash, this is the crash identifier.
static LLVM_ABI bool isRecoveringFromCrash()
Return true if the current thread is recovering from a crash.
static LLVM_ABI bool throwIfCrash(int RetCode)
Throw again a signal or an exception, after it was catched once by a CrashRecoveryContext.
LLVM_ABI void registerCleanup(CrashRecoveryContextCleanup *cleanup)
Register cleanup handler, which is used when the recovery context is finished.
LLVM_ABI bool RunSafelyOnThread(function_ref< void()>, unsigned RequestedStackSize=0)
Execute the provide callback function (with the given arguments) in a protected context which is run ...
An efficient, type-erasing, non-owning reference to a callable.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
IRHandle handle(const Type *Ty)
LLVM_ABI void DisableSystemDialogsOnCrash()
Disable all system dialog boxes that appear when the process crashes.
LLVM_ABI void unregisterHandlers()
LLVM_ABI void CleanupOnSignal(uintptr_t Context)
This function does the following:
This is an optimization pass for GlobalISel generic memory operations.
testing::Matcher< const detail::ErrorHolder & > Failed()
Definition Error.h:198