Mercurial > hg > Members > tobaru > cbc > CbC_llvm
comparison lib/Support/CrashRecoveryContext.cpp @ 0:95c75e76d11b
LLVM 3.4
author | Kaito Tokumori <e105711@ie.u-ryukyu.ac.jp> |
---|---|
date | Thu, 12 Dec 2013 13:56:28 +0900 |
parents | |
children | 54457678186b |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:95c75e76d11b |
---|---|
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/ADT/SmallString.h" | |
12 #include "llvm/Config/config.h" | |
13 #include "llvm/Support/ErrorHandling.h" | |
14 #include "llvm/Support/ManagedStatic.h" | |
15 #include "llvm/Support/Mutex.h" | |
16 #include "llvm/Support/ThreadLocal.h" | |
17 #include <cstdio> | |
18 #include <setjmp.h> | |
19 using namespace llvm; | |
20 | |
21 namespace { | |
22 | |
23 struct CrashRecoveryContextImpl; | |
24 | |
25 static ManagedStatic<sys::ThreadLocal<const CrashRecoveryContextImpl> > CurrentContext; | |
26 | |
27 struct CrashRecoveryContextImpl { | |
28 CrashRecoveryContext *CRC; | |
29 std::string Backtrace; | |
30 ::jmp_buf JumpBuffer; | |
31 volatile unsigned Failed : 1; | |
32 unsigned SwitchedThread : 1; | |
33 | |
34 public: | |
35 CrashRecoveryContextImpl(CrashRecoveryContext *CRC) : CRC(CRC), | |
36 Failed(false), | |
37 SwitchedThread(false) { | |
38 CurrentContext->set(this); | |
39 } | |
40 ~CrashRecoveryContextImpl() { | |
41 if (!SwitchedThread) | |
42 CurrentContext->erase(); | |
43 } | |
44 | |
45 /// \brief Called when the separate crash-recovery thread was finished, to | |
46 /// indicate that we don't need to clear the thread-local CurrentContext. | |
47 void setSwitchedThread() { SwitchedThread = true; } | |
48 | |
49 void HandleCrash() { | |
50 // Eliminate the current context entry, to avoid re-entering in case the | |
51 // cleanup code crashes. | |
52 CurrentContext->erase(); | |
53 | |
54 assert(!Failed && "Crash recovery context already failed!"); | |
55 Failed = true; | |
56 | |
57 // FIXME: Stash the backtrace. | |
58 | |
59 // Jump back to the RunSafely we were called under. | |
60 longjmp(JumpBuffer, 1); | |
61 } | |
62 }; | |
63 | |
64 } | |
65 | |
66 static ManagedStatic<sys::Mutex> gCrashRecoveryContextMutex; | |
67 static bool gCrashRecoveryEnabled = false; | |
68 | |
69 static ManagedStatic<sys::ThreadLocal<const CrashRecoveryContextCleanup> > | |
70 tlIsRecoveringFromCrash; | |
71 | |
72 CrashRecoveryContextCleanup::~CrashRecoveryContextCleanup() {} | |
73 | |
74 CrashRecoveryContext::~CrashRecoveryContext() { | |
75 // Reclaim registered resources. | |
76 CrashRecoveryContextCleanup *i = head; | |
77 tlIsRecoveringFromCrash->set(head); | |
78 while (i) { | |
79 CrashRecoveryContextCleanup *tmp = i; | |
80 i = tmp->next; | |
81 tmp->cleanupFired = true; | |
82 tmp->recoverResources(); | |
83 delete tmp; | |
84 } | |
85 tlIsRecoveringFromCrash->erase(); | |
86 | |
87 CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl; | |
88 delete CRCI; | |
89 } | |
90 | |
91 bool CrashRecoveryContext::isRecoveringFromCrash() { | |
92 return tlIsRecoveringFromCrash->get() != 0; | |
93 } | |
94 | |
95 CrashRecoveryContext *CrashRecoveryContext::GetCurrent() { | |
96 if (!gCrashRecoveryEnabled) | |
97 return 0; | |
98 | |
99 const CrashRecoveryContextImpl *CRCI = CurrentContext->get(); | |
100 if (!CRCI) | |
101 return 0; | |
102 | |
103 return CRCI->CRC; | |
104 } | |
105 | |
106 void CrashRecoveryContext::registerCleanup(CrashRecoveryContextCleanup *cleanup) | |
107 { | |
108 if (!cleanup) | |
109 return; | |
110 if (head) | |
111 head->prev = cleanup; | |
112 cleanup->next = head; | |
113 head = cleanup; | |
114 } | |
115 | |
116 void | |
117 CrashRecoveryContext::unregisterCleanup(CrashRecoveryContextCleanup *cleanup) { | |
118 if (!cleanup) | |
119 return; | |
120 if (cleanup == head) { | |
121 head = cleanup->next; | |
122 if (head) | |
123 head->prev = 0; | |
124 } | |
125 else { | |
126 cleanup->prev->next = cleanup->next; | |
127 if (cleanup->next) | |
128 cleanup->next->prev = cleanup->prev; | |
129 } | |
130 delete cleanup; | |
131 } | |
132 | |
133 #ifdef LLVM_ON_WIN32 | |
134 | |
135 #include "Windows/Windows.h" | |
136 | |
137 // On Windows, we can make use of vectored exception handling to | |
138 // catch most crashing situations. Note that this does mean | |
139 // we will be alerted of exceptions *before* structured exception | |
140 // handling has the opportunity to catch it. But that isn't likely | |
141 // to cause problems because nowhere in the project is SEH being | |
142 // used. | |
143 // | |
144 // Vectored exception handling is built on top of SEH, and so it | |
145 // works on a per-thread basis. | |
146 // | |
147 // The vectored exception handler functionality was added in Windows | |
148 // XP, so if support for older versions of Windows is required, | |
149 // it will have to be added. | |
150 // | |
151 // If we want to support as far back as Win2k, we could use the | |
152 // SetUnhandledExceptionFilter API, but there's a risk of that | |
153 // being entirely overwritten (it's not a chain). | |
154 | |
155 static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) | |
156 { | |
157 // Lookup the current thread local recovery object. | |
158 const CrashRecoveryContextImpl *CRCI = CurrentContext->get(); | |
159 | |
160 if (!CRCI) { | |
161 // Something has gone horribly wrong, so let's just tell everyone | |
162 // to keep searching | |
163 CrashRecoveryContext::Disable(); | |
164 return EXCEPTION_CONTINUE_SEARCH; | |
165 } | |
166 | |
167 // TODO: We can capture the stack backtrace here and store it on the | |
168 // implementation if we so choose. | |
169 | |
170 // Handle the crash | |
171 const_cast<CrashRecoveryContextImpl*>(CRCI)->HandleCrash(); | |
172 | |
173 // Note that we don't actually get here because HandleCrash calls | |
174 // longjmp, which means the HandleCrash function never returns. | |
175 llvm_unreachable("Handled the crash, should have longjmp'ed out of here"); | |
176 } | |
177 | |
178 // Because the Enable and Disable calls are static, it means that | |
179 // there may not actually be an Impl available, or even a current | |
180 // CrashRecoveryContext at all. So we make use of a thread-local | |
181 // exception table. The handles contained in here will either be | |
182 // non-NULL, valid VEH handles, or NULL. | |
183 static sys::ThreadLocal<const void> sCurrentExceptionHandle; | |
184 | |
185 void CrashRecoveryContext::Enable() { | |
186 sys::ScopedLock L(*gCrashRecoveryContextMutex); | |
187 | |
188 if (gCrashRecoveryEnabled) | |
189 return; | |
190 | |
191 gCrashRecoveryEnabled = true; | |
192 | |
193 // We can set up vectored exception handling now. We will install our | |
194 // handler as the front of the list, though there's no assurances that | |
195 // it will remain at the front (another call could install itself before | |
196 // our handler). This 1) isn't likely, and 2) shouldn't cause problems. | |
197 PVOID handle = ::AddVectoredExceptionHandler(1, ExceptionHandler); | |
198 sCurrentExceptionHandle.set(handle); | |
199 } | |
200 | |
201 void CrashRecoveryContext::Disable() { | |
202 sys::ScopedLock L(*gCrashRecoveryContextMutex); | |
203 | |
204 if (!gCrashRecoveryEnabled) | |
205 return; | |
206 | |
207 gCrashRecoveryEnabled = false; | |
208 | |
209 PVOID currentHandle = const_cast<PVOID>(sCurrentExceptionHandle.get()); | |
210 if (currentHandle) { | |
211 // Now we can remove the vectored exception handler from the chain | |
212 ::RemoveVectoredExceptionHandler(currentHandle); | |
213 | |
214 // Reset the handle in our thread-local set. | |
215 sCurrentExceptionHandle.set(NULL); | |
216 } | |
217 } | |
218 | |
219 #else | |
220 | |
221 // Generic POSIX implementation. | |
222 // | |
223 // This implementation relies on synchronous signals being delivered to the | |
224 // current thread. We use a thread local object to keep track of the active | |
225 // crash recovery context, and install signal handlers to invoke HandleCrash on | |
226 // the active object. | |
227 // | |
228 // This implementation does not to attempt to chain signal handlers in any | |
229 // reliable fashion -- if we get a signal outside of a crash recovery context we | |
230 // simply disable crash recovery and raise the signal again. | |
231 | |
232 #include <signal.h> | |
233 | |
234 static const int Signals[] = { SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGTRAP }; | |
235 static const unsigned NumSignals = sizeof(Signals) / sizeof(Signals[0]); | |
236 static struct sigaction PrevActions[NumSignals]; | |
237 | |
238 static void CrashRecoverySignalHandler(int Signal) { | |
239 // Lookup the current thread local recovery object. | |
240 const CrashRecoveryContextImpl *CRCI = CurrentContext->get(); | |
241 | |
242 if (!CRCI) { | |
243 // We didn't find a crash recovery context -- this means either we got a | |
244 // signal on a thread we didn't expect it on, the application got a signal | |
245 // outside of a crash recovery context, or something else went horribly | |
246 // wrong. | |
247 // | |
248 // Disable crash recovery and raise the signal again. The assumption here is | |
249 // that the enclosing application will terminate soon, and we won't want to | |
250 // attempt crash recovery again. | |
251 // | |
252 // This call of Disable isn't thread safe, but it doesn't actually matter. | |
253 CrashRecoveryContext::Disable(); | |
254 raise(Signal); | |
255 | |
256 // The signal will be thrown once the signal mask is restored. | |
257 return; | |
258 } | |
259 | |
260 // Unblock the signal we received. | |
261 sigset_t SigMask; | |
262 sigemptyset(&SigMask); | |
263 sigaddset(&SigMask, Signal); | |
264 sigprocmask(SIG_UNBLOCK, &SigMask, 0); | |
265 | |
266 if (CRCI) | |
267 const_cast<CrashRecoveryContextImpl*>(CRCI)->HandleCrash(); | |
268 } | |
269 | |
270 void CrashRecoveryContext::Enable() { | |
271 sys::ScopedLock L(*gCrashRecoveryContextMutex); | |
272 | |
273 if (gCrashRecoveryEnabled) | |
274 return; | |
275 | |
276 gCrashRecoveryEnabled = true; | |
277 | |
278 // Setup the signal handler. | |
279 struct sigaction Handler; | |
280 Handler.sa_handler = CrashRecoverySignalHandler; | |
281 Handler.sa_flags = 0; | |
282 sigemptyset(&Handler.sa_mask); | |
283 | |
284 for (unsigned i = 0; i != NumSignals; ++i) { | |
285 sigaction(Signals[i], &Handler, &PrevActions[i]); | |
286 } | |
287 } | |
288 | |
289 void CrashRecoveryContext::Disable() { | |
290 sys::ScopedLock L(*gCrashRecoveryContextMutex); | |
291 | |
292 if (!gCrashRecoveryEnabled) | |
293 return; | |
294 | |
295 gCrashRecoveryEnabled = false; | |
296 | |
297 // Restore the previous signal handlers. | |
298 for (unsigned i = 0; i != NumSignals; ++i) | |
299 sigaction(Signals[i], &PrevActions[i], 0); | |
300 } | |
301 | |
302 #endif | |
303 | |
304 bool CrashRecoveryContext::RunSafely(void (*Fn)(void*), void *UserData) { | |
305 // If crash recovery is disabled, do nothing. | |
306 if (gCrashRecoveryEnabled) { | |
307 assert(!Impl && "Crash recovery context already initialized!"); | |
308 CrashRecoveryContextImpl *CRCI = new CrashRecoveryContextImpl(this); | |
309 Impl = CRCI; | |
310 | |
311 if (setjmp(CRCI->JumpBuffer) != 0) { | |
312 return false; | |
313 } | |
314 } | |
315 | |
316 Fn(UserData); | |
317 return true; | |
318 } | |
319 | |
320 void CrashRecoveryContext::HandleCrash() { | |
321 CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl; | |
322 assert(CRCI && "Crash recovery context never initialized!"); | |
323 CRCI->HandleCrash(); | |
324 } | |
325 | |
326 const std::string &CrashRecoveryContext::getBacktrace() const { | |
327 CrashRecoveryContextImpl *CRC = (CrashRecoveryContextImpl *) Impl; | |
328 assert(CRC && "Crash recovery context never initialized!"); | |
329 assert(CRC->Failed && "No crash was detected!"); | |
330 return CRC->Backtrace; | |
331 } | |
332 | |
333 // | |
334 | |
335 namespace { | |
336 struct RunSafelyOnThreadInfo { | |
337 void (*UserFn)(void*); | |
338 void *UserData; | |
339 CrashRecoveryContext *CRC; | |
340 bool Result; | |
341 }; | |
342 } | |
343 | |
344 static void RunSafelyOnThread_Dispatch(void *UserData) { | |
345 RunSafelyOnThreadInfo *Info = | |
346 reinterpret_cast<RunSafelyOnThreadInfo*>(UserData); | |
347 Info->Result = Info->CRC->RunSafely(Info->UserFn, Info->UserData); | |
348 } | |
349 bool CrashRecoveryContext::RunSafelyOnThread(void (*Fn)(void*), void *UserData, | |
350 unsigned RequestedStackSize) { | |
351 RunSafelyOnThreadInfo Info = { Fn, UserData, this, false }; | |
352 llvm_execute_on_thread(RunSafelyOnThread_Dispatch, &Info, RequestedStackSize); | |
353 if (CrashRecoveryContextImpl *CRC = (CrashRecoveryContextImpl *)Impl) | |
354 CRC->setSwitchedThread(); | |
355 return Info.Result; | |
356 } |