blob: df6ebd1899f90bc7fa8f5f742aedd62e7460a3b0 [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() {
53 QuitThreadUnlocked();
54}
55
56Watchdog* Watchdog::GetInstance() {
57 static Watchdog* watchdog = new Watchdog(kDefaultPollingInterval);
58 return watchdog;
59}
60
61Watchdog::Timer Watchdog::CreateFatalTimer(uint32_t ms) {
62 return Watchdog::Timer(ms);
63}
64
65void Watchdog::SetMemoryLimit(uint32_t bytes, uint32_t window_ms) {
66 {
67 // Update the fields under the lock.
68 std::lock_guard<std::mutex> guard(mutex_);
69
70 PERFETTO_CHECK(IsMultipleOf(window_ms, polling_interval_ms_) || bytes == 0);
71
72 size_t size = bytes == 0 ? 0 : window_ms / polling_interval_ms_ + 1;
73 memory_window_bytes_.Reset(size);
74 memory_limit_bytes_ = bytes;
75 }
76 UpdateThreadStateUnlocked();
77}
78
79void Watchdog::SetCpuLimit(uint32_t percentage, uint32_t window_ms) {
80 {
81 std::lock_guard<std::mutex> guard(mutex_);
82
83 PERFETTO_CHECK(percentage <= 100);
84 PERFETTO_CHECK(IsMultipleOf(window_ms, polling_interval_ms_) ||
85 percentage == 0);
86
87 size_t size = percentage == 0 ? 0 : window_ms / polling_interval_ms_ + 1;
88 cpu_window_time_ticks_.Reset(size);
89 cpu_limit_percentage_ = percentage;
90 }
91 UpdateThreadStateUnlocked();
92}
93
94void Watchdog::ThreadMain() {
95 base::ScopedFile stat_fd(open("/proc/self/stat", O_RDONLY));
96 if (!stat_fd) {
97 PERFETTO_ELOG("Failed to open stat file to enforce resource limits.");
98 return;
99 }
100
101 std::unique_lock<std::mutex> guard(mutex_);
102 for (;;) {
103 exit_signal_.wait_for(guard,
104 std::chrono::milliseconds(polling_interval_ms_));
105 if (quit_)
106 return;
107
108 lseek(stat_fd.get(), 0, SEEK_SET);
109
110 char c[512];
111 if (read(stat_fd.get(), c, sizeof(c)) < 0) {
112 PERFETTO_ELOG("Failed to read stat file to enforce resource limits.");
113 return;
114 }
115 c[sizeof(c) - 1] = '\0';
116
117 unsigned long int utime = 0l;
118 unsigned long int stime = 0l;
119 long int rss_pages = -1l;
120 PERFETTO_CHECK(
121 sscanf(c,
122 "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %lu"
123 "%lu %*d %*d %*d %*d %*d %*d %*u %*u %ld",
124 &utime, &stime, &rss_pages) == 3);
125
126 uint64_t cpu_time = utime + stime;
127 uint64_t rss_bytes = static_cast<uint32_t>(rss_pages) * base::kPageSize;
128
129 CheckMemory(rss_bytes);
130 CheckCpu(cpu_time);
131 }
132}
133
134void Watchdog::CheckMemory(uint64_t rss_bytes) {
135 if (memory_limit_bytes_ == 0)
136 return;
137
138 // Add the current stat value to the ring buffer and check that the mean
139 // remains under our threshold.
140 if (memory_window_bytes_.Push(rss_bytes)) {
141 if (memory_window_bytes_.Mean() > memory_limit_bytes_) {
142 kill(getpid(), SIGABRT);
143 }
144 }
145}
146
147void Watchdog::CheckCpu(uint64_t cpu_time) {
148 if (cpu_limit_percentage_ == 0)
149 return;
150
151 // Add the cpu time to the ring buffer.
152 if (cpu_window_time_ticks_.Push(cpu_time)) {
153 // Compute the percentage over the whole window and check that it remains
154 // under the threshold.
155 uint64_t difference_ticks = cpu_window_time_ticks_.NewestWhenFull() -
156 cpu_window_time_ticks_.OldestWhenFull();
157 double window_interval_ticks =
158 (static_cast<double>(WindowTimeForRingBuffer(cpu_window_time_ticks_)) /
159 1000.0) *
160 sysconf(_SC_CLK_TCK);
161 double percentage = static_cast<double>(difference_ticks) /
162 static_cast<double>(window_interval_ticks) * 100;
163 if (percentage > cpu_limit_percentage_) {
164 kill(getpid(), SIGABRT);
165 }
166 }
167}
168
169void Watchdog::UpdateThreadStateUnlocked() {
170 if (cpu_limit_percentage_ > 0 || memory_limit_bytes_ > 0) {
171 StartThreadUnlocked();
172 } else if (cpu_limit_percentage_ == 0 && memory_limit_bytes_ == 0) {
173 QuitThreadUnlocked();
174 }
175}
176
177void Watchdog::StartThreadUnlocked() {
178 if (thread_.joinable()) {
179#if PERFETTO_DCHECK_IS_ON()
180 std::lock_guard<std::mutex> guard(mutex_);
181 PERFETTO_DCHECK(!quit_);
182#endif
183 } else {
184 // Don't need to lock because thread is not running.
185 PERFETTO_DCHECK(quit_);
186
187#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
188 PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
189 // Kick the thread to start running but only on Android or Linux.
190 quit_ = false;
191 thread_ = std::thread(&Watchdog::ThreadMain, this);
192#endif
193 }
194}
195
196void Watchdog::QuitThreadUnlocked() {
197 if (thread_.joinable()) {
198 {
199 std::lock_guard<std::mutex> guard(mutex_);
200 PERFETTO_DCHECK(!quit_);
201 quit_ = true;
202 }
203 exit_signal_.notify_one();
204 thread_.join();
205 thread_ = std::thread();
206 } else {
207 PERFETTO_DCHECK(quit_);
208 }
209}
210
211uint32_t Watchdog::WindowTimeForRingBuffer(const WindowedInterval& window) {
212 return static_cast<uint32_t>(window.size() - 1) * polling_interval_ms_;
213}
214
215bool Watchdog::WindowedInterval::Push(uint64_t sample) {
216 // Add the sample to the current position in the ring buffer.
217 buffer_[position_] = sample;
218
219 // Update the position with next one circularily.
220 position_ = (position_ + 1) % size_;
221
222 // Set the filled flag the first time we wrap.
223 filled_ = filled_ || position_ == 0;
224 return filled_;
225}
226
227double Watchdog::WindowedInterval::Mean() const {
228 return MeanForArray(buffer_.get(), size_);
229}
230
231void Watchdog::WindowedInterval::Clear() {
232 position_ = 0;
233 buffer_.reset(new uint64_t[size_]());
234}
235
236void Watchdog::WindowedInterval::Reset(size_t new_size) {
237 position_ = 0;
238 size_ = new_size;
239 buffer_.reset(new_size == 0 ? nullptr : new uint64_t[new_size]());
240}
241
242Watchdog::Timer::Timer(uint32_t ms) {
Florian Mayerf5b8d722018-02-01 13:12:17 +0000243 struct sigevent sev = {};
Florian Mayerb4334002018-02-01 11:10:36 +0000244 sev.sigev_notify = SIGEV_SIGNAL;
245 sev.sigev_signo = SIGABRT;
Florian Mayerb4334002018-02-01 11:10:36 +0000246 PERFETTO_CHECK(timer_create(CLOCK_MONOTONIC, &sev, &timerid_) != -1);
Florian Mayerf5b8d722018-02-01 13:12:17 +0000247 struct itimerspec its = {};
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000248 its.it_value.tv_sec = ms / 1000;
249 its.it_value.tv_nsec = 1000000L * (ms % 1000);
Florian Mayerb4334002018-02-01 11:10:36 +0000250 PERFETTO_CHECK(timer_settime(timerid_, 0, &its, nullptr) != -1);
251}
252
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000253Watchdog::Timer::~Timer() {
254 if (timerid_ != nullptr) {
255 timer_delete(timerid_);
256 }
257}
258
259Watchdog::Timer::Timer(Timer&& other) {
260 timerid_ = other.timerid_;
261 other.timerid_ = nullptr;
Florian Mayerb4334002018-02-01 11:10:36 +0000262}
263
264} // namespace base
265} // namespace perfetto