xref: /aosp_15_r20/external/perfetto/src/base/watchdog_unittest.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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