blob: e558662611f66e17023fd0146746b3aced413b4b [file] [log] [blame]
Daniel Dunbara309dac2010-07-28 15:40:20 +00001//===--- 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"
Daniel Dunbardade28e2010-07-29 01:52:04 +000012#include "llvm/Config/config.h"
Michael J. Spencer1f6efa32010-11-29 18:16:10 +000013#include "llvm/Support/Mutex.h"
14#include "llvm/Support/ThreadLocal.h"
Daniel Dunbara309dac2010-07-28 15:40:20 +000015#include <setjmp.h>
Daniel Dunbard9082df2010-07-29 01:21:47 +000016#include <cstdio>
Daniel Dunbara309dac2010-07-28 15:40:20 +000017using namespace llvm;
18
19namespace {
20
21struct CrashRecoveryContextImpl;
22
Daniel Dunbard9082df2010-07-29 01:21:47 +000023static sys::ThreadLocal<const CrashRecoveryContextImpl> CurrentContext;
24
Daniel Dunbara309dac2010-07-28 15:40:20 +000025struct CrashRecoveryContextImpl {
Daniel Dunbara8fa7982010-08-17 22:32:37 +000026 CrashRecoveryContext *CRC;
Daniel Dunbara309dac2010-07-28 15:40:20 +000027 std::string Backtrace;
28 ::jmp_buf JumpBuffer;
29 volatile unsigned Failed : 1;
30
31public:
Daniel Dunbara8fa7982010-08-17 22:32:37 +000032 CrashRecoveryContextImpl(CrashRecoveryContext *CRC) : CRC(CRC),
33 Failed(false) {
Daniel Dunbard9082df2010-07-29 01:21:47 +000034 CurrentContext.set(this);
35 }
36 ~CrashRecoveryContextImpl() {
Daniel Dunbara6855822010-07-29 15:24:21 +000037 CurrentContext.erase();
Daniel Dunbard9082df2010-07-29 01:21:47 +000038 }
Daniel Dunbara309dac2010-07-28 15:40:20 +000039
40 void HandleCrash() {
Daniel Dunbarebe7eb82010-08-17 22:32:39 +000041 // Eliminate the current context entry, to avoid re-entering in case the
42 // cleanup code crashes.
43 CurrentContext.erase();
44
Daniel Dunbara309dac2010-07-28 15:40:20 +000045 assert(!Failed && "Crash recovery context already failed!");
46 Failed = true;
47
48 // FIXME: Stash the backtrace.
49
50 // Jump back to the RunSafely we were called under.
51 longjmp(JumpBuffer, 1);
52 }
53};
54
55}
56
Daniel Dunbarc0c815e2010-08-17 22:32:34 +000057static sys::Mutex gCrashRecoveryContexMutex;
Daniel Dunbara309dac2010-07-28 15:40:20 +000058static bool gCrashRecoveryEnabled = false;
59
Ted Kremeneka4f98392011-03-18 02:05:11 +000060CrashRecoveryContextCleanup::~CrashRecoveryContextCleanup() {}
61
Daniel Dunbara309dac2010-07-28 15:40:20 +000062CrashRecoveryContext::~CrashRecoveryContext() {
Ted Kremeneka4f98392011-03-18 02:05:11 +000063 // Reclaim registered resources.
64 CrashRecoveryContextCleanup *i = head;
65 while (i) {
66 CrashRecoveryContextCleanup *tmp = i;
67 i = tmp->next;
Ted Kremenek1a06d572011-03-19 00:59:37 +000068 tmp->cleanupFired = true;
Ted Kremeneka4f98392011-03-18 02:05:11 +000069 tmp->recoverResources();
70 delete tmp;
71 }
72
Daniel Dunbara309dac2010-07-28 15:40:20 +000073 CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl;
74 delete CRCI;
75}
76
Daniel Dunbara8fa7982010-08-17 22:32:37 +000077CrashRecoveryContext *CrashRecoveryContext::GetCurrent() {
Ted Kremenekfb200e32011-03-19 00:59:33 +000078 if (!gCrashRecoveryEnabled)
79 return 0;
80
Daniel Dunbara8fa7982010-08-17 22:32:37 +000081 const CrashRecoveryContextImpl *CRCI = CurrentContext.get();
82 if (!CRCI)
83 return 0;
84
85 return CRCI->CRC;
86}
87
Ted Kremeneka4f98392011-03-18 02:05:11 +000088void CrashRecoveryContext::registerCleanup(CrashRecoveryContextCleanup *cleanup)
89{
90 if (!cleanup)
91 return;
92 if (head)
93 head->prev = cleanup;
94 cleanup->next = head;
95 head = cleanup;
96}
97
98void
99CrashRecoveryContext::unregisterCleanup(CrashRecoveryContextCleanup *cleanup) {
100 if (!cleanup)
101 return;
102 if (cleanup == head) {
103 head = cleanup->next;
104 if (head)
105 head->prev = 0;
106 }
107 else {
108 cleanup->prev->next = cleanup->next;
109 if (cleanup->next)
110 cleanup->next->prev = cleanup->prev;
111 }
112 delete cleanup;
113}
114
Daniel Dunbard9082df2010-07-29 01:21:47 +0000115#ifdef LLVM_ON_WIN32
116
117// FIXME: No real Win32 implementation currently.
118
Daniel Dunbara309dac2010-07-28 15:40:20 +0000119void CrashRecoveryContext::Enable() {
Daniel Dunbarc0c815e2010-08-17 22:32:34 +0000120 sys::ScopedLock L(gCrashRecoveryContexMutex);
121
Daniel Dunbara309dac2010-07-28 15:40:20 +0000122 if (gCrashRecoveryEnabled)
123 return;
124
125 gCrashRecoveryEnabled = true;
126}
127
128void CrashRecoveryContext::Disable() {
Daniel Dunbarc0c815e2010-08-17 22:32:34 +0000129 sys::ScopedLock L(gCrashRecoveryContexMutex);
130
Daniel Dunbara309dac2010-07-28 15:40:20 +0000131 if (!gCrashRecoveryEnabled)
132 return;
133
134 gCrashRecoveryEnabled = false;
135}
136
Daniel Dunbard9082df2010-07-29 01:21:47 +0000137#else
138
139// Generic POSIX implementation.
140//
141// This implementation relies on synchronous signals being delivered to the
142// current thread. We use a thread local object to keep track of the active
143// crash recovery context, and install signal handlers to invoke HandleCrash on
144// the active object.
145//
146// This implementation does not to attempt to chain signal handlers in any
147// reliable fashion -- if we get a signal outside of a crash recovery context we
148// simply disable crash recovery and raise the signal again.
149
150#include <signal.h>
151
Daniel Dunbar63cc2e12010-07-30 17:49:04 +0000152static int Signals[] = { SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGTRAP };
153static const unsigned NumSignals = sizeof(Signals) / sizeof(Signals[0]);
154static struct sigaction PrevActions[NumSignals];
Daniel Dunbard9082df2010-07-29 01:21:47 +0000155
156static void CrashRecoverySignalHandler(int Signal) {
157 // Lookup the current thread local recovery object.
158 const CrashRecoveryContextImpl *CRCI = CurrentContext.get();
159
160 if (!CRCI) {
161 // We didn't find a crash recovery context -- this means either we got a
162 // signal on a thread we didn't expect it on, the application got a signal
163 // outside of a crash recovery context, or something else went horribly
164 // wrong.
165 //
166 // Disable crash recovery and raise the signal again. The assumption here is
167 // that the enclosing application will terminate soon, and we won't want to
168 // attempt crash recovery again.
169 //
170 // This call of Disable isn't thread safe, but it doesn't actually matter.
171 CrashRecoveryContext::Disable();
172 raise(Signal);
Daniel Dunbard49e2aa2010-10-18 21:55:18 +0000173
174 // The signal will be thrown once the signal mask is restored.
175 return;
Daniel Dunbard9082df2010-07-29 01:21:47 +0000176 }
177
178 // Unblock the signal we received.
179 sigset_t SigMask;
180 sigemptyset(&SigMask);
181 sigaddset(&SigMask, Signal);
182 sigprocmask(SIG_UNBLOCK, &SigMask, 0);
183
184 if (CRCI)
185 const_cast<CrashRecoveryContextImpl*>(CRCI)->HandleCrash();
186}
187
188void CrashRecoveryContext::Enable() {
Daniel Dunbarc0c815e2010-08-17 22:32:34 +0000189 sys::ScopedLock L(gCrashRecoveryContexMutex);
190
Daniel Dunbard9082df2010-07-29 01:21:47 +0000191 if (gCrashRecoveryEnabled)
192 return;
193
194 gCrashRecoveryEnabled = true;
195
196 // Setup the signal handler.
197 struct sigaction Handler;
198 Handler.sa_handler = CrashRecoverySignalHandler;
199 Handler.sa_flags = 0;
200 sigemptyset(&Handler.sa_mask);
201
202 for (unsigned i = 0; i != NumSignals; ++i) {
Daniel Dunbar63cc2e12010-07-30 17:49:04 +0000203 sigaction(Signals[i], &Handler, &PrevActions[i]);
Daniel Dunbard9082df2010-07-29 01:21:47 +0000204 }
205}
206
207void CrashRecoveryContext::Disable() {
Daniel Dunbarc0c815e2010-08-17 22:32:34 +0000208 sys::ScopedLock L(gCrashRecoveryContexMutex);
209
Daniel Dunbard9082df2010-07-29 01:21:47 +0000210 if (!gCrashRecoveryEnabled)
211 return;
212
213 gCrashRecoveryEnabled = false;
214
215 // Restore the previous signal handlers.
216 for (unsigned i = 0; i != NumSignals; ++i)
Daniel Dunbar63cc2e12010-07-30 17:49:04 +0000217 sigaction(Signals[i], &PrevActions[i], 0);
Daniel Dunbard9082df2010-07-29 01:21:47 +0000218}
219
220#endif
221
Daniel Dunbara309dac2010-07-28 15:40:20 +0000222bool CrashRecoveryContext::RunSafely(void (*Fn)(void*), void *UserData) {
223 // If crash recovery is disabled, do nothing.
224 if (gCrashRecoveryEnabled) {
225 assert(!Impl && "Crash recovery context already initialized!");
Daniel Dunbara8fa7982010-08-17 22:32:37 +0000226 CrashRecoveryContextImpl *CRCI = new CrashRecoveryContextImpl(this);
Daniel Dunbara309dac2010-07-28 15:40:20 +0000227 Impl = CRCI;
228
229 if (setjmp(CRCI->JumpBuffer) != 0) {
230 return false;
231 }
232 }
233
234 Fn(UserData);
235 return true;
236}
237
238void CrashRecoveryContext::HandleCrash() {
239 CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl;
240 assert(CRCI && "Crash recovery context never initialized!");
241 CRCI->HandleCrash();
242}
243
244const std::string &CrashRecoveryContext::getBacktrace() const {
245 CrashRecoveryContextImpl *CRC = (CrashRecoveryContextImpl *) Impl;
246 assert(CRC && "Crash recovery context never initialized!");
247 assert(CRC->Failed && "No crash was detected!");
248 return CRC->Backtrace;
249}
Daniel Dunbarf8254d642010-11-05 07:19:09 +0000250
251//
252
253namespace {
254struct RunSafelyOnThreadInfo {
255 void (*UserFn)(void*);
256 void *UserData;
257 CrashRecoveryContext *CRC;
258 bool Result;
259};
260}
261
262static void RunSafelyOnThread_Dispatch(void *UserData) {
263 RunSafelyOnThreadInfo *Info =
264 reinterpret_cast<RunSafelyOnThreadInfo*>(UserData);
265 Info->Result = Info->CRC->RunSafely(Info->UserFn, Info->UserData);
266}
267bool CrashRecoveryContext::RunSafelyOnThread(void (*Fn)(void*), void *UserData,
268 unsigned RequestedStackSize) {
269 RunSafelyOnThreadInfo Info = { Fn, UserData, this, false };
270 llvm_execute_on_thread(RunSafelyOnThread_Dispatch, &Info, RequestedStackSize);
271 return Info.Result;
272}