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/Support/Compiler.h" 10 #include "llvm/Support/CrashRecoveryContext.h" 11 #include "llvm/Support/FileSystem.h" 12 #include "llvm/Support/Signals.h" 13 #include "gtest/gtest.h" 14 15 #ifdef _WIN32 16 #define WIN32_LEAN_AND_MEAN 17 #define NOGDI 18 #include <windows.h> 19 #endif 20 21 using namespace llvm; 22 using namespace llvm::sys; 23 24 static int GlobalInt = 0; 25 static void nullDeref() { *(volatile int *)0x10 = 0; } 26 static void incrementGlobal() { ++GlobalInt; } 27 static void llvmTrap() { LLVM_BUILTIN_TRAP; } 28 static void incrementGlobalWithParam(void *) { ++GlobalInt; } 29 30 TEST(CrashRecoveryTest, Basic) { 31 llvm::CrashRecoveryContext::Enable(); 32 GlobalInt = 0; 33 EXPECT_TRUE(CrashRecoveryContext().RunSafely(incrementGlobal)); 34 EXPECT_EQ(1, GlobalInt); 35 EXPECT_FALSE(CrashRecoveryContext().RunSafely(nullDeref)); 36 EXPECT_FALSE(CrashRecoveryContext().RunSafely(llvmTrap)); 37 } 38 39 struct IncrementGlobalCleanup : CrashRecoveryContextCleanup { 40 IncrementGlobalCleanup(CrashRecoveryContext *CRC) 41 : CrashRecoveryContextCleanup(CRC) {} 42 void recoverResources() override { ++GlobalInt; } 43 }; 44 45 static void noop() {} 46 47 TEST(CrashRecoveryTest, Cleanup) { 48 llvm::CrashRecoveryContext::Enable(); 49 GlobalInt = 0; 50 { 51 CrashRecoveryContext CRC; 52 CRC.registerCleanup(new IncrementGlobalCleanup(&CRC)); 53 EXPECT_TRUE(CRC.RunSafely(noop)); 54 } // run cleanups 55 EXPECT_EQ(1, GlobalInt); 56 57 GlobalInt = 0; 58 { 59 CrashRecoveryContext CRC; 60 CRC.registerCleanup(new IncrementGlobalCleanup(&CRC)); 61 EXPECT_FALSE(CRC.RunSafely(nullDeref)); 62 } // run cleanups 63 EXPECT_EQ(1, GlobalInt); 64 llvm::CrashRecoveryContext::Disable(); 65 } 66 67 TEST(CrashRecoveryTest, DumpStackCleanup) { 68 SmallString<128> Filename; 69 std::error_code EC = sys::fs::createTemporaryFile("crash", "test", Filename); 70 EXPECT_FALSE(EC); 71 sys::RemoveFileOnSignal(Filename); 72 llvm::sys::AddSignalHandler(incrementGlobalWithParam, nullptr); 73 GlobalInt = 0; 74 llvm::CrashRecoveryContext::Enable(); 75 { 76 CrashRecoveryContext CRC; 77 CRC.DumpStackAndCleanupOnFailure = true; 78 EXPECT_TRUE(CRC.RunSafely(noop)); 79 } 80 EXPECT_TRUE(sys::fs::exists(Filename)); 81 EXPECT_EQ(GlobalInt, 0); 82 { 83 CrashRecoveryContext CRC; 84 CRC.DumpStackAndCleanupOnFailure = true; 85 EXPECT_FALSE(CRC.RunSafely(nullDeref)); 86 EXPECT_NE(CRC.RetCode, 0); 87 } 88 EXPECT_FALSE(sys::fs::exists(Filename)); 89 EXPECT_EQ(GlobalInt, 1); 90 llvm::CrashRecoveryContext::Disable(); 91 } 92 93 #ifdef _WIN32 94 static void raiseIt() { 95 RaiseException(123, EXCEPTION_NONCONTINUABLE, 0, NULL); 96 } 97 98 TEST(CrashRecoveryTest, RaiseException) { 99 llvm::CrashRecoveryContext::Enable(); 100 EXPECT_FALSE(CrashRecoveryContext().RunSafely(raiseIt)); 101 } 102 103 static void outputString() { 104 OutputDebugStringA("output for debugger\n"); 105 } 106 107 TEST(CrashRecoveryTest, CallOutputDebugString) { 108 llvm::CrashRecoveryContext::Enable(); 109 EXPECT_TRUE(CrashRecoveryContext().RunSafely(outputString)); 110 } 111 112 #endif 113