blob: 9864e7be5f63ac54dac8bece9f33df780c092a47 [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 Tucci808d6df2018-03-31 13:24:18 +010017#include "perfetto/base/watchdog_posix.h"
Florian Mayerb4334002018-02-01 11:10:36 +000018
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
Primiano Tucci808d6df2018-03-31 13:24:18 +010029#if PERFETTO_BUILDFLAG(PERFETTO_CHROMIUM_BUILD)
30#error perfetto::base::Watchdog should not be used in Chromium
31#endif
32
Florian Mayerb4334002018-02-01 11:10:36 +000033namespace perfetto {
34namespace base {
35
Lalit Maganti44ff2a72018-02-27 16:12:24 +000036namespace {
37
Florian Mayer22e4b392018-03-08 10:20:11 +000038constexpr uint32_t kDefaultPollingInterval = 30 * 1000;
Lalit Maganti44ff2a72018-02-27 16:12:24 +000039
40bool IsMultipleOf(uint32_t number, uint32_t divisor) {
41 return number >= divisor && number % divisor == 0;
42}
43
Florian Mayer22e4b392018-03-08 10:20:11 +000044double MeanForArray(const uint64_t array[], size_t size) {
Lalit Maganti44ff2a72018-02-27 16:12:24 +000045 uint64_t total = 0;
46 for (size_t i = 0; i < size; i++) {
47 total += array[i];
48 }
49 return total / size;
50}
51
52} // namespace
53
54Watchdog::Watchdog(uint32_t polling_interval_ms)
55 : polling_interval_ms_(polling_interval_ms) {}
56
57Watchdog::~Watchdog() {
Lalit Magantie419ccb2018-03-06 11:48:03 +000058 if (!thread_.joinable()) {
59 PERFETTO_DCHECK(quit_);
60 return;
61 }
62
63 {
64 std::lock_guard<std::mutex> guard(mutex_);
65 PERFETTO_DCHECK(!quit_);
66 quit_ = true;
67 }
68 exit_signal_.notify_one();
69 thread_.join();
Lalit Maganti44ff2a72018-02-27 16:12:24 +000070}
71
72Watchdog* Watchdog::GetInstance() {
73 static Watchdog* watchdog = new Watchdog(kDefaultPollingInterval);
74 return watchdog;
75}
76
77Watchdog::Timer Watchdog::CreateFatalTimer(uint32_t ms) {
78 return Watchdog::Timer(ms);
79}
80
Lalit Magantie419ccb2018-03-06 11:48:03 +000081void Watchdog::Start() {
82 std::lock_guard<std::mutex> guard(mutex_);
83 if (thread_.joinable()) {
84 PERFETTO_DCHECK(!quit_);
85 } else {
86 PERFETTO_DCHECK(quit_);
Lalit Maganti44ff2a72018-02-27 16:12:24 +000087
Lalit Magantie419ccb2018-03-06 11:48:03 +000088#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
89 PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
90 // Kick the thread to start running but only on Android or Linux.
91 quit_ = false;
92 thread_ = std::thread(&Watchdog::ThreadMain, this);
93#endif
Lalit Maganti44ff2a72018-02-27 16:12:24 +000094 }
Lalit Magantie419ccb2018-03-06 11:48:03 +000095}
96
Primiano Tucci3cbb10a2018-04-10 17:52:40 +010097void Watchdog::SetMemoryLimit(uint64_t bytes, uint32_t window_ms) {
Lalit Magantie419ccb2018-03-06 11:48:03 +000098 // Update the fields under the lock.
99 std::lock_guard<std::mutex> guard(mutex_);
100
101 PERFETTO_CHECK(IsMultipleOf(window_ms, polling_interval_ms_) || bytes == 0);
102
103 size_t size = bytes == 0 ? 0 : window_ms / polling_interval_ms_ + 1;
104 memory_window_bytes_.Reset(size);
105 memory_limit_bytes_ = bytes;
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000106}
107
108void Watchdog::SetCpuLimit(uint32_t percentage, uint32_t window_ms) {
Lalit Magantie419ccb2018-03-06 11:48:03 +0000109 std::lock_guard<std::mutex> guard(mutex_);
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000110
Lalit Magantie419ccb2018-03-06 11:48:03 +0000111 PERFETTO_CHECK(percentage <= 100);
112 PERFETTO_CHECK(IsMultipleOf(window_ms, polling_interval_ms_) ||
113 percentage == 0);
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000114
Lalit Magantie419ccb2018-03-06 11:48:03 +0000115 size_t size = percentage == 0 ? 0 : window_ms / polling_interval_ms_ + 1;
116 cpu_window_time_ticks_.Reset(size);
117 cpu_limit_percentage_ = percentage;
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000118}
119
120void Watchdog::ThreadMain() {
121 base::ScopedFile stat_fd(open("/proc/self/stat", O_RDONLY));
122 if (!stat_fd) {
123 PERFETTO_ELOG("Failed to open stat file to enforce resource limits.");
124 return;
125 }
126
127 std::unique_lock<std::mutex> guard(mutex_);
128 for (;;) {
129 exit_signal_.wait_for(guard,
130 std::chrono::milliseconds(polling_interval_ms_));
131 if (quit_)
132 return;
133
134 lseek(stat_fd.get(), 0, SEEK_SET);
135
136 char c[512];
137 if (read(stat_fd.get(), c, sizeof(c)) < 0) {
138 PERFETTO_ELOG("Failed to read stat file to enforce resource limits.");
139 return;
140 }
141 c[sizeof(c) - 1] = '\0';
142
143 unsigned long int utime = 0l;
144 unsigned long int stime = 0l;
145 long int rss_pages = -1l;
146 PERFETTO_CHECK(
147 sscanf(c,
148 "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %lu"
149 "%lu %*d %*d %*d %*d %*d %*d %*u %*u %ld",
150 &utime, &stime, &rss_pages) == 3);
151
152 uint64_t cpu_time = utime + stime;
Primiano Tucci3cbb10a2018-04-10 17:52:40 +0100153 uint64_t rss_bytes = static_cast<uint64_t>(rss_pages) * base::kPageSize;
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000154
155 CheckMemory(rss_bytes);
156 CheckCpu(cpu_time);
157 }
158}
159
160void Watchdog::CheckMemory(uint64_t rss_bytes) {
161 if (memory_limit_bytes_ == 0)
162 return;
163
164 // Add the current stat value to the ring buffer and check that the mean
165 // remains under our threshold.
166 if (memory_window_bytes_.Push(rss_bytes)) {
167 if (memory_window_bytes_.Mean() > memory_limit_bytes_) {
Anna Zappone78fbbf32018-03-26 18:29:05 +0100168 PERFETTO_ELOG(
169 "Memory watchdog trigger. Memory window of %f bytes is above the "
Primiano Tucci3cbb10a2018-04-10 17:52:40 +0100170 "%" PRIu64 " bytes limit.",
Anna Zappone78fbbf32018-03-26 18:29:05 +0100171 memory_window_bytes_.Mean(), memory_limit_bytes_);
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000172 kill(getpid(), SIGABRT);
173 }
174 }
175}
176
177void Watchdog::CheckCpu(uint64_t cpu_time) {
178 if (cpu_limit_percentage_ == 0)
179 return;
180
181 // Add the cpu time to the ring buffer.
182 if (cpu_window_time_ticks_.Push(cpu_time)) {
183 // Compute the percentage over the whole window and check that it remains
184 // under the threshold.
185 uint64_t difference_ticks = cpu_window_time_ticks_.NewestWhenFull() -
186 cpu_window_time_ticks_.OldestWhenFull();
187 double window_interval_ticks =
188 (static_cast<double>(WindowTimeForRingBuffer(cpu_window_time_ticks_)) /
189 1000.0) *
190 sysconf(_SC_CLK_TCK);
191 double percentage = static_cast<double>(difference_ticks) /
192 static_cast<double>(window_interval_ticks) * 100;
193 if (percentage > cpu_limit_percentage_) {
Anna Zappone78fbbf32018-03-26 18:29:05 +0100194 PERFETTO_ELOG("CPU watchdog trigger. %f%% CPU use is above the %" PRIu32
195 "%% CPU limit.",
196 percentage, cpu_limit_percentage_);
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000197 kill(getpid(), SIGABRT);
198 }
199 }
200}
201
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000202uint32_t Watchdog::WindowTimeForRingBuffer(const WindowedInterval& window) {
203 return static_cast<uint32_t>(window.size() - 1) * polling_interval_ms_;
204}
205
206bool Watchdog::WindowedInterval::Push(uint64_t sample) {
207 // Add the sample to the current position in the ring buffer.
208 buffer_[position_] = sample;
209
210 // Update the position with next one circularily.
211 position_ = (position_ + 1) % size_;
212
213 // Set the filled flag the first time we wrap.
214 filled_ = filled_ || position_ == 0;
215 return filled_;
216}
217
218double Watchdog::WindowedInterval::Mean() const {
219 return MeanForArray(buffer_.get(), size_);
220}
221
222void Watchdog::WindowedInterval::Clear() {
223 position_ = 0;
224 buffer_.reset(new uint64_t[size_]());
225}
226
227void Watchdog::WindowedInterval::Reset(size_t new_size) {
228 position_ = 0;
229 size_ = new_size;
230 buffer_.reset(new_size == 0 ? nullptr : new uint64_t[new_size]());
231}
232
233Watchdog::Timer::Timer(uint32_t ms) {
Florian Mayerf5b8d722018-02-01 13:12:17 +0000234 struct sigevent sev = {};
Florian Mayerb4334002018-02-01 11:10:36 +0000235 sev.sigev_notify = SIGEV_SIGNAL;
236 sev.sigev_signo = SIGABRT;
Florian Mayerb4334002018-02-01 11:10:36 +0000237 PERFETTO_CHECK(timer_create(CLOCK_MONOTONIC, &sev, &timerid_) != -1);
Florian Mayerf5b8d722018-02-01 13:12:17 +0000238 struct itimerspec its = {};
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000239 its.it_value.tv_sec = ms / 1000;
240 its.it_value.tv_nsec = 1000000L * (ms % 1000);
Florian Mayerb4334002018-02-01 11:10:36 +0000241 PERFETTO_CHECK(timer_settime(timerid_, 0, &its, nullptr) != -1);
242}
243
Lalit Maganti44ff2a72018-02-27 16:12:24 +0000244Watchdog::Timer::~Timer() {
245 if (timerid_ != nullptr) {
246 timer_delete(timerid_);
247 }
248}
249
250Watchdog::Timer::Timer(Timer&& other) {
251 timerid_ = other.timerid_;
252 other.timerid_ = nullptr;
Florian Mayerb4334002018-02-01 11:10:36 +0000253}
254
255} // namespace base
256} // namespace perfetto