1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "perfetto/ext/base/watchdog.h"
18
19 #include "perfetto/base/logging.h"
20 #include "perfetto/base/thread_utils.h"
21 #include "perfetto/ext/base/paged_memory.h"
22 #include "perfetto/ext/base/scoped_file.h"
23 #include "test/gtest_and_gmock.h"
24
25 #include <signal.h>
26 #include <time.h>
27
28 #include <condition_variable>
29 #include <map>
30 #include <memory>
31 #include <mutex>
32 #include <thread>
33 #include <vector>
34
35 namespace perfetto {
36 namespace base {
37 namespace {
38
39 static auto kCrashReasonIgnored = WatchdogCrashReason::kUnspecified;
40
41 class TestWatchdog : public Watchdog {
42 public:
TestWatchdog(uint32_t polling_interval_ms)43 explicit TestWatchdog(uint32_t polling_interval_ms)
44 : Watchdog(polling_interval_ms) {
45 disable_kill_failsafe_for_testing_ = true;
46 }
~TestWatchdog()47 ~TestWatchdog() override {}
48 };
49
TEST(WatchdogTest,NoTimerCrashIfNotEnabled)50 TEST(WatchdogTest, NoTimerCrashIfNotEnabled) {
51 // CreateFatalTimer should be a noop if the watchdog is not enabled.
52 TestWatchdog watchdog(100);
53 auto handle = watchdog.CreateFatalTimer(1, kCrashReasonIgnored);
54 usleep(100 * 1000);
55 }
56
TEST(WatchdogTest,TimerCrash)57 TEST(WatchdogTest, TimerCrash) {
58 // Create a timer for 20 ms and don't release wihin the time.
59 EXPECT_DEATH(
60 {
61 TestWatchdog watchdog(100);
62 watchdog.Start();
63 auto handle = watchdog.CreateFatalTimer(20, kCrashReasonIgnored);
64 usleep(200 * 1000);
65 },
66 "");
67 }
68
TEST(WatchdogTest,CrashEvenWhenMove)69 TEST(WatchdogTest, CrashEvenWhenMove) {
70 std::map<int, Watchdog::Timer> timers;
71 EXPECT_DEATH(
72 {
73 TestWatchdog watchdog(100);
74 watchdog.Start();
75 timers.emplace(0, watchdog.CreateFatalTimer(20, kCrashReasonIgnored));
76 usleep(200 * 1000);
77 },
78 "");
79 }
80
TEST(WatchdogTest,CrashMemory)81 TEST(WatchdogTest, CrashMemory) {
82 EXPECT_DEATH(
83 {
84 // Allocate 8MB of data and use it to increase RSS.
85 const size_t kSize = 8 * 1024 * 1024;
86 auto void_ptr = PagedMemory::Allocate(kSize);
87 volatile uint8_t* ptr = static_cast<volatile uint8_t*>(void_ptr.Get());
88 for (size_t i = 0; i < kSize; i += sizeof(size_t)) {
89 *reinterpret_cast<volatile size_t*>(&ptr[i]) = i;
90 }
91
92 TestWatchdog watchdog(5);
93 watchdog.SetMemoryLimit(8 * 1024 * 1024, 25);
94 watchdog.Start();
95
96 // Sleep so that the watchdog has some time to pick it up.
97 usleep(1000 * 1000);
98 },
99 "");
100 }
101
TEST(WatchdogTest,CrashCpu)102 TEST(WatchdogTest, CrashCpu) {
103 EXPECT_DEATH(
104 {
105 TestWatchdog watchdog(1);
106 watchdog.SetCpuLimit(10, 25);
107 watchdog.Start();
108 volatile int x = 0;
109 for (;;) {
110 x++;
111 }
112 },
113 "");
114 }
115
116 // The test below tests that the fatal timer signal is sent to the thread that
117 // created the timer and not a random one.
118
RestoreSIGABRT(const struct sigaction * act)119 int RestoreSIGABRT(const struct sigaction* act) {
120 return sigaction(SIGABRT, act, nullptr);
121 }
122
123 PlatformThreadId g_aborted_thread = 0;
SIGABRTHandler(int)124 void SIGABRTHandler(int) {
125 g_aborted_thread = GetThreadId();
126 }
127
TEST(WatchdogTest,TimerCrashDeliveredToCallerThread)128 TEST(WatchdogTest, TimerCrashDeliveredToCallerThread) {
129 // Setup a signal handler so that SIGABRT doesn't cause a crash but just
130 // records the current thread id.
131 struct sigaction oldact;
132 struct sigaction newact = {};
133 newact.sa_handler = SIGABRTHandler;
134 ASSERT_EQ(sigaction(SIGABRT, &newact, &oldact), 0);
135 base::ScopedResource<const struct sigaction*, RestoreSIGABRT, nullptr>
136 auto_restore(&oldact);
137
138 // Create 8 threads. All of them but one will just sleep. The selected one
139 // will register a watchdog and fail.
140 const size_t kKillThreadNum = 3;
141 std::mutex mutex;
142 std::condition_variable cv;
143 bool quit = false;
144 g_aborted_thread = 0;
145 PlatformThreadId expected_tid = 0;
146
147 auto thread_fn = [&mutex, &cv, &quit, &expected_tid](size_t thread_num) {
148 if (thread_num == kKillThreadNum) {
149 expected_tid = GetThreadId();
150 TestWatchdog watchdog(100);
151 watchdog.Start();
152 auto handle = watchdog.CreateFatalTimer(2, kCrashReasonIgnored);
153 usleep(200 * 1000); // This will be interrupted by the fatal timer.
154 std::unique_lock<std::mutex> lock(mutex);
155 quit = true;
156 cv.notify_all();
157 } else {
158 std::unique_lock<std::mutex> lock(mutex);
159 cv.wait(lock, [&quit] { return quit; });
160 }
161 };
162
163 std::vector<std::thread> threads;
164
165 for (size_t i = 0; i < 8; i++)
166 threads.emplace_back(thread_fn, i);
167
168 // Join them all.
169 for (auto& thread : threads)
170 thread.join();
171
172 EXPECT_EQ(g_aborted_thread, expected_tid);
173 }
174
175 } // namespace
176 } // namespace base
177 } // namespace perfetto
178