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/ADT/Triple.h" 10 #include "llvm/Support/Compiler.h" 11 #include "llvm/Support/CrashRecoveryContext.h" 12 #include "llvm/Support/FileSystem.h" 13 #include "llvm/Support/Host.h" 14 #include "llvm/Support/Signals.h" 15 #include "llvm/Support/raw_ostream.h" 16 #include "gtest/gtest.h" 17 18 #ifdef _WIN32 19 #define WIN32_LEAN_AND_MEAN 20 #define NOGDI 21 #include <windows.h> 22 #endif 23 24 using namespace llvm; 25 using namespace llvm::sys; 26 27 static int GlobalInt = 0; 28 static void nullDeref() { *(volatile int *)0x10 = 0; } 29 static void incrementGlobal() { ++GlobalInt; } 30 static void llvmTrap() { LLVM_BUILTIN_TRAP; } 31 static void incrementGlobalWithParam(void *) { ++GlobalInt; } 32 33 TEST(CrashRecoveryTest, Basic) { 34 llvm::CrashRecoveryContext::Enable(); 35 GlobalInt = 0; 36 EXPECT_TRUE(CrashRecoveryContext().RunSafely(incrementGlobal)); 37 EXPECT_EQ(1, GlobalInt); 38 EXPECT_FALSE(CrashRecoveryContext().RunSafely(nullDeref)); 39 EXPECT_FALSE(CrashRecoveryContext().RunSafely(llvmTrap)); 40 } 41 42 struct IncrementGlobalCleanup : CrashRecoveryContextCleanup { 43 IncrementGlobalCleanup(CrashRecoveryContext *CRC) 44 : CrashRecoveryContextCleanup(CRC) {} 45 void recoverResources() override { ++GlobalInt; } 46 }; 47 48 static void noop() {} 49 50 TEST(CrashRecoveryTest, Cleanup) { 51 llvm::CrashRecoveryContext::Enable(); 52 GlobalInt = 0; 53 { 54 CrashRecoveryContext CRC; 55 CRC.registerCleanup(new IncrementGlobalCleanup(&CRC)); 56 EXPECT_TRUE(CRC.RunSafely(noop)); 57 } // run cleanups 58 EXPECT_EQ(1, GlobalInt); 59 60 GlobalInt = 0; 61 { 62 CrashRecoveryContext CRC; 63 CRC.registerCleanup(new IncrementGlobalCleanup(&CRC)); 64 EXPECT_FALSE(CRC.RunSafely(nullDeref)); 65 } // run cleanups 66 EXPECT_EQ(1, GlobalInt); 67 llvm::CrashRecoveryContext::Disable(); 68 } 69 70 TEST(CrashRecoveryTest, DumpStackCleanup) { 71 SmallString<128> Filename; 72 std::error_code EC = sys::fs::createTemporaryFile("crash", "test", Filename); 73 EXPECT_FALSE(EC); 74 sys::RemoveFileOnSignal(Filename); 75 llvm::sys::AddSignalHandler(incrementGlobalWithParam, nullptr); 76 GlobalInt = 0; 77 llvm::CrashRecoveryContext::Enable(); 78 { 79 CrashRecoveryContext CRC; 80 CRC.DumpStackAndCleanupOnFailure = true; 81 EXPECT_TRUE(CRC.RunSafely(noop)); 82 } 83 EXPECT_TRUE(sys::fs::exists(Filename)); 84 EXPECT_EQ(GlobalInt, 0); 85 { 86 CrashRecoveryContext CRC; 87 CRC.DumpStackAndCleanupOnFailure = true; 88 EXPECT_FALSE(CRC.RunSafely(nullDeref)); 89 EXPECT_NE(CRC.RetCode, 0); 90 } 91 EXPECT_FALSE(sys::fs::exists(Filename)); 92 EXPECT_EQ(GlobalInt, 1); 93 llvm::CrashRecoveryContext::Disable(); 94 } 95 96 TEST(CrashRecoveryTest, LimitedStackTrace) { 97 std::string Res; 98 llvm::raw_string_ostream RawStream(Res); 99 PrintStackTrace(RawStream, 1); 100 std::string Str = RawStream.str(); 101 // FIXME: Handle "Depth" parameter in PrintStackTrace() function 102 // to print stack trace upto a specified Depth. 103 if (!Triple(sys::getProcessTriple()).isOSWindows()) 104 EXPECT_EQ(std::string::npos, Str.find("#1")); 105 } 106 107 #ifdef _WIN32 108 static void raiseIt() { 109 RaiseException(123, EXCEPTION_NONCONTINUABLE, 0, NULL); 110 } 111 112 TEST(CrashRecoveryTest, RaiseException) { 113 llvm::CrashRecoveryContext::Enable(); 114 EXPECT_FALSE(CrashRecoveryContext().RunSafely(raiseIt)); 115 } 116 117 static void outputString() { 118 OutputDebugStringA("output for debugger\n"); 119 } 120 121 TEST(CrashRecoveryTest, CallOutputDebugString) { 122 llvm::CrashRecoveryContext::Enable(); 123 EXPECT_TRUE(CrashRecoveryContext().RunSafely(outputString)); 124 } 125 126 TEST(CrashRecoveryTest, Abort) { 127 llvm::CrashRecoveryContext::Enable(); 128 auto A = []() { abort(); }; 129 EXPECT_FALSE(CrashRecoveryContext().RunSafely(A)); 130 // Test a second time to ensure we reinstall the abort signal handler. 131 EXPECT_FALSE(CrashRecoveryContext().RunSafely(A)); 132 } 133 #endif 134