blob: 9a8965efb1b10fc8fc82f0c07376522707a968c7 [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 Tucci2c5488f2019-06-01 03:27:28 +010026#include "perfetto/ext/base/watchdog_posix.h"
Florian Mayerb4334002018-02-01 11:10:36 +000027
Lalit Maganti44ff2a72018-02-27 16:12:24 +000028#include <fcntl.h>
Anna Zappone78fbbf32018-03-26 18:29:05 +010029#include <inttypes.h>
Florian Mayerb4334002018-02-01 11:10:36 +000030#include <signal.h>
31#include <stdint.h>
Primiano Tucci5e6bc992018-12-12 15:04:36 +000032
Lalit Maganti44ff2a72018-02-27 16:12:24 +000033#include <fstream>
34#include <thread>
Florian Mayerb4334002018-02-01 11:10:36 +000035
Primiano Tucci5e6bc992018-12-12 15:04:36 +000036#include "perfetto/base/build_config.h"
37#include "perfetto/base/logging.h"
Primiano Tucci2c5488f2019-06-01 03:27:28 +010038#include "perfetto/ext/base/scoped_file.h"
39#include "perfetto/ext/base/thread_utils.h"
Primiano Tucci5e6bc992018-12-12 15:04:36 +000040
Primiano Tucci7c7f5f52019-01-15 21:47:04 +000041#if PERFETTO_BUILDFLAG(PERFETTO_EMBEDDER_BUILD)
42#error perfetto::base::Watchdog should not be used in Chromium or embedders
Primiano Tucci808d6df2018-03-31 13:24:18 +010043#endif
44
Florian Mayerb4334002018-02-01 11:10:36 +000045namespace perfetto {
46namespace base {
47
Lalit Maganti44ff2a72018-02-27 16:12:24 +000048namespace {
49
Florian Mayer22e4b392018-03-08 10:20:11 +000050constexpr uint32_t kDefaultPollingInterval = 30 * 1000;
Lalit Maganti44ff2a72018-02-27 16:12:24 +000051
52bool IsMultipleOf(uint32_t number, uint32_t divisor) {
53 return number >= divisor && number % divisor == 0;
54}
55
Florian Mayer22e4b392018-03-08 10:20:11 +000056double MeanForArray(const uint64_t array[], size_t size) {
Lalit Maganti44ff2a72018-02-27 16:12:24 +000057 uint64_t total = 0;
58 for (size_t i = 0; i < size; i++) {
59 total += array[i];
60 }
61 return total / size;
62}
63
64} // namespace
65
66Watchdog::Watchdog(uint32_t polling_interval_ms)
67 : polling_interval_ms_(polling_interval_ms) {}
68
69Watchdog::~Watchdog() {
Lalit Magantie419ccb2018-03-06 11:48:03 +000070 if (!thread_.joinable()) {
Primiano Tucci0d3bd5e2018-10-07 20:49:35 +020071 PERFETTO_DCHECK(!enabled_);
Lalit Magantie419ccb2018-03-06 11:48:03 +000072 return;
73 }
Primiano Tucci0d3bd5e2018-10-07 20:49:35 +020074 PERFETTO_DCHECK(enabled_);
75 enabled_ = false;
Lalit Magantie419ccb2018-03-06 11:48:03 +000076 exit_signal_.notify_one();
77 thread_.join();
Lalit Maganti44ff2a72018-02-27 16:12:24 +000078}
79
80Watchdog* Watchdog::GetInstance() {
81 static Watchdog* watchdog = new Watchdog(kDefaultPollingInterval);
82 return watchdog;
83}
84
85Watchdog::Timer Watchdog::CreateFatalTimer(uint32_t ms) {
Primiano Tucci0d3bd5e2018-10-07 20:49:35 +020086 if (!enabled_.load(std::memory_order_relaxed))
87 return Watchdog::Timer(0);
88
Lalit Maganti44ff2a72018-02-27 16:12:24 +000089 return Watchdog::Timer(ms);
90}
91
Lalit Magantie419ccb2018-03-06 11:48:03 +000092void Watchdog::Start() {
93 std::lock_guard<std::mutex> guard(mutex_);
94 if (thread_.joinable()) {
Primiano Tucci0d3bd5e2018-10-07 20:49:35 +020095 PERFETTO_DCHECK(enabled_);
Lalit Magantie419ccb2018-03-06 11:48:03 +000096 } else {
Primiano Tucci0d3bd5e2018-10-07 20:49:35 +020097 PERFETTO_DCHECK(!enabled_);
Lalit Maganti44ff2a72018-02-27 16:12:24 +000098
Lalit Magantie419ccb2018-03-06 11:48:03 +000099#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
100 PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
101 // Kick the thread to start running but only on Android or Linux.
Primiano Tucci0d3bd5e2018-10-07 20:49:35 +0200102 enabled_ = true;
Lalit Magantie419ccb2018-03-06 11:48:03 +0000103 thread_ = std::thread(&Watchdog::ThreadMain, this);
104#endif
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000105 }
Lalit Magantie419ccb2018-03-06 11:48:03 +0000106}
107
Primiano Tucci3cbb10a2018-04-10 17:52:40 +0100108void Watchdog::SetMemoryLimit(uint64_t bytes, uint32_t window_ms) {
Lalit Magantie419ccb2018-03-06 11:48:03 +0000109 // Update the fields under the lock.
110 std::lock_guard<std::mutex> guard(mutex_);
111
112 PERFETTO_CHECK(IsMultipleOf(window_ms, polling_interval_ms_) || bytes == 0);
113
114 size_t size = bytes == 0 ? 0 : window_ms / polling_interval_ms_ + 1;
115 memory_window_bytes_.Reset(size);
116 memory_limit_bytes_ = bytes;
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000117}
118
119void Watchdog::SetCpuLimit(uint32_t percentage, uint32_t window_ms) {
Lalit Magantie419ccb2018-03-06 11:48:03 +0000120 std::lock_guard<std::mutex> guard(mutex_);
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000121
Lalit Magantie419ccb2018-03-06 11:48:03 +0000122 PERFETTO_CHECK(percentage <= 100);
123 PERFETTO_CHECK(IsMultipleOf(window_ms, polling_interval_ms_) ||
124 percentage == 0);
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000125
Lalit Magantie419ccb2018-03-06 11:48:03 +0000126 size_t size = percentage == 0 ? 0 : window_ms / polling_interval_ms_ + 1;
127 cpu_window_time_ticks_.Reset(size);
128 cpu_limit_percentage_ = percentage;
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000129}
130
131void Watchdog::ThreadMain() {
Florian Mayerb03fd282018-10-03 16:05:16 +0100132 base::ScopedFile stat_fd(base::OpenFile("/proc/self/stat", O_RDONLY));
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000133 if (!stat_fd) {
134 PERFETTO_ELOG("Failed to open stat file to enforce resource limits.");
135 return;
136 }
137
138 std::unique_lock<std::mutex> guard(mutex_);
139 for (;;) {
140 exit_signal_.wait_for(guard,
141 std::chrono::milliseconds(polling_interval_ms_));
Primiano Tucci0d3bd5e2018-10-07 20:49:35 +0200142 if (!enabled_)
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000143 return;
144
145 lseek(stat_fd.get(), 0, SEEK_SET);
146
147 char c[512];
148 if (read(stat_fd.get(), c, sizeof(c)) < 0) {
149 PERFETTO_ELOG("Failed to read stat file to enforce resource limits.");
150 return;
151 }
152 c[sizeof(c) - 1] = '\0';
153
154 unsigned long int utime = 0l;
155 unsigned long int stime = 0l;
156 long int rss_pages = -1l;
157 PERFETTO_CHECK(
158 sscanf(c,
159 "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %lu"
160 "%lu %*d %*d %*d %*d %*d %*d %*u %*u %ld",
161 &utime, &stime, &rss_pages) == 3);
162
163 uint64_t cpu_time = utime + stime;
Primiano Tucci3cbb10a2018-04-10 17:52:40 +0100164 uint64_t rss_bytes = static_cast<uint64_t>(rss_pages) * base::kPageSize;
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000165
166 CheckMemory(rss_bytes);
167 CheckCpu(cpu_time);
168 }
169}
170
171void Watchdog::CheckMemory(uint64_t rss_bytes) {
172 if (memory_limit_bytes_ == 0)
173 return;
174
175 // Add the current stat value to the ring buffer and check that the mean
176 // remains under our threshold.
177 if (memory_window_bytes_.Push(rss_bytes)) {
178 if (memory_window_bytes_.Mean() > memory_limit_bytes_) {
Anna Zappone78fbbf32018-03-26 18:29:05 +0100179 PERFETTO_ELOG(
180 "Memory watchdog trigger. Memory window of %f bytes is above the "
Primiano Tucci3cbb10a2018-04-10 17:52:40 +0100181 "%" PRIu64 " bytes limit.",
Anna Zappone78fbbf32018-03-26 18:29:05 +0100182 memory_window_bytes_.Mean(), memory_limit_bytes_);
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000183 kill(getpid(), SIGABRT);
184 }
185 }
186}
187
188void Watchdog::CheckCpu(uint64_t cpu_time) {
189 if (cpu_limit_percentage_ == 0)
190 return;
191
192 // Add the cpu time to the ring buffer.
193 if (cpu_window_time_ticks_.Push(cpu_time)) {
194 // Compute the percentage over the whole window and check that it remains
195 // under the threshold.
196 uint64_t difference_ticks = cpu_window_time_ticks_.NewestWhenFull() -
197 cpu_window_time_ticks_.OldestWhenFull();
198 double window_interval_ticks =
199 (static_cast<double>(WindowTimeForRingBuffer(cpu_window_time_ticks_)) /
200 1000.0) *
201 sysconf(_SC_CLK_TCK);
202 double percentage = static_cast<double>(difference_ticks) /
203 static_cast<double>(window_interval_ticks) * 100;
204 if (percentage > cpu_limit_percentage_) {
Anna Zappone78fbbf32018-03-26 18:29:05 +0100205 PERFETTO_ELOG("CPU watchdog trigger. %f%% CPU use is above the %" PRIu32
206 "%% CPU limit.",
207 percentage, cpu_limit_percentage_);
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000208 kill(getpid(), SIGABRT);
209 }
210 }
211}
212
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000213uint32_t Watchdog::WindowTimeForRingBuffer(const WindowedInterval& window) {
214 return static_cast<uint32_t>(window.size() - 1) * polling_interval_ms_;
215}
216
217bool Watchdog::WindowedInterval::Push(uint64_t sample) {
218 // Add the sample to the current position in the ring buffer.
219 buffer_[position_] = sample;
220
221 // Update the position with next one circularily.
222 position_ = (position_ + 1) % size_;
223
224 // Set the filled flag the first time we wrap.
225 filled_ = filled_ || position_ == 0;
226 return filled_;
227}
228
229double Watchdog::WindowedInterval::Mean() const {
230 return MeanForArray(buffer_.get(), size_);
231}
232
233void Watchdog::WindowedInterval::Clear() {
234 position_ = 0;
235 buffer_.reset(new uint64_t[size_]());
236}
237
238void Watchdog::WindowedInterval::Reset(size_t new_size) {
239 position_ = 0;
240 size_ = new_size;
241 buffer_.reset(new_size == 0 ? nullptr : new uint64_t[new_size]());
242}
243
244Watchdog::Timer::Timer(uint32_t ms) {
Primiano Tucci0d3bd5e2018-10-07 20:49:35 +0200245 if (!ms)
246 return; // No-op timer created when the watchdog is disabled.
247
Florian Mayerf5b8d722018-02-01 13:12:17 +0000248 struct sigevent sev = {};
Primiano Tucci5e6bc992018-12-12 15:04:36 +0000249 sev.sigev_notify = SIGEV_THREAD_ID;
250 sev._sigev_un._tid = base::GetThreadId();
Florian Mayerb4334002018-02-01 11:10:36 +0000251 sev.sigev_signo = SIGABRT;
Florian Mayerb4334002018-02-01 11:10:36 +0000252 PERFETTO_CHECK(timer_create(CLOCK_MONOTONIC, &sev, &timerid_) != -1);
Florian Mayerf5b8d722018-02-01 13:12:17 +0000253 struct itimerspec its = {};
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000254 its.it_value.tv_sec = ms / 1000;
255 its.it_value.tv_nsec = 1000000L * (ms % 1000);
Florian Mayerb4334002018-02-01 11:10:36 +0000256 PERFETTO_CHECK(timer_settime(timerid_, 0, &its, nullptr) != -1);
257}
258
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000259Watchdog::Timer::~Timer() {
260 if (timerid_ != nullptr) {
261 timer_delete(timerid_);
262 }
263}
264
Hector Dearmana0eb1a42018-06-20 10:48:12 +0100265Watchdog::Timer::Timer(Timer&& other) noexcept {
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000266 timerid_ = other.timerid_;
267 other.timerid_ = nullptr;
Florian Mayerb4334002018-02-01 11:10:36 +0000268}
269
270} // namespace base
271} // namespace perfetto
Primiano Tucciee418772018-09-24 23:01:03 +0100272
273#endif // PERFETTO_OS_MACOSX