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 class TestWatchdog : public Watchdog {
40 public:
TestWatchdog(uint32_t polling_interval_ms)41 explicit TestWatchdog(uint32_t polling_interval_ms)
42 : Watchdog(polling_interval_ms) {}
~TestWatchdog()43 ~TestWatchdog() override {}
44 };
45
TEST(WatchdogTest,NoTimerCrashIfNotEnabled)46 TEST(WatchdogTest, NoTimerCrashIfNotEnabled) {
47 // CreateFatalTimer should be a noop if the watchdog is not enabled.
48 TestWatchdog watchdog(100);
49 auto handle = watchdog.CreateFatalTimer(1);
50 usleep(100 * 1000);
51 }
52
TEST(WatchdogTest,TimerCrash)53 TEST(WatchdogTest, TimerCrash) {
54 // Create a timer for 20 ms and don't release wihin the time.
55 EXPECT_DEATH(
56 {
57 TestWatchdog watchdog(100);
58 watchdog.Start();
59 auto handle = watchdog.CreateFatalTimer(20);
60 usleep(200 * 1000);
61 },
62 "");
63 }
64
TEST(WatchdogTest,CrashEvenWhenMove)65 TEST(WatchdogTest, CrashEvenWhenMove) {
66 std::map<int, Watchdog::Timer> timers;
67 EXPECT_DEATH(
68 {
69 TestWatchdog watchdog(100);
70 watchdog.Start();
71 timers.emplace(0, watchdog.CreateFatalTimer(20));
72 usleep(200 * 1000);
73 },
74 "");
75 }
76
TEST(WatchdogTest,CrashMemory)77 TEST(WatchdogTest, CrashMemory) {
78 EXPECT_DEATH(
79 {
80 // Allocate 8MB of data and use it to increase RSS.
81 const size_t kSize = 8 * 1024 * 1024;
82 auto void_ptr = PagedMemory::Allocate(kSize);
83 volatile uint8_t* ptr = static_cast<volatile uint8_t*>(void_ptr.Get());
84 for (size_t i = 0; i < kSize; i += sizeof(size_t)) {
85 *reinterpret_cast<volatile size_t*>(&ptr[i]) = i;
86 }
87
88 TestWatchdog watchdog(5);
89 watchdog.SetMemoryLimit(8 * 1024 * 1024, 25);
90 watchdog.Start();
91
92 // Sleep so that the watchdog has some time to pick it up.
93 usleep(1000 * 1000);
94 },
95 "");
96 }
97
TEST(WatchdogTest,CrashCpu)98 TEST(WatchdogTest, CrashCpu) {
99 EXPECT_DEATH(
100 {
101 TestWatchdog watchdog(1);
102 watchdog.SetCpuLimit(10, 25);
103 watchdog.Start();
104 volatile int x = 0;
105 for (;;) {
106 x++;
107 }
108 },
109 "");
110 }
111
112 // The test below tests that the fatal timer signal is sent to the thread that
113 // created the timer and not a random one.
114
RestoreSIGABRT(const struct sigaction * act)115 int RestoreSIGABRT(const struct sigaction* act) {
116 return sigaction(SIGABRT, act, nullptr);
117 }
118
119 PlatformThreadId g_aborted_thread = 0;
SIGABRTHandler(int)120 void SIGABRTHandler(int) {
121 g_aborted_thread = GetThreadId();
122 }
123
TEST(WatchdogTest,TimerCrashDeliveredToCallerThread)124 TEST(WatchdogTest, TimerCrashDeliveredToCallerThread) {
125 // Setup a signal handler so that SIGABRT doesn't cause a crash but just
126 // records the current thread id.
127 struct sigaction oldact;
128 struct sigaction newact = {};
129 newact.sa_handler = SIGABRTHandler;
130 ASSERT_EQ(sigaction(SIGABRT, &newact, &oldact), 0);
131 base::ScopedResource<const struct sigaction*, RestoreSIGABRT, nullptr>
132 auto_restore(&oldact);
133
134 // Create 8 threads. All of them but one will just sleep. The selected one
135 // will register a watchdog and fail.
136 const size_t kKillThreadNum = 3;
137 std::mutex mutex;
138 std::condition_variable cv;
139 bool quit = false;
140 g_aborted_thread = 0;
141 PlatformThreadId expected_tid = 0;
142
143 auto thread_fn = [&mutex, &cv, &quit, &expected_tid](size_t thread_num) {
144 if (thread_num == kKillThreadNum) {
145 expected_tid = GetThreadId();
146 TestWatchdog watchdog(100);
147 watchdog.Start();
148 auto handle = watchdog.CreateFatalTimer(2);
149 usleep(200 * 1000); // This will be interrupted by the fatal timer.
150 std::unique_lock<std::mutex> lock(mutex);
151 quit = true;
152 cv.notify_all();
153 } else {
154 std::unique_lock<std::mutex> lock(mutex);
155 cv.wait(lock, [&quit] { return quit; });
156 }
157 };
158
159 std::vector<std::thread> threads;
160 for (size_t i = 0; i < 8; i++)
161 threads.emplace_back(thread_fn, i);
162
163 // Join them all.
164 for (auto& thread : threads)
165 thread.join();
166
167 EXPECT_EQ(g_aborted_thread, expected_tid);
168 }
169
170 } // namespace
171 } // namespace base
172 } // namespace perfetto
173