1 //===--- CrashRecoveryContext.cpp - Crash Recovery ------------------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 #include "llvm/Support/CrashRecoveryContext.h"
11 #include "llvm/ADT/SmallString.h"
12 #include "llvm/Config/config.h"
13 #include "llvm/Support/Mutex.h"
14 #include "llvm/Support/ThreadLocal.h"
21 struct CrashRecoveryContextImpl
;
23 static sys::ThreadLocal
<const CrashRecoveryContextImpl
> CurrentContext
;
25 struct CrashRecoveryContextImpl
{
26 CrashRecoveryContext
*CRC
;
27 std::string Backtrace
;
29 volatile unsigned Failed
: 1;
32 CrashRecoveryContextImpl(CrashRecoveryContext
*CRC
) : CRC(CRC
),
34 CurrentContext
.set(this);
36 ~CrashRecoveryContextImpl() {
37 CurrentContext
.erase();
41 // Eliminate the current context entry, to avoid re-entering in case the
42 // cleanup code crashes.
43 CurrentContext
.erase();
45 assert(!Failed
&& "Crash recovery context already failed!");
48 // FIXME: Stash the backtrace.
50 // Jump back to the RunSafely we were called under.
51 longjmp(JumpBuffer
, 1);
57 static sys::Mutex gCrashRecoveryContexMutex
;
58 static bool gCrashRecoveryEnabled
= false;
60 static sys::ThreadLocal
<const CrashRecoveryContextCleanup
>
61 tlIsRecoveringFromCrash
;
63 CrashRecoveryContextCleanup::~CrashRecoveryContextCleanup() {}
65 CrashRecoveryContext::~CrashRecoveryContext() {
66 // Reclaim registered resources.
67 CrashRecoveryContextCleanup
*i
= head
;
68 tlIsRecoveringFromCrash
.set(head
);
70 CrashRecoveryContextCleanup
*tmp
= i
;
72 tmp
->cleanupFired
= true;
73 tmp
->recoverResources();
76 tlIsRecoveringFromCrash
.erase();
78 CrashRecoveryContextImpl
*CRCI
= (CrashRecoveryContextImpl
*) Impl
;
82 bool CrashRecoveryContext::isRecoveringFromCrash() {
83 return tlIsRecoveringFromCrash
.get() != 0;
86 CrashRecoveryContext
*CrashRecoveryContext::GetCurrent() {
87 if (!gCrashRecoveryEnabled
)
90 const CrashRecoveryContextImpl
*CRCI
= CurrentContext
.get();
97 void CrashRecoveryContext::registerCleanup(CrashRecoveryContextCleanup
*cleanup
)
102 head
->prev
= cleanup
;
103 cleanup
->next
= head
;
108 CrashRecoveryContext::unregisterCleanup(CrashRecoveryContextCleanup
*cleanup
) {
111 if (cleanup
== head
) {
112 head
= cleanup
->next
;
117 cleanup
->prev
->next
= cleanup
->next
;
119 cleanup
->next
->prev
= cleanup
->prev
;
126 // FIXME: No real Win32 implementation currently.
128 void CrashRecoveryContext::Enable() {
129 sys::ScopedLock
L(gCrashRecoveryContexMutex
);
131 if (gCrashRecoveryEnabled
)
134 gCrashRecoveryEnabled
= true;
137 void CrashRecoveryContext::Disable() {
138 sys::ScopedLock
L(gCrashRecoveryContexMutex
);
140 if (!gCrashRecoveryEnabled
)
143 gCrashRecoveryEnabled
= false;
148 // Generic POSIX implementation.
150 // This implementation relies on synchronous signals being delivered to the
151 // current thread. We use a thread local object to keep track of the active
152 // crash recovery context, and install signal handlers to invoke HandleCrash on
153 // the active object.
155 // This implementation does not to attempt to chain signal handlers in any
156 // reliable fashion -- if we get a signal outside of a crash recovery context we
157 // simply disable crash recovery and raise the signal again.
161 static int Signals
[] = { SIGABRT
, SIGBUS
, SIGFPE
, SIGILL
, SIGSEGV
, SIGTRAP
};
162 static const unsigned NumSignals
= sizeof(Signals
) / sizeof(Signals
[0]);
163 static struct sigaction PrevActions
[NumSignals
];
165 static void CrashRecoverySignalHandler(int Signal
) {
166 // Lookup the current thread local recovery object.
167 const CrashRecoveryContextImpl
*CRCI
= CurrentContext
.get();
170 // We didn't find a crash recovery context -- this means either we got a
171 // signal on a thread we didn't expect it on, the application got a signal
172 // outside of a crash recovery context, or something else went horribly
175 // Disable crash recovery and raise the signal again. The assumption here is
176 // that the enclosing application will terminate soon, and we won't want to
177 // attempt crash recovery again.
179 // This call of Disable isn't thread safe, but it doesn't actually matter.
180 CrashRecoveryContext::Disable();
183 // The signal will be thrown once the signal mask is restored.
187 // Unblock the signal we received.
189 sigemptyset(&SigMask
);
190 sigaddset(&SigMask
, Signal
);
191 sigprocmask(SIG_UNBLOCK
, &SigMask
, 0);
194 const_cast<CrashRecoveryContextImpl
*>(CRCI
)->HandleCrash();
197 void CrashRecoveryContext::Enable() {
198 sys::ScopedLock
L(gCrashRecoveryContexMutex
);
200 if (gCrashRecoveryEnabled
)
203 gCrashRecoveryEnabled
= true;
205 // Setup the signal handler.
206 struct sigaction Handler
;
207 Handler
.sa_handler
= CrashRecoverySignalHandler
;
208 Handler
.sa_flags
= 0;
209 sigemptyset(&Handler
.sa_mask
);
211 for (unsigned i
= 0; i
!= NumSignals
; ++i
) {
212 sigaction(Signals
[i
], &Handler
, &PrevActions
[i
]);
216 void CrashRecoveryContext::Disable() {
217 sys::ScopedLock
L(gCrashRecoveryContexMutex
);
219 if (!gCrashRecoveryEnabled
)
222 gCrashRecoveryEnabled
= false;
224 // Restore the previous signal handlers.
225 for (unsigned i
= 0; i
!= NumSignals
; ++i
)
226 sigaction(Signals
[i
], &PrevActions
[i
], 0);
231 bool CrashRecoveryContext::RunSafely(void (*Fn
)(void*), void *UserData
) {
232 // If crash recovery is disabled, do nothing.
233 if (gCrashRecoveryEnabled
) {
234 assert(!Impl
&& "Crash recovery context already initialized!");
235 CrashRecoveryContextImpl
*CRCI
= new CrashRecoveryContextImpl(this);
238 if (setjmp(CRCI
->JumpBuffer
) != 0) {
247 void CrashRecoveryContext::HandleCrash() {
248 CrashRecoveryContextImpl
*CRCI
= (CrashRecoveryContextImpl
*) Impl
;
249 assert(CRCI
&& "Crash recovery context never initialized!");
253 const std::string
&CrashRecoveryContext::getBacktrace() const {
254 CrashRecoveryContextImpl
*CRC
= (CrashRecoveryContextImpl
*) Impl
;
255 assert(CRC
&& "Crash recovery context never initialized!");
256 assert(CRC
->Failed
&& "No crash was detected!");
257 return CRC
->Backtrace
;
263 struct RunSafelyOnThreadInfo
{
264 void (*UserFn
)(void*);
266 CrashRecoveryContext
*CRC
;
271 static void RunSafelyOnThread_Dispatch(void *UserData
) {
272 RunSafelyOnThreadInfo
*Info
=
273 reinterpret_cast<RunSafelyOnThreadInfo
*>(UserData
);
274 Info
->Result
= Info
->CRC
->RunSafely(Info
->UserFn
, Info
->UserData
);
276 bool CrashRecoveryContext::RunSafelyOnThread(void (*Fn
)(void*), void *UserData
,
277 unsigned RequestedStackSize
) {
278 RunSafelyOnThreadInfo Info
= { Fn
, UserData
, this, false };
279 llvm_execute_on_thread(RunSafelyOnThread_Dispatch
, &Info
, RequestedStackSize
);