1 //===- llvm/unittest/Support/CrashRecoveryTest.cpp ------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "llvm/Config/config.h"
10 #include "llvm/Support/CommandLine.h"
11 #include "llvm/Support/Compiler.h"
12 #include "llvm/Support/CrashRecoveryContext.h"
13 #include "llvm/Support/FileSystem.h"
14 #include "llvm/Support/Program.h"
15 #include "llvm/Support/Signals.h"
16 #include "llvm/Support/raw_ostream.h"
17 #include "llvm/TargetParser/Host.h"
18 #include "llvm/TargetParser/Triple.h"
19 #include "gtest/gtest.h"
22 #define WIN32_LEAN_AND_MEAN
34 using namespace llvm::sys
;
36 static int GlobalInt
= 0;
37 static void nullDeref() { *(volatile int *)0x10 = 0; }
38 static void incrementGlobal() { ++GlobalInt
; }
39 static void llvmTrap() { LLVM_BUILTIN_TRAP
; }
40 static void incrementGlobalWithParam(void *) { ++GlobalInt
; }
42 TEST(CrashRecoveryTest
, Basic
) {
43 llvm::CrashRecoveryContext::Enable();
45 EXPECT_TRUE(CrashRecoveryContext().RunSafely(incrementGlobal
));
46 EXPECT_EQ(1, GlobalInt
);
47 EXPECT_FALSE(CrashRecoveryContext().RunSafely(nullDeref
));
48 EXPECT_FALSE(CrashRecoveryContext().RunSafely(llvmTrap
));
51 struct IncrementGlobalCleanup
: CrashRecoveryContextCleanup
{
52 IncrementGlobalCleanup(CrashRecoveryContext
*CRC
)
53 : CrashRecoveryContextCleanup(CRC
) {}
54 void recoverResources() override
{ ++GlobalInt
; }
59 TEST(CrashRecoveryTest
, Cleanup
) {
60 llvm::CrashRecoveryContext::Enable();
63 CrashRecoveryContext CRC
;
64 CRC
.registerCleanup(new IncrementGlobalCleanup(&CRC
));
65 EXPECT_TRUE(CRC
.RunSafely(noop
));
67 EXPECT_EQ(1, GlobalInt
);
71 CrashRecoveryContext CRC
;
72 CRC
.registerCleanup(new IncrementGlobalCleanup(&CRC
));
73 EXPECT_FALSE(CRC
.RunSafely(nullDeref
));
75 EXPECT_EQ(1, GlobalInt
);
76 llvm::CrashRecoveryContext::Disable();
79 TEST(CrashRecoveryTest
, DumpStackCleanup
) {
80 SmallString
<128> Filename
;
81 std::error_code EC
= sys::fs::createTemporaryFile("crash", "test", Filename
);
83 sys::RemoveFileOnSignal(Filename
);
84 llvm::sys::AddSignalHandler(incrementGlobalWithParam
, nullptr);
86 llvm::CrashRecoveryContext::Enable();
88 CrashRecoveryContext CRC
;
89 CRC
.DumpStackAndCleanupOnFailure
= true;
90 EXPECT_TRUE(CRC
.RunSafely(noop
));
92 EXPECT_TRUE(sys::fs::exists(Filename
));
93 EXPECT_EQ(GlobalInt
, 0);
95 CrashRecoveryContext CRC
;
96 CRC
.DumpStackAndCleanupOnFailure
= true;
97 EXPECT_FALSE(CRC
.RunSafely(nullDeref
));
98 EXPECT_NE(CRC
.RetCode
, 0);
100 EXPECT_FALSE(sys::fs::exists(Filename
));
101 EXPECT_EQ(GlobalInt
, 1);
102 llvm::CrashRecoveryContext::Disable();
105 TEST(CrashRecoveryTest
, LimitedStackTrace
) {
106 // FIXME: Handle "Depth" parameter in PrintStackTrace() function
107 // to print stack trace upto a specified Depth.
108 if (Triple(sys::getProcessTriple()).isOSWindows())
111 llvm::raw_string_ostream
RawStream(Res
);
112 PrintStackTrace(RawStream
, 1);
113 std::string Str
= RawStream
.str();
114 EXPECT_EQ(std::string::npos
, Str
.find("#1"));
118 static void raiseIt() {
119 RaiseException(123, EXCEPTION_NONCONTINUABLE
, 0, NULL
);
122 TEST(CrashRecoveryTest
, RaiseException
) {
123 llvm::CrashRecoveryContext::Enable();
124 EXPECT_FALSE(CrashRecoveryContext().RunSafely(raiseIt
));
127 static void outputString() {
128 OutputDebugStringA("output for debugger\n");
131 TEST(CrashRecoveryTest
, CallOutputDebugString
) {
132 llvm::CrashRecoveryContext::Enable();
133 EXPECT_TRUE(CrashRecoveryContext().RunSafely(outputString
));
136 TEST(CrashRecoveryTest
, Abort
) {
137 llvm::CrashRecoveryContext::Enable();
138 auto A
= []() { abort(); };
139 EXPECT_FALSE(CrashRecoveryContext().RunSafely(A
));
140 // Test a second time to ensure we reinstall the abort signal handler.
141 EXPECT_FALSE(CrashRecoveryContext().RunSafely(A
));
145 // Specifically ensure that programs that signal() or abort() through the
146 // CrashRecoveryContext can re-throw again their signal, so that `not --crash`
149 // See llvm/utils/unittest/UnitTestMain/TestMain.cpp
150 extern const char *TestMainArgv0
;
152 // Just a reachable symbol to ease resolving of the executable's path.
153 static cl::opt
<std::string
> CrashTestStringArg1("crash-test-string-arg1");
155 TEST(CrashRecoveryTest
, UnixCRCReturnCode
) {
156 using namespace llvm::sys
;
157 if (getenv("LLVM_CRC_UNIXCRCRETURNCODE")) {
158 llvm::CrashRecoveryContext::Enable();
159 CrashRecoveryContext CRC
;
160 // This path runs in a subprocess that exits by signalling, so don't use
161 // the googletest macros to verify things as they won't report properly.
162 if (CRC
.RunSafely(abort
))
163 llvm_unreachable("RunSafely returned true!");
164 if (CRC
.RetCode
!= 128 + SIGABRT
)
165 llvm_unreachable("Unexpected RetCode!");
167 llvm::sys::unregisterHandlers();
168 raise(CRC
.RetCode
- 128);
169 llvm_unreachable("Should have exited already!");
172 std::string Executable
=
173 sys::fs::getMainExecutable(TestMainArgv0
, &CrashTestStringArg1
);
175 Executable
, "--gtest_filter=CrashRecoveryTest.UnixCRCReturnCode"};
177 // Add LLVM_CRC_UNIXCRCRETURNCODE to the environment of the child process.
178 int Res
= setenv("LLVM_CRC_UNIXCRCRETURNCODE", "1", 0);
181 Res
= unsetenv("GTEST_SHARD_INDEX");
183 Res
= unsetenv("GTEST_TOTAL_SHARDS");
187 bool ExecutionFailed
;
188 int RetCode
= ExecuteAndWait(Executable
, argv
, {}, {}, 0, 0, &Error
,
190 ASSERT_EQ(-2, RetCode
);