blob: 82cd6ace4b42c121af7af36cfc4d7563eac5b99d [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
Primiano Tucciee418772018-09-24 23:01:03 +010017#include "perfetto/base/build_config.h"
18
19// Watchdog is currently not supported on Mac. This ifdef-based exclusion is
20// here only for the Mac build in AOSP. The standalone and chromium builds
21// exclude this file at the GN level. However, propagating the per-os exclusion
22// through our GN -> BP build file translator is not worth the effort for a
23// one-off case.
24#if !PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX)
25
Primiano Tucci808d6df2018-03-31 13:24:18 +010026#include "perfetto/base/watchdog_posix.h"
Florian Mayerb4334002018-02-01 11:10:36 +000027
28#include "perfetto/base/logging.h"
Lalit Maganti44ff2a72018-02-27 16:12:24 +000029#include "perfetto/base/scoped_file.h"
Florian Mayerb4334002018-02-01 11:10:36 +000030
Lalit Maganti44ff2a72018-02-27 16:12:24 +000031#include <fcntl.h>
Anna Zappone78fbbf32018-03-26 18:29:05 +010032#include <inttypes.h>
Florian Mayerb4334002018-02-01 11:10:36 +000033#include <signal.h>
34#include <stdint.h>
Lalit Maganti44ff2a72018-02-27 16:12:24 +000035#include <fstream>
36#include <thread>
Florian Mayerb4334002018-02-01 11:10:36 +000037
Primiano Tucci808d6df2018-03-31 13:24:18 +010038#if PERFETTO_BUILDFLAG(PERFETTO_CHROMIUM_BUILD)
39#error perfetto::base::Watchdog should not be used in Chromium
40#endif
41
Florian Mayerb4334002018-02-01 11:10:36 +000042namespace perfetto {
43namespace base {
44
Lalit Maganti44ff2a72018-02-27 16:12:24 +000045namespace {
46
Florian Mayer22e4b392018-03-08 10:20:11 +000047constexpr uint32_t kDefaultPollingInterval = 30 * 1000;
Lalit Maganti44ff2a72018-02-27 16:12:24 +000048
49bool IsMultipleOf(uint32_t number, uint32_t divisor) {
50 return number >= divisor && number % divisor == 0;
51}
52
Florian Mayer22e4b392018-03-08 10:20:11 +000053double MeanForArray(const uint64_t array[], size_t size) {
Lalit Maganti44ff2a72018-02-27 16:12:24 +000054 uint64_t total = 0;
55 for (size_t i = 0; i < size; i++) {
56 total += array[i];
57 }
58 return total / size;
59}
60
61} // namespace
62
63Watchdog::Watchdog(uint32_t polling_interval_ms)
64 : polling_interval_ms_(polling_interval_ms) {}
65
66Watchdog::~Watchdog() {
Lalit Magantie419ccb2018-03-06 11:48:03 +000067 if (!thread_.joinable()) {
Primiano Tucci0d3bd5e2018-10-07 20:49:35 +020068 PERFETTO_DCHECK(!enabled_);
Lalit Magantie419ccb2018-03-06 11:48:03 +000069 return;
70 }
Primiano Tucci0d3bd5e2018-10-07 20:49:35 +020071 PERFETTO_DCHECK(enabled_);
72 enabled_ = false;
Lalit Magantie419ccb2018-03-06 11:48:03 +000073 exit_signal_.notify_one();
74 thread_.join();
Lalit Maganti44ff2a72018-02-27 16:12:24 +000075}
76
77Watchdog* Watchdog::GetInstance() {
78 static Watchdog* watchdog = new Watchdog(kDefaultPollingInterval);
79 return watchdog;
80}
81
82Watchdog::Timer Watchdog::CreateFatalTimer(uint32_t ms) {
Primiano Tucci0d3bd5e2018-10-07 20:49:35 +020083 if (!enabled_.load(std::memory_order_relaxed))
84 return Watchdog::Timer(0);
85
Lalit Maganti44ff2a72018-02-27 16:12:24 +000086 return Watchdog::Timer(ms);
87}
88
Lalit Magantie419ccb2018-03-06 11:48:03 +000089void Watchdog::Start() {
90 std::lock_guard<std::mutex> guard(mutex_);
91 if (thread_.joinable()) {
Primiano Tucci0d3bd5e2018-10-07 20:49:35 +020092 PERFETTO_DCHECK(enabled_);
Lalit Magantie419ccb2018-03-06 11:48:03 +000093 } else {
Primiano Tucci0d3bd5e2018-10-07 20:49:35 +020094 PERFETTO_DCHECK(!enabled_);
Lalit Maganti44ff2a72018-02-27 16:12:24 +000095
Lalit Magantie419ccb2018-03-06 11:48:03 +000096#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
97 PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
98 // Kick the thread to start running but only on Android or Linux.
Primiano Tucci0d3bd5e2018-10-07 20:49:35 +020099 enabled_ = true;
Lalit Magantie419ccb2018-03-06 11:48:03 +0000100 thread_ = std::thread(&Watchdog::ThreadMain, this);
101#endif
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000102 }
Lalit Magantie419ccb2018-03-06 11:48:03 +0000103}
104
Primiano Tucci3cbb10a2018-04-10 17:52:40 +0100105void Watchdog::SetMemoryLimit(uint64_t bytes, uint32_t window_ms) {
Lalit Magantie419ccb2018-03-06 11:48:03 +0000106 // Update the fields under the lock.
107 std::lock_guard<std::mutex> guard(mutex_);
108
109 PERFETTO_CHECK(IsMultipleOf(window_ms, polling_interval_ms_) || bytes == 0);
110
111 size_t size = bytes == 0 ? 0 : window_ms / polling_interval_ms_ + 1;
112 memory_window_bytes_.Reset(size);
113 memory_limit_bytes_ = bytes;
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000114}
115
116void Watchdog::SetCpuLimit(uint32_t percentage, uint32_t window_ms) {
Lalit Magantie419ccb2018-03-06 11:48:03 +0000117 std::lock_guard<std::mutex> guard(mutex_);
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000118
Lalit Magantie419ccb2018-03-06 11:48:03 +0000119 PERFETTO_CHECK(percentage <= 100);
120 PERFETTO_CHECK(IsMultipleOf(window_ms, polling_interval_ms_) ||
121 percentage == 0);
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000122
Lalit Magantie419ccb2018-03-06 11:48:03 +0000123 size_t size = percentage == 0 ? 0 : window_ms / polling_interval_ms_ + 1;
124 cpu_window_time_ticks_.Reset(size);
125 cpu_limit_percentage_ = percentage;
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000126}
127
128void Watchdog::ThreadMain() {
Florian Mayerb03fd282018-10-03 16:05:16 +0100129 base::ScopedFile stat_fd(base::OpenFile("/proc/self/stat", O_RDONLY));
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000130 if (!stat_fd) {
131 PERFETTO_ELOG("Failed to open stat file to enforce resource limits.");
132 return;
133 }
134
135 std::unique_lock<std::mutex> guard(mutex_);
136 for (;;) {
137 exit_signal_.wait_for(guard,
138 std::chrono::milliseconds(polling_interval_ms_));
Primiano Tucci0d3bd5e2018-10-07 20:49:35 +0200139 if (!enabled_)
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000140 return;
141
142 lseek(stat_fd.get(), 0, SEEK_SET);
143
144 char c[512];
145 if (read(stat_fd.get(), c, sizeof(c)) < 0) {
146 PERFETTO_ELOG("Failed to read stat file to enforce resource limits.");
147 return;
148 }
149 c[sizeof(c) - 1] = '\0';
150
151 unsigned long int utime = 0l;
152 unsigned long int stime = 0l;
153 long int rss_pages = -1l;
154 PERFETTO_CHECK(
155 sscanf(c,
156 "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %lu"
157 "%lu %*d %*d %*d %*d %*d %*d %*u %*u %ld",
158 &utime, &stime, &rss_pages) == 3);
159
160 uint64_t cpu_time = utime + stime;
Primiano Tucci3cbb10a2018-04-10 17:52:40 +0100161 uint64_t rss_bytes = static_cast<uint64_t>(rss_pages) * base::kPageSize;
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000162
163 CheckMemory(rss_bytes);
164 CheckCpu(cpu_time);
165 }
166}
167
168void Watchdog::CheckMemory(uint64_t rss_bytes) {
169 if (memory_limit_bytes_ == 0)
170 return;
171
172 // Add the current stat value to the ring buffer and check that the mean
173 // remains under our threshold.
174 if (memory_window_bytes_.Push(rss_bytes)) {
175 if (memory_window_bytes_.Mean() > memory_limit_bytes_) {
Anna Zappone78fbbf32018-03-26 18:29:05 +0100176 PERFETTO_ELOG(
177 "Memory watchdog trigger. Memory window of %f bytes is above the "
Primiano Tucci3cbb10a2018-04-10 17:52:40 +0100178 "%" PRIu64 " bytes limit.",
Anna Zappone78fbbf32018-03-26 18:29:05 +0100179 memory_window_bytes_.Mean(), memory_limit_bytes_);
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000180 kill(getpid(), SIGABRT);
181 }
182 }
183}
184
185void Watchdog::CheckCpu(uint64_t cpu_time) {
186 if (cpu_limit_percentage_ == 0)
187 return;
188
189 // Add the cpu time to the ring buffer.
190 if (cpu_window_time_ticks_.Push(cpu_time)) {
191 // Compute the percentage over the whole window and check that it remains
192 // under the threshold.
193 uint64_t difference_ticks = cpu_window_time_ticks_.NewestWhenFull() -
194 cpu_window_time_ticks_.OldestWhenFull();
195 double window_interval_ticks =
196 (static_cast<double>(WindowTimeForRingBuffer(cpu_window_time_ticks_)) /
197 1000.0) *
198 sysconf(_SC_CLK_TCK);
199 double percentage = static_cast<double>(difference_ticks) /
200 static_cast<double>(window_interval_ticks) * 100;
201 if (percentage > cpu_limit_percentage_) {
Anna Zappone78fbbf32018-03-26 18:29:05 +0100202 PERFETTO_ELOG("CPU watchdog trigger. %f%% CPU use is above the %" PRIu32
203 "%% CPU limit.",
204 percentage, cpu_limit_percentage_);
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000205 kill(getpid(), SIGABRT);
206 }
207 }
208}
209
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000210uint32_t Watchdog::WindowTimeForRingBuffer(const WindowedInterval& window) {
211 return static_cast<uint32_t>(window.size() - 1) * polling_interval_ms_;
212}
213
214bool Watchdog::WindowedInterval::Push(uint64_t sample) {
215 // Add the sample to the current position in the ring buffer.
216 buffer_[position_] = sample;
217
218 // Update the position with next one circularily.
219 position_ = (position_ + 1) % size_;
220
221 // Set the filled flag the first time we wrap.
222 filled_ = filled_ || position_ == 0;
223 return filled_;
224}
225
226double Watchdog::WindowedInterval::Mean() const {
227 return MeanForArray(buffer_.get(), size_);
228}
229
230void Watchdog::WindowedInterval::Clear() {
231 position_ = 0;
232 buffer_.reset(new uint64_t[size_]());
233}
234
235void Watchdog::WindowedInterval::Reset(size_t new_size) {
236 position_ = 0;
237 size_ = new_size;
238 buffer_.reset(new_size == 0 ? nullptr : new uint64_t[new_size]());
239}
240
241Watchdog::Timer::Timer(uint32_t ms) {
Primiano Tucci0d3bd5e2018-10-07 20:49:35 +0200242 if (!ms)
243 return; // No-op timer created when the watchdog is disabled.
244
Florian Mayerf5b8d722018-02-01 13:12:17 +0000245 struct sigevent sev = {};
Florian Mayerb4334002018-02-01 11:10:36 +0000246 sev.sigev_notify = SIGEV_SIGNAL;
247 sev.sigev_signo = SIGABRT;
Florian Mayerb4334002018-02-01 11:10:36 +0000248 PERFETTO_CHECK(timer_create(CLOCK_MONOTONIC, &sev, &timerid_) != -1);
Florian Mayerf5b8d722018-02-01 13:12:17 +0000249 struct itimerspec its = {};
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000250 its.it_value.tv_sec = ms / 1000;
251 its.it_value.tv_nsec = 1000000L * (ms % 1000);
Florian Mayerb4334002018-02-01 11:10:36 +0000252 PERFETTO_CHECK(timer_settime(timerid_, 0, &its, nullptr) != -1);
253}
254
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000255Watchdog::Timer::~Timer() {
256 if (timerid_ != nullptr) {
257 timer_delete(timerid_);
258 }
259}
260
Hector Dearmana0eb1a42018-06-20 10:48:12 +0100261Watchdog::Timer::Timer(Timer&& other) noexcept {
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000262 timerid_ = other.timerid_;
263 other.timerid_ = nullptr;
Florian Mayerb4334002018-02-01 11:10:36 +0000264}
265
266} // namespace base
267} // namespace perfetto
Primiano Tucciee418772018-09-24 23:01:03 +0100268
269#endif // PERFETTO_OS_MACOSX