xref: /llvm-project/llvm/unittests/Support/CrashRecoveryTest.cpp (revision 24f510570fedf2ac6ea421478c7500d777c8c3c6)
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