blob: 97ece02285ac5cb9e038de09964517721dbfa256 [file] [log] [blame]
Florian Mayerb4334002018-02-01 11:10:36 +00001/*
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 "perfetto/base/logging.h"
Lalit Maganti44ff2a72018-02-27 16:12:24 +000020#include "perfetto/base/scoped_file.h"
Florian Mayerb4334002018-02-01 11:10:36 +000021
Lalit Maganti44ff2a72018-02-27 16:12:24 +000022#include <fcntl.h>
Florian Mayerb4334002018-02-01 11:10:36 +000023#include <signal.h>
24#include <stdint.h>
Lalit Maganti44ff2a72018-02-27 16:12:24 +000025#include <fstream>
26#include <thread>
Florian Mayerb4334002018-02-01 11:10:36 +000027
28namespace perfetto {
29namespace base {
30
Lalit Maganti44ff2a72018-02-27 16:12:24 +000031namespace {
32
33static constexpr uint32_t kDefaultPollingInterval = 30 * 1000;
34
35bool IsMultipleOf(uint32_t number, uint32_t divisor) {
36 return number >= divisor && number % divisor == 0;
37}
38
39double MeanForArray(uint64_t array[], size_t size) {
40 uint64_t total = 0;
41 for (size_t i = 0; i < size; i++) {
42 total += array[i];
43 }
44 return total / size;
45}
46
47} // namespace
48
49Watchdog::Watchdog(uint32_t polling_interval_ms)
50 : polling_interval_ms_(polling_interval_ms) {}
51
52Watchdog::~Watchdog() {
Lalit Magantie419ccb2018-03-06 11:48:03 +000053 if (!thread_.joinable()) {
54 PERFETTO_DCHECK(quit_);
55 return;
56 }
57
58 {
59 std::lock_guard<std::mutex> guard(mutex_);
60 PERFETTO_DCHECK(!quit_);
61 quit_ = true;
62 }
63 exit_signal_.notify_one();
64 thread_.join();
Lalit Maganti44ff2a72018-02-27 16:12:24 +000065}
66
67Watchdog* Watchdog::GetInstance() {
Lalit Magantie419ccb2018-03-06 11:48:03 +000068#if PERFETTO_BUILDFLAG(PERFETTO_CHROMIUM_BUILD)
69 // Ensure that it is impossible to use watchdog on a Chromium build.
70 PERFETTO_CHECK(false);
71#endif
72
Lalit Maganti44ff2a72018-02-27 16:12:24 +000073 static Watchdog* watchdog = new Watchdog(kDefaultPollingInterval);
74 return watchdog;
75}
76
77Watchdog::Timer Watchdog::CreateFatalTimer(uint32_t ms) {
78 return Watchdog::Timer(ms);
79}
80
Lalit Magantie419ccb2018-03-06 11:48:03 +000081void Watchdog::Start() {
82 std::lock_guard<std::mutex> guard(mutex_);
83 if (thread_.joinable()) {
84 PERFETTO_DCHECK(!quit_);
85 } else {
86 PERFETTO_DCHECK(quit_);
Lalit Maganti44ff2a72018-02-27 16:12:24 +000087
Lalit Magantie419ccb2018-03-06 11:48:03 +000088#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
89 PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
90 // Kick the thread to start running but only on Android or Linux.
91 quit_ = false;
92 thread_ = std::thread(&Watchdog::ThreadMain, this);
93#endif
Lalit Maganti44ff2a72018-02-27 16:12:24 +000094 }
Lalit Magantie419ccb2018-03-06 11:48:03 +000095}
96
97void Watchdog::SetMemoryLimit(uint32_t bytes, uint32_t window_ms) {
98 // Update the fields under the lock.
99 std::lock_guard<std::mutex> guard(mutex_);
100
101 PERFETTO_CHECK(IsMultipleOf(window_ms, polling_interval_ms_) || bytes == 0);
102
103 size_t size = bytes == 0 ? 0 : window_ms / polling_interval_ms_ + 1;
104 memory_window_bytes_.Reset(size);
105 memory_limit_bytes_ = bytes;
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000106}
107
108void Watchdog::SetCpuLimit(uint32_t percentage, uint32_t window_ms) {
Lalit Magantie419ccb2018-03-06 11:48:03 +0000109 std::lock_guard<std::mutex> guard(mutex_);
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000110
Lalit Magantie419ccb2018-03-06 11:48:03 +0000111 PERFETTO_CHECK(percentage <= 100);
112 PERFETTO_CHECK(IsMultipleOf(window_ms, polling_interval_ms_) ||
113 percentage == 0);
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000114
Lalit Magantie419ccb2018-03-06 11:48:03 +0000115 size_t size = percentage == 0 ? 0 : window_ms / polling_interval_ms_ + 1;
116 cpu_window_time_ticks_.Reset(size);
117 cpu_limit_percentage_ = percentage;
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000118}
119
120void Watchdog::ThreadMain() {
121 base::ScopedFile stat_fd(open("/proc/self/stat", O_RDONLY));
122 if (!stat_fd) {
123 PERFETTO_ELOG("Failed to open stat file to enforce resource limits.");
124 return;
125 }
126
127 std::unique_lock<std::mutex> guard(mutex_);
128 for (;;) {
129 exit_signal_.wait_for(guard,
130 std::chrono::milliseconds(polling_interval_ms_));
131 if (quit_)
132 return;
133
134 lseek(stat_fd.get(), 0, SEEK_SET);
135
136 char c[512];
137 if (read(stat_fd.get(), c, sizeof(c)) < 0) {
138 PERFETTO_ELOG("Failed to read stat file to enforce resource limits.");
139 return;
140 }
141 c[sizeof(c) - 1] = '\0';
142
143 unsigned long int utime = 0l;
144 unsigned long int stime = 0l;
145 long int rss_pages = -1l;
146 PERFETTO_CHECK(
147 sscanf(c,
148 "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %lu"
149 "%lu %*d %*d %*d %*d %*d %*d %*u %*u %ld",
150 &utime, &stime, &rss_pages) == 3);
151
152 uint64_t cpu_time = utime + stime;
153 uint64_t rss_bytes = static_cast<uint32_t>(rss_pages) * base::kPageSize;
154
155 CheckMemory(rss_bytes);
156 CheckCpu(cpu_time);
157 }
158}
159
160void Watchdog::CheckMemory(uint64_t rss_bytes) {
161 if (memory_limit_bytes_ == 0)
162 return;
163
164 // Add the current stat value to the ring buffer and check that the mean
165 // remains under our threshold.
166 if (memory_window_bytes_.Push(rss_bytes)) {
167 if (memory_window_bytes_.Mean() > memory_limit_bytes_) {
168 kill(getpid(), SIGABRT);
169 }
170 }
171}
172
173void Watchdog::CheckCpu(uint64_t cpu_time) {
174 if (cpu_limit_percentage_ == 0)
175 return;
176
177 // Add the cpu time to the ring buffer.
178 if (cpu_window_time_ticks_.Push(cpu_time)) {
179 // Compute the percentage over the whole window and check that it remains
180 // under the threshold.
181 uint64_t difference_ticks = cpu_window_time_ticks_.NewestWhenFull() -
182 cpu_window_time_ticks_.OldestWhenFull();
183 double window_interval_ticks =
184 (static_cast<double>(WindowTimeForRingBuffer(cpu_window_time_ticks_)) /
185 1000.0) *
186 sysconf(_SC_CLK_TCK);
187 double percentage = static_cast<double>(difference_ticks) /
188 static_cast<double>(window_interval_ticks) * 100;
189 if (percentage > cpu_limit_percentage_) {
190 kill(getpid(), SIGABRT);
191 }
192 }
193}
194
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000195uint32_t Watchdog::WindowTimeForRingBuffer(const WindowedInterval& window) {
196 return static_cast<uint32_t>(window.size() - 1) * polling_interval_ms_;
197}
198
199bool Watchdog::WindowedInterval::Push(uint64_t sample) {
200 // Add the sample to the current position in the ring buffer.
201 buffer_[position_] = sample;
202
203 // Update the position with next one circularily.
204 position_ = (position_ + 1) % size_;
205
206 // Set the filled flag the first time we wrap.
207 filled_ = filled_ || position_ == 0;
208 return filled_;
209}
210
211double Watchdog::WindowedInterval::Mean() const {
212 return MeanForArray(buffer_.get(), size_);
213}
214
215void Watchdog::WindowedInterval::Clear() {
216 position_ = 0;
217 buffer_.reset(new uint64_t[size_]());
218}
219
220void Watchdog::WindowedInterval::Reset(size_t new_size) {
221 position_ = 0;
222 size_ = new_size;
223 buffer_.reset(new_size == 0 ? nullptr : new uint64_t[new_size]());
224}
225
226Watchdog::Timer::Timer(uint32_t ms) {
Florian Mayerf5b8d722018-02-01 13:12:17 +0000227 struct sigevent sev = {};
Florian Mayerb4334002018-02-01 11:10:36 +0000228 sev.sigev_notify = SIGEV_SIGNAL;
229 sev.sigev_signo = SIGABRT;
Florian Mayerb4334002018-02-01 11:10:36 +0000230 PERFETTO_CHECK(timer_create(CLOCK_MONOTONIC, &sev, &timerid_) != -1);
Florian Mayerf5b8d722018-02-01 13:12:17 +0000231 struct itimerspec its = {};
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000232 its.it_value.tv_sec = ms / 1000;
233 its.it_value.tv_nsec = 1000000L * (ms % 1000);
Florian Mayerb4334002018-02-01 11:10:36 +0000234 PERFETTO_CHECK(timer_settime(timerid_, 0, &its, nullptr) != -1);
235}
236
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000237Watchdog::Timer::~Timer() {
238 if (timerid_ != nullptr) {
239 timer_delete(timerid_);
240 }
241}
242
243Watchdog::Timer::Timer(Timer&& other) {
244 timerid_ = other.timerid_;
245 other.timerid_ = nullptr;
Florian Mayerb4334002018-02-01 11:10:36 +0000246}
247
248} // namespace base
249} // namespace perfetto