blob: c7188c1dced90287ef5260472d5331f25d85b39d [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>
Anna Zappone78fbbf32018-03-26 18:29:05 +010023#include <inttypes.h>
Florian Mayerb4334002018-02-01 11:10:36 +000024#include <signal.h>
25#include <stdint.h>
Lalit Maganti44ff2a72018-02-27 16:12:24 +000026#include <fstream>
27#include <thread>
Florian Mayerb4334002018-02-01 11:10:36 +000028
29namespace perfetto {
30namespace base {
31
Lalit Maganti44ff2a72018-02-27 16:12:24 +000032namespace {
33
Florian Mayer22e4b392018-03-08 10:20:11 +000034constexpr uint32_t kDefaultPollingInterval = 30 * 1000;
Lalit Maganti44ff2a72018-02-27 16:12:24 +000035
36bool IsMultipleOf(uint32_t number, uint32_t divisor) {
37 return number >= divisor && number % divisor == 0;
38}
39
Florian Mayer22e4b392018-03-08 10:20:11 +000040double MeanForArray(const uint64_t array[], size_t size) {
Lalit Maganti44ff2a72018-02-27 16:12:24 +000041 uint64_t total = 0;
42 for (size_t i = 0; i < size; i++) {
43 total += array[i];
44 }
45 return total / size;
46}
47
48} // namespace
49
50Watchdog::Watchdog(uint32_t polling_interval_ms)
51 : polling_interval_ms_(polling_interval_ms) {}
52
53Watchdog::~Watchdog() {
Lalit Magantie419ccb2018-03-06 11:48:03 +000054 if (!thread_.joinable()) {
55 PERFETTO_DCHECK(quit_);
56 return;
57 }
58
59 {
60 std::lock_guard<std::mutex> guard(mutex_);
61 PERFETTO_DCHECK(!quit_);
62 quit_ = true;
63 }
64 exit_signal_.notify_one();
65 thread_.join();
Lalit Maganti44ff2a72018-02-27 16:12:24 +000066}
67
68Watchdog* Watchdog::GetInstance() {
Lalit Magantie419ccb2018-03-06 11:48:03 +000069#if PERFETTO_BUILDFLAG(PERFETTO_CHROMIUM_BUILD)
70 // Ensure that it is impossible to use watchdog on a Chromium build.
71 PERFETTO_CHECK(false);
72#endif
73
Lalit Maganti44ff2a72018-02-27 16:12:24 +000074 static Watchdog* watchdog = new Watchdog(kDefaultPollingInterval);
75 return watchdog;
76}
77
78Watchdog::Timer Watchdog::CreateFatalTimer(uint32_t ms) {
79 return Watchdog::Timer(ms);
80}
81
Lalit Magantie419ccb2018-03-06 11:48:03 +000082void Watchdog::Start() {
83 std::lock_guard<std::mutex> guard(mutex_);
84 if (thread_.joinable()) {
85 PERFETTO_DCHECK(!quit_);
86 } else {
87 PERFETTO_DCHECK(quit_);
Lalit Maganti44ff2a72018-02-27 16:12:24 +000088
Lalit Magantie419ccb2018-03-06 11:48:03 +000089#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 quit_ = false;
93 thread_ = std::thread(&Watchdog::ThreadMain, this);
94#endif
Lalit Maganti44ff2a72018-02-27 16:12:24 +000095 }
Lalit Magantie419ccb2018-03-06 11:48:03 +000096}
97
98void Watchdog::SetMemoryLimit(uint32_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;
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000107}
108
109void Watchdog::SetCpuLimit(uint32_t percentage, uint32_t window_ms) {
Lalit Magantie419ccb2018-03-06 11:48:03 +0000110 std::lock_guard<std::mutex> guard(mutex_);
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000111
Lalit Magantie419ccb2018-03-06 11:48:03 +0000112 PERFETTO_CHECK(percentage <= 100);
113 PERFETTO_CHECK(IsMultipleOf(window_ms, polling_interval_ms_) ||
114 percentage == 0);
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000115
Lalit Magantie419ccb2018-03-06 11:48:03 +0000116 size_t size = percentage == 0 ? 0 : window_ms / polling_interval_ms_ + 1;
117 cpu_window_time_ticks_.Reset(size);
118 cpu_limit_percentage_ = percentage;
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000119}
120
121void Watchdog::ThreadMain() {
122 base::ScopedFile stat_fd(open("/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 (quit_)
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<uint32_t>(rss_pages) * base::kPageSize;
155
156 CheckMemory(rss_bytes);
157 CheckCpu(cpu_time);
158 }
159}
160
161void 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() > memory_limit_bytes_) {
Anna Zappone78fbbf32018-03-26 18:29:05 +0100169 PERFETTO_ELOG(
170 "Memory watchdog trigger. Memory window of %f bytes is above the "
171 "%" PRIu32 " bytes limit.",
172 memory_window_bytes_.Mean(), memory_limit_bytes_);
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000173 kill(getpid(), SIGABRT);
174 }
175 }
176}
177
178void 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 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_) {
Anna Zappone78fbbf32018-03-26 18:29:05 +0100195 PERFETTO_ELOG("CPU watchdog trigger. %f%% CPU use is above the %" PRIu32
196 "%% CPU limit.",
197 percentage, cpu_limit_percentage_);
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000198 kill(getpid(), SIGABRT);
199 }
200 }
201}
202
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000203uint32_t Watchdog::WindowTimeForRingBuffer(const WindowedInterval& window) {
204 return static_cast<uint32_t>(window.size() - 1) * polling_interval_ms_;
205}
206
207bool 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
219double Watchdog::WindowedInterval::Mean() const {
220 return MeanForArray(buffer_.get(), size_);
221}
222
223void Watchdog::WindowedInterval::Clear() {
224 position_ = 0;
225 buffer_.reset(new uint64_t[size_]());
226}
227
228void 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
234Watchdog::Timer::Timer(uint32_t ms) {
Florian Mayerf5b8d722018-02-01 13:12:17 +0000235 struct sigevent sev = {};
Florian Mayerb4334002018-02-01 11:10:36 +0000236 sev.sigev_notify = SIGEV_SIGNAL;
237 sev.sigev_signo = SIGABRT;
Florian Mayerb4334002018-02-01 11:10:36 +0000238 PERFETTO_CHECK(timer_create(CLOCK_MONOTONIC, &sev, &timerid_) != -1);
Florian Mayerf5b8d722018-02-01 13:12:17 +0000239 struct itimerspec its = {};
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000240 its.it_value.tv_sec = ms / 1000;
241 its.it_value.tv_nsec = 1000000L * (ms % 1000);
Florian Mayerb4334002018-02-01 11:10:36 +0000242 PERFETTO_CHECK(timer_settime(timerid_, 0, &its, nullptr) != -1);
243}
244
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000245Watchdog::Timer::~Timer() {
246 if (timerid_ != nullptr) {
247 timer_delete(timerid_);
248 }
249}
250
251Watchdog::Timer::Timer(Timer&& other) {
252 timerid_ = other.timerid_;
253 other.timerid_ = nullptr;
Florian Mayerb4334002018-02-01 11:10:36 +0000254}
255
256} // namespace base
257} // namespace perfetto