blob: d8c064cbcbc06e56fb03b4f0e0aae48b8425f565 [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()) {
68 PERFETTO_DCHECK(quit_);
69 return;
70 }
71
72 {
73 std::lock_guard<std::mutex> guard(mutex_);
74 PERFETTO_DCHECK(!quit_);
75 quit_ = true;
76 }
77 exit_signal_.notify_one();
78 thread_.join();
Lalit Maganti44ff2a72018-02-27 16:12:24 +000079}
80
81Watchdog* Watchdog::GetInstance() {
82 static Watchdog* watchdog = new Watchdog(kDefaultPollingInterval);
83 return watchdog;
84}
85
86Watchdog::Timer Watchdog::CreateFatalTimer(uint32_t ms) {
87 return Watchdog::Timer(ms);
88}
89
Lalit Magantie419ccb2018-03-06 11:48:03 +000090void Watchdog::Start() {
91 std::lock_guard<std::mutex> guard(mutex_);
92 if (thread_.joinable()) {
93 PERFETTO_DCHECK(!quit_);
94 } else {
95 PERFETTO_DCHECK(quit_);
Lalit Maganti44ff2a72018-02-27 16:12:24 +000096
Lalit Magantie419ccb2018-03-06 11:48:03 +000097#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
98 PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
99 // Kick the thread to start running but only on Android or Linux.
100 quit_ = false;
101 thread_ = std::thread(&Watchdog::ThreadMain, this);
102#endif
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000103 }
Lalit Magantie419ccb2018-03-06 11:48:03 +0000104}
105
Primiano Tucci3cbb10a2018-04-10 17:52:40 +0100106void Watchdog::SetMemoryLimit(uint64_t bytes, uint32_t window_ms) {
Lalit Magantie419ccb2018-03-06 11:48:03 +0000107 // Update the fields under the lock.
108 std::lock_guard<std::mutex> guard(mutex_);
109
110 PERFETTO_CHECK(IsMultipleOf(window_ms, polling_interval_ms_) || bytes == 0);
111
112 size_t size = bytes == 0 ? 0 : window_ms / polling_interval_ms_ + 1;
113 memory_window_bytes_.Reset(size);
114 memory_limit_bytes_ = bytes;
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000115}
116
117void Watchdog::SetCpuLimit(uint32_t percentage, uint32_t window_ms) {
Lalit Magantie419ccb2018-03-06 11:48:03 +0000118 std::lock_guard<std::mutex> guard(mutex_);
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000119
Lalit Magantie419ccb2018-03-06 11:48:03 +0000120 PERFETTO_CHECK(percentage <= 100);
121 PERFETTO_CHECK(IsMultipleOf(window_ms, polling_interval_ms_) ||
122 percentage == 0);
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000123
Lalit Magantie419ccb2018-03-06 11:48:03 +0000124 size_t size = percentage == 0 ? 0 : window_ms / polling_interval_ms_ + 1;
125 cpu_window_time_ticks_.Reset(size);
126 cpu_limit_percentage_ = percentage;
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000127}
128
129void Watchdog::ThreadMain() {
130 base::ScopedFile stat_fd(open("/proc/self/stat", O_RDONLY));
131 if (!stat_fd) {
132 PERFETTO_ELOG("Failed to open stat file to enforce resource limits.");
133 return;
134 }
135
136 std::unique_lock<std::mutex> guard(mutex_);
137 for (;;) {
138 exit_signal_.wait_for(guard,
139 std::chrono::milliseconds(polling_interval_ms_));
140 if (quit_)
141 return;
142
143 lseek(stat_fd.get(), 0, SEEK_SET);
144
145 char c[512];
146 if (read(stat_fd.get(), c, sizeof(c)) < 0) {
147 PERFETTO_ELOG("Failed to read stat file to enforce resource limits.");
148 return;
149 }
150 c[sizeof(c) - 1] = '\0';
151
152 unsigned long int utime = 0l;
153 unsigned long int stime = 0l;
154 long int rss_pages = -1l;
155 PERFETTO_CHECK(
156 sscanf(c,
157 "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %lu"
158 "%lu %*d %*d %*d %*d %*d %*d %*u %*u %ld",
159 &utime, &stime, &rss_pages) == 3);
160
161 uint64_t cpu_time = utime + stime;
Primiano Tucci3cbb10a2018-04-10 17:52:40 +0100162 uint64_t rss_bytes = static_cast<uint64_t>(rss_pages) * base::kPageSize;
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000163
164 CheckMemory(rss_bytes);
165 CheckCpu(cpu_time);
166 }
167}
168
169void Watchdog::CheckMemory(uint64_t rss_bytes) {
170 if (memory_limit_bytes_ == 0)
171 return;
172
173 // Add the current stat value to the ring buffer and check that the mean
174 // remains under our threshold.
175 if (memory_window_bytes_.Push(rss_bytes)) {
176 if (memory_window_bytes_.Mean() > memory_limit_bytes_) {
Anna Zappone78fbbf32018-03-26 18:29:05 +0100177 PERFETTO_ELOG(
178 "Memory watchdog trigger. Memory window of %f bytes is above the "
Primiano Tucci3cbb10a2018-04-10 17:52:40 +0100179 "%" PRIu64 " bytes limit.",
Anna Zappone78fbbf32018-03-26 18:29:05 +0100180 memory_window_bytes_.Mean(), memory_limit_bytes_);
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000181 kill(getpid(), SIGABRT);
182 }
183 }
184}
185
186void Watchdog::CheckCpu(uint64_t cpu_time) {
187 if (cpu_limit_percentage_ == 0)
188 return;
189
190 // Add the cpu time to the ring buffer.
191 if (cpu_window_time_ticks_.Push(cpu_time)) {
192 // Compute the percentage over the whole window and check that it remains
193 // under the threshold.
194 uint64_t difference_ticks = cpu_window_time_ticks_.NewestWhenFull() -
195 cpu_window_time_ticks_.OldestWhenFull();
196 double window_interval_ticks =
197 (static_cast<double>(WindowTimeForRingBuffer(cpu_window_time_ticks_)) /
198 1000.0) *
199 sysconf(_SC_CLK_TCK);
200 double percentage = static_cast<double>(difference_ticks) /
201 static_cast<double>(window_interval_ticks) * 100;
202 if (percentage > cpu_limit_percentage_) {
Anna Zappone78fbbf32018-03-26 18:29:05 +0100203 PERFETTO_ELOG("CPU watchdog trigger. %f%% CPU use is above the %" PRIu32
204 "%% CPU limit.",
205 percentage, cpu_limit_percentage_);
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000206 kill(getpid(), SIGABRT);
207 }
208 }
209}
210
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000211uint32_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
Hector Dearmana0eb1a42018-06-20 10:48:12 +0100259Watchdog::Timer::Timer(Timer&& other) noexcept {
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000260 timerid_ = other.timerid_;
261 other.timerid_ = nullptr;
Florian Mayerb4334002018-02-01 11:10:36 +0000262}
263
264} // namespace base
265} // namespace perfetto
Primiano Tucciee418772018-09-24 23:01:03 +0100266
267#endif // PERFETTO_OS_MACOSX