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 #if PERFETTO_BUILDFLAG(PERFETTO_WATCHDOG)
20 
21 #include <fcntl.h>
22 #include <inttypes.h>
23 #include <signal.h>
24 #include <stdint.h>
25 
26 #include <fstream>
27 #include <thread>
28 
29 #include "perfetto/base/build_config.h"
30 #include "perfetto/base/logging.h"
31 #include "perfetto/base/thread_utils.h"
32 #include "perfetto/ext/base/scoped_file.h"
33 
34 namespace perfetto {
35 namespace base {
36 
37 namespace {
38 
39 constexpr uint32_t kDefaultPollingInterval = 30 * 1000;
40 
IsMultipleOf(uint32_t number,uint32_t divisor)41 bool IsMultipleOf(uint32_t number, uint32_t divisor) {
42   return number >= divisor && number % divisor == 0;
43 }
44 
MeanForArray(const uint64_t array[],size_t size)45 double MeanForArray(const uint64_t array[], size_t size) {
46   uint64_t total = 0;
47   for (size_t i = 0; i < size; i++) {
48     total += array[i];
49   }
50   return static_cast<double>(total / size);
51 
52 }
53 
54 }  //  namespace
55 
Watchdog(uint32_t polling_interval_ms)56 Watchdog::Watchdog(uint32_t polling_interval_ms)
57     : polling_interval_ms_(polling_interval_ms) {}
58 
~Watchdog()59 Watchdog::~Watchdog() {
60   if (!thread_.joinable()) {
61     PERFETTO_DCHECK(!enabled_);
62     return;
63   }
64   PERFETTO_DCHECK(enabled_);
65   enabled_ = false;
66   exit_signal_.notify_one();
67   thread_.join();
68 }
69 
GetInstance()70 Watchdog* Watchdog::GetInstance() {
71   static Watchdog* watchdog = new Watchdog(kDefaultPollingInterval);
72   return watchdog;
73 }
74 
CreateFatalTimer(uint32_t ms)75 Watchdog::Timer Watchdog::CreateFatalTimer(uint32_t ms) {
76   if (!enabled_.load(std::memory_order_relaxed))
77     return Watchdog::Timer(0);
78 
79   return Watchdog::Timer(ms);
80 }
81 
Start()82 void Watchdog::Start() {
83   std::lock_guard<std::mutex> guard(mutex_);
84   if (thread_.joinable()) {
85     PERFETTO_DCHECK(enabled_);
86   } else {
87     PERFETTO_DCHECK(!enabled_);
88 
89 #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
90     PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
91     // Kick the thread to start running but only on Android or Linux.
92     enabled_ = true;
93     thread_ = std::thread(&Watchdog::ThreadMain, this);
94 #endif
95   }
96 }
97 
SetMemoryLimit(uint64_t bytes,uint32_t window_ms)98 void Watchdog::SetMemoryLimit(uint64_t bytes, uint32_t window_ms) {
99   // Update the fields under the lock.
100   std::lock_guard<std::mutex> guard(mutex_);
101 
102   PERFETTO_CHECK(IsMultipleOf(window_ms, polling_interval_ms_) || bytes == 0);
103 
104   size_t size = bytes == 0 ? 0 : window_ms / polling_interval_ms_ + 1;
105   memory_window_bytes_.Reset(size);
106   memory_limit_bytes_ = bytes;
107 }
108 
SetCpuLimit(uint32_t percentage,uint32_t window_ms)109 void Watchdog::SetCpuLimit(uint32_t percentage, uint32_t window_ms) {
110   std::lock_guard<std::mutex> guard(mutex_);
111 
112   PERFETTO_CHECK(percentage <= 100);
113   PERFETTO_CHECK(IsMultipleOf(window_ms, polling_interval_ms_) ||
114                  percentage == 0);
115 
116   size_t size = percentage == 0 ? 0 : window_ms / polling_interval_ms_ + 1;
117   cpu_window_time_ticks_.Reset(size);
118   cpu_limit_percentage_ = percentage;
119 }
120 
ThreadMain()121 void Watchdog::ThreadMain() {
122   base::ScopedFile stat_fd(base::OpenFile("/proc/self/stat", O_RDONLY));
123   if (!stat_fd) {
124     PERFETTO_ELOG("Failed to open stat file to enforce resource limits.");
125     return;
126   }
127 
128   std::unique_lock<std::mutex> guard(mutex_);
129   for (;;) {
130     exit_signal_.wait_for(guard,
131                           std::chrono::milliseconds(polling_interval_ms_));
132     if (!enabled_)
133       return;
134 
135     lseek(stat_fd.get(), 0, SEEK_SET);
136 
137     char c[512];
138     if (read(stat_fd.get(), c, sizeof(c)) < 0) {
139       PERFETTO_ELOG("Failed to read stat file to enforce resource limits.");
140       return;
141     }
142     c[sizeof(c) - 1] = '\0';
143 
144     unsigned long int utime = 0l;
145     unsigned long int stime = 0l;
146     long int rss_pages = -1l;
147     PERFETTO_CHECK(
148         sscanf(c,
149                "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %lu"
150                "%lu %*d %*d %*d %*d %*d %*d %*u %*u %ld",
151                &utime, &stime, &rss_pages) == 3);
152 
153     uint64_t cpu_time = utime + stime;
154     uint64_t rss_bytes = static_cast<uint64_t>(rss_pages) * base::kPageSize;
155 
156     CheckMemory(rss_bytes);
157     CheckCpu(cpu_time);
158   }
159 }
160 
CheckMemory(uint64_t rss_bytes)161 void Watchdog::CheckMemory(uint64_t rss_bytes) {
162   if (memory_limit_bytes_ == 0)
163     return;
164 
165   // Add the current stat value to the ring buffer and check that the mean
166   // remains under our threshold.
167   if (memory_window_bytes_.Push(rss_bytes)) {
168     if (memory_window_bytes_.Mean() > static_cast<double>(memory_limit_bytes_)) {
169       PERFETTO_ELOG(
170           "Memory watchdog trigger. Memory window of %f bytes is above the "
171           "%" PRIu64 " bytes limit.",
172           memory_window_bytes_.Mean(), memory_limit_bytes_);
173       kill(getpid(), SIGABRT);
174     }
175   }
176 }
177 
CheckCpu(uint64_t cpu_time)178 void Watchdog::CheckCpu(uint64_t cpu_time) {
179   if (cpu_limit_percentage_ == 0)
180     return;
181 
182   // Add the cpu time to the ring buffer.
183   if (cpu_window_time_ticks_.Push(cpu_time)) {
184     // Compute the percentage over the whole window and check that it remains
185     // under the threshold.
186     uint64_t difference_ticks = cpu_window_time_ticks_.NewestWhenFull() -
187                                 cpu_window_time_ticks_.OldestWhenFull();
188     double window_interval_ticks =
189         (static_cast<double>(WindowTimeForRingBuffer(cpu_window_time_ticks_)) /
190          1000.0) *
191         static_cast<double>(sysconf(_SC_CLK_TCK));
192     double percentage = static_cast<double>(difference_ticks) /
193                         static_cast<double>(window_interval_ticks) * 100;
194     if (percentage > cpu_limit_percentage_) {
195       PERFETTO_ELOG("CPU watchdog trigger. %f%% CPU use is above the %" PRIu32
196                     "%% CPU limit.",
197                     percentage, cpu_limit_percentage_);
198       kill(getpid(), SIGABRT);
199     }
200   }
201 }
202 
WindowTimeForRingBuffer(const WindowedInterval & window)203 uint32_t Watchdog::WindowTimeForRingBuffer(const WindowedInterval& window) {
204   return static_cast<uint32_t>(window.size() - 1) * polling_interval_ms_;
205 }
206 
Push(uint64_t sample)207 bool Watchdog::WindowedInterval::Push(uint64_t sample) {
208   // Add the sample to the current position in the ring buffer.
209   buffer_[position_] = sample;
210 
211   // Update the position with next one circularily.
212   position_ = (position_ + 1) % size_;
213 
214   // Set the filled flag the first time we wrap.
215   filled_ = filled_ || position_ == 0;
216   return filled_;
217 }
218 
Mean() const219 double Watchdog::WindowedInterval::Mean() const {
220   return MeanForArray(buffer_.get(), size_);
221 }
222 
Clear()223 void Watchdog::WindowedInterval::Clear() {
224   position_ = 0;
225   buffer_.reset(new uint64_t[size_]());
226 }
227 
Reset(size_t new_size)228 void Watchdog::WindowedInterval::Reset(size_t new_size) {
229   position_ = 0;
230   size_ = new_size;
231   buffer_.reset(new_size == 0 ? nullptr : new uint64_t[new_size]());
232 }
233 
Timer(uint32_t ms)234 Watchdog::Timer::Timer(uint32_t ms) {
235   if (!ms)
236     return;  // No-op timer created when the watchdog is disabled.
237 
238   struct sigevent sev = {};
239   sev.sigev_notify = SIGEV_THREAD_ID;
240   sev._sigev_un._tid = base::GetThreadId();
241   sev.sigev_signo = SIGABRT;
242   PERFETTO_CHECK(timer_create(CLOCK_MONOTONIC, &sev, &timerid_) != -1);
243   struct itimerspec its = {};
244   its.it_value.tv_sec = ms / 1000;
245   its.it_value.tv_nsec = 1000000L * (ms % 1000);
246   PERFETTO_CHECK(timer_settime(timerid_, 0, &its, nullptr) != -1);
247 }
248 
~Timer()249 Watchdog::Timer::~Timer() {
250   if (timerid_ != nullptr) {
251     timer_delete(timerid_);
252   }
253 }
254 
Timer(Timer && other)255 Watchdog::Timer::Timer(Timer&& other) noexcept {
256   timerid_ = other.timerid_;
257   other.timerid_ = nullptr;
258 }
259 
260 }  // namespace base
261 }  // namespace perfetto
262 
263 #endif  // PERFETTO_BUILDFLAG(PERFETTO_WATCHDOG)
264