llvm.org GIT mirror llvm / 8d2c1f5
[CrashRecovery] Use SEH __try instead of VEH when available Summary: It avoids problems when other libraries raise exceptions. In particular, OutputDebugString raises an exception that the debugger is supposed to catch and suppress. VEH kicks in first right now, and that is entirely incorrect. Unfortunately, GCC does not support SEH, so I've kept the old buggy VEH codepath around. We could fix it with SetUnhandledExceptionFilter, but that is not per-thread, so a well-behaved library shouldn't set it. Reviewers: zturner Subscribers: llvm-commits, mgorny Differential Revision: https://reviews.llvm.org/D33261 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@303274 91177308-0d34-0410-b5e6-96231b3b80d8 Reid Kleckner 3 years ago
3 changed file(s) with 153 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 bool Result = true;
176 __try {
177 Fn();
178 } __except (1) { // Catch any exception.
179 Result = false;
180 }
181 return Result;
182 }
183
184 #else // !_MSC_VER
185
186 #if defined(LLVM_ON_WIN32)
187 // This is a non-MSVC compiler, probably mingw gcc or clang without
188 // -fms-extensions. Use vectored exception handling (VEH).
189 //
190 // On Windows, we can make use of vectored exception handling to catch most
191 // crashing situations. Note that this does mean we will be alerted of
192 // exceptions *before* structured exception handling has the opportunity to
193 // catch it. Unfortunately, this causes problems in practice with other code
194 // running on threads with LLVM crash recovery contexts, so we would like to
195 // eventually move away from VEH.
196 //
197 // Vectored works on a per-thread basis, which is an advantage over
198 // SetUnhandledExceptionFilter. SetUnhandledExceptionFilter also doesn't have
199 // any native support for chaining exception handlers, but VEH allows more than
200 // one.
155201 //
156202 // The vectored exception handler functionality was added in Windows
157203 // XP, so if support for older versions of Windows is required,
158204 // 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).
205
206 #include "Windows/WindowsSupport.h"
163207
164208 static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo)
165209 {
202246 // non-NULL, valid VEH handles, or NULL.
203247 static sys::ThreadLocal sCurrentExceptionHandle;
204248
205 void CrashRecoveryContext::Enable() {
206 sys::ScopedLock L(*gCrashRecoveryContextMutex);
207
208 if (gCrashRecoveryEnabled)
209 return;
210
211 gCrashRecoveryEnabled = true;
212
249 static void installExceptionOrSignalHandlers() {
213250 // We can set up vectored exception handling now. We will install our
214251 // handler as the front of the list, though there's no assurances that
215252 // it will remain at the front (another call could install itself before
218255 sCurrentExceptionHandle.set(handle);
219256 }
220257
221 void CrashRecoveryContext::Disable() {
222 sys::ScopedLock L(*gCrashRecoveryContextMutex);
223
224 if (!gCrashRecoveryEnabled)
225 return;
226
227 gCrashRecoveryEnabled = false;
228
258 static void uninstallExceptionOrSignalHandlers() {
229259 PVOID currentHandle = const_cast(sCurrentExceptionHandle.get());
230260 if (currentHandle) {
231261 // Now we can remove the vectored exception handler from the chain
236266 }
237267 }
238268
239 #else
269 #else // !LLVM_ON_WIN32
240270
241271 // Generic POSIX implementation.
242272 //
288318 const_cast(CRCI)->HandleCrash();
289319 }
290320
291 void CrashRecoveryContext::Enable() {
292 sys::ScopedLock L(*gCrashRecoveryContextMutex);
293
294 if (gCrashRecoveryEnabled)
295 return;
296
297 gCrashRecoveryEnabled = true;
298
321 static void installExceptionOrSignalHandlers() {
299322 // Setup the signal handler.
300323 struct sigaction Handler;
301324 Handler.sa_handler = CrashRecoverySignalHandler;
307330 }
308331 }
309332
310 void CrashRecoveryContext::Disable() {
311 sys::ScopedLock L(*gCrashRecoveryContextMutex);
312
313 if (!gCrashRecoveryEnabled)
314 return;
315
316 gCrashRecoveryEnabled = false;
317
333 static void uninstallExceptionOrSignalHandlers() {
318334 // Restore the previous signal handlers.
319335 for (unsigned i = 0; i != NumSignals; ++i)
320336 sigaction(Signals[i], &PrevActions[i], nullptr);
321337 }
322338
323 #endif
339 #endif // !LLVM_ON_WIN32
324340
325341 bool CrashRecoveryContext::RunSafely(function_ref Fn) {
326342 // If crash recovery is disabled, do nothing.
337353 Fn();
338354 return true;
339355 }
356
357 #endif // !_MSC_VER
340358
341359 void CrashRecoveryContext::HandleCrash() {
342360 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