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