1 /*
2 * Copyright (C) 2020 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 "src/profiling/common/profiler_guardrails.h"
18
19 #include <unistd.h>
20 #include <algorithm>
21 #include <optional>
22
23 #include "perfetto/ext/base/file_utils.h"
24 #include "perfetto/ext/base/scoped_file.h"
25 #include "perfetto/ext/base/watchdog_posix.h"
26
27 namespace perfetto {
28 namespace profiling {
29
GetCputimeSecForCurrentProcess()30 std::optional<uint64_t> GetCputimeSecForCurrentProcess() {
31 return GetCputimeSecForCurrentProcess(
32 base::OpenFile("/proc/self/stat", O_RDONLY));
33 }
34
GetCputimeSecForCurrentProcess(base::ScopedFile stat_fd)35 std::optional<uint64_t> GetCputimeSecForCurrentProcess(
36 base::ScopedFile stat_fd) {
37 if (!stat_fd)
38 return std::nullopt;
39 base::ProcStat stat;
40 if (!ReadProcStat(stat_fd.get(), &stat)) {
41 PERFETTO_ELOG("Failed to read stat file to enforce guardrails.");
42 return std::nullopt;
43 }
44 return (stat.utime + stat.stime) /
45 static_cast<unsigned long>(sysconf(_SC_CLK_TCK));
46 }
47
ProfilerMemoryGuardrails()48 ProfilerMemoryGuardrails::ProfilerMemoryGuardrails()
49 : ProfilerMemoryGuardrails(base::OpenFile("/proc/self/status", O_RDONLY)) {}
50
ProfilerMemoryGuardrails(base::ScopedFile status_fd)51 ProfilerMemoryGuardrails::ProfilerMemoryGuardrails(base::ScopedFile status_fd) {
52 std::string status;
53 if (base::ReadFileDescriptor(*status_fd, &status))
54 anon_and_swap_ = GetRssAnonAndSwap(status);
55
56 if (!anon_and_swap_) {
57 PERFETTO_ELOG("Failed to read memory usage.");
58 return;
59 }
60 }
61
IsOverMemoryThreshold(const GuardrailConfig & ds)62 bool ProfilerMemoryGuardrails::IsOverMemoryThreshold(
63 const GuardrailConfig& ds) {
64 uint32_t ds_max_mem = ds.memory_guardrail_kb;
65 if (!ds_max_mem || !anon_and_swap_)
66 return false;
67
68 if (ds_max_mem > 0 && *anon_and_swap_ > ds_max_mem) {
69 PERFETTO_ELOG("Exceeded data-source memory guardrail (%" PRIu32
70 " > %" PRIu32 "). Shutting down.",
71 *anon_and_swap_, ds_max_mem);
72 return true;
73 }
74 return false;
75 }
76
ProfilerCpuGuardrails()77 ProfilerCpuGuardrails::ProfilerCpuGuardrails() {
78 opt_cputime_sec_ = GetCputimeSecForCurrentProcess();
79 if (!opt_cputime_sec_) {
80 PERFETTO_ELOG("Failed to get CPU time.");
81 }
82 }
83
84 // For testing.
ProfilerCpuGuardrails(base::ScopedFile stat_fd)85 ProfilerCpuGuardrails::ProfilerCpuGuardrails(base::ScopedFile stat_fd) {
86 opt_cputime_sec_ = GetCputimeSecForCurrentProcess(std::move(stat_fd));
87 if (!opt_cputime_sec_) {
88 PERFETTO_ELOG("Failed to get CPU time.");
89 }
90 }
91
IsOverCpuThreshold(const GuardrailConfig & ds)92 bool ProfilerCpuGuardrails::IsOverCpuThreshold(const GuardrailConfig& ds) {
93 uint64_t ds_max_cpu = ds.cpu_guardrail_sec;
94 if (!ds_max_cpu || !opt_cputime_sec_)
95 return false;
96 uint64_t cputime_sec = *opt_cputime_sec_;
97
98 auto start_cputime_sec = ds.cpu_start_secs;
99 // We reject data-sources with CPU guardrails if we cannot read the
100 // initial value, which means we get a non-nullopt value here.
101 PERFETTO_CHECK(start_cputime_sec);
102 uint64_t cpu_diff = cputime_sec - *start_cputime_sec;
103 if (cputime_sec > *start_cputime_sec && cpu_diff > ds_max_cpu) {
104 PERFETTO_ELOG("Exceeded data-source CPU guardrail (%" PRIu64 " > %" PRIu64
105 "). Shutting down.",
106 cpu_diff, ds_max_cpu);
107 return true;
108 }
109 return false;
110 }
111
112 } // namespace profiling
113 } // namespace perfetto
114