llvm.org GIT mirror llvm / aabc860
Re-land r303274: "[CrashRecovery] Use SEH __try instead of VEH when available" We have to check gCrashRecoveryEnabled before using __try. In other words, SEH works too well and we ended up recovering from crashes in implicit module builds that we weren't supposed to. Only libclang is supposed to enable CrashRecoveryContext to allow implicit module builds to crash. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@303279 91177308-0d34-0410-b5e6-96231b3b80d8 Reid Kleckner 3 years ago
3 changed file(s) with 158 addition(s) and 51 deletion(s). Raw diff Collapse all Expand all
7676
7777 static ManagedStatic>
7878 tlIsRecoveringFromCrash;
79
80 static void installExceptionOrSignalHandlers();
81 static void uninstallExceptionOrSignalHandlers();
7982
8083 CrashRecoveryContextCleanup::~CrashRecoveryContextCleanup() {}
8184
112115 return CRCI->CRC;
113116 }
114117
118 void CrashRecoveryContext::Enable() {
119 sys::ScopedLock L(*gCrashRecoveryContextMutex);
120 // FIXME: Shouldn't this be a refcount or something?
121 if (gCrashRecoveryEnabled)
122 return;
123 gCrashRecoveryEnabled = true;
124 installExceptionOrSignalHandlers();
125 }
126
127 void CrashRecoveryContext::Disable() {
128 sys::ScopedLock L(*gCrashRecoveryContextMutex);
129 if (!gCrashRecoveryEnabled)
130 return;
131 gCrashRecoveryEnabled = false;
132 uninstallExceptionOrSignalHandlers();
133 }
134
115135 void CrashRecoveryContext::registerCleanup(CrashRecoveryContextCleanup *cleanup)
116136 {
117137 if (!cleanup)
139159 delete cleanup;
140160 }
141161
142 #ifdef LLVM_ON_WIN32
143
144 #include "Windows/WindowsSupport.h"
145
146 // On Windows, we can make use of vectored exception handling to
147 // catch most crashing situations. Note that this does mean
148 // we will be alerted of exceptions *before* structured exception
149 // handling has the opportunity to catch it. But that isn't likely
150 // to cause problems because nowhere in the project is SEH being
151 // used.
152 //
153 // Vectored exception handling is built on top of SEH, and so it
154 // works on a per-thread basis.
162 #if defined(_MSC_VER)
163 // If _MSC_VER is defined, we must have SEH. Use it if it's available. It's way
164 // better than VEH. Vectored exception handling catches all exceptions happening
165 // on the thread with installed exception handlers, so it can interfere with
166 // internal exception handling of other libraries on that thread. SEH works
167 // exactly as you would expect normal exception handling to work: it only
168 // catches exceptions if they would bubble out from the stack frame with __try /
169 // __except.
170
171 static void installExceptionOrSignalHandlers() {}
172 static void uninstallExceptionOrSignalHandlers() {}
173
174 bool CrashRecoveryContext::RunSafely(function_ref Fn) {
175 if (!gCrashRecoveryEnabled) {
176 Fn();
177 return true;
178 }
179
180 bool Result = true;
181 __try {
182 Fn();
183 } __except (1) { // Catch any exception.
184 Result = false;
185 }
186 return Result;
187 }
188
189 #else // !_MSC_VER
190
191 #if defined(LLVM_ON_WIN32)
192 // This is a non-MSVC compiler, probably mingw gcc or clang without
193 // -fms-extensions. Use vectored exception handling (VEH).
194 //
195 // On Windows, we can make use of vectored exception handling to catch most
196 // crashing situations. Note that this does mean we will be alerted of
197 // exceptions *before* structured exception handling has the opportunity to
198 // catch it. Unfortunately, this causes problems in practice with other code
199 // running on threads with LLVM crash recovery contexts, so we would like to
200 // eventually move away from VEH.
201 //
202 // Vectored works on a per-thread basis, which is an advantage over
203 // SetUnhandledExceptionFilter. SetUnhandledExceptionFilter also doesn't have
204 // any native support for chaining exception handlers, but VEH allows more than
205 // one.
155206 //
156207 // The vectored exception handler functionality was added in Windows
157208 // XP, so if support for older versions of Windows is required,
158209 // it will have to be added.
159 //
160 // If we want to support as far back as Win2k, we could use the
161 // SetUnhandledExceptionFilter API, but there's a risk of that
162 // being entirely overwritten (it's not a chain).
210
211 #include "Windows/WindowsSupport.h"
163212
164213 static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo)
165214 {
202251 // non-NULL, valid VEH handles, or NULL.
203252 static sys::ThreadLocal sCurrentExceptionHandle;
204253
205 void CrashRecoveryContext::Enable() {
206 sys::ScopedLock L(*gCrashRecoveryContextMutex);
207
208 if (gCrashRecoveryEnabled)
209 return;
210
211 gCrashRecoveryEnabled = true;
212
254 static void installExceptionOrSignalHandlers() {
213255 // We can set up vectored exception handling now. We will install our
214256 // handler as the front of the list, though there's no assurances that
215257 // it will remain at the front (another call could install itself before
218260 sCurrentExceptionHandle.set(handle);
219261 }
220262
221 void CrashRecoveryContext::Disable() {
222 sys::ScopedLock L(*gCrashRecoveryContextMutex);
223
224 if (!gCrashRecoveryEnabled)
225 return;
226
227 gCrashRecoveryEnabled = false;
228
263 static void uninstallExceptionOrSignalHandlers() {
229264 PVOID currentHandle = const_cast(sCurrentExceptionHandle.get());
230265 if (currentHandle) {
231266 // Now we can remove the vectored exception handler from the chain
236271 }
237272 }
238273
239 #else
274 #else // !LLVM_ON_WIN32
240275
241276 // Generic POSIX implementation.
242277 //
288323 const_cast(CRCI)->HandleCrash();
289324 }
290325
291 void CrashRecoveryContext::Enable() {
292 sys::ScopedLock L(*gCrashRecoveryContextMutex);
293
294 if (gCrashRecoveryEnabled)
295 return;
296
297 gCrashRecoveryEnabled = true;
298
326 static void installExceptionOrSignalHandlers() {
299327 // Setup the signal handler.
300328 struct sigaction Handler;
301329 Handler.sa_handler = CrashRecoverySignalHandler;
307335 }
308336 }
309337
310 void CrashRecoveryContext::Disable() {
311 sys::ScopedLock L(*gCrashRecoveryContextMutex);
312
313 if (!gCrashRecoveryEnabled)
314 return;
315
316 gCrashRecoveryEnabled = false;
317
338 static void uninstallExceptionOrSignalHandlers() {
318339 // Restore the previous signal handlers.
319340 for (unsigned i = 0; i != NumSignals; ++i)
320341 sigaction(Signals[i], &PrevActions[i], nullptr);
321342 }
322343
323 #endif
344 #endif // !LLVM_ON_WIN32
324345
325346 bool CrashRecoveryContext::RunSafely(function_ref Fn) {
326347 // If crash recovery is disabled, do nothing.
337358 Fn();
338359 return true;
339360 }
361
362 #endif // !_MSC_VER
340363
341364 void CrashRecoveryContext::HandleCrash() {
342365 CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl;
1010 BlockFrequencyTest.cpp
1111 BranchProbabilityTest.cpp
1212 CachePruningTest.cpp
13 CrashRecoveryTest.cpp
1314 Casting.cpp
1415 Chrono.cpp
1516 CommandLineTest.cpp
0 //===- llvm/unittest/Support/CrashRecoveryTest.cpp ------------------------===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "llvm/Support/CrashRecoveryContext.h"
10 #include "llvm/Support/Compiler.h"
11 #include "gtest/gtest.h"
12
13 #ifdef LLVM_ON_WIN32
14 #define WIN32_LEAN_AND_MEAN
15 #define NOGDI
16 #include
17 #endif
18
19 using namespace llvm;
20 using namespace llvm::sys;
21
22 static int GlobalInt = 0;
23 static void nullDeref() { *(volatile int *)nullptr = 0; }
24 static void incrementGlobal() { ++GlobalInt; }
25 static void llvmTrap() { LLVM_BUILTIN_TRAP; }
26
27 TEST(CrashRecoveryTest, Basic) {
28 llvm::CrashRecoveryContext::Enable();
29 GlobalInt = 0;
30 EXPECT_TRUE(CrashRecoveryContext().RunSafely(incrementGlobal));
31 EXPECT_EQ(1, GlobalInt);
32 EXPECT_FALSE(CrashRecoveryContext().RunSafely(nullDeref));
33 EXPECT_FALSE(CrashRecoveryContext().RunSafely(llvmTrap));
34 }
35
36 struct IncrementGlobalCleanup : CrashRecoveryContextCleanup {
37 IncrementGlobalCleanup(CrashRecoveryContext *CRC)
38 : CrashRecoveryContextCleanup(CRC) {}
39 virtual void recoverResources() { ++GlobalInt; }
40 };
41
42 static void noop() {}
43
44 TEST(CrashRecoveryTest, Cleanup) {
45 llvm::CrashRecoveryContext::Enable();
46 GlobalInt = 0;
47 {
48 CrashRecoveryContext CRC;
49 CRC.registerCleanup(new IncrementGlobalCleanup(&CRC));
50 EXPECT_TRUE(CRC.RunSafely(noop));
51 } // run cleanups
52 EXPECT_EQ(1, GlobalInt);
53
54 GlobalInt = 0;
55 {
56 CrashRecoveryContext CRC;
57 CRC.registerCleanup(new IncrementGlobalCleanup(&CRC));
58 EXPECT_FALSE(CRC.RunSafely(nullDeref));
59 } // run cleanups
60 EXPECT_EQ(1, GlobalInt);
61 }
62
63 #ifdef LLVM_ON_WIN32
64 static void raiseIt() {
65 RaiseException(123, EXCEPTION_NONCONTINUABLE, 0, NULL);
66 }
67
68 TEST(CrashRecoveryTest, RaiseException) {
69 llvm::CrashRecoveryContext::Enable();
70 EXPECT_FALSE(CrashRecoveryContext().RunSafely(raiseIt));
71 }
72
73 static void outputString() {
74 OutputDebugStringA("output for debugger\n");
75 }
76
77 TEST(CrashRecoveryTest, CallOutputDebugString) {
78 llvm::CrashRecoveryContext::Enable();
79 EXPECT_TRUE(CrashRecoveryContext().RunSafely(outputString));
80 }
81
82 #endif