• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/base/watchdog.h"
18 
19 #include "gtest/gtest.h"
20 #include "perfetto/base/logging.h"
21 #include "perfetto/base/paged_memory.h"
22 #include "perfetto/base/scoped_file.h"
23 #include "perfetto/base/thread_utils.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