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