1 //===- llvm/unittest/Support/CrashRecoveryTest.cpp ------------------------===// 2 // 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 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "llvm/Config/config.h" 10 #include "llvm/Config/llvm-config.h" // for LLVM_ON_UNIX 11 #include "llvm/Support/CommandLine.h" 12 #include "llvm/Support/Compiler.h" 13 #include "llvm/Support/CrashRecoveryContext.h" 14 #include "llvm/Support/FileSystem.h" 15 #include "llvm/Support/Program.h" 16 #include "llvm/Support/Signals.h" 17 #include "llvm/Support/raw_ostream.h" 18 #include "llvm/TargetParser/Host.h" 19 #include "llvm/TargetParser/Triple.h" 20 #include "gtest/gtest.h" 21 22 #ifdef _WIN32 23 #define WIN32_LEAN_AND_MEAN 24 #define NOGDI 25 #include <windows.h> 26 #endif 27 28 #ifdef LLVM_ON_UNIX 29 #include <signal.h> 30 #endif 31 32 using namespace llvm; 33 using namespace llvm::sys; 34 35 static int GlobalInt = 0; 36 static void nullDeref() { *(volatile int *)0x10 = 0; } 37 static void incrementGlobal() { ++GlobalInt; } 38 static void llvmTrap() { LLVM_BUILTIN_TRAP; } 39 static void incrementGlobalWithParam(void *) { ++GlobalInt; } 40 41 TEST(CrashRecoveryTest, Basic) { 42 llvm::CrashRecoveryContext::Enable(); 43 GlobalInt = 0; 44 EXPECT_TRUE(CrashRecoveryContext().RunSafely(incrementGlobal)); 45 EXPECT_EQ(1, GlobalInt); 46 EXPECT_FALSE(CrashRecoveryContext().RunSafely(nullDeref)); 47 EXPECT_FALSE(CrashRecoveryContext().RunSafely(llvmTrap)); 48 } 49 50 struct IncrementGlobalCleanup : CrashRecoveryContextCleanup { 51 IncrementGlobalCleanup(CrashRecoveryContext *CRC) 52 : CrashRecoveryContextCleanup(CRC) {} 53 void recoverResources() override { ++GlobalInt; } 54 }; 55 56 static void noop() {} 57 58 TEST(CrashRecoveryTest, Cleanup) { 59 llvm::CrashRecoveryContext::Enable(); 60 GlobalInt = 0; 61 { 62 CrashRecoveryContext CRC; 63 CRC.registerCleanup(new IncrementGlobalCleanup(&CRC)); 64 EXPECT_TRUE(CRC.RunSafely(noop)); 65 } // run cleanups 66 EXPECT_EQ(1, GlobalInt); 67 68 GlobalInt = 0; 69 { 70 CrashRecoveryContext CRC; 71 CRC.registerCleanup(new IncrementGlobalCleanup(&CRC)); 72 EXPECT_FALSE(CRC.RunSafely(nullDeref)); 73 } // run cleanups 74 EXPECT_EQ(1, GlobalInt); 75 llvm::CrashRecoveryContext::Disable(); 76 } 77 78 TEST(CrashRecoveryTest, DumpStackCleanup) { 79 SmallString<128> Filename; 80 std::error_code EC = sys::fs::createTemporaryFile("crash", "test", Filename); 81 EXPECT_FALSE(EC); 82 sys::RemoveFileOnSignal(Filename); 83 llvm::sys::AddSignalHandler(incrementGlobalWithParam, nullptr); 84 GlobalInt = 0; 85 llvm::CrashRecoveryContext::Enable(); 86 { 87 CrashRecoveryContext CRC; 88 CRC.DumpStackAndCleanupOnFailure = true; 89 EXPECT_TRUE(CRC.RunSafely(noop)); 90 } 91 EXPECT_TRUE(sys::fs::exists(Filename)); 92 EXPECT_EQ(GlobalInt, 0); 93 { 94 CrashRecoveryContext CRC; 95 CRC.DumpStackAndCleanupOnFailure = true; 96 EXPECT_FALSE(CRC.RunSafely(nullDeref)); 97 EXPECT_NE(CRC.RetCode, 0); 98 } 99 EXPECT_FALSE(sys::fs::exists(Filename)); 100 EXPECT_EQ(GlobalInt, 1); 101 llvm::CrashRecoveryContext::Disable(); 102 } 103 104 TEST(CrashRecoveryTest, LimitedStackTrace) { 105 // FIXME: Handle "Depth" parameter in PrintStackTrace() function 106 // to print stack trace upto a specified Depth. 107 if (Triple(sys::getProcessTriple()).isOSWindows()) 108 GTEST_SKIP(); 109 std::string Res; 110 llvm::raw_string_ostream RawStream(Res); 111 PrintStackTrace(RawStream, 1); 112 EXPECT_EQ(std::string::npos, Res.find("#1")); 113 } 114 115 #ifdef _WIN32 116 static void raiseIt() { 117 RaiseException(123, EXCEPTION_NONCONTINUABLE, 0, NULL); 118 } 119 120 TEST(CrashRecoveryTest, RaiseException) { 121 llvm::CrashRecoveryContext::Enable(); 122 EXPECT_FALSE(CrashRecoveryContext().RunSafely(raiseIt)); 123 } 124 125 static void outputString() { 126 OutputDebugStringA("output for debugger\n"); 127 } 128 129 TEST(CrashRecoveryTest, CallOutputDebugString) { 130 llvm::CrashRecoveryContext::Enable(); 131 EXPECT_TRUE(CrashRecoveryContext().RunSafely(outputString)); 132 } 133 134 TEST(CrashRecoveryTest, Abort) { 135 llvm::CrashRecoveryContext::Enable(); 136 auto A = []() { abort(); }; 137 EXPECT_FALSE(CrashRecoveryContext().RunSafely(A)); 138 // Test a second time to ensure we reinstall the abort signal handler. 139 EXPECT_FALSE(CrashRecoveryContext().RunSafely(A)); 140 } 141 #endif 142 143 // Specifically ensure that programs that signal() or abort() through the 144 // CrashRecoveryContext can re-throw again their signal, so that `not --crash` 145 // succeeds. 146 #ifdef LLVM_ON_UNIX 147 // See llvm/utils/unittest/UnitTestMain/TestMain.cpp 148 extern const char *TestMainArgv0; 149 150 // Just a reachable symbol to ease resolving of the executable's path. 151 static cl::opt<std::string> CrashTestStringArg1("crash-test-string-arg1"); 152 153 TEST(CrashRecoveryTest, UnixCRCReturnCode) { 154 using namespace llvm::sys; 155 if (getenv("LLVM_CRC_UNIXCRCRETURNCODE")) { 156 llvm::CrashRecoveryContext::Enable(); 157 CrashRecoveryContext CRC; 158 // This path runs in a subprocess that exits by signalling, so don't use 159 // the googletest macros to verify things as they won't report properly. 160 if (CRC.RunSafely(abort)) 161 llvm_unreachable("RunSafely returned true!"); 162 if (CRC.RetCode != 128 + SIGABRT) 163 llvm_unreachable("Unexpected RetCode!"); 164 // re-throw signal 165 llvm::sys::unregisterHandlers(); 166 raise(CRC.RetCode - 128); 167 llvm_unreachable("Should have exited already!"); 168 } 169 170 std::string Executable = 171 sys::fs::getMainExecutable(TestMainArgv0, &CrashTestStringArg1); 172 StringRef argv[] = { 173 Executable, "--gtest_filter=CrashRecoveryTest.UnixCRCReturnCode"}; 174 175 // Add LLVM_CRC_UNIXCRCRETURNCODE to the environment of the child process. 176 int Res = setenv("LLVM_CRC_UNIXCRCRETURNCODE", "1", 0); 177 ASSERT_EQ(Res, 0); 178 179 Res = unsetenv("GTEST_SHARD_INDEX"); 180 ASSERT_EQ(Res, 0); 181 Res = unsetenv("GTEST_TOTAL_SHARDS"); 182 ASSERT_EQ(Res, 0); 183 184 std::string Error; 185 bool ExecutionFailed; 186 int RetCode = ExecuteAndWait(Executable, argv, {}, {}, 0, 0, &Error, 187 &ExecutionFailed); 188 ASSERT_EQ(-2, RetCode); 189 } 190 #endif 191