blob: c079182bd1c51b8d8e4812b7b501294c80ce9403 [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 Dunbard9082df2010-07-29 01:21:47 +000012#include "llvm/System/ThreadLocal.h"
Daniel Dunbara309dac2010-07-28 15:40:20 +000013#include <setjmp.h>
Daniel Dunbard9082df2010-07-29 01:21:47 +000014#include <cstdio>
Daniel Dunbara309dac2010-07-28 15:40:20 +000015using namespace llvm;
16
17namespace {
18
19struct CrashRecoveryContextImpl;
20
Daniel Dunbard9082df2010-07-29 01:21:47 +000021static sys::ThreadLocal<const CrashRecoveryContextImpl> CurrentContext;
22
Daniel Dunbara309dac2010-07-28 15:40:20 +000023struct CrashRecoveryContextImpl {
24 std::string Backtrace;
25 ::jmp_buf JumpBuffer;
26 volatile unsigned Failed : 1;
27
28public:
Daniel Dunbard9082df2010-07-29 01:21:47 +000029 CrashRecoveryContextImpl() : Failed(false) {
30 CurrentContext.set(this);
31 }
32 ~CrashRecoveryContextImpl() {
33 CurrentContext.set(0);
34 }
Daniel Dunbara309dac2010-07-28 15:40:20 +000035
36 void HandleCrash() {
37 assert(!Failed && "Crash recovery context already failed!");
38 Failed = true;
39
40 // FIXME: Stash the backtrace.
41
42 // Jump back to the RunSafely we were called under.
43 longjmp(JumpBuffer, 1);
44 }
45};
46
47}
48
49static bool gCrashRecoveryEnabled = false;
50
51CrashRecoveryContext::~CrashRecoveryContext() {
52 CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl;
53 delete CRCI;
54}
55
Daniel Dunbard9082df2010-07-29 01:21:47 +000056#ifdef LLVM_ON_WIN32
57
58// FIXME: No real Win32 implementation currently.
59
Daniel Dunbara309dac2010-07-28 15:40:20 +000060void CrashRecoveryContext::Enable() {
61 if (gCrashRecoveryEnabled)
62 return;
63
64 gCrashRecoveryEnabled = true;
65}
66
67void CrashRecoveryContext::Disable() {
68 if (!gCrashRecoveryEnabled)
69 return;
70
71 gCrashRecoveryEnabled = false;
72}
73
Daniel Dunbard9082df2010-07-29 01:21:47 +000074#else
75
76// Generic POSIX implementation.
77//
78// This implementation relies on synchronous signals being delivered to the
79// current thread. We use a thread local object to keep track of the active
80// crash recovery context, and install signal handlers to invoke HandleCrash on
81// the active object.
82//
83// This implementation does not to attempt to chain signal handlers in any
84// reliable fashion -- if we get a signal outside of a crash recovery context we
85// simply disable crash recovery and raise the signal again.
86
87#include <signal.h>
88
89static struct {
90 int Signal;
91 struct sigaction PrevAction;
92} SignalInfo[] = {
93 { SIGABRT, {} },
94 { SIGBUS, {} },
95 { SIGFPE, {} },
96 { SIGILL, {} },
97 { SIGSEGV, {} },
98 { SIGTRAP, {} },
99};
100static const unsigned NumSignals = sizeof(SignalInfo) / sizeof(SignalInfo[0]);
101
102static void CrashRecoverySignalHandler(int Signal) {
103 // Lookup the current thread local recovery object.
104 const CrashRecoveryContextImpl *CRCI = CurrentContext.get();
105
106 if (!CRCI) {
107 // We didn't find a crash recovery context -- this means either we got a
108 // signal on a thread we didn't expect it on, the application got a signal
109 // outside of a crash recovery context, or something else went horribly
110 // wrong.
111 //
112 // Disable crash recovery and raise the signal again. The assumption here is
113 // that the enclosing application will terminate soon, and we won't want to
114 // attempt crash recovery again.
115 //
116 // This call of Disable isn't thread safe, but it doesn't actually matter.
117 CrashRecoveryContext::Disable();
118 raise(Signal);
119 }
120
121 // Unblock the signal we received.
122 sigset_t SigMask;
123 sigemptyset(&SigMask);
124 sigaddset(&SigMask, Signal);
125 sigprocmask(SIG_UNBLOCK, &SigMask, 0);
126
127 if (CRCI)
128 const_cast<CrashRecoveryContextImpl*>(CRCI)->HandleCrash();
129}
130
131void CrashRecoveryContext::Enable() {
132 if (gCrashRecoveryEnabled)
133 return;
134
135 gCrashRecoveryEnabled = true;
136
137 // Setup the signal handler.
138 struct sigaction Handler;
139 Handler.sa_handler = CrashRecoverySignalHandler;
140 Handler.sa_flags = 0;
141 sigemptyset(&Handler.sa_mask);
142
143 for (unsigned i = 0; i != NumSignals; ++i) {
144 sigaction(SignalInfo[i].Signal, &Handler,
145 &SignalInfo[i].PrevAction);
146 }
147}
148
149void CrashRecoveryContext::Disable() {
150 if (!gCrashRecoveryEnabled)
151 return;
152
153 gCrashRecoveryEnabled = false;
154
155 // Restore the previous signal handlers.
156 for (unsigned i = 0; i != NumSignals; ++i)
157 sigaction(SignalInfo[i].Signal, &SignalInfo[i].PrevAction, 0);
158}
159
160#endif
161
Daniel Dunbara309dac2010-07-28 15:40:20 +0000162bool CrashRecoveryContext::RunSafely(void (*Fn)(void*), void *UserData) {
163 // If crash recovery is disabled, do nothing.
164 if (gCrashRecoveryEnabled) {
165 assert(!Impl && "Crash recovery context already initialized!");
166 CrashRecoveryContextImpl *CRCI = new CrashRecoveryContextImpl;
167 Impl = CRCI;
168
169 if (setjmp(CRCI->JumpBuffer) != 0) {
170 return false;
171 }
172 }
173
174 Fn(UserData);
175 return true;
176}
177
178void CrashRecoveryContext::HandleCrash() {
179 CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl;
180 assert(CRCI && "Crash recovery context never initialized!");
181 CRCI->HandleCrash();
182}
183
184const std::string &CrashRecoveryContext::getBacktrace() const {
185 CrashRecoveryContextImpl *CRC = (CrashRecoveryContextImpl *) Impl;
186 assert(CRC && "Crash recovery context never initialized!");
187 assert(CRC->Failed && "No crash was detected!");
188 return CRC->Backtrace;
189}